Browse Source

Merge branch 'develop-evmcc' into pr-jit

cl-refactor
Paweł Bylica 10 years ago
parent
commit
14a919e5aa
  1. 13
      evmcc/evmcc.cpp
  2. 24
      libevm/VM.h
  3. 8
      libevmface/Instruction.cpp
  4. 19
      libevmface/Instruction.h
  5. 36
      libevmjit/Arith256.cpp
  6. 2
      libevmjit/BasicBlock.cpp
  7. 270
      libevmjit/Compiler.cpp
  8. 69
      libevmjit/Compiler.h
  9. 4
      libevmjit/Endianness.cpp
  10. 40
      libevmjit/Ext.cpp
  11. 11
      libevmjit/GasMeter.cpp
  12. 4
      libevmjit/GasMeter.h
  13. 32
      libevmjit/Memory.cpp
  14. 5
      libevmjit/Runtime.cpp
  15. 10
      libevmjit/Stack.cpp
  16. 10
      libevmjit/Type.cpp
  17. 2
      libevmjit/Type.h
  18. 3
      libevmjit/VM.cpp
  19. 2
      liblll/Assembly.cpp
  20. 14
      liblll/CodeFragment.cpp
  21. 215
      libserpent/opcodes.h
  22. 61
      libsolidity/AST.cpp
  23. 163
      libsolidity/AST.h
  24. 30
      libsolidity/ASTPrinter.cpp
  25. 5
      libsolidity/ASTPrinter.h
  26. 12
      libsolidity/ASTVisitor.h
  27. 6
      libsolidity/BaseTypes.h
  28. 401
      libsolidity/Compiler.cpp
  29. 146
      libsolidity/Compiler.h
  30. 21
      libsolidity/NameAndTypeResolver.cpp
  31. 23
      libsolidity/NameAndTypeResolver.h
  32. 13
      libsolidity/Parser.h
  33. 45
      libsolidity/Scanner.cpp
  34. 40
      libsolidity/Scanner.h
  35. 4
      libsolidity/Scope.cpp
  36. 10
      libsolidity/Scope.h
  37. 36
      libsolidity/SourceReferenceFormatter.cpp
  38. 20
      libsolidity/Token.h
  39. 92
      libsolidity/Types.cpp
  40. 68
      libsolidity/Types.h
  41. 43
      solc/main.cpp
  42. 229
      test/solidityCompiler.cpp
  43. 2
      test/solidityNameAndTypeResolution.cpp
  44. 2
      test/solidityParser.cpp
  45. 8
      test/vm.cpp
  46. 12
      test/vmArithmeticTestFiller.json
  47. 336
      test/vmBitwiseLogicOperationTestFiller.json

13
evmcc/evmcc.cpp

@ -101,7 +101,10 @@ int main(int argc, char** argv)
{ {
auto compilationStartTime = std::chrono::high_resolution_clock::now(); auto compilationStartTime = std::chrono::high_resolution_clock::now();
auto compiler = eth::jit::Compiler(); eth::jit::Compiler::Options options;
options.dumpCFG = opt_dump_graph;
auto compiler = eth::jit::Compiler(options);
auto module = compiler.compile({bytecode.data(), bytecode.size()}); auto module = compiler.compile({bytecode.data(), bytecode.size()});
auto compilationEndTime = std::chrono::high_resolution_clock::now(); auto compilationEndTime = std::chrono::high_resolution_clock::now();
@ -115,14 +118,6 @@ int main(int argc, char** argv)
<< std::endl; << std::endl;
} }
if (opt_dump_graph)
{
std::ofstream ofs("blocks.dot");
compiler.dumpBasicBlockGraph(ofs);
ofs.close();
std::cout << "Basic blocks graph written to block.dot\n";
}
if (opt_interpret) if (opt_interpret)
{ {
auto engine = eth::jit::ExecutionEngine(); auto engine = eth::jit::ExecutionEngine();

24
libevm/VM.h

@ -240,8 +240,8 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
case Instruction::PUSH31: case Instruction::PUSH31:
case Instruction::PUSH32: case Instruction::PUSH32:
break; break;
case Instruction::BNOT:
case Instruction::NOT: case Instruction::NOT:
case Instruction::ISZERO:
case Instruction::CALLDATALOAD: case Instruction::CALLDATALOAD:
case Instruction::EXTCODESIZE: case Instruction::EXTCODESIZE:
case Instruction::POP: case Instruction::POP:
@ -373,7 +373,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(2) << 256); m_stack.back() = (u256)boost::multiprecision::powm((bigint)base, (bigint)expon, bigint(2) << 256);
break; break;
} }
case Instruction::BNOT: case Instruction::NOT:
m_stack.back() = ~m_stack.back(); m_stack.back() = ~m_stack.back();
break; break;
case Instruction::LT: case Instruction::LT:
@ -396,7 +396,7 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack[m_stack.size() - 2] = m_stack.back() == m_stack[m_stack.size() - 2] ? 1 : 0; m_stack[m_stack.size() - 2] = m_stack.back() == m_stack[m_stack.size() - 2] ? 1 : 0;
m_stack.pop_back(); m_stack.pop_back();
break; break;
case Instruction::NOT: case Instruction::ISZERO:
m_stack.back() = m_stack.back() ? 0 : 1; m_stack.back() = m_stack.back() ? 0 : 1;
break; break;
case Instruction::AND: case Instruction::AND:
@ -484,12 +484,12 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
{ {
unsigned mf = (unsigned)m_stack.back(); unsigned mf = (unsigned)m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned cf = (unsigned)m_stack.back(); u256 cf = m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned l = (unsigned)m_stack.back(); unsigned l = (unsigned)m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned el = cf + l > _ext.data.size() ? _ext.data.size() < cf ? 0 : _ext.data.size() - cf : l; unsigned el = cf + l > (u256)_ext.data.size() ? (u256)_ext.data.size() < cf ? 0 : _ext.data.size() - (unsigned)cf : l;
memcpy(m_temp.data() + mf, _ext.data.data() + cf, el); memcpy(m_temp.data() + mf, _ext.data.data() + (unsigned)cf, el);
memset(m_temp.data() + mf + el, 0, l - el); memset(m_temp.data() + mf + el, 0, l - el);
break; break;
} }
@ -500,12 +500,12 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
{ {
unsigned mf = (unsigned)m_stack.back(); unsigned mf = (unsigned)m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned cf = (unsigned)m_stack.back(); u256 cf = (u256)m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned l = (unsigned)m_stack.back(); unsigned l = (unsigned)m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned el = cf + l > _ext.code.size() ? _ext.code.size() < cf ? 0 : _ext.code.size() - cf : l; unsigned el = cf + l > (u256)_ext.code.size() ? (u256)_ext.code.size() < cf ? 0 : _ext.code.size() - (unsigned)cf : l;
memcpy(m_temp.data() + mf, _ext.code.data() + cf, el); memcpy(m_temp.data() + mf, _ext.code.data() + (unsigned)cf, el);
memset(m_temp.data() + mf + el, 0, l - el); memset(m_temp.data() + mf + el, 0, l - el);
break; break;
} }
@ -518,12 +518,12 @@ template <class Ext> dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con
m_stack.pop_back(); m_stack.pop_back();
unsigned mf = (unsigned)m_stack.back(); unsigned mf = (unsigned)m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned cf = (unsigned)m_stack.back(); u256 cf = m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned l = (unsigned)m_stack.back(); unsigned l = (unsigned)m_stack.back();
m_stack.pop_back(); m_stack.pop_back();
unsigned el = cf + l > _ext.codeAt(a).size() ? _ext.codeAt(a).size() < cf ? 0 : _ext.codeAt(a).size() - cf : l; unsigned el = cf + l > (u256)_ext.codeAt(a).size() ? (u256)_ext.codeAt(a).size() < cf ? 0 : _ext.codeAt(a).size() - (unsigned)cf : l;
memcpy(m_temp.data() + mf, _ext.codeAt(a).data() + cf, el); memcpy(m_temp.data() + mf, _ext.codeAt(a).data() + (unsigned)cf, el);
memset(m_temp.data() + mf + el, 0, l - el); memset(m_temp.data() + mf + el, 0, l - el);
break; break;
} }

8
libevmface/Instruction.cpp

@ -39,13 +39,13 @@ const std::map<std::string, Instruction> dev::eth::c_instructions =
{ "MOD", Instruction::MOD }, { "MOD", Instruction::MOD },
{ "SMOD", Instruction::SMOD }, { "SMOD", Instruction::SMOD },
{ "EXP", Instruction::EXP }, { "EXP", Instruction::EXP },
{ "BNOT", Instruction::BNOT }, { "BNOT", Instruction::NOT },
{ "LT", Instruction::LT }, { "LT", Instruction::LT },
{ "GT", Instruction::GT }, { "GT", Instruction::GT },
{ "SLT", Instruction::SLT }, { "SLT", Instruction::SLT },
{ "SGT", Instruction::SGT }, { "SGT", Instruction::SGT },
{ "EQ", Instruction::EQ }, { "EQ", Instruction::EQ },
{ "NOT", Instruction::NOT }, { "NOT", Instruction::ISZERO },
{ "AND", Instruction::AND }, { "AND", Instruction::AND },
{ "OR", Instruction::OR }, { "OR", Instruction::OR },
{ "XOR", Instruction::XOR }, { "XOR", Instruction::XOR },
@ -172,13 +172,13 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::MOD, { "MOD", 0, 2, 1 } }, { Instruction::MOD, { "MOD", 0, 2, 1 } },
{ Instruction::SMOD, { "SMOD", 0, 2, 1 } }, { Instruction::SMOD, { "SMOD", 0, 2, 1 } },
{ Instruction::EXP, { "EXP", 0, 2, 1 } }, { Instruction::EXP, { "EXP", 0, 2, 1 } },
{ Instruction::BNOT, { "BNOT", 0, 1, 1 } }, { Instruction::NOT, { "BNOT", 0, 1, 1 } },
{ Instruction::LT, { "LT", 0, 2, 1 } }, { Instruction::LT, { "LT", 0, 2, 1 } },
{ Instruction::GT, { "GT", 0, 2, 1 } }, { Instruction::GT, { "GT", 0, 2, 1 } },
{ Instruction::SLT, { "SLT", 0, 2, 1 } }, { Instruction::SLT, { "SLT", 0, 2, 1 } },
{ Instruction::SGT, { "SGT", 0, 2, 1 } }, { Instruction::SGT, { "SGT", 0, 2, 1 } },
{ Instruction::EQ, { "EQ", 0, 2, 1 } }, { Instruction::EQ, { "EQ", 0, 2, 1 } },
{ Instruction::NOT, { "NOT", 0, 1, 1 } }, { Instruction::ISZERO, { "NOT", 0, 1, 1 } },
{ Instruction::AND, { "AND", 0, 2, 1 } }, { Instruction::AND, { "AND", 0, 2, 1 } },
{ Instruction::OR, { "OR", 0, 2, 1 } }, { Instruction::OR, { "OR", 0, 2, 1 } },
{ Instruction::XOR, { "XOR", 0, 2, 1 } }, { Instruction::XOR, { "XOR", 0, 2, 1 } },

19
libevmface/Instruction.h

@ -43,22 +43,23 @@ enum class Instruction: uint8_t
SDIV, ///< signed integer division operation SDIV, ///< signed integer division operation
MOD, ///< modulo remainder operation MOD, ///< modulo remainder operation
SMOD, ///< signed modulo remainder operation SMOD, ///< signed modulo remainder operation
ADDMOD, ///< unsigned modular addition
MULMOD, ///< unsigned modular multiplication
EXP, ///< exponential operation EXP, ///< exponential operation
BNOT, ///< bitwise not SIGNEXTEND, ///< extend length of signed integer
LT, ///< less-than comparision
LT = 0x10, ///< less-than comparision
GT, ///< greater-than comparision GT, ///< greater-than comparision
SLT, ///< signed less-than comparision SLT, ///< signed less-than comparision
SGT, ///< signed greater-than comparision SGT, ///< signed greater-than comparision
EQ, ///< equality comparision EQ, ///< equality comparision
NOT, ///< simple not operator ISZERO, ///< simple not operator
AND, ///< bitwise AND operation
AND = 0x10, ///< bitwise AND operation
OR, ///< bitwise OR operation OR, ///< bitwise OR operation
XOR, ///< bitwise XOR operation XOR, ///< bitwise XOR operation
NOT, ///< bitwise NOT opertation
BYTE, ///< retrieve single byte from word BYTE, ///< retrieve single byte from word
ADDMOD, ///< unsigned modular addition
MULMOD, ///< unsigned modular multiplication
SIGNEXTEND, ///< extend length of signed integer
SHA3 = 0x20, ///< compute SHA3-256 hash SHA3 = 0x20, ///< compute SHA3-256 hash
ADDRESS = 0x30, ///< get address of currently executing account ADDRESS = 0x30, ///< get address of currently executing account
@ -83,7 +84,7 @@ enum class Instruction: uint8_t
GASLIMIT, ///< get the block's gas limit GASLIMIT, ///< get the block's gas limit
POP = 0x50, ///< remove item from stack POP = 0x50, ///< remove item from stack
MLOAD = 0x53, ///< load word from memory MLOAD, ///< load word from memory
MSTORE, ///< save word to memory MSTORE, ///< save word to memory
MSTORE8, ///< save byte to memory MSTORE8, ///< save byte to memory
SLOAD, ///< load word from storage SLOAD, ///< load word from storage

36
libevmjit/Arith256.cpp

@ -18,10 +18,10 @@ Arith256::Arith256(llvm::IRBuilder<>& _builder) :
{ {
using namespace llvm; using namespace llvm;
m_result = m_builder.CreateAlloca(Type::i256, nullptr, "arith.result"); m_result = m_builder.CreateAlloca(Type::Word, nullptr, "arith.result");
m_arg1 = m_builder.CreateAlloca(Type::i256, nullptr, "arith.arg1"); m_arg1 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg1");
m_arg2 = m_builder.CreateAlloca(Type::i256, nullptr, "arith.arg2"); m_arg2 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg2");
m_arg3 = m_builder.CreateAlloca(Type::i256, nullptr, "arith.arg3"); m_arg3 = m_builder.CreateAlloca(Type::Word, nullptr, "arith.arg3");
using Linkage = GlobalValue::LinkageTypes; using Linkage = GlobalValue::LinkageTypes;
@ -103,55 +103,55 @@ extern "C"
using namespace dev::eth::jit; using namespace dev::eth::jit;
EXPORT void arith_mul(i256* _arg1, i256* _arg2, i256* _result) EXPORT void arith_mul(i256* _arg1, i256* _arg2, i256* o_result)
{ {
dev::u256 arg1 = llvm2eth(*_arg1); dev::u256 arg1 = llvm2eth(*_arg1);
dev::u256 arg2 = llvm2eth(*_arg2); dev::u256 arg2 = llvm2eth(*_arg2);
*_result = eth2llvm(arg1 * arg2); *o_result = eth2llvm(arg1 * arg2);
} }
EXPORT void arith_div(i256* _arg1, i256* _arg2, i256* _result) EXPORT void arith_div(i256* _arg1, i256* _arg2, i256* o_result)
{ {
dev::u256 arg1 = llvm2eth(*_arg1); dev::u256 arg1 = llvm2eth(*_arg1);
dev::u256 arg2 = llvm2eth(*_arg2); dev::u256 arg2 = llvm2eth(*_arg2);
*_result = eth2llvm(arg2 == 0 ? arg2 : arg1 / arg2); *o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 / arg2);
} }
EXPORT void arith_mod(i256* _arg1, i256* _arg2, i256* _result) EXPORT void arith_mod(i256* _arg1, i256* _arg2, i256* o_result)
{ {
dev::u256 arg1 = llvm2eth(*_arg1); dev::u256 arg1 = llvm2eth(*_arg1);
dev::u256 arg2 = llvm2eth(*_arg2); dev::u256 arg2 = llvm2eth(*_arg2);
*_result = eth2llvm(arg2 == 0 ? arg2 : arg1 % arg2); *o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 % arg2);
} }
EXPORT void arith_sdiv(i256* _arg1, i256* _arg2, i256* _result) EXPORT void arith_sdiv(i256* _arg1, i256* _arg2, i256* o_result)
{ {
dev::u256 arg1 = llvm2eth(*_arg1); dev::u256 arg1 = llvm2eth(*_arg1);
dev::u256 arg2 = llvm2eth(*_arg2); dev::u256 arg2 = llvm2eth(*_arg2);
*_result = eth2llvm(arg2 == 0 ? arg2 : dev::s2u(dev::u2s(arg1) / dev::u2s(arg2))); *o_result = eth2llvm(arg2 == 0 ? arg2 : dev::s2u(dev::u2s(arg1) / dev::u2s(arg2)));
} }
EXPORT void arith_smod(i256* _arg1, i256* _arg2, i256* _result) EXPORT void arith_smod(i256* _arg1, i256* _arg2, i256* o_result)
{ {
dev::u256 arg1 = llvm2eth(*_arg1); dev::u256 arg1 = llvm2eth(*_arg1);
dev::u256 arg2 = llvm2eth(*_arg2); dev::u256 arg2 = llvm2eth(*_arg2);
*_result = eth2llvm(arg2 == 0 ? arg2 : dev::s2u(dev::u2s(arg1) % dev::u2s(arg2))); *o_result = eth2llvm(arg2 == 0 ? arg2 : dev::s2u(dev::u2s(arg1) % dev::u2s(arg2)));
} }
EXPORT void arith_mulmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* _result) EXPORT void arith_mulmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result)
{ {
dev::u256 arg1 = llvm2eth(*_arg1); dev::u256 arg1 = llvm2eth(*_arg1);
dev::u256 arg2 = llvm2eth(*_arg2); dev::u256 arg2 = llvm2eth(*_arg2);
dev::u256 arg3 = llvm2eth(*_arg3); dev::u256 arg3 = llvm2eth(*_arg3);
*_result = eth2llvm(dev::u256((dev::bigint(arg1) * dev::bigint(arg2)) % arg3)); *o_result = eth2llvm(dev::u256((dev::bigint(arg1) * dev::bigint(arg2)) % arg3));
} }
EXPORT void arith_addmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* _result) EXPORT void arith_addmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result)
{ {
dev::u256 arg1 = llvm2eth(*_arg1); dev::u256 arg1 = llvm2eth(*_arg1);
dev::u256 arg2 = llvm2eth(*_arg2); dev::u256 arg2 = llvm2eth(*_arg2);
dev::u256 arg3 = llvm2eth(*_arg3); dev::u256 arg3 = llvm2eth(*_arg3);
*_result = eth2llvm(dev::u256((dev::bigint(arg1) + dev::bigint(arg2)) % arg3)); *o_result = eth2llvm(dev::u256((dev::bigint(arg1) + dev::bigint(arg2)) % arg3));
} }
} }

2
libevmjit/BasicBlock.cpp

@ -171,7 +171,7 @@ llvm::Value* BasicBlock::LocalStack::get(size_t _index)
assert(m_initialStack[initialIdx] == nullptr); assert(m_initialStack[initialIdx] == nullptr);
// Create a dummy value. // Create a dummy value.
std::string name = "get_" + boost::lexical_cast<std::string>(_index); std::string name = "get_" + boost::lexical_cast<std::string>(_index);
m_initialStack[initialIdx] = m_builder.CreatePHI(Type::i256, 0, name); m_initialStack[initialIdx] = m_builder.CreatePHI(Type::Word, 0, name);
*itemIter = m_initialStack[initialIdx]; *itemIter = m_initialStack[initialIdx];
} }

270
libevmjit/Compiler.cpp

@ -32,28 +32,27 @@ namespace eth
namespace jit namespace jit
{ {
Compiler::Compiler(): Compiler::Compiler(Options const& _options):
m_builder(llvm::getGlobalContext()), m_options(_options),
m_jumpTableBlock(), m_builder(llvm::getGlobalContext())
m_badJumpBlock()
{ {
Type::init(m_builder.getContext()); Type::init(m_builder.getContext());
} }
void Compiler::createBasicBlocks(bytesConstRef bytecode) void Compiler::createBasicBlocks(bytesConstRef _bytecode)
{ {
std::set<ProgramCounter> splitPoints; // Sorted collections of instruction indices where basic blocks start/end std::set<ProgramCounter> splitPoints; // Sorted collections of instruction indices where basic blocks start/end
std::map<ProgramCounter, ProgramCounter> directJumpTargets; std::map<ProgramCounter, ProgramCounter> directJumpTargets;
std::vector<ProgramCounter> indirectJumpTargets; std::vector<ProgramCounter> indirectJumpTargets;
boost::dynamic_bitset<> validJumpTargets(std::max(bytecode.size(), size_t(1))); boost::dynamic_bitset<> validJumpTargets(std::max(_bytecode.size(), size_t(1)));
splitPoints.insert(0); // First basic block splitPoints.insert(0); // First basic block
validJumpTargets[0] = true; validJumpTargets[0] = true;
for (auto curr = bytecode.begin(); curr != bytecode.end(); ++curr) for (auto curr = _bytecode.begin(); curr != _bytecode.end(); ++curr)
{ {
ProgramCounter currentPC = curr - bytecode.begin(); ProgramCounter currentPC = curr - _bytecode.begin();
validJumpTargets[currentPC] = true; validJumpTargets[currentPC] = true;
auto inst = Instruction(*curr); auto inst = Instruction(*curr);
@ -62,19 +61,19 @@ void Compiler::createBasicBlocks(bytesConstRef bytecode)
case Instruction::ANY_PUSH: case Instruction::ANY_PUSH:
{ {
auto val = readPushData(curr, bytecode.end()); auto val = readPushData(curr, _bytecode.end());
auto next = curr + 1; auto next = curr + 1;
if (next == bytecode.end()) if (next == _bytecode.end())
break; break;
auto nextInst = Instruction(*next); auto nextInst = Instruction(*next);
if (nextInst == Instruction::JUMP || nextInst == Instruction::JUMPI) if (nextInst == Instruction::JUMP || nextInst == Instruction::JUMPI)
{ {
// Create a block for the JUMP target. // Create a block for the JUMP target.
ProgramCounter targetPC = val < bytecode.size() ? val.convert_to<ProgramCounter>() : bytecode.size(); ProgramCounter targetPC = val < _bytecode.size() ? val.convert_to<ProgramCounter>() : _bytecode.size();
splitPoints.insert(targetPC); splitPoints.insert(targetPC);
ProgramCounter jumpPC = (next - bytecode.begin()); ProgramCounter jumpPC = (next - _bytecode.begin());
directJumpTargets[jumpPC] = targetPC; directJumpTargets[jumpPC] = targetPC;
} }
break; break;
@ -83,7 +82,7 @@ void Compiler::createBasicBlocks(bytesConstRef bytecode)
case Instruction::JUMPDEST: case Instruction::JUMPDEST:
{ {
// A basic block starts at the next instruction. // A basic block starts at the next instruction.
if (currentPC + 1 < bytecode.size()) if (currentPC + 1 < _bytecode.size())
{ {
splitPoints.insert(currentPC + 1); splitPoints.insert(currentPC + 1);
indirectJumpTargets.push_back(currentPC + 1); indirectJumpTargets.push_back(currentPC + 1);
@ -98,7 +97,7 @@ void Compiler::createBasicBlocks(bytesConstRef bytecode)
case Instruction::SUICIDE: case Instruction::SUICIDE:
{ {
// Create a basic block starting at the following instruction. // Create a basic block starting at the following instruction.
if (curr + 1 < bytecode.end()) if (curr + 1 < _bytecode.end())
{ {
splitPoints.insert(currentPC + 1); splitPoints.insert(currentPC + 1);
} }
@ -113,7 +112,7 @@ void Compiler::createBasicBlocks(bytesConstRef bytecode)
// Remove split points generated from jumps out of code or into data. // Remove split points generated from jumps out of code or into data.
for (auto it = splitPoints.cbegin(); it != splitPoints.cend();) for (auto it = splitPoints.cbegin(); it != splitPoints.cend();)
{ {
if (*it > bytecode.size() || !validJumpTargets[*it]) if (*it > _bytecode.size() || !validJumpTargets[*it])
it = splitPoints.erase(it); it = splitPoints.erase(it);
else else
++it; ++it;
@ -123,7 +122,7 @@ void Compiler::createBasicBlocks(bytesConstRef bytecode)
{ {
auto beginInstIdx = *it; auto beginInstIdx = *it;
++it; ++it;
auto endInstIdx = it != splitPoints.cend() ? *it : bytecode.size(); auto endInstIdx = it != splitPoints.cend() ? *it : _bytecode.size();
basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginInstIdx), std::forward_as_tuple(beginInstIdx, endInstIdx, m_mainFunc, m_builder)); basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginInstIdx), std::forward_as_tuple(beginInstIdx, endInstIdx, m_mainFunc, m_builder));
} }
@ -133,7 +132,7 @@ void Compiler::createBasicBlocks(bytesConstRef bytecode)
for (auto it = directJumpTargets.cbegin(); it != directJumpTargets.cend(); ++it) for (auto it = directJumpTargets.cbegin(); it != directJumpTargets.cend(); ++it)
{ {
if (it->second >= bytecode.size()) if (it->second >= _bytecode.size())
{ {
// Jumping out of code means STOP // Jumping out of code means STOP
m_directJumpTargets[it->first] = m_stopBB; m_directJumpTargets[it->first] = m_stopBB;
@ -154,12 +153,10 @@ void Compiler::createBasicBlocks(bytesConstRef bytecode)
} }
for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it) for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it)
{
m_indirectJumpTargets.push_back(&basicBlocks.find(*it)->second); m_indirectJumpTargets.push_back(&basicBlocks.find(*it)->second);
}
} }
std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode) std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef _bytecode)
{ {
auto module = std::unique_ptr<llvm::Module>(new llvm::Module("main", m_builder.getContext())); auto module = std::unique_ptr<llvm::Module>(new llvm::Module("main", m_builder.getContext()));
@ -173,7 +170,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), "entry", m_mainFunc); auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), "entry", m_mainFunc);
m_builder.SetInsertPoint(entryBlock); m_builder.SetInsertPoint(entryBlock);
createBasicBlocks(bytecode); createBasicBlocks(_bytecode);
// Init runtime structures. // Init runtime structures.
RuntimeManager runtimeManager(m_builder); RuntimeManager runtimeManager(m_builder);
@ -191,13 +188,11 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
auto iterCopy = basicBlockPairIt; auto iterCopy = basicBlockPairIt;
++iterCopy; ++iterCopy;
auto nextBasicBlock = (iterCopy != basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; auto nextBasicBlock = (iterCopy != basicBlocks.end()) ? iterCopy->second.llvm() : nullptr;
compileBasicBlock(basicBlock, bytecode, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock); compileBasicBlock(basicBlock, _bytecode, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock);
} }
// Code for special blocks: // Code for special blocks:
// TODO: move to separate function. // TODO: move to separate function.
// Note: Right now the codegen for special blocks depends only on createBasicBlock(),
// not on the codegen for 'regular' blocks. But it has to be done before linkBasicBlocks().
m_builder.SetInsertPoint(m_stopBB); m_builder.SetInsertPoint(m_stopBB);
m_builder.CreateRet(Constant::get(ReturnCode::Stop)); m_builder.CreateRet(Constant::get(ReturnCode::Stop));
@ -218,75 +213,59 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
} }
} }
else else
{
m_builder.CreateBr(m_badJumpBlock->llvm()); m_builder.CreateBr(m_badJumpBlock->llvm());
}
removeDeadBlocks(); removeDeadBlocks();
if (getenv("EVMCC_DEBUG_BLOCKS")) dumpCFGifRequired("blocks-init.dot");
{
std::ofstream ofs("blocks-init.dot");
dumpBasicBlockGraph(ofs);
ofs.close();
std::cerr << "\n\nAfter dead block elimination \n\n";
dump();
}
//if (getenv("EVMCC_OPTIMIZE_STACK")) if (m_options.optimizeStack)
//{ {
std::vector<BasicBlock*> blockList; std::vector<BasicBlock*> blockList;
for (auto& entry : basicBlocks) for (auto& entry : basicBlocks)
blockList.push_back(&entry.second); blockList.push_back(&entry.second);
if (m_jumpTableBlock != nullptr) if (m_jumpTableBlock)
blockList.push_back(m_jumpTableBlock.get()); blockList.push_back(m_jumpTableBlock.get());
BasicBlock::linkLocalStacks(blockList, m_builder); BasicBlock::linkLocalStacks(blockList, m_builder);
if (getenv("EVMCC_DEBUG_BLOCKS")) dumpCFGifRequired("blocks-opt.dot");
{ }
std::ofstream ofs("blocks-opt.dot");
dumpBasicBlockGraph(ofs);
ofs.close();
std::cerr << "\n\nAfter stack optimization \n\n";
dump();
}
//}
for (auto& entry : basicBlocks) for (auto& entry : basicBlocks)
entry.second.localStack().synchronize(stack); entry.second.localStack().synchronize(stack);
if (m_jumpTableBlock != nullptr) if (m_jumpTableBlock)
m_jumpTableBlock->localStack().synchronize(stack); m_jumpTableBlock->localStack().synchronize(stack);
if (getenv("EVMCC_DEBUG_BLOCKS")) dumpCFGifRequired("blocks-sync.dot");
if (m_jumpTableBlock && m_options.rewriteSwitchToBranches)
{ {
std::ofstream ofs("blocks-sync.dot"); llvm::FunctionPassManager fpManager(module.get());
dumpBasicBlockGraph(ofs); fpManager.add(llvm::createLowerSwitchPass());
ofs.close(); fpManager.doInitialization();
std::cerr << "\n\nAfter stack synchronization \n\n"; fpManager.run(*m_mainFunc);
dump();
} }
llvm::FunctionPassManager fpManager(module.get());
fpManager.add(llvm::createLowerSwitchPass());
fpManager.doInitialization();
fpManager.run(*m_mainFunc);
return module; return module;
} }
void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode, RuntimeManager& _runtimeManager, Arith256& arith, Memory& memory, Ext& ext, GasMeter& gasMeter, llvm::BasicBlock* nextBasicBlock) void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytesConstRef _bytecode, RuntimeManager& _runtimeManager,
Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock)
{ {
m_builder.SetInsertPoint(basicBlock.llvm()); if (!_nextBasicBlock) // this is the last block in the code
auto& stack = basicBlock.localStack(); _nextBasicBlock = m_stopBB;
m_builder.SetInsertPoint(_basicBlock.llvm());
auto& stack = _basicBlock.localStack();
for (auto currentPC = basicBlock.begin(); currentPC != basicBlock.end(); ++currentPC) for (auto currentPC = _basicBlock.begin(); currentPC != _basicBlock.end(); ++currentPC)
{ {
auto inst = static_cast<Instruction>(bytecode[currentPC]); auto inst = static_cast<Instruction>(_bytecode[currentPC]);
gasMeter.count(inst); _gasMeter.count(inst);
switch (inst) switch (inst)
{ {
@ -313,7 +292,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
{ {
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res = arith.mul(lhs, rhs); auto res = _arith.mul(lhs, rhs);
stack.push(res); stack.push(res);
break; break;
} }
@ -322,7 +301,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
{ {
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res = arith.div(lhs, rhs); auto res = _arith.div(lhs, rhs);
stack.push(res); stack.push(res);
break; break;
} }
@ -331,7 +310,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
{ {
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res = arith.sdiv(lhs, rhs); auto res = _arith.sdiv(lhs, rhs);
stack.push(res); stack.push(res);
break; break;
} }
@ -340,7 +319,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
{ {
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res = arith.mod(lhs, rhs); auto res = _arith.mod(lhs, rhs);
stack.push(res); stack.push(res);
break; break;
} }
@ -349,7 +328,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
{ {
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res = arith.smod(lhs, rhs); auto res = _arith.smod(lhs, rhs);
stack.push(res); stack.push(res);
break; break;
} }
@ -358,12 +337,12 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
{ {
auto left = stack.pop(); auto left = stack.pop();
auto right = stack.pop(); auto right = stack.pop();
auto ret = ext.exp(left, right); auto ret = _ext.exp(left, right);
stack.push(ret); stack.push(ret);
break; break;
} }
case Instruction::BNOT: case Instruction::NOT:
{ {
auto value = stack.pop(); auto value = stack.pop();
auto ret = m_builder.CreateXor(value, Constant::get(-1), "bnot"); auto ret = m_builder.CreateXor(value, Constant::get(-1), "bnot");
@ -376,7 +355,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res1 = m_builder.CreateICmpULT(lhs, rhs); auto res1 = m_builder.CreateICmpULT(lhs, rhs);
auto res256 = m_builder.CreateZExt(res1, Type::i256); auto res256 = m_builder.CreateZExt(res1, Type::Word);
stack.push(res256); stack.push(res256);
break; break;
} }
@ -386,7 +365,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res1 = m_builder.CreateICmpUGT(lhs, rhs); auto res1 = m_builder.CreateICmpUGT(lhs, rhs);
auto res256 = m_builder.CreateZExt(res1, Type::i256); auto res256 = m_builder.CreateZExt(res1, Type::Word);
stack.push(res256); stack.push(res256);
break; break;
} }
@ -396,7 +375,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res1 = m_builder.CreateICmpSLT(lhs, rhs); auto res1 = m_builder.CreateICmpSLT(lhs, rhs);
auto res256 = m_builder.CreateZExt(res1, Type::i256); auto res256 = m_builder.CreateZExt(res1, Type::Word);
stack.push(res256); stack.push(res256);
break; break;
} }
@ -406,7 +385,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res1 = m_builder.CreateICmpSGT(lhs, rhs); auto res1 = m_builder.CreateICmpSGT(lhs, rhs);
auto res256 = m_builder.CreateZExt(res1, Type::i256); auto res256 = m_builder.CreateZExt(res1, Type::Word);
stack.push(res256); stack.push(res256);
break; break;
} }
@ -416,16 +395,16 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res1 = m_builder.CreateICmpEQ(lhs, rhs); auto res1 = m_builder.CreateICmpEQ(lhs, rhs);
auto res256 = m_builder.CreateZExt(res1, Type::i256); auto res256 = m_builder.CreateZExt(res1, Type::Word);
stack.push(res256); stack.push(res256);
break; break;
} }
case Instruction::NOT: case Instruction::ISZERO:
{ {
auto top = stack.pop(); auto top = stack.pop();
auto iszero = m_builder.CreateICmpEQ(top, Constant::get(0), "iszero"); auto iszero = m_builder.CreateICmpEQ(top, Constant::get(0), "iszero");
auto result = m_builder.CreateZExt(iszero, Type::i256); auto result = m_builder.CreateZExt(iszero, Type::Word);
stack.push(result); stack.push(result);
break; break;
} }
@ -462,16 +441,14 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
const auto byteNum = stack.pop(); const auto byteNum = stack.pop();
auto value = stack.pop(); auto value = stack.pop();
//
value = Endianness::toBE(m_builder, value); value = Endianness::toBE(m_builder, value);
auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes");
auto byte = m_builder.CreateExtractElement(bytes, byteNum, "byte"); auto byte = m_builder.CreateExtractElement(bytes, byteNum, "byte");
value = m_builder.CreateZExt(byte, Type::i256); value = m_builder.CreateZExt(byte, Type::Word);
auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32)); auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32));
value = m_builder.CreateSelect(byteNumValid, value, Constant::get(0)); value = m_builder.CreateSelect(byteNumValid, value, Constant::get(0));
stack.push(value); stack.push(value);
break; break;
} }
@ -480,7 +457,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto mod = stack.pop(); auto mod = stack.pop();
auto res = arith.addmod(lhs, rhs, mod); auto res = _arith.addmod(lhs, rhs, mod);
stack.push(res); stack.push(res);
break; break;
} }
@ -490,7 +467,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto mod = stack.pop(); auto mod = stack.pop();
auto res = arith.mulmod(lhs, rhs, mod); auto res = _arith.mulmod(lhs, rhs, mod);
stack.push(res); stack.push(res);
break; break;
} }
@ -501,7 +478,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto word = stack.pop(); auto word = stack.pop();
auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32"); auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32");
auto k32 = m_builder.CreateZExt(k32_, Type::i256); auto k32 = m_builder.CreateZExt(k32_, Type::Word);
auto k32x8 = m_builder.CreateMul(k32, Constant::get(8), "kx8"); auto k32x8 = m_builder.CreateMul(k32, Constant::get(8), "kx8");
// test for word >> (k * 8 + 7) // test for word >> (k * 8 + 7)
@ -512,16 +489,15 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto mask_ = m_builder.CreateShl(Constant::get(1), bitpos); auto mask_ = m_builder.CreateShl(Constant::get(1), bitpos);
auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask");
auto negmask = m_builder.CreateXor(mask, llvm::ConstantInt::getAllOnesValue(Type::i256), "negmask"); auto negmask = m_builder.CreateXor(mask, llvm::ConstantInt::getAllOnesValue(Type::Word), "negmask");
auto val1 = m_builder.CreateOr(word, negmask); auto val1 = m_builder.CreateOr(word, negmask);
auto val0 = m_builder.CreateAnd(word, mask); auto val0 = m_builder.CreateAnd(word, mask);
auto kInRange = m_builder.CreateICmpULE(idx, llvm::ConstantInt::get(Type::i256, 30)); auto kInRange = m_builder.CreateICmpULE(idx, llvm::ConstantInt::get(Type::Word, 30));
auto result = m_builder.CreateSelect(kInRange, auto result = m_builder.CreateSelect(kInRange,
m_builder.CreateSelect(bittest, val1, val0), m_builder.CreateSelect(bittest, val1, val0),
word); word);
stack.push(result); stack.push(result);
break; break;
} }
@ -529,8 +505,8 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
{ {
auto inOff = stack.pop(); auto inOff = stack.pop();
auto inSize = stack.pop(); auto inSize = stack.pop();
memory.require(inOff, inSize); _memory.require(inOff, inSize);
auto hash = ext.sha3(inOff, inSize); auto hash = _ext.sha3(inOff, inSize);
stack.push(hash); stack.push(hash);
break; break;
} }
@ -543,9 +519,9 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
case Instruction::ANY_PUSH: case Instruction::ANY_PUSH:
{ {
auto curr = bytecode.begin() + currentPC; // TODO: replace currentPC with iterator auto curr = _bytecode.begin() + currentPC; // TODO: replace currentPC with iterator
auto value = readPushData(curr, bytecode.end()); auto value = readPushData(curr, _bytecode.end());
currentPC = curr - bytecode.begin(); currentPC = curr - _bytecode.begin();
stack.push(Constant::get(value)); stack.push(Constant::get(value));
break; break;
@ -568,7 +544,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
case Instruction::MLOAD: case Instruction::MLOAD:
{ {
auto addr = stack.pop(); auto addr = stack.pop();
auto word = memory.loadWord(addr); auto word = _memory.loadWord(addr);
stack.push(word); stack.push(word);
break; break;
} }
@ -577,7 +553,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
{ {
auto addr = stack.pop(); auto addr = stack.pop();
auto word = stack.pop(); auto word = stack.pop();
memory.storeWord(addr, word); _memory.storeWord(addr, word);
break; break;
} }
@ -585,13 +561,13 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
{ {
auto addr = stack.pop(); auto addr = stack.pop();
auto word = stack.pop(); auto word = stack.pop();
memory.storeByte(addr, word); _memory.storeByte(addr, word);
break; break;
} }
case Instruction::MSIZE: case Instruction::MSIZE:
{ {
auto word = memory.getSize(); auto word = _memory.getSize();
stack.push(word); stack.push(word);
break; break;
} }
@ -599,7 +575,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
case Instruction::SLOAD: case Instruction::SLOAD:
{ {
auto index = stack.pop(); auto index = stack.pop();
auto value = ext.store(index); auto value = _ext.store(index);
stack.push(value); stack.push(value);
break; break;
} }
@ -608,8 +584,8 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
{ {
auto index = stack.pop(); auto index = stack.pop();
auto value = stack.pop(); auto value = stack.pop();
gasMeter.countSStore(ext, index, value); _gasMeter.countSStore(_ext, index, value);
ext.setStore(index, value); _ext.setStore(index, value);
break; break;
} }
@ -621,7 +597,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
// 2. m_directJumpTargets[currentPC] is defined (meaning that the previous instruction is a PUSH) // 2. m_directJumpTargets[currentPC] is defined (meaning that the previous instruction is a PUSH)
// Otherwise generate a indirect jump (a switch). // Otherwise generate a indirect jump (a switch).
llvm::BasicBlock* targetBlock = nullptr; llvm::BasicBlock* targetBlock = nullptr;
if (currentPC != basicBlock.begin()) if (currentPC != _basicBlock.begin())
{ {
auto pairIter = m_directJumpTargets.find(currentPC); auto pairIter = m_directJumpTargets.find(currentPC);
if (pairIter != m_directJumpTargets.end()) if (pairIter != m_directJumpTargets.end())
@ -647,17 +623,13 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto zero = Constant::get(0); auto zero = Constant::get(0);
auto cond = m_builder.CreateICmpNE(val, zero, "nonzero"); auto cond = m_builder.CreateICmpNE(val, zero, "nonzero");
if (!nextBasicBlock) // In case JUMPI is the last instruction
nextBasicBlock = m_stopBB;
if (targetBlock) if (targetBlock)
{ {
stack.pop(); stack.pop();
m_builder.CreateCondBr(cond, targetBlock, nextBasicBlock); m_builder.CreateCondBr(cond, targetBlock, _nextBasicBlock);
} }
else else
m_builder.CreateCondBr(cond, m_jumpTableBlock->llvm(), nextBasicBlock); m_builder.CreateCondBr(cond, m_jumpTableBlock->llvm(), _nextBasicBlock);
} }
break; break;
@ -699,7 +671,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
case Instruction::BALANCE: case Instruction::BALANCE:
{ {
auto address = stack.pop(); auto address = stack.pop();
auto value = ext.balance(address); auto value = _ext.balance(address);
stack.push(value); stack.push(value);
break; break;
} }
@ -707,7 +679,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
case Instruction::EXTCODESIZE: case Instruction::EXTCODESIZE:
{ {
auto addr = stack.pop(); auto addr = stack.pop();
auto value = ext.codesizeAt(addr); auto value = _ext.codesizeAt(addr);
stack.push(value); stack.push(value);
break; break;
} }
@ -721,7 +693,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto srcPtr = _runtimeManager.getCallData(); auto srcPtr = _runtimeManager.getCallData();
auto srcSize = _runtimeManager.get(RuntimeData::CallDataSize); auto srcSize = _runtimeManager.get(RuntimeData::CallDataSize);
memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes);
break; break;
} }
@ -734,7 +706,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, feature #80814234 auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, feature #80814234
auto srcSize = _runtimeManager.get(RuntimeData::CodeSize); auto srcSize = _runtimeManager.get(RuntimeData::CodeSize);
memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes);
break; break;
} }
@ -745,17 +717,17 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto srcIdx = stack.pop(); auto srcIdx = stack.pop();
auto reqBytes = stack.pop(); auto reqBytes = stack.pop();
auto srcPtr = ext.codeAt(extAddr); auto srcPtr = _ext.codeAt(extAddr);
auto srcSize = ext.codesizeAt(extAddr); auto srcSize = _ext.codesizeAt(extAddr);
memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes);
break; break;
} }
case Instruction::CALLDATALOAD: case Instruction::CALLDATALOAD:
{ {
auto index = stack.pop(); auto index = stack.pop();
auto value = ext.calldataload(index); auto value = _ext.calldataload(index);
stack.push(value); stack.push(value);
break; break;
} }
@ -765,9 +737,9 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto endowment = stack.pop(); auto endowment = stack.pop();
auto initOff = stack.pop(); auto initOff = stack.pop();
auto initSize = stack.pop(); auto initSize = stack.pop();
memory.require(initOff, initSize); _memory.require(initOff, initSize);
auto address = ext.create(endowment, initOff, initSize); auto address = _ext.create(endowment, initOff, initSize);
stack.push(address); stack.push(address);
break; break;
} }
@ -783,18 +755,18 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto outOff = stack.pop(); auto outOff = stack.pop();
auto outSize = stack.pop(); auto outSize = stack.pop();
gasMeter.commitCostBlock(gas); _gasMeter.commitCostBlock(gas);
// Require memory for in and out buffers // Require memory for in and out buffers
memory.require(outOff, outSize); // Out buffer first as we guess it will be after the in one _memory.require(outOff, outSize); // Out buffer first as we guess it will be after the in one
memory.require(inOff, inSize); _memory.require(inOff, inSize);
auto receiveAddress = codeAddress; auto receiveAddress = codeAddress;
if (inst == Instruction::CALLCODE) if (inst == Instruction::CALLCODE)
receiveAddress = _runtimeManager.get(RuntimeData::Address); receiveAddress = _runtimeManager.get(RuntimeData::Address);
auto ret = ext.call(gas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress); auto ret = _ext.call(gas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress);
gasMeter.giveBack(gas); _gasMeter.giveBack(gas);
stack.push(ret); stack.push(ret);
break; break;
} }
@ -804,7 +776,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
auto index = stack.pop(); auto index = stack.pop();
auto size = stack.pop(); auto size = stack.pop();
memory.require(index, size); _memory.require(index, size);
_runtimeManager.registerReturnData(index, size); _runtimeManager.registerReturnData(index, size);
m_builder.CreateRet(Constant::get(ReturnCode::Return)); m_builder.CreateRet(Constant::get(ReturnCode::Return));
@ -812,13 +784,14 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
} }
case Instruction::SUICIDE: case Instruction::SUICIDE:
{
auto address = stack.pop();
ext.suicide(address);
// Fall through
}
case Instruction::STOP: case Instruction::STOP:
{ {
if (inst == Instruction::SUICIDE)
{
auto address = stack.pop();
_ext.suicide(address);
}
m_builder.CreateRet(Constant::get(ReturnCode::Stop)); m_builder.CreateRet(Constant::get(ReturnCode::Stop));
break; break;
} }
@ -831,15 +804,11 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
} }
} }
gasMeter.commitCostBlock(); _gasMeter.commitCostBlock();
if (!basicBlock.llvm()->getTerminator()) // If block not terminated // Block may have no terminator if the next instruction is a jump destination.
{ if (!_basicBlock.llvm()->getTerminator())
if (nextBasicBlock) m_builder.CreateBr(_nextBasicBlock);
m_builder.CreateBr(nextBasicBlock); // Branch to the next block
else
m_builder.CreateRet(Constant::get(ReturnCode::Stop)); // Return STOP code
}
} }
@ -874,11 +843,22 @@ void Compiler::removeDeadBlocks()
} }
} }
void Compiler::dumpBasicBlockGraph(std::ostream& out) void Compiler::dumpCFGifRequired(std::string const& _dotfilePath)
{
if (! m_options.dumpCFG)
return;
// TODO: handle i/o failures
std::ofstream ofs(_dotfilePath);
dumpCFGtoStream(ofs);
ofs.close();
}
void Compiler::dumpCFGtoStream(std::ostream& _out)
{ {
out << "digraph BB {\n" _out << "digraph BB {\n"
<< " node [shape=record, fontname=Courier, fontsize=10];\n" << " node [shape=record, fontname=Courier, fontsize=10];\n"
<< " entry [share=record, label=\"entry block\"];\n"; << " entry [share=record, label=\"entry block\"];\n";
std::vector<BasicBlock*> blocks; std::vector<BasicBlock*> blocks;
for (auto& pair : basicBlocks) for (auto& pair : basicBlocks)
@ -898,7 +878,7 @@ void Compiler::dumpBasicBlockGraph(std::ostream& out)
std::ostringstream oss; std::ostringstream oss;
bb->dump(oss, true); bb->dump(oss, true);
out << " \"" << blockName << "\" [shape=record, label=\" { " << blockName << "|" << oss.str() << "} \"];\n"; _out << " \"" << blockName << "\" [shape=record, label=\" { " << blockName << "|" << oss.str() << "} \"];\n";
} }
// Output edges // Output edges
@ -909,15 +889,13 @@ void Compiler::dumpBasicBlockGraph(std::ostream& out)
auto end = llvm::pred_end(bb->llvm()); auto end = llvm::pred_end(bb->llvm());
for (llvm::pred_iterator it = llvm::pred_begin(bb->llvm()); it != end; ++it) for (llvm::pred_iterator it = llvm::pred_begin(bb->llvm()); it != end; ++it)
{ {
out << " \"" << (*it)->getName().str() << "\" -> \"" << blockName << "\" [" _out << " \"" << (*it)->getName().str() << "\" -> \"" << blockName << "\" ["
<< ((m_jumpTableBlock.get() && *it == m_jumpTableBlock.get()->llvm()) ? "style = dashed, " : "") << ((m_jumpTableBlock.get() && *it == m_jumpTableBlock.get()->llvm()) ? "style = dashed, " : "")
//<< "label = \"" << "];\n";
//<< phiNodesPerBlock[bb]
<< "];\n";
} }
} }
out << "}\n"; _out << "}\n";
} }
void Compiler::dump() void Compiler::dump()

69
libevmjit/Compiler.h

@ -18,55 +18,70 @@ class Compiler
{ {
public: public:
using ProgramCounter = uint64_t; struct Options
{
/// Optimize stack operations between basic blocks
bool optimizeStack;
/// Rewrite switch instructions to sequences of branches
bool rewriteSwitchToBranches;
Compiler(); /// Dump CFG as a .dot file for graphviz
bool dumpCFG;
std::unique_ptr<llvm::Module> compile(bytesConstRef bytecode); Options():
optimizeStack(true),
rewriteSwitchToBranches(true),
dumpCFG(false)
{}
};
using ProgramCounter = uint64_t;
void dumpBasicBlockGraph(std::ostream& out); Compiler(Options const& _options);
std::unique_ptr<llvm::Module> compile(bytesConstRef _bytecode);
private: private:
void createBasicBlocks(bytesConstRef bytecode); void createBasicBlocks(bytesConstRef _bytecode);
void compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode, class RuntimeManager& _runtimeManager, class Arith256& arith, class Memory& memory, class Ext& ext, class GasMeter& gasMeter, llvm::BasicBlock* nextBasicBlock); void compileBasicBlock(BasicBlock& _basicBlock, bytesConstRef _bytecode, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock);
void removeDeadBlocks(); void removeDeadBlocks();
/// Dump all basic blocks to stderr. Useful in a debugging session. /// Dumps basic block graph in graphviz format to a file, if option dumpCFG is enabled.
void dumpCFGifRequired(std::string const& _dotfilePath);
/// Dumps basic block graph in graphviz format to a stream.
void dumpCFGtoStream(std::ostream& _out);
/// Dumps all basic blocks to stderr. Useful in a debugging session.
void dump(); void dump();
/// Compiler options
Options const& m_options;
/// Helper class for generating IR
llvm::IRBuilder<> m_builder; llvm::IRBuilder<> m_builder;
/** /// Maps a program counter pc to a basic block that starts at pc (if any).
* Maps a program counter pc to a basic block that starts at pc (if any). std::map<ProgramCounter, BasicBlock> basicBlocks = {};
*/
std::map<ProgramCounter, BasicBlock> basicBlocks;
/** /// Maps a pc at which there is a JUMP or JUMPI to the target block of the jump.
* Maps a pc at which there is a JUMP or JUMPI to the target block of the jump. std::map<ProgramCounter, llvm::BasicBlock*> m_directJumpTargets = {};
*/
std::map<ProgramCounter, llvm::BasicBlock*> m_directJumpTargets;
/** /// A list of possible blocks to which there may be indirect jumps.
* A list of possible blocks to which there may be indirect jumps. std::vector<BasicBlock*> m_indirectJumpTargets = {};
*/
std::vector<BasicBlock*> m_indirectJumpTargets;
/// Stop basic block - terminates execution with STOP code (0) /// Stop basic block - terminates execution with STOP code (0)
llvm::BasicBlock* m_stopBB = nullptr; llvm::BasicBlock* m_stopBB = nullptr;
/** /// Block with a jump table.
* Block with a jump table. std::unique_ptr<BasicBlock> m_jumpTableBlock = nullptr;
*/
std::unique_ptr<BasicBlock> m_jumpTableBlock;
/** /// Default destination for indirect jumps.
* Default destination for indirect jumps. std::unique_ptr<BasicBlock> m_badJumpBlock = nullptr;
*/
std::unique_ptr<BasicBlock> m_badJumpBlock;
/// Main program function /// Main program function
llvm::Function* m_mainFunc = nullptr; llvm::Function* m_mainFunc = nullptr;

4
libevmjit/Endianness.cpp

@ -15,8 +15,8 @@ namespace jit
llvm::Value* Endianness::bswap(llvm::IRBuilder<>& _builder, llvm::Value* _word) llvm::Value* Endianness::bswap(llvm::IRBuilder<>& _builder, llvm::Value* _word)
{ {
// TODO: Native is Little Endian // TODO: Native is Little Endian
assert(_word->getType() == Type::i256); assert(_word->getType() == Type::Word);
auto bswap = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::i256); auto bswap = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::Word);
return _builder.CreateCall(bswap, _word); return _builder.CreateCall(bswap, _word);
} }

40
libevmjit/Ext.cpp

@ -168,11 +168,11 @@ extern "C"
using namespace dev::eth::jit; using namespace dev::eth::jit;
EXPORT void ext_store(Runtime* _rt, i256* _index, i256* _value) EXPORT void ext_store(Runtime* _rt, i256* _index, i256* o_value)
{ {
auto index = llvm2eth(*_index); auto index = llvm2eth(*_index);
auto value = _rt->getExt().store(index); // Interface uses native endianness auto value = _rt->getExt().store(index); // Interface uses native endianness
*_value = eth2llvm(value); *o_value = eth2llvm(value);
} }
EXPORT void ext_setStore(Runtime* _rt, i256* _index, i256* _value) EXPORT void ext_setStore(Runtime* _rt, i256* _index, i256* _value)
@ -187,28 +187,28 @@ extern "C"
ext.setStore(index, value); // Interface uses native endianness ext.setStore(index, value); // Interface uses native endianness
} }
EXPORT void ext_calldataload(Runtime* _rt, i256* _index, i256* _value) EXPORT void ext_calldataload(Runtime* _rt, i256* _index, i256* o_value)
{ {
auto index = static_cast<size_t>(llvm2eth(*_index)); auto index = static_cast<size_t>(llvm2eth(*_index));
assert(index + 31 > index); // TODO: Handle large index assert(index + 31 > index); // TODO: Handle large index
auto b = reinterpret_cast<byte*>(_value); auto b = reinterpret_cast<byte*>(o_value);
for (size_t i = index, j = 0; i <= index + 31; ++i, ++j) for (size_t i = index, j = 0; i <= index + 31; ++i, ++j)
b[j] = i < _rt->getExt().data.size() ? _rt->getExt().data[i] : 0; // Keep Big Endian b[j] = i < _rt->getExt().data.size() ? _rt->getExt().data[i] : 0; // Keep Big Endian
// TODO: It all can be done by adding padding to data or by using min() algorithm without branch // TODO: It all can be done by adding padding to data or by using min() algorithm without branch
} }
EXPORT void ext_balance(Runtime* _rt, h256* _address, i256* _value) EXPORT void ext_balance(Runtime* _rt, h256* _address, i256* o_value)
{ {
auto u = _rt->getExt().balance(right160(*_address)); auto u = _rt->getExt().balance(right160(*_address));
*_value = eth2llvm(u); *o_value = eth2llvm(u);
} }
EXPORT void ext_suicide(Runtime* _rt, h256* _address) EXPORT void ext_suicide(Runtime* _rt, h256 const* _address)
{ {
_rt->getExt().suicide(right160(*_address)); _rt->getExt().suicide(right160(*_address));
} }
EXPORT void ext_create(Runtime* _rt, i256* _endowment, i256* _initOff, i256* _initSize, h256* _address) EXPORT void ext_create(Runtime* _rt, i256* _endowment, i256* _initOff, i256* _initSize, h256* o_address)
{ {
auto&& ext = _rt->getExt(); auto&& ext = _rt->getExt();
auto endowment = llvm2eth(*_endowment); auto endowment = llvm2eth(*_endowment);
@ -222,21 +222,21 @@ extern "C"
auto&& initRef = bytesConstRef(_rt->getMemory().data() + initOff, initSize); auto&& initRef = bytesConstRef(_rt->getMemory().data() + initOff, initSize);
OnOpFunc onOp {}; // TODO: Handle that thing OnOpFunc onOp {}; // TODO: Handle that thing
h256 address(ext.create(endowment, &gas, initRef, onOp), h256::AlignRight); h256 address(ext.create(endowment, &gas, initRef, onOp), h256::AlignRight);
*_address = address; *o_address = address;
} }
else else
*_address = {}; *o_address = {};
} }
EXPORT void ext_call(Runtime* _rt, i256* _gas, h256* _receiveAddress, i256* _value, i256* _inOff, i256* _inSize, i256* _outOff, i256* _outSize, h256* _codeAddress, i256* _ret) EXPORT void ext_call(Runtime* _rt, i256* io_gas, h256* _receiveAddress, i256* _value, i256* _inOff, i256* _inSize, i256* _outOff, i256* _outSize, h256* _codeAddress, i256* o_ret)
{ {
auto&& ext = _rt->getExt(); auto&& ext = _rt->getExt();
auto value = llvm2eth(*_value); auto value = llvm2eth(*_value);
auto ret = false; auto ret = false;
auto gas = llvm2eth(*_gas); auto gas = llvm2eth(*io_gas);
if (ext.balance(ext.myAddress) >= value) if (ext.balance(ext.myAddress) >= value)
{ {
ext.subBalance(value); ext.subBalance(value);
@ -252,25 +252,25 @@ extern "C"
ret = ext.call(receiveAddress, value, inRef, &gas, outRef, onOp, {}, codeAddress); ret = ext.call(receiveAddress, value, inRef, &gas, outRef, onOp, {}, codeAddress);
} }
*_gas = eth2llvm(gas); *io_gas = eth2llvm(gas);
_ret->a = ret ? 1 : 0; o_ret->a = ret ? 1 : 0;
} }
EXPORT void ext_sha3(Runtime* _rt, i256* _inOff, i256* _inSize, i256* _ret) EXPORT void ext_sha3(Runtime* _rt, i256* _inOff, i256* _inSize, i256* o_ret)
{ {
auto inOff = static_cast<size_t>(llvm2eth(*_inOff)); auto inOff = static_cast<size_t>(llvm2eth(*_inOff));
auto inSize = static_cast<size_t>(llvm2eth(*_inSize)); auto inSize = static_cast<size_t>(llvm2eth(*_inSize));
auto dataRef = bytesConstRef(_rt->getMemory().data() + inOff, inSize); auto dataRef = bytesConstRef(_rt->getMemory().data() + inOff, inSize);
auto hash = sha3(dataRef); auto hash = sha3(dataRef);
*_ret = *reinterpret_cast<i256*>(&hash); *o_ret = *reinterpret_cast<i256*>(&hash);
} }
EXPORT void ext_exp(Runtime*, i256* _left, i256* _right, i256* _ret) EXPORT void ext_exp(Runtime*, i256* _left, i256* _right, i256* o_ret)
{ {
bigint left = llvm2eth(*_left); bigint left = llvm2eth(*_left);
bigint right = llvm2eth(*_right); bigint right = llvm2eth(*_right);
auto ret = static_cast<u256>(boost::multiprecision::powm(left, right, bigint(2) << 256)); auto ret = static_cast<u256>(boost::multiprecision::powm(left, right, bigint(2) << 256));
*_ret = eth2llvm(ret); *o_ret = eth2llvm(ret);
} }
EXPORT unsigned char* ext_codeAt(Runtime* _rt, h256* _addr256) EXPORT unsigned char* ext_codeAt(Runtime* _rt, h256* _addr256)
@ -281,12 +281,12 @@ extern "C"
return const_cast<unsigned char*>(code.data()); return const_cast<unsigned char*>(code.data());
} }
EXPORT void ext_codesizeAt(Runtime* _rt, h256* _addr256, i256* _ret) EXPORT void ext_codesizeAt(Runtime* _rt, h256* _addr256, i256* o_ret)
{ {
auto&& ext = _rt->getExt(); auto&& ext = _rt->getExt();
auto addr = right160(*_addr256); auto addr = right160(*_addr256);
auto& code = ext.codeAt(addr); auto& code = ext.codeAt(addr);
*_ret = eth2llvm(u256(code.size())); *o_ret = eth2llvm(u256(code.size()));
} }
} }

11
libevmjit/GasMeter.cpp

@ -84,7 +84,7 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
{ {
auto module = getModule(); auto module = getModule();
m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::i256, false), llvm::Function::PrivateLinkage, "gas.check", module); m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::Word, false), llvm::Function::PrivateLinkage, "gas.check", module);
InsertPointGuard guard(m_builder); InsertPointGuard guard(m_builder);
auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc); auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc);
@ -113,7 +113,7 @@ void GasMeter::count(Instruction _inst)
if (!m_checkCall) if (!m_checkCall)
{ {
// Create gas check call with mocked block cost at begining of current cost-block // Create gas check call with mocked block cost at begining of current cost-block
m_checkCall = m_builder.CreateCall(m_gasCheckFunc, llvm::UndefValue::get(Type::i256)); m_checkCall = m_builder.CreateCall(m_gasCheckFunc, llvm::UndefValue::get(Type::Word));
} }
m_blockCost += getStepCost(_inst); m_blockCost += getStepCost(_inst);
@ -169,11 +169,10 @@ void GasMeter::commitCostBlock(llvm::Value* _additionalCost)
assert(m_blockCost == 0); assert(m_blockCost == 0);
} }
void GasMeter::checkMemory(llvm::Value* _additionalMemoryInWords, llvm::IRBuilder<>& _builder) void GasMeter::checkMemory(llvm::Value* _additionalMemoryInWords)
{ {
// Memory uses other builder, but that can be changes later auto cost = m_builder.CreateNUWMul(_additionalMemoryInWords, Constant::get(static_cast<uint64_t>(c_memoryGas)), "memcost");
auto cost = _builder.CreateMul(_additionalMemoryInWords, Constant::get(static_cast<uint64_t>(c_memoryGas)), "memcost"); m_builder.CreateCall(m_gasCheckFunc, cost);
_builder.CreateCall(m_gasCheckFunc, cost);
} }
} }

4
libevmjit/GasMeter.h

@ -32,7 +32,7 @@ public:
void giveBack(llvm::Value* _gas); void giveBack(llvm::Value* _gas);
/// Generate code that checks the cost of additional memory used by program /// Generate code that checks the cost of additional memory used by program
void checkMemory(llvm::Value* _additionalMemoryInWords, llvm::IRBuilder<>& _builder); void checkMemory(llvm::Value* _additionalMemoryInWords);
private: private:
/// Cumulative gas cost of a block of instructions /// Cumulative gas cost of a block of instructions
@ -40,7 +40,7 @@ private:
uint64_t m_blockCost = 0; uint64_t m_blockCost = 0;
llvm::CallInst* m_checkCall = nullptr; llvm::CallInst* m_checkCall = nullptr;
llvm::Function* m_gasCheckFunc; llvm::Function* m_gasCheckFunc = nullptr;
RuntimeManager& m_runtimeManager; RuntimeManager& m_runtimeManager;
}; };

32
libevmjit/Memory.cpp

@ -29,7 +29,7 @@ Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
RuntimeHelper(_runtimeManager) RuntimeHelper(_runtimeManager)
{ {
auto module = getModule(); auto module = getModule();
llvm::Type* argTypes[] = {Type::i256, Type::i256}; llvm::Type* argTypes[] = {Type::Word, Type::Word};
auto dumpTy = llvm::FunctionType::get(m_builder.getVoidTy(), llvm::ArrayRef<llvm::Type*>(argTypes), false); auto dumpTy = llvm::FunctionType::get(m_builder.getVoidTy(), llvm::ArrayRef<llvm::Type*>(argTypes), false);
m_memDump = llvm::Function::Create(dumpTy, llvm::GlobalValue::LinkageTypes::ExternalLinkage, m_memDump = llvm::Function::Create(dumpTy, llvm::GlobalValue::LinkageTypes::ExternalLinkage,
"evmccrt_memory_dump", module); "evmccrt_memory_dump", module);
@ -37,7 +37,7 @@ Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
m_data = new llvm::GlobalVariable(*module, Type::BytePtr, false, llvm::GlobalVariable::PrivateLinkage, llvm::UndefValue::get(Type::BytePtr), "mem.data"); m_data = new llvm::GlobalVariable(*module, Type::BytePtr, false, llvm::GlobalVariable::PrivateLinkage, llvm::UndefValue::get(Type::BytePtr), "mem.data");
m_data->setUnnamedAddr(true); // Address is not important m_data->setUnnamedAddr(true); // Address is not important
m_size = new llvm::GlobalVariable(*module, Type::i256, false, llvm::GlobalVariable::PrivateLinkage, Constant::get(0), "mem.size"); m_size = new llvm::GlobalVariable(*module, Type::Word, false, llvm::GlobalVariable::PrivateLinkage, Constant::get(0), "mem.size");
m_size->setUnnamedAddr(true); // Address is not important m_size->setUnnamedAddr(true); // Address is not important
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr}; llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
@ -47,14 +47,14 @@ Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder)); m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder));
m_require = createRequireFunc(_gasMeter, _runtimeManager); m_require = createRequireFunc(_gasMeter, _runtimeManager);
m_loadWord = createFunc(false, Type::i256, _gasMeter); m_loadWord = createFunc(false, Type::Word, _gasMeter);
m_storeWord = createFunc(true, Type::i256, _gasMeter); m_storeWord = createFunc(true, Type::Word, _gasMeter);
m_storeByte = createFunc(true, Type::Byte, _gasMeter); m_storeByte = createFunc(true, Type::Byte, _gasMeter);
} }
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter, RuntimeManager& _runtimeManager) llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter, RuntimeManager& _runtimeManager)
{ {
llvm::Type* argTypes[] = {Type::i256, Type::i256}; llvm::Type* argTypes[] = {Type::Word, Type::Word};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule());
auto offset = func->arg_begin(); auto offset = func->arg_begin();
offset->setName("offset"); offset->setName("offset");
@ -67,9 +67,9 @@ llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter, RuntimeManager& _
InsertPointGuard guard(m_builder); // Restores insert point at function exit InsertPointGuard guard(m_builder); // Restores insert point at function exit
// BB "check" // BB "Check"
m_builder.SetInsertPoint(checkBB); m_builder.SetInsertPoint(checkBB);
auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::i256); auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word);
auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res");
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq");
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1");
@ -78,7 +78,7 @@ llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter, RuntimeManager& _
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded");
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights? m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
// BB "resize" // BB "Resize"
m_builder.SetInsertPoint(resizeBB); m_builder.SetInsertPoint(resizeBB);
// Check gas first // Check gas first
uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res"); uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res");
@ -90,14 +90,14 @@ llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter, RuntimeManager& _
sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq");
auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k
auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords");
_gasMeter.checkMemory(newWords, m_builder); _gasMeter.checkMemory(newWords);
// Resize // Resize
m_builder.CreateStore(sizeRequired, m_size); m_builder.CreateStore(sizeRequired, m_size);
auto newData = m_builder.CreateCall2(m_resize, _runtimeManager.getRuntimePtr(), m_size, "newData"); auto newData = m_builder.CreateCall2(m_resize, _runtimeManager.getRuntimePtr(), m_size, "newData");
m_builder.CreateStore(newData, m_data); m_builder.CreateStore(newData, m_data);
m_builder.CreateBr(returnBB); m_builder.CreateBr(returnBB);
// BB "return" // BB "Return"
m_builder.SetInsertPoint(returnBB); m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
return func; return func;
@ -105,11 +105,11 @@ llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter, RuntimeManager& _
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&)
{ {
auto isWord = _valueType == Type::i256; auto isWord = _valueType == Type::Word;
llvm::Type* storeArgs[] = {Type::i256, _valueType}; llvm::Type* storeArgs[] = {Type::Word, _valueType};
auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload";
auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::i256, Type::i256, false); auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, Type::Word, false);
auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule());
InsertPointGuard guard(m_builder); // Restores insert point at function exit InsertPointGuard guard(m_builder); // Restores insert point at function exit
@ -185,7 +185,7 @@ void Memory::require(llvm::Value* _offset, llvm::Value* _size)
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,
llvm::Value* _destMemIdx, llvm::Value* _reqBytes) llvm::Value* _destMemIdx, llvm::Value* _reqBytes)
{ {
auto zero256 = llvm::ConstantInt::get(Type::i256, 0); auto zero256 = llvm::ConstantInt::get(Type::Word, 0);
require(_destMemIdx, _reqBytes); require(_destMemIdx, _reqBytes);
@ -224,7 +224,6 @@ void Memory::dump(uint64_t _begin, uint64_t _end)
extern "C" extern "C"
{ {
using namespace dev::eth::jit; using namespace dev::eth::jit;
EXPORT uint8_t* mem_resize(Runtime* _rt, i256* _size) EXPORT uint8_t* mem_resize(Runtime* _rt, i256* _size)
@ -234,5 +233,4 @@ extern "C"
memory.resize(size); memory.resize(size);
return memory.data(); return memory.data();
} }
}
} // extern "C"

5
libevmjit/Runtime.cpp

@ -3,6 +3,7 @@
#include <llvm/IR/GlobalVariable.h> #include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Function.h> #include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h>
#include <libevm/VM.h> #include <libevm/VM.h>
@ -22,7 +23,7 @@ llvm::StructType* RuntimeData::getType()
{ {
llvm::Type* elems[] = llvm::Type* elems[] =
{ {
llvm::ArrayType::get(Type::i256, _size), llvm::ArrayType::get(Type::Word, _size),
Type::BytePtr, Type::BytePtr,
Type::BytePtr, Type::BytePtr,
Type::BytePtr Type::BytePtr
@ -105,7 +106,7 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder): CompilerHelper(_bui
{ {
m_dataPtr = new llvm::GlobalVariable(*getModule(), Type::RuntimePtr, false, llvm::GlobalVariable::PrivateLinkage, llvm::UndefValue::get(Type::RuntimePtr), "rt"); m_dataPtr = new llvm::GlobalVariable(*getModule(), Type::RuntimePtr, false, llvm::GlobalVariable::PrivateLinkage, llvm::UndefValue::get(Type::RuntimePtr), "rt");
llvm::Type* args[] = {Type::BytePtr, m_builder.getInt32Ty()}; llvm::Type* args[] = {Type::BytePtr, m_builder.getInt32Ty()};
m_longjmp = llvm::Function::Create(llvm::FunctionType::get(Type::Void, args, false), llvm::Function::ExternalLinkage, "longjmp", getModule()); m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::longjmp);
// Export data // Export data
auto mainFunc = getMainFunction(); auto mainFunc = getMainFunction();

10
libevmjit/Stack.cpp

@ -18,7 +18,7 @@ Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
CompilerHelper(_builder), CompilerHelper(_builder),
m_runtimeManager(_runtimeManager) m_runtimeManager(_runtimeManager)
{ {
m_arg = m_builder.CreateAlloca(Type::i256, nullptr, "stack.arg"); m_arg = m_builder.CreateAlloca(Type::Word, nullptr, "stack.arg");
using namespace llvm; using namespace llvm;
using Linkage = GlobalValue::LinkageTypes; using Linkage = GlobalValue::LinkageTypes;
@ -79,7 +79,7 @@ extern "C"
stack.erase(stack.end() - _count, stack.end()); stack.erase(stack.end() - _count, stack.end());
} }
EXPORT void stack_push(Runtime* _rt, i256* _word) EXPORT void stack_push(Runtime* _rt, i256 const* _word)
{ {
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
stack.push_back(*_word); stack.push_back(*_word);
@ -88,17 +88,17 @@ extern "C"
Stack::maxStackSize = stack.size(); Stack::maxStackSize = stack.size();
} }
EXPORT void stack_get(Runtime* _rt, uint64_t _index, i256* _ret) EXPORT void stack_get(Runtime* _rt, uint64_t _index, i256* o_ret)
{ {
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code // TODO: encode _index and stack size in the return code
if (stack.size() <= _index) if (stack.size() <= _index)
longjmp(_rt->getJmpBuf(), static_cast<uint64_t>(ReturnCode::StackTooSmall)); longjmp(_rt->getJmpBuf(), static_cast<uint64_t>(ReturnCode::StackTooSmall));
*_ret = *(stack.rbegin() + _index); *o_ret = *(stack.rbegin() + _index);
} }
EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256* _word) EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256 const* _word)
{ {
auto& stack = _rt->getStack(); auto& stack = _rt->getStack();
// TODO: encode _index and stack size in the return code // TODO: encode _index and stack size in the return code

10
libevmjit/Type.cpp

@ -12,7 +12,7 @@ namespace eth
namespace jit namespace jit
{ {
llvm::IntegerType* Type::i256; llvm::IntegerType* Type::Word;
llvm::PointerType* Type::WordPtr; llvm::PointerType* Type::WordPtr;
llvm::IntegerType* Type::lowPrecision; llvm::IntegerType* Type::lowPrecision;
llvm::IntegerType* Type::Size; llvm::IntegerType* Type::Size;
@ -24,8 +24,8 @@ llvm::PointerType* Type::RuntimePtr;
void Type::init(llvm::LLVMContext& _context) void Type::init(llvm::LLVMContext& _context)
{ {
i256 = llvm::Type::getIntNTy(_context, 256); Word = llvm::Type::getIntNTy(_context, 256);
WordPtr = i256->getPointerTo(); WordPtr = Word->getPointerTo();
lowPrecision = llvm::Type::getInt64Ty(_context); lowPrecision = llvm::Type::getInt64Ty(_context);
// TODO: Size should be architecture-dependent // TODO: Size should be architecture-dependent
Size = llvm::Type::getInt64Ty(_context); Size = llvm::Type::getInt64Ty(_context);
@ -38,14 +38,14 @@ void Type::init(llvm::LLVMContext& _context)
llvm::ConstantInt* Constant::get(int64_t _n) llvm::ConstantInt* Constant::get(int64_t _n)
{ {
return llvm::ConstantInt::getSigned(Type::i256, _n); return llvm::ConstantInt::getSigned(Type::Word, _n);
} }
llvm::ConstantInt* Constant::get(u256 _n) llvm::ConstantInt* Constant::get(u256 _n)
{ {
llvm::APInt n(256, _n.str(0, std::ios_base::hex), 16); llvm::APInt n(256, _n.str(0, std::ios_base::hex), 16);
assert(n.toString(10, false) == _n.str()); assert(n.toString(10, false) == _n.str());
return static_cast<llvm::ConstantInt*>(llvm::ConstantInt::get(Type::i256, n)); return static_cast<llvm::ConstantInt*>(llvm::ConstantInt::get(Type::Word, n));
} }
llvm::ConstantInt* Constant::get(ReturnCode _returnCode) llvm::ConstantInt* Constant::get(ReturnCode _returnCode)

2
libevmjit/Type.h

@ -14,7 +14,7 @@ namespace jit
struct Type struct Type
{ {
static llvm::IntegerType* i256; static llvm::IntegerType* Word;
static llvm::PointerType* WordPtr; static llvm::PointerType* WordPtr;
/// Type for doing low precision arithmetics where 256-bit precision is not supported by native target /// Type for doing low precision arithmetics where 256-bit precision is not supported by native target

3
libevmjit/VM.cpp

@ -16,7 +16,8 @@ namespace jit
bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t) bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
{ {
auto module = Compiler().compile(_ext.code); Compiler::Options defaultOptions;
auto module = Compiler(defaultOptions).compile(_ext.code);
ExecutionEngine engine; ExecutionEngine engine;
auto exitCode = engine.run(std::move(module), m_gas, &_ext); auto exitCode = engine.run(std::move(module), m_gas, &_ext);

2
liblll/Assembly.cpp

@ -251,7 +251,7 @@ Assembly& Assembly::optimise(bool _enable)
{ { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } }, { { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } },
{ { Instruction::NOT, Instruction::NOT }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
}; };
for (auto const& i: c_simple) for (auto const& i: c_simple)

14
liblll/CodeFragment.cpp

@ -328,7 +328,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
std::map<std::string, Instruction> const c_arith = { { "+", Instruction::ADD }, { "-", Instruction::SUB }, { "*", Instruction::MUL }, { "/", Instruction::DIV }, { "%", Instruction::MOD }, { "&", Instruction::AND }, { "|", Instruction::OR }, { "^", Instruction::XOR } }; std::map<std::string, Instruction> const c_arith = { { "+", Instruction::ADD }, { "-", Instruction::SUB }, { "*", Instruction::MUL }, { "/", Instruction::DIV }, { "%", Instruction::MOD }, { "&", Instruction::AND }, { "|", Instruction::OR }, { "^", Instruction::XOR } };
std::map<std::string, pair<Instruction, bool>> const c_binary = { { "<", { Instruction::LT, false } }, { "<=", { Instruction::GT, true } }, { ">", { Instruction::GT, false } }, { ">=", { Instruction::LT, true } }, { "S<", { Instruction::SLT, false } }, { "S<=", { Instruction::SGT, true } }, { "S>", { Instruction::SGT, false } }, { "S>=", { Instruction::SLT, true } }, { "=", { Instruction::EQ, false } }, { "!=", { Instruction::EQ, true } } }; std::map<std::string, pair<Instruction, bool>> const c_binary = { { "<", { Instruction::LT, false } }, { "<=", { Instruction::GT, true } }, { ">", { Instruction::GT, false } }, { ">=", { Instruction::LT, true } }, { "S<", { Instruction::SLT, false } }, { "S<=", { Instruction::SGT, true } }, { "S>", { Instruction::SGT, false } }, { "S>=", { Instruction::SLT, true } }, { "=", { Instruction::EQ, false } }, { "!=", { Instruction::EQ, true } } };
std::map<std::string, Instruction> const c_unary = { { "!", Instruction::NOT } }; std::map<std::string, Instruction> const c_unary = { { "!", Instruction::ISZERO } };
vector<CodeFragment> code; vector<CodeFragment> code;
CompilerState ns = _s; CompilerState ns = _s;
@ -403,7 +403,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
m_asm.append(code[0].m_asm, 1); m_asm.append(code[0].m_asm, 1);
m_asm.append(it->second.first); m_asm.append(it->second.first);
if (it->second.second) if (it->second.second)
m_asm.append(Instruction::NOT); m_asm.append(Instruction::ISZERO);
} }
else if (c_unary.count(us)) else if (c_unary.count(us))
{ {
@ -437,7 +437,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
m_asm.append(code[0].m_asm); m_asm.append(code[0].m_asm);
if (us == "WHEN") if (us == "WHEN")
m_asm.append(Instruction::NOT); m_asm.append(Instruction::ISZERO);
auto end = m_asm.appendJumpI(); auto end = m_asm.appendJumpI();
m_asm.onePath(); m_asm.onePath();
m_asm.otherPath(); m_asm.otherPath();
@ -452,7 +452,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
auto begin = m_asm.append(); auto begin = m_asm.append();
m_asm.append(code[0].m_asm); m_asm.append(code[0].m_asm);
m_asm.append(Instruction::NOT); m_asm.append(Instruction::ISZERO);
auto end = m_asm.appendJumpI(); auto end = m_asm.appendJumpI();
m_asm.append(code[1].m_asm, 0); m_asm.append(code[1].m_asm, 0);
m_asm.appendJump(begin); m_asm.appendJump(begin);
@ -466,7 +466,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
m_asm.append(code[0].m_asm, 0); m_asm.append(code[0].m_asm, 0);
auto begin = m_asm.append(); auto begin = m_asm.append();
m_asm.append(code[1].m_asm); m_asm.append(code[1].m_asm);
m_asm.append(Instruction::NOT); m_asm.append(Instruction::ISZERO);
auto end = m_asm.appendJumpI(); auto end = m_asm.appendJumpI();
m_asm.append(code[3].m_asm, 0); m_asm.append(code[3].m_asm, 0);
m_asm.append(code[2].m_asm, 0); m_asm.append(code[2].m_asm, 0);
@ -502,7 +502,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
requireDeposit(2, 1); requireDeposit(2, 1);
m_asm.append(code[2].m_asm, 1); m_asm.append(code[2].m_asm, 1);
m_asm.append(Instruction::LT); m_asm.append(Instruction::LT);
m_asm.append(Instruction::NOT); m_asm.append(Instruction::ISZERO);
m_asm.append(Instruction::MUL); m_asm.append(Instruction::MUL);
m_asm.append(Instruction::DUP1); m_asm.append(Instruction::DUP1);
} }
@ -525,7 +525,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
// Check if true - predicate // Check if true - predicate
m_asm.append(code[i - 1].m_asm, 1); m_asm.append(code[i - 1].m_asm, 1);
if (us == "&&") if (us == "&&")
m_asm.append(Instruction::NOT); m_asm.append(Instruction::ISZERO);
m_asm.appendJumpI(end); m_asm.appendJumpI(end);
} }
m_asm.append(Instruction::POP); m_asm.append(Instruction::POP);

215
libserpent/opcodes.h

@ -1,3 +1,20 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ETHSERP_OPCODES #ifndef ETHSERP_OPCODES
#define ETHSERP_OPCODES #define ETHSERP_OPCODES
@ -7,122 +24,128 @@
#include <map> #include <map>
class Mapping { class Mapping {
public: public:
Mapping(std::string Op, int Opcode, int In, int Out) { Mapping(std::string Op, int Opcode, int In, int Out) {
op = Op; op = Op;
opcode = Opcode; opcode = Opcode;
in = In; in = In;
out = Out; out = Out;
} }
std::string op; std::string op;
int opcode; int opcode;
int in; int in;
int out; int out;
}; };
Mapping mapping[] = { Mapping mapping[] = {
Mapping("STOP", 0x00, 0, 0), Mapping("STOP", 0x00, 0, 0),
Mapping("ADD", 0x01, 2, 1), Mapping("ADD", 0x01, 2, 1),
Mapping("MUL", 0x02, 2, 1), Mapping("MUL", 0x02, 2, 1),
Mapping("SUB", 0x03, 2, 1), Mapping("SUB", 0x03, 2, 1),
Mapping("DIV", 0x04, 2, 1), Mapping("DIV", 0x04, 2, 1),
Mapping("SDIV", 0x05, 2, 1), Mapping("SDIV", 0x05, 2, 1),
Mapping("MOD", 0x06, 2, 1), Mapping("MOD", 0x06, 2, 1),
Mapping("SMOD", 0x07, 2, 1), Mapping("SMOD", 0x07, 2, 1),
Mapping("EXP", 0x08, 2, 1), Mapping("EXP", 0x08, 2, 1),
Mapping("NEG", 0x09, 1, 1), Mapping("NEG", 0x09, 1, 1),
Mapping("LT", 0x0a, 2, 1), Mapping("LT", 0x0a, 2, 1),
Mapping("GT", 0x0b, 2, 1), Mapping("GT", 0x0b, 2, 1),
Mapping("SLT", 0x0c, 2, 1), Mapping("SLT", 0x0c, 2, 1),
Mapping("SGT", 0x0d, 2, 1), Mapping("SGT", 0x0d, 2, 1),
Mapping("EQ", 0x0e, 2, 1), Mapping("EQ", 0x0e, 2, 1),
Mapping("NOT", 0x0f, 1, 1), Mapping("NOT", 0x0f, 1, 1),
Mapping("AND", 0x10, 2, 1), Mapping("AND", 0x10, 2, 1),
Mapping("OR", 0x11, 2, 1), Mapping("OR", 0x11, 2, 1),
Mapping("XOR", 0x12, 2, 1), Mapping("XOR", 0x12, 2, 1),
Mapping("BYTE", 0x13, 2, 1), Mapping("BYTE", 0x13, 2, 1),
Mapping("ADDMOD", 0x14, 3, 1), Mapping("ADDMOD", 0x14, 3, 1),
Mapping("MULMOD", 0x15, 3, 1), Mapping("MULMOD", 0x15, 3, 1),
Mapping("SHA3", 0x20, 2, 1), Mapping("SIGNEXTEND", 0x16, 2, 1),
Mapping("ADDRESS", 0x30, 0, 1), Mapping("SHA3", 0x20, 2, 1),
Mapping("BALANCE", 0x31, 1, 1), Mapping("ADDRESS", 0x30, 0, 1),
Mapping("ORIGIN", 0x32, 0, 1), Mapping("BALANCE", 0x31, 1, 1),
Mapping("CALLER", 0x33, 0, 1), Mapping("ORIGIN", 0x32, 0, 1),
Mapping("CALLVALUE", 0x34, 0, 1), Mapping("CALLER", 0x33, 0, 1),
Mapping("CALLDATALOAD", 0x35, 1, 1), Mapping("CALLVALUE", 0x34, 0, 1),
Mapping("CALLDATASIZE", 0x36, 0, 1), Mapping("CALLDATALOAD", 0x35, 1, 1),
Mapping("CALLDATACOPY", 0x37, 3, 1), Mapping("CALLDATASIZE", 0x36, 0, 1),
Mapping("CODESIZE", 0x38, 0, 1), Mapping("CALLDATACOPY", 0x37, 3, 1),
Mapping("CODECOPY", 0x39, 3, 1), Mapping("CODESIZE", 0x38, 0, 1),
Mapping("GASPRICE", 0x3a, 0, 1), Mapping("CODECOPY", 0x39, 3, 1),
Mapping("PREVHASH", 0x40, 0, 1), Mapping("GASPRICE", 0x3a, 0, 1),
Mapping("COINBASE", 0x41, 0, 1), Mapping("PREVHASH", 0x40, 0, 1),
Mapping("TIMESTAMP", 0x42, 0, 1), Mapping("COINBASE", 0x41, 0, 1),
Mapping("NUMBER", 0x43, 0, 1), Mapping("TIMESTAMP", 0x42, 0, 1),
Mapping("DIFFICULTY", 0x44, 0, 1), Mapping("NUMBER", 0x43, 0, 1),
Mapping("GASLIMIT", 0x45, 0, 1), Mapping("DIFFICULTY", 0x44, 0, 1),
Mapping("POP", 0x50, 1, 0), Mapping("GASLIMIT", 0x45, 0, 1),
Mapping("MLOAD", 0x53, 1, 1), Mapping("POP", 0x50, 1, 0),
Mapping("MSTORE", 0x54, 2, 0), Mapping("MLOAD", 0x53, 1, 1),
Mapping("MSTORE8", 0x55, 2, 0), Mapping("MSTORE", 0x54, 2, 0),
Mapping("SLOAD", 0x56, 1, 1), Mapping("MSTORE8", 0x55, 2, 0),
Mapping("SSTORE", 0x57, 2, 0), Mapping("SLOAD", 0x56, 1, 1),
Mapping("JUMP", 0x58, 1, 0), Mapping("SSTORE", 0x57, 2, 0),
Mapping("JUMPI", 0x59, 2, 0), Mapping("JUMP", 0x58, 1, 0),
Mapping("PC", 0x5a, 0, 1), Mapping("JUMPI", 0x59, 2, 0),
Mapping("MSIZE", 0x5b, 0, 1), Mapping("PC", 0x5a, 0, 1),
Mapping("GAS", 0x5c, 0, 1), Mapping("MSIZE", 0x5b, 0, 1),
Mapping("JUMPDEST", 0x5d, 0, 0), Mapping("GAS", 0x5c, 0, 1),
Mapping("CREATE", 0xf0, 3, 1), Mapping("JUMPDEST", 0x5d, 0, 0),
Mapping("CALL", 0xf1, 7, 1), Mapping("CREATE", 0xf0, 3, 1),
Mapping("RETURN", 0xf2, 2, 0), Mapping("CALL", 0xf1, 7, 1),
Mapping("CALL_CODE", 0xf3, 7, 1), Mapping("RETURN", 0xf2, 2, 0),
Mapping("SUICIDE", 0xff, 1, 0), Mapping("CALL_CODE", 0xf3, 7, 1),
Mapping("---END---", 0x00, 0, 0), Mapping("SUICIDE", 0xff, 1, 0),
Mapping("---END---", 0x00, 0, 0),
}; };
std::map<std::string, std::vector<int> > opcodes; std::map<std::string, std::vector<int> > opcodes;
std::map<int, std::string> reverseOpcodes; std::map<int, std::string> reverseOpcodes;
// Fetches everything EXCEPT PUSH1..32 // Fetches everything EXCEPT PUSH1..32
std::pair<std::string, std::vector<int> > _opdata(std::string ops, int opi) { std::pair<std::string, std::vector<int> > _opdata(std::string ops, int opi)
if (!opcodes.size()) { {
int i = 0; if (!opcodes.size())
while (mapping[i].op != "---END---") { {
Mapping mi = mapping[i]; int i = 0;
opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out); while (mapping[i].op != "---END---")
i++; {
} Mapping mi = mapping[i];
for (i = 1; i <= 16; i++) { opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out);
opcodes["DUP"+unsignedToDecimal(i)] = triple(0x7f + i, i, i+1); i++;
opcodes["SWAP"+unsignedToDecimal(i)] = triple(0x8f + i, i+1, i+1); }
} for (i = 1; i <= 16; i++)
for (std::map<std::string, std::vector<int> >::iterator it=opcodes.begin(); {
it != opcodes.end(); opcodes["DUP"+unsignedToDecimal(i)] = triple(0x7f + i, i, i+1);
it++) { opcodes["SWAP"+unsignedToDecimal(i)] = triple(0x8f + i, i+1, i+1);
reverseOpcodes[(*it).second[0]] = (*it).first; }
} for (std::map<std::string, std::vector<int> >::iterator it=opcodes.begin(); it != opcodes.end(); it++)
} reverseOpcodes[(*it).second[0]] = (*it).first;
std::string op; }
std::vector<int> opdata; std::string op;
op = reverseOpcodes.count(opi) ? reverseOpcodes[opi] : ""; std::vector<int> opdata;
opdata = opcodes.count(ops) ? opcodes[ops] : triple(-1, -1, -1); op = reverseOpcodes.count(opi) ? reverseOpcodes[opi] : "";
return std::pair<std::string, std::vector<int> >(op, opdata); opdata = opcodes.count(ops) ? opcodes[ops] : triple(-1, -1, -1);
return std::pair<std::string, std::vector<int> >(op, opdata);
} }
int opcode(std::string op) { int opcode(std::string op)
{
return _opdata(op, -1).second[0]; return _opdata(op, -1).second[0];
} }
int opinputs(std::string op) { int opinputs(std::string op)
{
return _opdata(op, -1).second[1]; return _opdata(op, -1).second[1];
} }
int opoutputs(std::string op) { int opoutputs(std::string op)
{
return _opdata(op, -1).second[2]; return _opdata(op, -1).second[2];
} }
std::string op(int opcode) { std::string op(int opcode)
{
return _opdata("", opcode).first; return _opdata("", opcode).first;
} }

61
libsolidity/AST.cpp

@ -26,6 +26,8 @@
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
using namespace std;
namespace dev namespace dev
{ {
namespace solidity namespace solidity
@ -248,12 +250,12 @@ void Literal::accept(ASTVisitor& _visitor)
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
TypeError ASTNode::createTypeError(std::string const& _description) TypeError ASTNode::createTypeError(string const& _description)
{ {
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
} }
void Statement::expectType(Expression& _expression, const Type& _expectedType) void Statement::expectType(Expression& _expression, Type const& _expectedType)
{ {
_expression.checkTypeRequirements(); _expression.checkTypeRequirements();
if (!_expression.getType()->isImplicitlyConvertibleTo(_expectedType)) if (!_expression.getType()->isImplicitlyConvertibleTo(_expectedType))
@ -263,7 +265,7 @@ void Statement::expectType(Expression& _expression, const Type& _expectedType)
void Block::checkTypeRequirements() void Block::checkTypeRequirements()
{ {
for (std::shared_ptr<Statement> const& statement: m_statements) for (shared_ptr<Statement> const& statement: m_statements)
statement->checkTypeRequirements(); statement->checkTypeRequirements();
} }
@ -291,7 +293,7 @@ void Break::checkTypeRequirements()
void Return::checkTypeRequirements() void Return::checkTypeRequirements()
{ {
BOOST_ASSERT(m_returnParameters); assert(m_returnParameters);
if (m_returnParameters->getParameters().size() != 1) if (m_returnParameters->getParameters().size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement " BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement "
"than in returns declaration.")); "than in returns declaration."));
@ -328,7 +330,7 @@ void Assignment::checkTypeRequirements()
m_type = m_leftHandSide->getType(); m_type = m_leftHandSide->getType();
if (m_assigmentOperator != Token::ASSIGN) if (m_assigmentOperator != Token::ASSIGN)
{ {
// complex assignment // compound assignment
if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator)))
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type.")); BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
} }
@ -339,7 +341,7 @@ void UnaryOperation::checkTypeRequirements()
// INC, DEC, NOT, BIT_NOT, DELETE // INC, DEC, NOT, BIT_NOT, DELETE
m_subExpression->checkTypeRequirements(); m_subExpression->checkTypeRequirements();
m_type = m_subExpression->getType(); m_type = m_subExpression->getType();
if (m_type->acceptsUnaryOperator(m_operator)) if (!m_type->acceptsUnaryOperator(m_operator))
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type.")); BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
} }
@ -354,10 +356,10 @@ void BinaryOperation::checkTypeRequirements()
else else
BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation.")); BOOST_THROW_EXCEPTION(createTypeError("No common type found in binary operation."));
if (Token::isCompareOp(m_operator)) if (Token::isCompareOp(m_operator))
m_type = std::make_shared<BoolType>(); m_type = make_shared<BoolType>();
else else
{ {
BOOST_ASSERT(Token::isBinaryOp(m_operator)); assert(Token::isBinaryOp(m_operator));
m_type = m_commonType; m_type = m_commonType;
if (!m_commonType->acceptsBinaryOperator(m_operator)) if (!m_commonType->acceptsBinaryOperator(m_operator))
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type.")); BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type."));
@ -369,12 +371,12 @@ void FunctionCall::checkTypeRequirements()
m_expression->checkTypeRequirements(); m_expression->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments) for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements(); argument->checkTypeRequirements();
Type const& expressionType = *m_expression->getType();
Type::Category const category = expressionType.getCategory(); Type const* expressionType = m_expression->getType().get();
if (category == Type::Category::TYPE) if (isTypeConversion())
{ {
TypeType const* type = dynamic_cast<TypeType const*>(&expressionType); TypeType const* type = dynamic_cast<TypeType const*>(expressionType);
BOOST_ASSERT(type); assert(type);
//@todo for structs, we have to check the number of arguments to be equal to the //@todo for structs, we have to check the number of arguments to be equal to the
// number of non-mapping members // number of non-mapping members
if (m_arguments.size() != 1) if (m_arguments.size() != 1)
@ -384,15 +386,15 @@ void FunctionCall::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type->getActualType(); m_type = type->getActualType();
} }
else if (category == Type::Category::FUNCTION) else
{ {
//@todo would be nice to create a struct type from the arguments //@todo would be nice to create a struct type from the arguments
// and then ask if that is implicitly convertible to the struct represented by the // and then ask if that is implicitly convertible to the struct represented by the
// function parameters // function parameters
FunctionType const* function = dynamic_cast<FunctionType const*>(&expressionType); FunctionType const* function = dynamic_cast<FunctionType const*>(expressionType);
BOOST_ASSERT(function); assert(function);
FunctionDefinition const& fun = function->getFunction(); FunctionDefinition const& fun = function->getFunction();
std::vector<ASTPointer<VariableDeclaration>> const& parameters = fun.getParameters(); vector<ASTPointer<VariableDeclaration>> const& parameters = fun.getParameters();
if (parameters.size() != m_arguments.size()) if (parameters.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
for (size_t i = 0; i < m_arguments.size(); ++i) for (size_t i = 0; i < m_arguments.size(); ++i)
@ -401,29 +403,32 @@ void FunctionCall::checkTypeRequirements()
// @todo actually the return type should be an anonymous struct, // @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs // but we change it to the type of the first return value until we have structs
if (fun.getReturnParameterList()->getParameters().empty()) if (fun.getReturnParameterList()->getParameters().empty())
m_type = std::make_shared<VoidType>(); m_type = make_shared<VoidType>();
else else
m_type = fun.getReturnParameterList()->getParameters().front()->getType(); m_type = fun.getReturnParameterList()->getParameters().front()->getType();
} }
else }
BOOST_THROW_EXCEPTION(createTypeError("Type does not support invocation."));
bool FunctionCall::isTypeConversion() const
{
return m_expression->getType()->getCategory() == Type::Category::TYPE;
} }
void MemberAccess::checkTypeRequirements() void MemberAccess::checkTypeRequirements()
{ {
BOOST_ASSERT(false); // not yet implemented assert(false); // not yet implemented
// m_type = ; // m_type = ;
} }
void IndexAccess::checkTypeRequirements() void IndexAccess::checkTypeRequirements()
{ {
BOOST_ASSERT(false); // not yet implemented assert(false); // not yet implemented
// m_type = ; // m_type = ;
} }
void Identifier::checkTypeRequirements() void Identifier::checkTypeRequirements()
{ {
BOOST_ASSERT(m_referencedDeclaration); assert(m_referencedDeclaration);
//@todo these dynamic casts here are not really nice... //@todo these dynamic casts here are not really nice...
// is i useful to have an AST visitor here? // is i useful to have an AST visitor here?
// or can this already be done in NameAndTypeResolver? // or can this already be done in NameAndTypeResolver?
@ -446,7 +451,7 @@ void Identifier::checkTypeRequirements()
if (structDef) if (structDef)
{ {
// note that we do not have a struct type here // note that we do not have a struct type here
m_type = std::make_shared<TypeType>(std::make_shared<StructType>(*structDef)); m_type = make_shared<TypeType>(make_shared<StructType>(*structDef));
return; return;
} }
FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration); FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration);
@ -455,21 +460,21 @@ void Identifier::checkTypeRequirements()
// a function reference is not a TypeType, because calling a TypeType converts to the type. // a function reference is not a TypeType, because calling a TypeType converts to the type.
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion. // conversion.
m_type = std::make_shared<FunctionType>(*functionDef); m_type = make_shared<FunctionType>(*functionDef);
return; return;
} }
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration); ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration);
if (contractDef) if (contractDef)
{ {
m_type = std::make_shared<TypeType>(std::make_shared<ContractType>(*contractDef)); m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef));
return; return;
} }
BOOST_ASSERT(false); // declaration reference of unknown/forbidden type assert(false); // declaration reference of unknown/forbidden type
} }
void ElementaryTypeNameExpression::checkTypeRequirements() void ElementaryTypeNameExpression::checkTypeRequirements()
{ {
m_type = std::make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken)); m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken));
} }
void Literal::checkTypeRequirements() void Literal::checkTypeRequirements()

163
libsolidity/AST.h

@ -40,6 +40,12 @@ namespace solidity
class ASTVisitor; class ASTVisitor;
/**
* The root (abstract) class of the AST inheritance tree.
* It is possible to traverse all direct and indirect children of an AST node by calling
* accept, providing an ASTVisitor.
*/
class ASTNode: private boost::noncopyable class ASTNode: private boost::noncopyable
{ {
public: public:
@ -55,28 +61,45 @@ public:
element->accept(_visitor); element->accept(_visitor);
} }
/// Returns the source code location of this node.
Location const& getLocation() const { return m_location; } Location const& getLocation() const { return m_location; }
/// Creates a @ref TypeError exception and decorates it with the location of the node and /// Creates a @ref TypeError exception and decorates it with the location of the node and
/// the given description /// the given description
TypeError createTypeError(std::string const& _description); TypeError createTypeError(std::string const& _description);
///@{
///@name equality operators
/// Equality relies on the fact that nodes cannot be copied.
bool operator==(ASTNode const& _other) const { return this == &_other; }
bool operator!=(ASTNode const& _other) const { return !operator==(_other); }
///@}
private: private:
Location m_location; Location m_location;
}; };
/**
* Abstract AST class for a declaration (contract, function, struct, variable).
*/
class Declaration: public ASTNode class Declaration: public ASTNode
{ {
public: public:
Declaration(Location const& _location, ASTPointer<ASTString> const& _name): Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
ASTNode(_location), m_name(_name) {} ASTNode(_location), m_name(_name) {}
const ASTString& getName() const { return *m_name; } /// Returns the declared name.
ASTString const& getName() const { return *m_name; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
}; };
/**
* Definition of a contract. This is the only AST nodes where child nodes are not visited in
* document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations.
*/
class ContractDefinition: public Declaration class ContractDefinition: public Declaration
{ {
public: public:
@ -116,9 +139,11 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_members; std::vector<ASTPointer<VariableDeclaration>> m_members;
}; };
/// Used as function parameter list and return list /**
/// None of the parameters is allowed to contain mappings (not even recursively * Parameter list, used as function parameter list and return list.
/// inside structs) * None of the parameters is allowed to contain mappings (not even recursively
* inside structs), but (@todo) this is not yet enforced.
*/
class ParameterList: public ASTNode class ParameterList: public ASTNode
{ {
public: public:
@ -161,6 +186,10 @@ private:
ASTPointer<Block> m_body; ASTPointer<Block> m_body;
}; };
/**
* Declaration of a variable. This can be used in various places, e.g. in function parameter
* lists, struct definitions and even function bodys.
*/
class VariableDeclaration: public Declaration class VariableDeclaration: public Declaration
{ {
public: public:
@ -172,30 +201,38 @@ public:
bool isTypeGivenExplicitly() const { return bool(m_typeName); } bool isTypeGivenExplicitly() const { return bool(m_typeName); }
TypeName* getTypeName() const { return m_typeName.get(); } TypeName* getTypeName() const { return m_typeName.get(); }
//! Returns the declared or inferred type. Can be an empty pointer if no type was explicitly /// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
//! declared and there is no assignment to the variable that fixes the type. /// declared and there is no assignment to the variable that fixes the type.
std::shared_ptr<Type const> const& getType() const { return m_type; } std::shared_ptr<Type const> const& getType() const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; } void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
private: private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var") ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
std::shared_ptr<Type const> m_type; std::shared_ptr<Type const> m_type; ///< derived type, initially empty
}; };
/// types /// Types
/// @{ /// @{
/**
* Abstract base class of a type name, can be any built-in or user-defined type.
*/
class TypeName: public ASTNode class TypeName: public ASTNode
{ {
public: public:
explicit TypeName(Location const& _location): ASTNode(_location) {} explicit TypeName(Location const& _location): ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
/// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared
/// pointer until the types have been resolved using the @ref NameAndTypeResolver.
virtual std::shared_ptr<Type> toType() = 0; virtual std::shared_ptr<Type> toType() = 0;
}; };
/// any pre-defined type that is not a mapping /**
* Any pre-defined type name represented by a single keyword, i.e. it excludes mappings,
* contracts, functions, etc.
*/
class ElementaryTypeName: public TypeName class ElementaryTypeName: public TypeName
{ {
public: public:
@ -204,12 +241,16 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() override { return Type::fromElementaryTypeName(m_type); } virtual std::shared_ptr<Type> toType() override { return Type::fromElementaryTypeName(m_type); }
Token::Value getType() const { return m_type; } Token::Value getTypeName() const { return m_type; }
private: private:
Token::Value m_type; Token::Value m_type;
}; };
/**
* Name referring to a user-defined type (i.e. a struct).
* @todo some changes are necessary if this is also used to refer to contract types later
*/
class UserDefinedTypeName: public TypeName class UserDefinedTypeName: public TypeName
{ {
public: public:
@ -218,7 +259,7 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual std::shared_ptr<Type> toType() override { return Type::fromUserDefinedTypeName(*this); } virtual std::shared_ptr<Type> toType() override { return Type::fromUserDefinedTypeName(*this); }
const ASTString& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; } void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; }
StructDefinition const* getReferencedStruct() const { return m_referencedStruct; } StructDefinition const* getReferencedStruct() const { return m_referencedStruct; }
@ -228,6 +269,9 @@ private:
StructDefinition* m_referencedStruct; StructDefinition* m_referencedStruct;
}; };
/**
* A mapping type. Its source form is "mapping('keyType' => 'valueType')"
*/
class Mapping: public TypeName class Mapping: public TypeName
{ {
public: public:
@ -247,23 +291,30 @@ private:
/// Statements /// Statements
/// @{ /// @{
/**
* Abstract base class for statements.
*/
class Statement: public ASTNode class Statement: public ASTNode
{ {
public: public:
explicit Statement(Location const& _location): ASTNode(_location) {} explicit Statement(Location const& _location): ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
//! Check all type requirements, throws exception if some requirement is not met. /// Check all type requirements, throws exception if some requirement is not met.
//! For expressions, this also returns the inferred type of the expression. For other /// This includes checking that operators are applicable to their arguments but also that
//! statements, returns the empty pointer. /// the number of function call arguments matches the number of formal parameters and so forth.
virtual void checkTypeRequirements() = 0; virtual void checkTypeRequirements() = 0;
protected: protected:
//! Check that the inferred type for _expression is _expectedType or at least implicitly /// Helper function, check that the inferred type for @a _expression is @a _expectedType or at
//! convertible to _expectedType. If not, throw exception. /// least implicitly convertible to @a _expectedType. If not, throw exception.
void expectType(Expression& _expression, Type const& _expectedType); void expectType(Expression& _expression, Type const& _expectedType);
}; };
/**
* Brace-enclosed block containing zero or more statements.
*/
class Block: public Statement class Block: public Statement
{ {
public: public:
@ -277,6 +328,10 @@ private:
std::vector<ASTPointer<Statement>> m_statements; std::vector<ASTPointer<Statement>> m_statements;
}; };
/**
* If-statement with an optional "else" part. Note that "else if" is modeled by having a new
* if-statement as the false (else) body.
*/
class IfStatement: public Statement class IfStatement: public Statement
{ {
public: public:
@ -290,9 +345,13 @@ public:
private: private:
ASTPointer<Expression> m_condition; ASTPointer<Expression> m_condition;
ASTPointer<Statement> m_trueBody; ASTPointer<Statement> m_trueBody;
ASTPointer<Statement> m_falseBody; //< "else" part, optional ASTPointer<Statement> m_falseBody; ///< "else" part, optional
}; };
/**
* Statement in which a break statement is legal.
* @todo actually check this requirement.
*/
class BreakableStatement: public Statement class BreakableStatement: public Statement
{ {
public: public:
@ -341,11 +400,17 @@ public:
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; }
private: private:
ASTPointer<Expression> m_expression; //< value to return, optional ASTPointer<Expression> m_expression; ///< value to return, optional
ParameterList* m_returnParameters; //< extracted from the function declaration /// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver.
ParameterList* m_returnParameters;
}; };
/**
* Definition of a variable as a statement inside a function. It requires a type name (which can
* also be "var") but the actual assignment can be missing.
* Examples: var a = 2; uint256 a;
*/
class VariableDefinition: public Statement class VariableDefinition: public Statement
{ {
public: public:
@ -357,17 +422,22 @@ public:
private: private:
ASTPointer<VariableDeclaration> m_variable; ASTPointer<VariableDeclaration> m_variable;
ASTPointer<Expression> m_value; ///< can be missing ASTPointer<Expression> m_value; ///< the assigned value, can be missing
}; };
/**
* An expression, i.e. something that has a value (which can also be of type "void" in case
* of function calls).
*/
class Expression: public Statement class Expression: public Statement
{ {
public: public:
Expression(Location const& _location): Statement(_location) {} Expression(Location const& _location): Statement(_location) {}
std::shared_ptr<Type const> const& getType() const { return m_type; } std::shared_ptr<Type const> const& getType() const { return m_type; }
protected: protected:
//! Inferred type of the expression, only filled after a call to checkTypeRequirements(). /// Inferred type of the expression, only filled after a call to checkTypeRequirements().
std::shared_ptr<Type const> m_type; std::shared_ptr<Type const> m_type;
}; };
@ -376,6 +446,10 @@ protected:
/// Expressions /// Expressions
/// @{ /// @{
/**
* Assignment, can also be a compound assignment.
* Examples: (a = 7 + 8) or (a *= 2)
*/
class Assignment: public Expression class Assignment: public Expression
{ {
public: public:
@ -386,7 +460,9 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getLeftHandSide() const { return *m_leftHandSide; }
Token::Value getAssignmentOperator() const { return m_assigmentOperator; } Token::Value getAssignmentOperator() const { return m_assigmentOperator; }
Expression& getRightHandSide() const { return *m_rightHandSide; }
private: private:
ASTPointer<Expression> m_leftHandSide; ASTPointer<Expression> m_leftHandSide;
@ -394,6 +470,10 @@ private:
ASTPointer<Expression> m_rightHandSide; ASTPointer<Expression> m_rightHandSide;
}; };
/**
* Operation involving a unary operator, pre- or postfix.
* Examples: ++i, delete x or !true
*/
class UnaryOperation: public Expression class UnaryOperation: public Expression
{ {
public: public:
@ -413,6 +493,10 @@ private:
bool m_isPrefix; bool m_isPrefix;
}; };
/**
* Operation involving a binary operator.
* Examples: 1 + 2, true && false or 1 <= 4
*/
class BinaryOperation: public Expression class BinaryOperation: public Expression
{ {
public: public:
@ -422,6 +506,8 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression& getLeftExpression() const { return *m_left; }
Expression& getRightExpression() const { return *m_right; }
Token::Value getOperator() const { return m_operator; } Token::Value getOperator() const { return m_operator; }
private: private:
@ -432,7 +518,9 @@ private:
std::shared_ptr<Type const> m_commonType; std::shared_ptr<Type const> m_commonType;
}; };
/// Can be ordinary function call, type cast or struct construction. /**
* Can be ordinary function call, type cast or struct construction.
*/
class FunctionCall: public Expression class FunctionCall: public Expression
{ {
public: public:
@ -442,11 +530,18 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
/// Returns true if this is not an actual function call, but an explicit type conversion
/// or constructor call.
bool isTypeConversion() const;
private: private:
ASTPointer<Expression> m_expression; ASTPointer<Expression> m_expression;
std::vector<ASTPointer<Expression>> m_arguments; std::vector<ASTPointer<Expression>> m_arguments;
}; };
/**
* Access to a member of an object. Example: x.name
*/
class MemberAccess: public Expression class MemberAccess: public Expression
{ {
public: public:
@ -454,7 +549,7 @@ public:
ASTPointer<ASTString> const& _memberName): ASTPointer<ASTString> const& _memberName):
Expression(_location), m_expression(_expression), m_memberName(_memberName) {} Expression(_location), m_expression(_expression), m_memberName(_memberName) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
const ASTString& getMemberName() const { return *m_memberName; } ASTString const& getMemberName() const { return *m_memberName; }
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
private: private:
@ -462,6 +557,9 @@ private:
ASTPointer<ASTString> m_memberName; ASTPointer<ASTString> m_memberName;
}; };
/**
* Index access to an array. Example: a[2]
*/
class IndexAccess: public Expression class IndexAccess: public Expression
{ {
public: public:
@ -476,12 +574,19 @@ private:
ASTPointer<Expression> m_index; ASTPointer<Expression> m_index;
}; };
/**
* Primary expression, i.e. an expression that cannot be divided any further. Examples are literals
* or variable references.
*/
class PrimaryExpression: public Expression class PrimaryExpression: public Expression
{ {
public: public:
PrimaryExpression(Location const& _location): Expression(_location) {} PrimaryExpression(Location const& _location): Expression(_location) {}
}; };
/**
* An identifier, i.e. a reference to a declaration by name like a variable or function.
*/
class Identifier: public PrimaryExpression class Identifier: public PrimaryExpression
{ {
public: public:
@ -491,16 +596,22 @@ public:
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
Declaration* getReferencedDeclaration() { return m_referencedDeclaration; } Declaration* getReferencedDeclaration() { return m_referencedDeclaration; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
//! Declaration the name refers to. /// Declaration the name refers to.
Declaration* m_referencedDeclaration; Declaration* m_referencedDeclaration;
}; };
/**
* An elementary type name expression is used in expressions like "a = uint32(2)" to change the
* type of an expression explicitly. Here, "uint32" is the elementary type name expression and
* "uint32(2)" is a @ref FunctionCall.
*/
class ElementaryTypeNameExpression: public PrimaryExpression class ElementaryTypeNameExpression: public PrimaryExpression
{ {
public: public:
@ -515,6 +626,9 @@ private:
Token::Value m_typeToken; Token::Value m_typeToken;
}; };
/**
* A literal string or number. @see Type::literalToBigEndian is used to actually parse its value.
*/
class Literal: public PrimaryExpression class Literal: public PrimaryExpression
{ {
public: public:
@ -524,6 +638,7 @@ public:
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Token::Value getToken() const { return m_token; } Token::Value getToken() const { return m_token; }
/// @returns the non-parsed value of the literal
ASTString const& getValue() const { return *m_value; } ASTString const& getValue() const { return *m_value; }
private: private:

30
libsolidity/ASTPrinter.cpp

@ -23,17 +23,19 @@
#include <libsolidity/ASTPrinter.h> #include <libsolidity/ASTPrinter.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
using namespace std;
namespace dev namespace dev
{ {
namespace solidity namespace solidity
{ {
ASTPrinter::ASTPrinter(ASTPointer<ASTNode> const& _ast, std::string const& _source): ASTPrinter::ASTPrinter(ASTPointer<ASTNode> const& _ast, string const& _source):
m_indentation(0), m_source(_source), m_ast(_ast) m_indentation(0), m_source(_source), m_ast(_ast)
{ {
} }
void ASTPrinter::print(std::ostream& _stream) void ASTPrinter::print(ostream& _stream)
{ {
m_ostream = &_stream; m_ostream = &_stream;
m_ast->accept(*this); m_ast->accept(*this);
@ -87,7 +89,7 @@ bool ASTPrinter::visit(TypeName& _node)
bool ASTPrinter::visit(ElementaryTypeName& _node) bool ASTPrinter::visit(ElementaryTypeName& _node)
{ {
writeLine(std::string("ElementaryTypeName ") + Token::toString(_node.getType())); writeLine(string("ElementaryTypeName ") + Token::toString(_node.getTypeName()));
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
@ -179,7 +181,7 @@ bool ASTPrinter::visit(Expression& _node)
bool ASTPrinter::visit(Assignment& _node) bool ASTPrinter::visit(Assignment& _node)
{ {
writeLine(std::string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator())); writeLine(string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator()));
printType(_node); printType(_node);
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
@ -187,7 +189,7 @@ bool ASTPrinter::visit(Assignment& _node)
bool ASTPrinter::visit(UnaryOperation& _node) bool ASTPrinter::visit(UnaryOperation& _node)
{ {
writeLine(std::string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
") " + Token::toString(_node.getOperator())); ") " + Token::toString(_node.getOperator()));
printType(_node); printType(_node);
printSourcePart(_node); printSourcePart(_node);
@ -196,7 +198,7 @@ bool ASTPrinter::visit(UnaryOperation& _node)
bool ASTPrinter::visit(BinaryOperation& _node) bool ASTPrinter::visit(BinaryOperation& _node)
{ {
writeLine(std::string("BinaryOperation using operator ") + Token::toString(_node.getOperator())); writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
printType(_node); printType(_node);
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
@ -236,7 +238,7 @@ bool ASTPrinter::visit(PrimaryExpression& _node)
bool ASTPrinter::visit(Identifier& _node) bool ASTPrinter::visit(Identifier& _node)
{ {
writeLine(std::string("Identifier ") + _node.getName()); writeLine(string("Identifier ") + _node.getName());
printType(_node); printType(_node);
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
@ -244,7 +246,7 @@ bool ASTPrinter::visit(Identifier& _node)
bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) bool ASTPrinter::visit(ElementaryTypeNameExpression& _node)
{ {
writeLine(std::string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken())); writeLine(string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken()));
printType(_node); printType(_node);
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
@ -255,7 +257,7 @@ bool ASTPrinter::visit(Literal& _node)
char const* tokenString = Token::toString(_node.getToken()); char const* tokenString = Token::toString(_node.getToken());
if (!tokenString) if (!tokenString)
tokenString = "[no token]"; tokenString = "[no token]";
writeLine(std::string("Literal, token: ") + tokenString + " value: " + _node.getValue()); writeLine(string("Literal, token: ") + tokenString + " value: " + _node.getValue());
printType(_node); printType(_node);
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
@ -417,7 +419,7 @@ void ASTPrinter::printSourcePart(ASTNode const& _node)
{ {
Location const& location(_node.getLocation()); Location const& location(_node.getLocation());
*m_ostream << getIndentation() << " Source: |" *m_ostream << getIndentation() << " Source: |"
<< m_source.substr(location.start, location.end - location.start) << "|" << std::endl; << m_source.substr(location.start, location.end - location.start) << "|" << endl;
} }
} }
@ -429,14 +431,14 @@ void ASTPrinter::printType(Expression const& _expression)
*m_ostream << getIndentation() << " Type unknown.\n"; *m_ostream << getIndentation() << " Type unknown.\n";
} }
std::string ASTPrinter::getIndentation() const string ASTPrinter::getIndentation() const
{ {
return std::string(m_indentation * 2, ' '); return string(m_indentation * 2, ' ');
} }
void ASTPrinter::writeLine(std::string const& _line) void ASTPrinter::writeLine(string const& _line)
{ {
*m_ostream << getIndentation() << _line << std::endl; *m_ostream << getIndentation() << _line << endl;
} }
} }

5
libsolidity/ASTPrinter.h

@ -30,12 +30,15 @@ namespace dev
namespace solidity namespace solidity
{ {
/**
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable) for debugging purposes.
*/
class ASTPrinter: public ASTVisitor class ASTPrinter: public ASTVisitor
{ {
public: public:
/// Create a printer for the given abstract syntax tree. If the source is specified, /// Create a printer for the given abstract syntax tree. If the source is specified,
/// the corresponding parts of the source are printed with each node. /// the corresponding parts of the source are printed with each node.
ASTPrinter(ASTPointer<ASTNode> const& _ast, const std::string& _source = std::string()); ASTPrinter(ASTPointer<ASTNode> const& _ast, std::string const& _source = std::string());
/// Output the string representation of the AST to _stream. /// Output the string representation of the AST to _stream.
void print(std::ostream& _stream); void print(std::ostream& _stream);

12
libsolidity/ASTVisitor.h

@ -30,13 +30,17 @@ namespace dev
namespace solidity namespace solidity
{ {
/**
* Visitor interface for the abstract syntax tree. This class is tightly bound to the
* implementation of @ref ASTNode::accept and its overrides. After a call to
* @ref ASTNode::accept, the function visit for the appropriate parameter is called and then
* (if it returns true) this continues recursively for all child nodes in document order
* (there is an exception for contracts). After all child nodes have been visited, endVisit is
* called for the node.
*/
class ASTVisitor class ASTVisitor
{ {
public: public:
/// These functions are called after a call to ASTNode::accept,
/// first visit, then (if visit returns true) recursively for all
/// child nodes in document order (exception for contracts) and then
/// endVisit.
virtual bool visit(ASTNode&) { return true; } virtual bool visit(ASTNode&) { return true; }
virtual bool visit(ContractDefinition&) { return true; } virtual bool visit(ContractDefinition&) { return true; }
virtual bool visit(StructDefinition&) { return true; } virtual bool visit(StructDefinition&) { return true; }

6
libsolidity/BaseTypes.h

@ -29,8 +29,10 @@ namespace dev
namespace solidity namespace solidity
{ {
/// Representation of an interval of source positions. /**
/// The interval includes start and excludes end. * Representation of an interval of source positions.
* The interval includes start and excludes end.
*/
struct Location struct Location
{ {
Location(int _start, int _end): start(_start), end(_end) { } Location(int _start, int _end): start(_start), end(_end) { }

401
libsolidity/Compiler.cpp

@ -0,0 +1,401 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity AST to EVM bytecode compiler.
*/
#include <cassert>
#include <utility>
#include <libsolidity/AST.h>
#include <libsolidity/Compiler.h>
namespace dev {
namespace solidity {
void CompilerContext::setLabelPosition(uint32_t _label, uint32_t _position)
{
assert(m_labelPositions.find(_label) == m_labelPositions.end());
m_labelPositions[_label] = _position;
}
uint32_t CompilerContext::getLabelPosition(uint32_t _label) const
{
auto iter = m_labelPositions.find(_label);
assert(iter != m_labelPositions.end());
return iter->second;
}
void ExpressionCompiler::compile(Expression& _expression)
{
m_assemblyItems.clear();
_expression.accept(*this);
}
bytes ExpressionCompiler::getAssembledBytecode() const
{
bytes assembled;
assembled.reserve(m_assemblyItems.size());
// resolve label references
for (uint32_t pos = 0; pos < m_assemblyItems.size(); ++pos)
{
AssemblyItem const& item = m_assemblyItems[pos];
if (item.getType() == AssemblyItem::Type::LABEL)
m_context.setLabelPosition(item.getLabel(), pos + 1);
}
for (AssemblyItem const& item: m_assemblyItems)
if (item.getType() == AssemblyItem::Type::LABELREF)
assembled.push_back(m_context.getLabelPosition(item.getLabel()));
else
assembled.push_back(item.getData());
return assembled;
}
AssemblyItems ExpressionCompiler::compileExpression(CompilerContext& _context,
Expression& _expression)
{
ExpressionCompiler compiler(_context);
compiler.compile(_expression);
return compiler.getAssemblyItems();
}
void ExpressionCompiler::endVisit(Assignment& _assignment)
{
Expression& rightHandSide = _assignment.getRightHandSide();
Token::Value op = _assignment.getAssignmentOperator();
if (op != Token::ASSIGN)
{
// compound assignment
// @todo retrieve lvalue value
rightHandSide.accept(*this);
Type const& resultType = *_assignment.getType();
cleanHigherOrderBitsIfNeeded(*rightHandSide.getType(), resultType);
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), resultType);
}
else
rightHandSide.accept(*this);
// @todo store value
}
void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
{
//@todo type checking and creating code for an operator should be in the same place:
// the operator should know how to convert itself and to which types it applies, so
// put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
// represents the operator
switch (_unaryOperation.getOperator())
{
case Token::NOT: // !
append(eth::Instruction::ISZERO);
break;
case Token::BIT_NOT: // ~
append(eth::Instruction::NOT);
break;
case Token::DELETE: // delete
// a -> a xor a (= 0).
// @todo this should also be an assignment
// @todo semantics change for complex types
append(eth::Instruction::DUP1);
append(eth::Instruction::XOR);
break;
case Token::INC: // ++ (pre- or postfix)
// @todo this should also be an assignment
if (_unaryOperation.isPrefixOperation())
{
append(eth::Instruction::PUSH1);
append(1);
append(eth::Instruction::ADD);
}
break;
case Token::DEC: // -- (pre- or postfix)
// @todo this should also be an assignment
if (_unaryOperation.isPrefixOperation())
{
append(eth::Instruction::PUSH1);
append(1);
append(eth::Instruction::SWAP1); //@todo avoid this
append(eth::Instruction::SUB);
}
break;
case Token::ADD: // +
// unary add, so basically no-op
break;
case Token::SUB: // -
// unary -x translates into "0-x"
append(eth::Instruction::PUSH1);
append(0);
append(eth::Instruction::SUB);
break;
default:
assert(false); // invalid operation
}
}
bool ExpressionCompiler::visit(BinaryOperation& _binaryOperation)
{
Expression& leftExpression = _binaryOperation.getLeftExpression();
Expression& rightExpression = _binaryOperation.getRightExpression();
Type const& resultType = *_binaryOperation.getType();
Token::Value const op = _binaryOperation.getOperator();
if (op == Token::AND || op == Token::OR)
{
// special case: short-circuiting
appendAndOrOperatorCode(_binaryOperation);
}
else if (Token::isCompareOp(op))
{
leftExpression.accept(*this);
rightExpression.accept(*this);
// the types to compare have to be the same, but the resulting type is always bool
assert(*leftExpression.getType() == *rightExpression.getType());
appendCompareOperatorCode(op, *leftExpression.getType());
}
else
{
leftExpression.accept(*this);
cleanHigherOrderBitsIfNeeded(*leftExpression.getType(), resultType);
rightExpression.accept(*this);
cleanHigherOrderBitsIfNeeded(*rightExpression.getType(), resultType);
appendOrdinaryBinaryOperatorCode(op, resultType);
}
// do not visit the child nodes, we already did that explicitly
return false;
}
void ExpressionCompiler::endVisit(FunctionCall& _functionCall)
{
if (_functionCall.isTypeConversion())
{
//@todo binary representation for all supported types (bool and int) is the same, so no-op
// here for now.
}
else
{
//@todo
}
}
void ExpressionCompiler::endVisit(MemberAccess&)
{
}
void ExpressionCompiler::endVisit(IndexAccess&)
{
}
void ExpressionCompiler::endVisit(Identifier&)
{
}
void ExpressionCompiler::endVisit(Literal& _literal)
{
switch (_literal.getType()->getCategory())
{
case Type::Category::INTEGER:
case Type::Category::BOOL:
{
bytes value = _literal.getType()->literalToBigEndian(_literal);
assert(value.size() <= 32);
assert(!value.empty());
append(static_cast<byte>(eth::Instruction::PUSH1) + static_cast<byte>(value.size() - 1));
append(value);
break;
}
default:
assert(false); // @todo
}
}
void ExpressionCompiler::cleanHigherOrderBitsIfNeeded(Type const& _typeOnStack, Type const& _targetType)
{
// If the type of one of the operands is extended, we need to remove all
// higher-order bits that we might have ignored in previous operations.
// @todo: store in the AST whether the operand might have "dirty" higher
// order bits
if (_typeOnStack == _targetType)
return;
if (_typeOnStack.getCategory() == Type::Category::INTEGER &&
_targetType.getCategory() == Type::Category::INTEGER)
{
//@todo
}
else
{
// If we get here, there is either an implementation missing to clean higher oder bits
// for non-integer types that are explicitly convertible or we got here in error.
assert(!_typeOnStack.isExplicitlyConvertibleTo(_targetType));
assert(false); // these types should not be convertible.
}
}
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation& _binaryOperation)
{
Token::Value const op = _binaryOperation.getOperator();
assert(op == Token::OR || op == Token::AND);
_binaryOperation.getLeftExpression().accept(*this);
append(eth::Instruction::DUP1);
if (op == Token::AND)
append(eth::Instruction::NOT);
uint32_t endLabel = appendConditionalJump();
_binaryOperation.getRightExpression().accept(*this);
appendLabel(endLabel);
}
void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type)
{
if (_operator == Token::EQ || _operator == Token::NE)
{
append(eth::Instruction::EQ);
if (_operator == Token::NE)
append(eth::Instruction::NOT);
}
else
{
IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
assert(type);
bool const isSigned = type->isSigned();
// note that EVM opcodes compare like "stack[0] < stack[1]",
// but our left value is at stack[1], so everyhing is reversed.
switch (_operator)
{
case Token::GTE:
append(isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
append(eth::Instruction::NOT);
break;
case Token::LTE:
append(isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
append(eth::Instruction::NOT);
break;
case Token::GT:
append(isSigned ? eth::Instruction::SLT : eth::Instruction::LT);
break;
case Token::LT:
append(isSigned ? eth::Instruction::SGT : eth::Instruction::GT);
break;
default:
assert(false);
}
}
}
void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type)
{
if (Token::isArithmeticOp(_operator))
appendArithmeticOperatorCode(_operator, _type);
else if (Token::isBitOp(_operator))
appendBitOperatorCode(_operator);
else if (Token::isShiftOp(_operator))
appendShiftOperatorCode(_operator);
else
assert(false); // unknown binary operator
}
void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type)
{
IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
assert(type);
bool const isSigned = type->isSigned();
switch (_operator)
{
case Token::ADD:
append(eth::Instruction::ADD);
break;
case Token::SUB:
append(eth::Instruction::SWAP1);
append(eth::Instruction::SUB);
break;
case Token::MUL:
append(eth::Instruction::MUL);
break;
case Token::DIV:
append(isSigned ? eth::Instruction::SDIV : eth::Instruction::DIV);
break;
case Token::MOD:
append(isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD);
break;
default:
assert(false);
}
}
void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
{
switch (_operator)
{
case Token::BIT_OR:
append(eth::Instruction::OR);
break;
case Token::BIT_AND:
append(eth::Instruction::AND);
break;
case Token::BIT_XOR:
append(eth::Instruction::XOR);
break;
default:
assert(false);
}
}
void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
{
switch (_operator)
{
case Token::SHL:
assert(false); //@todo
break;
case Token::SAR:
assert(false); //@todo
break;
default:
assert(false);
}
}
uint32_t ExpressionCompiler::appendConditionalJump()
{
uint32_t label = m_context.dispenseNewLabel();
append(eth::Instruction::PUSH1);
appendLabelref(label);
append(eth::Instruction::JUMPI);
return label;
}
void ExpressionCompiler::append(bytes const& _data)
{
m_assemblyItems.reserve(m_assemblyItems.size() + _data.size());
for (byte b: _data)
append(b);
}
}
}

146
libsolidity/Compiler.h

@ -0,0 +1,146 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity AST to EVM bytecode compiler.
*/
#include <libevmface/Instruction.h>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/Types.h>
#include <libsolidity/Token.h>
namespace dev {
namespace solidity {
/**
* A single item of compiled code that can be assembled to a single byte value in the final
* bytecode. Its main purpose is to inject jump labels and label references into the opcode stream,
* which can be resolved in the final step.
*/
class AssemblyItem
{
public:
enum class Type
{
CODE, ///< m_data is opcode, m_label is empty.
DATA, ///< m_data is actual data, m_label is empty
LABEL, ///< m_data is JUMPDEST opcode, m_label is id of label
LABELREF ///< m_data is empty, m_label is id of label
};
explicit AssemblyItem(eth::Instruction _instruction) : m_type(Type::CODE), m_data(byte(_instruction)) {}
explicit AssemblyItem(byte _data): m_type(Type::DATA), m_data(_data) {}
/// Factory functions
static AssemblyItem labelRef(uint32_t _label) { return AssemblyItem(Type::LABELREF, 0, _label); }
static AssemblyItem label(uint32_t _label) { return AssemblyItem(Type::LABEL, byte(eth::Instruction::JUMPDEST), _label); }
Type getType() const { return m_type; }
byte getData() const { return m_data; }
uint32_t getLabel() const { return m_label; }
private:
AssemblyItem(Type _type, byte _data, uint32_t _label): m_type(_type), m_data(_data), m_label(_label) {}
Type m_type;
byte m_data; ///< data to be written to the bytecode stream (or filled by a label if this is a LABELREF)
uint32_t m_label; ///< the id of a label either referenced or defined by this item
};
using AssemblyItems = std::vector<AssemblyItem>;
/**
* Context to be shared by all units that compile the same contract. Its current usage only
* concerns dispensing unique jump label IDs and storing their actual positions in the bytecode
* stream.
*/
class CompilerContext
{
public:
CompilerContext(): m_nextLabel(0) {}
uint32_t dispenseNewLabel() { return m_nextLabel++; }
void setLabelPosition(uint32_t _label, uint32_t _position);
uint32_t getLabelPosition(uint32_t _label) const;
private:
uint32_t m_nextLabel;
std::map<uint32_t, uint32_t> m_labelPositions;
};
/**
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
* of EVM instructions. It needs a compiler context that is the same for the whole compilation
* unit.
*/
class ExpressionCompiler: public ASTVisitor
{
public:
ExpressionCompiler(CompilerContext& _compilerContext): m_context(_compilerContext) {}
/// Compile the given expression and (re-)populate the assembly item list.
void compile(Expression& _expression);
AssemblyItems const& getAssemblyItems() const { return m_assemblyItems; }
bytes getAssembledBytecode() const;
/// Compile the given expression and return the assembly items right away.
static AssemblyItems compileExpression(CompilerContext& _context, Expression& _expression);
private:
virtual void endVisit(Assignment& _assignment) override;
virtual void endVisit(UnaryOperation& _unaryOperation) override;
virtual bool visit(BinaryOperation& _binaryOperation) override;
virtual void endVisit(FunctionCall& _functionCall) override;
virtual void endVisit(MemberAccess& _memberAccess) override;
virtual void endVisit(IndexAccess& _indexAccess) override;
virtual void endVisit(Identifier& _identifier) override;
virtual void endVisit(Literal& _literal) override;
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
void cleanHigherOrderBitsIfNeeded(Type const& _typeOnStack, Type const& _targetType);
///@{
///@name Append code for various operator types
void appendAndOrOperatorCode(BinaryOperation& _binaryOperation);
void appendCompareOperatorCode(Token::Value _operator, Type const& _type);
void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type);
void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type);
void appendBitOperatorCode(Token::Value _operator);
void appendShiftOperatorCode(Token::Value _operator);
/// @}
/// Appends a JUMPI instruction to a new label and returns the label
uint32_t appendConditionalJump();
/// Append elements to the current instruction list.
void append(eth::Instruction const& _instruction) { m_assemblyItems.push_back(AssemblyItem(_instruction)); }
void append(byte _value) { m_assemblyItems.push_back(AssemblyItem(_value)); }
void append(bytes const& _data);
void appendLabelref(byte _label) { m_assemblyItems.push_back(AssemblyItem::labelRef(_label)); }
void appendLabel(byte _label) { m_assemblyItems.push_back(AssemblyItem::label(_label)); }
AssemblyItems m_assemblyItems;
CompilerContext& m_context;
};
}
}

21
libsolidity/NameAndTypeResolver.cpp

@ -20,11 +20,12 @@
* Parser part that determines the declarations corresponding to names and the types of expressions. * Parser part that determines the declarations corresponding to names and the types of expressions.
*/ */
#include <cassert>
#include <libsolidity/NameAndTypeResolver.h> #include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <boost/assert.hpp>
using namespace std;
namespace dev namespace dev
{ {
@ -68,7 +69,7 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name
} }
DeclarationRegistrationHelper::DeclarationRegistrationHelper(std::map<ASTNode*, Scope>& _scopes, DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode*, Scope>& _scopes,
ASTNode& _astRoot): ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(&m_scopes[nullptr]) m_scopes(_scopes), m_currentScope(&m_scopes[nullptr])
{ {
@ -120,22 +121,22 @@ void DeclarationRegistrationHelper::endVisit(VariableDeclaration&)
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node) void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node)
{ {
std::map<ASTNode*, Scope>::iterator iter; map<ASTNode*, Scope>::iterator iter;
bool newlyAdded; bool newlyAdded;
std::tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope)); tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope));
BOOST_ASSERT(newlyAdded); assert(newlyAdded);
m_currentScope = &iter->second; m_currentScope = &iter->second;
} }
void DeclarationRegistrationHelper::closeCurrentScope() void DeclarationRegistrationHelper::closeCurrentScope()
{ {
BOOST_ASSERT(m_currentScope); assert(m_currentScope);
m_currentScope = m_currentScope->getOuterScope(); m_currentScope = m_currentScope->getEnclosingScope();
} }
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
{ {
BOOST_ASSERT(m_currentScope); assert(m_currentScope);
if (!m_currentScope->registerDeclaration(_declaration)) if (!m_currentScope->registerDeclaration(_declaration))
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation())
<< errinfo_comment("Identifier already declared.")); << errinfo_comment("Identifier already declared."));
@ -162,7 +163,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
bool ReferencesResolver::visit(Return& _return) bool ReferencesResolver::visit(Return& _return)
{ {
BOOST_ASSERT(m_returnParameters); assert(m_returnParameters);
_return.setFunctionReturnParameters(*m_returnParameters); _return.setFunctionReturnParameters(*m_returnParameters);
return true; return true;
} }

23
libsolidity/NameAndTypeResolver.h

@ -33,8 +33,11 @@ namespace dev
namespace solidity namespace solidity
{ {
//! Resolves name references, resolves all types and checks that all operations are valid for the /**
//! inferred types. An exception is throw on the first error. * Resolves name references, types and checks types of all expressions.
* Specifically, it checks that all operations are valid for the inferred types.
* An exception is throw on the first error.
*/
class NameAndTypeResolver: private boost::noncopyable class NameAndTypeResolver: private boost::noncopyable
{ {
public: public:
@ -46,15 +49,17 @@ public:
private: private:
void reset(); void reset();
//! Maps nodes declaring a scope to scopes, i.e. ContractDefinition, FunctionDeclaration and /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition, FunctionDeclaration and
//! StructDefinition (@todo not yet implemented), where nullptr denotes the global scope. /// StructDefinition (@todo not yet implemented), where nullptr denotes the global scope.
std::map<ASTNode*, Scope> m_scopes; std::map<ASTNode*, Scope> m_scopes;
Scope* m_currentScope; Scope* m_currentScope;
}; };
//! Traverses the given AST upon construction and fills _scopes with all declarations inside the /**
//! AST. * Traverses the given AST upon construction and fills _scopes with all declarations inside the
* AST.
*/
class DeclarationRegistrationHelper: private ASTVisitor class DeclarationRegistrationHelper: private ASTVisitor
{ {
public: public:
@ -78,8 +83,10 @@ private:
Scope* m_currentScope; Scope* m_currentScope;
}; };
//! Resolves references to declarations (of variables and types) and also establishes the link /**
//! between a return statement and the return parameter list. * Resolves references to declarations (of variables and types) and also establishes the link
* between a return statement and the return parameter list.
*/
class ReferencesResolver: private ASTVisitor class ReferencesResolver: private ASTVisitor
{ {
public: public:

13
libsolidity/Parser.h

@ -44,8 +44,8 @@ private:
/// End position of the current token /// End position of the current token
int getEndPosition() const; int getEndPosition() const;
/// Parsing functions for the AST nodes ///@{
/// @{ ///@name Parsing functions for the AST nodes
ASTPointer<ContractDefinition> parseContractDefinition(); ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic); ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<StructDefinition> parseStructDefinition();
@ -64,16 +64,17 @@ private:
ASTPointer<Expression> parseLeftHandSideExpression(); ASTPointer<Expression> parseLeftHandSideExpression();
ASTPointer<Expression> parsePrimaryExpression(); ASTPointer<Expression> parsePrimaryExpression();
std::vector<ASTPointer<Expression>> parseFunctionCallArguments(); std::vector<ASTPointer<Expression>> parseFunctionCallArguments();
/// @} ///@}
///@{
///@name Helper functions
/// Helper functions
/// @{
/// If current token value is not _value, throw exception otherwise advance token. /// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value); void expectToken(Token::Value _value);
Token::Value expectAssignmentOperator(); Token::Value expectAssignmentOperator();
ASTPointer<ASTString> expectIdentifierToken(); ASTPointer<ASTString> expectIdentifierToken();
ASTPointer<ASTString> getLiteralAndAdvance(); ASTPointer<ASTString> getLiteralAndAdvance();
/// @} ///@}
/// Creates a @ref ParserError exception and annotates it with the current position and the /// Creates a @ref ParserError exception and annotates it with the current position and the
/// given @a _description. /// given @a _description.

45
libsolidity/Scanner.cpp

@ -50,11 +50,13 @@
* Solidity scanner. * Solidity scanner.
*/ */
#include <cassert>
#include <algorithm> #include <algorithm>
#include <tuple> #include <tuple>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
using namespace std;
namespace dev namespace dev
{ {
namespace solidity namespace solidity
@ -118,7 +120,7 @@ void Scanner::reset(CharStream const& _source)
bool Scanner::scanHexNumber(char& o_scannedNumber, int _expectedLength) bool Scanner::scanHexNumber(char& o_scannedNumber, int _expectedLength)
{ {
BOOST_ASSERT(_expectedLength <= 4); // prevent overflow assert(_expectedLength <= 4); // prevent overflow
char x = 0; char x = 0;
for (int i = 0; i < _expectedLength; i++) for (int i = 0; i < _expectedLength; i++)
{ {
@ -178,7 +180,7 @@ Token::Value Scanner::skipSingleLineComment()
Token::Value Scanner::skipMultiLineComment() Token::Value Scanner::skipMultiLineComment()
{ {
BOOST_ASSERT(m_char == '*'); assert(m_char == '*');
advance(); advance();
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
@ -471,7 +473,7 @@ void Scanner::scanDecimalDigits()
Token::Value Scanner::scanNumber(bool _periodSeen) Token::Value Scanner::scanNumber(bool _periodSeen)
{ {
BOOST_ASSERT(IsDecimalDigit(m_char)); // the first digit of the number or the fraction assert(IsDecimalDigit(m_char)); // the first digit of the number or the fraction
enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL; enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL;
LiteralScope literal(this); LiteralScope literal(this);
if (_periodSeen) if (_periodSeen)
@ -513,7 +515,7 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
// scan exponent, if any // scan exponent, if any
if (m_char == 'e' || m_char == 'E') if (m_char == 'e' || m_char == 'E')
{ {
BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number assert(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
if (kind != DECIMAL) return Token::ILLEGAL; if (kind != DECIMAL) return Token::ILLEGAL;
// scan exponent // scan exponent
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
@ -607,9 +609,9 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
KEYWORD("while", Token::WHILE) \ KEYWORD("while", Token::WHILE) \
static Token::Value KeywordOrIdentifierToken(std::string const& input) static Token::Value KeywordOrIdentifierToken(string const& input)
{ {
BOOST_ASSERT(!input.empty()); assert(!input.empty());
int const kMinLength = 2; int const kMinLength = 2;
int const kMaxLength = 10; int const kMaxLength = 10;
if (input.size() < kMinLength || input.size() > kMaxLength) if (input.size() < kMinLength || input.size() > kMaxLength)
@ -637,7 +639,7 @@ case ch:
Token::Value Scanner::scanIdentifierOrKeyword() Token::Value Scanner::scanIdentifierOrKeyword()
{ {
BOOST_ASSERT(IsIdentifierStart(m_char)); assert(IsIdentifierStart(m_char));
LiteralScope literal(this); LiteralScope literal(this);
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
// Scan the rest of the identifier characters. // Scan the rest of the identifier characters.
@ -659,42 +661,41 @@ char CharStream::advanceAndGet()
char CharStream::rollback(size_t _amount) char CharStream::rollback(size_t _amount)
{ {
BOOST_ASSERT(m_pos >= _amount); assert(m_pos >= _amount);
m_pos -= _amount; m_pos -= _amount;
return get(); return get();
} }
std::string CharStream::getLineAtPosition(int _position) const string CharStream::getLineAtPosition(int _position) const
{ {
// if _position points to \n, it returns the line before the \n // if _position points to \n, it returns the line before the \n
using size_type = std::string::size_type; using size_type = string::size_type;
size_type searchStart = std::min<size_type>(m_source.size(), _position); size_type searchStart = min<size_type>(m_source.size(), _position);
if (searchStart > 0) if (searchStart > 0)
searchStart--; searchStart--;
size_type lineStart = m_source.rfind('\n', searchStart); size_type lineStart = m_source.rfind('\n', searchStart);
if (lineStart == std::string::npos) if (lineStart == string::npos)
lineStart = 0; lineStart = 0;
else else
lineStart++; lineStart++;
return m_source.substr(lineStart, return m_source.substr(lineStart, min(m_source.find('\n', lineStart),
std::min(m_source.find('\n', lineStart), m_source.size()) - lineStart);
m_source.size()) - lineStart);
} }
std::tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const
{ {
using size_type = std::string::size_type; using size_type = string::size_type;
size_type searchPosition = std::min<size_type>(m_source.size(), _position); size_type searchPosition = min<size_type>(m_source.size(), _position);
int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n'); int lineNumber = count(m_source.begin(), m_source.begin() + searchPosition, '\n');
size_type lineStart; size_type lineStart;
if (searchPosition == 0) if (searchPosition == 0)
lineStart = 0; lineStart = 0;
else else
{ {
lineStart = m_source.rfind('\n', searchPosition - 1); lineStart = m_source.rfind('\n', searchPosition - 1);
lineStart = lineStart == std::string::npos ? 0 : lineStart + 1; lineStart = lineStart == string::npos ? 0 : lineStart + 1;
} }
return std::tuple<int, int>(lineNumber, searchPosition - lineStart); return tuple<int, int>(lineNumber, searchPosition - lineStart);
} }

40
libsolidity/Scanner.h

@ -52,8 +52,6 @@
#pragma once #pragma once
#include <boost/assert.hpp>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
@ -81,12 +79,13 @@ public:
char advanceAndGet(); char advanceAndGet();
char rollback(size_t _amount); char rollback(size_t _amount);
///@{
///@name Error printing helper functions
/// Functions that help pretty-printing parse errors /// Functions that help pretty-printing parse errors
/// Do only use in error cases, they are quite expensive. /// Do only use in error cases, they are quite expensive.
/// @{
std::string getLineAtPosition(int _position) const; std::string getLineAtPosition(int _position) const;
std::tuple<int, int> translatePositionToLineColumn(int _position) const; std::tuple<int, int> translatePositionToLineColumn(int _position) const;
/// @} ///@}
private: private:
std::string m_source; std::string m_source;
@ -119,29 +118,31 @@ public:
/// Returns the next token and advances input. /// Returns the next token and advances input.
Token::Value next(); Token::Value next();
/// Information about the current token ///@{
/// @{ ///@name Information about the current token
/// Returns the current token /// Returns the current token
Token::Value getCurrentToken() { return m_current_token.token; } Token::Value getCurrentToken() { return m_current_token.token; }
Location getCurrentLocation() const { return m_current_token.location; } Location getCurrentLocation() const { return m_current_token.location; }
const std::string& getCurrentLiteral() const { return m_current_token.literal; } std::string const& getCurrentLiteral() const { return m_current_token.literal; }
/// @} ///@}
///@{
///@name Information about the next token
/// Information about the next token
/// @{
/// Returns the next token without advancing input. /// Returns the next token without advancing input.
Token::Value peekNextToken() const { return m_next_token.token; } Token::Value peekNextToken() const { return m_next_token.token; }
Location peekLocation() const { return m_next_token.location; } Location peekLocation() const { return m_next_token.location; }
const std::string& peekLiteral() const { return m_next_token.literal; } std::string const& peekLiteral() const { return m_next_token.literal; }
/// @} ///@}
/// Functions that help pretty-printing parse errors. ///@{
///@name Error printing helper functions
/// Functions that help pretty-printing parse errors
/// Do only use in error cases, they are quite expensive. /// Do only use in error cases, they are quite expensive.
/// @{
std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); } std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); }
std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); } std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); }
/// @} ///@}
private: private:
// Used for the current and look-ahead token. // Used for the current and look-ahead token.
@ -152,13 +153,13 @@ private:
std::string literal; std::string literal;
}; };
/// Literal buffer support ///@{
/// @{ ///@name Literal buffer support
inline void startNewLiteral() { m_next_token.literal.clear(); } inline void startNewLiteral() { m_next_token.literal.clear(); }
inline void addLiteralChar(char c) { m_next_token.literal.push_back(c); } inline void addLiteralChar(char c) { m_next_token.literal.push_back(c); }
inline void dropLiteral() { m_next_token.literal.clear(); } inline void dropLiteral() { m_next_token.literal.clear(); }
inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); } inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
/// @} ///@}
bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); }
void rollback(int _amount) { m_char = m_source.rollback(_amount); } void rollback(int _amount) { m_char = m_source.rollback(_amount); }
@ -169,9 +170,10 @@ private:
bool scanHexNumber(char& o_scannedNumber, int _expectedLength); bool scanHexNumber(char& o_scannedNumber, int _expectedLength);
// Scans a single JavaScript token. /// Scans a single JavaScript token.
void scanToken(); void scanToken();
/// Skips all whitespace and @returns true if something was skipped.
bool skipWhitespace(); bool skipWhitespace();
Token::Value skipSingleLineComment(); Token::Value skipSingleLineComment();
Token::Value skipMultiLineComment(); Token::Value skipMultiLineComment();

4
libsolidity/Scope.cpp

@ -41,8 +41,8 @@ Declaration* Scope::resolveName(ASTString const& _name, bool _recursive) const
auto result = m_declarations.find(_name); auto result = m_declarations.find(_name);
if (result != m_declarations.end()) if (result != m_declarations.end())
return result->second; return result->second;
if (_recursive && m_outerScope) if (_recursive && m_enclosingScope)
return m_outerScope->resolveName(_name, true); return m_enclosingScope->resolveName(_name, true);
return nullptr; return nullptr;
} }

10
libsolidity/Scope.h

@ -32,18 +32,22 @@ namespace dev
namespace solidity namespace solidity
{ {
/**
* Container that stores mappings betwee names and declarations. It also contains a link to the
* enclosing scope.
*/
class Scope class Scope
{ {
public: public:
explicit Scope(Scope* _outerScope = nullptr): m_outerScope(_outerScope) {} explicit Scope(Scope* _enclosingScope = nullptr): m_enclosingScope(_enclosingScope) {}
/// Registers the declaration in the scope unless its name is already declared. Returns true iff /// Registers the declaration in the scope unless its name is already declared. Returns true iff
/// it was not yet declared. /// it was not yet declared.
bool registerDeclaration(Declaration& _declaration); bool registerDeclaration(Declaration& _declaration);
Declaration* resolveName(ASTString const& _name, bool _recursive = false) const; Declaration* resolveName(ASTString const& _name, bool _recursive = false) const;
Scope* getOuterScope() const { return m_outerScope; } Scope* getEnclosingScope() const { return m_enclosingScope; }
private: private:
Scope* m_outerScope; Scope* m_enclosingScope;
std::map<ASTString, Declaration*> m_declarations; std::map<ASTString, Declaration*> m_declarations;
}; };

36
libsolidity/SourceReferenceFormatter.cpp

@ -24,57 +24,59 @@
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
using namespace std;
namespace dev namespace dev
{ {
namespace solidity namespace solidity
{ {
void SourceReferenceFormatter::printSourceLocation(std::ostream& _stream, void SourceReferenceFormatter::printSourceLocation(ostream& _stream,
Location const& _location, Location const& _location,
Scanner const& _scanner) Scanner const& _scanner)
{ {
int startLine; int startLine;
int startColumn; int startColumn;
std::tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start); tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start);
_stream << "starting at line " << (startLine + 1) << ", column " << (startColumn + 1) << "\n"; _stream << "starting at line " << (startLine + 1) << ", column " << (startColumn + 1) << "\n";
int endLine; int endLine;
int endColumn; int endColumn;
std::tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end); tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end);
if (startLine == endLine) if (startLine == endLine)
{ {
_stream << _scanner.getLineAtPosition(_location.start) << std::endl _stream << _scanner.getLineAtPosition(_location.start) << endl
<< std::string(startColumn, ' ') << "^"; << string(startColumn, ' ') << "^";
if (endColumn > startColumn + 2) if (endColumn > startColumn + 2)
_stream << std::string(endColumn - startColumn - 2, '-'); _stream << string(endColumn - startColumn - 2, '-');
if (endColumn > startColumn + 1) if (endColumn > startColumn + 1)
_stream << "^"; _stream << "^";
_stream << std::endl; _stream << endl;
} }
else else
_stream << _scanner.getLineAtPosition(_location.start) << std::endl _stream << _scanner.getLineAtPosition(_location.start) << endl
<< std::string(startColumn, ' ') << "^\n" << string(startColumn, ' ') << "^\n"
<< "Spanning multiple lines.\n"; << "Spanning multiple lines.\n";
} }
void SourceReferenceFormatter::printSourcePosition(std::ostream& _stream, void SourceReferenceFormatter::printSourcePosition(ostream& _stream,
int _position, int _position,
const Scanner& _scanner) const Scanner& _scanner)
{ {
int line; int line;
int column; int column;
std::tie(line, column) = _scanner.translatePositionToLineColumn(_position); tie(line, column) = _scanner.translatePositionToLineColumn(_position);
_stream << "at line " << (line + 1) << ", column " << (column + 1) << std::endl _stream << "at line " << (line + 1) << ", column " << (column + 1) << endl
<< _scanner.getLineAtPosition(_position) << std::endl << _scanner.getLineAtPosition(_position) << endl
<< std::string(column, ' ') << "^" << std::endl; << string(column, ' ') << "^" << endl;
} }
void SourceReferenceFormatter::printExceptionInformation(std::ostream& _stream, void SourceReferenceFormatter::printExceptionInformation(ostream& _stream,
Exception const& _exception, Exception const& _exception,
std::string const& _name, string const& _name,
Scanner const& _scanner) Scanner const& _scanner)
{ {
_stream << _name; _stream << _name;
if (std::string const* description = boost::get_error_info<errinfo_comment>(_exception)) if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
_stream << ": " << *description; _stream << ": " << *description;
if (int const* position = boost::get_error_info<errinfo_sourcePosition>(_exception)) if (int const* position = boost::get_error_info<errinfo_sourcePosition>(_exception))

20
libsolidity/Token.h

@ -42,8 +42,7 @@
#pragma once #pragma once
#include <boost/assert.hpp> #include <cassert>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
@ -225,7 +224,7 @@ public:
// (e.g. "LT" for the token LT). // (e.g. "LT" for the token LT).
static char const* getName(Value tok) static char const* getName(Value tok)
{ {
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned assert(tok < NUM_TOKENS); // tok is unsigned
return m_name[tok]; return m_name[tok];
} }
@ -236,6 +235,7 @@ public:
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; } static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; }
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; } static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
static bool isTruncatingBinaryOp(Value op) { return BIT_OR <= op && op <= SHR; } static bool isTruncatingBinaryOp(Value op) { return BIT_OR <= op && op <= SHR; }
static bool isArithmeticOp(Value op) { return ADD <= op && op <= MOD; }
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; } static bool isCompareOp(Value op) { return EQ <= op && op <= IN; }
static bool isOrderedRelationalCompareOp(Value op) static bool isOrderedRelationalCompareOp(Value op)
{ {
@ -251,7 +251,7 @@ public:
static Value negateCompareOp(Value op) static Value negateCompareOp(Value op)
{ {
BOOST_ASSERT(isArithmeticCompareOp(op)); assert(isArithmeticCompareOp(op));
switch (op) switch (op)
{ {
case EQ: case EQ:
@ -267,14 +267,14 @@ public:
case GTE: case GTE:
return LT; return LT;
default: default:
BOOST_ASSERT(false); // should not get here assert(false); // should not get here
return op; return op;
} }
} }
static Value reverseCompareOp(Value op) static Value reverseCompareOp(Value op)
{ {
BOOST_ASSERT(isArithmeticCompareOp(op)); assert(isArithmeticCompareOp(op));
switch (op) switch (op)
{ {
case EQ: case EQ:
@ -290,14 +290,14 @@ public:
case GTE: case GTE:
return LTE; return LTE;
default: default:
BOOST_ASSERT(false); // should not get here assert(false); // should not get here
return op; return op;
} }
} }
static Value AssignmentToBinaryOp(Value op) static Value AssignmentToBinaryOp(Value op)
{ {
BOOST_ASSERT(isAssignmentOp(op) && op != ASSIGN); assert(isAssignmentOp(op) && op != ASSIGN);
return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR)); return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR));
} }
@ -311,7 +311,7 @@ public:
// have a (unique) string (e.g. an IDENTIFIER). // have a (unique) string (e.g. an IDENTIFIER).
static char const* toString(Value tok) static char const* toString(Value tok)
{ {
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. assert(tok < NUM_TOKENS); // tok is unsigned.
return m_string[tok]; return m_string[tok];
} }
@ -319,7 +319,7 @@ public:
// operators; returns 0 otherwise. // operators; returns 0 otherwise.
static int precedence(Value tok) static int precedence(Value tok)
{ {
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. assert(tok < NUM_TOKENS); // tok is unsigned.
return m_precedence[tok]; return m_precedence[tok];
} }

92
libsolidity/Types.cpp

@ -20,7 +20,9 @@
* Solidity data types * Solidity data types
*/ */
#include <cassert>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libsolidity/Types.h> #include <libsolidity/Types.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
@ -50,7 +52,7 @@ std::shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
else if (_typeToken == Token::BOOL) else if (_typeToken == Token::BOOL)
return std::make_shared<BoolType>(); return std::make_shared<BoolType>();
else else
BOOST_ASSERT(false); // @todo add other tyes assert(false); // @todo add other tyes
return std::shared_ptr<Type>(); return std::shared_ptr<Type>();
} }
@ -61,7 +63,7 @@ std::shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _
std::shared_ptr<Type> Type::fromMapping(Mapping const&) std::shared_ptr<Type> Type::fromMapping(Mapping const&)
{ {
BOOST_ASSERT(false); //@todo not yet implemented assert(false); //@todo not yet implemented
return std::shared_ptr<Type>(); return std::shared_ptr<Type>();
} }
@ -92,12 +94,12 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
{ {
if (isAddress()) if (isAddress())
_bits = 160; _bits = 160;
BOOST_ASSERT(_bits > 0 && _bits <= 256 && _bits % 8 == 0); assert(_bits > 0 && _bits <= 256 && _bits % 8 == 0);
} }
bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{ {
if (_convertTo.getCategory() != Category::INTEGER) if (_convertTo.getCategory() != getCategory())
return false; return false;
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (convertTo.m_bits < m_bits) if (convertTo.m_bits < m_bits)
@ -114,7 +116,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
return _convertTo.getCategory() == Category::INTEGER; return _convertTo.getCategory() == getCategory();
} }
bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const
@ -129,7 +131,24 @@ bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const
bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const
{ {
return _operator == Token::DELETE || (!isAddress() && _operator == Token::BIT_NOT); if (_operator == Token::DELETE)
return true;
if (isAddress())
return false;
if (_operator == Token::BIT_NOT)
return true;
if (isHash())
return false;
return _operator == Token::ADD || _operator == Token::SUB ||
_operator == Token::INC || _operator == Token::DEC;
}
bool IntegerType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
IntegerType const& other = dynamic_cast<IntegerType const&>(_other);
return other.m_bits == m_bits && other.m_modifier == m_modifier;
} }
std::string IntegerType::toString() const std::string IntegerType::toString() const
@ -140,11 +159,21 @@ std::string IntegerType::toString() const
return prefix + dev::toString(m_bits); return prefix + dev::toString(m_bits);
} }
bytes IntegerType::literalToBigEndian(Literal const& _literal) const
{
bigint value(_literal.getValue());
if (!isSigned() && value < 0)
return bytes(); // @todo this should already be caught by "smallestTypeforLiteral"
//@todo check that the number of bits is correct
//@todo does "toCompactBigEndian" work for signed numbers?
return toCompactBigEndian(value);
}
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
// conversion to integer is fine, but not to address // conversion to integer is fine, but not to address
// this is an example of explicit conversions being not transitive (though implicit should be) // this is an example of explicit conversions being not transitive (though implicit should be)
if (_convertTo.getCategory() == Category::INTEGER) if (_convertTo.getCategory() == getCategory())
{ {
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (!convertTo.isAddress()) if (!convertTo.isAddress())
@ -153,22 +182,55 @@ bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return isImplicitlyConvertibleTo(_convertTo); return isImplicitlyConvertibleTo(_convertTo);
} }
bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const bytes BoolType::literalToBigEndian(Literal const& _literal) const
{ {
if (_convertTo.getCategory() != Category::CONTRACT) if (_literal.getToken() == Token::TRUE_LITERAL)
return bytes(1, 1);
else if (_literal.getToken() == Token::FALSE_LITERAL)
return bytes(1, 0);
else
return NullBytes;
}
bool ContractType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false; return false;
ContractType const& convertTo = dynamic_cast<ContractType const&>(_convertTo); ContractType const& other = dynamic_cast<ContractType const&>(_other);
return &m_contract == &convertTo.m_contract; return other.m_contract == m_contract;
} }
bool StructType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool StructType::operator==(Type const& _other) const
{ {
if (_convertTo.getCategory() != Category::STRUCT) if (_other.getCategory() != getCategory())
return false; return false;
StructType const& convertTo = dynamic_cast<StructType const&>(_convertTo); StructType const& other = dynamic_cast<StructType const&>(_other);
return &m_struct == &convertTo.m_struct; return other.m_struct == m_struct;
} }
bool FunctionType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
return other.m_function == m_function;
}
bool MappingType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
MappingType const& other = dynamic_cast<MappingType const&>(_other);
return *other.m_keyType == *m_keyType && *other.m_valueType == *m_valueType;
}
bool TypeType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
TypeType const& other = dynamic_cast<TypeType const&>(_other);
return *getActualType() == *other.getActualType();
}
} }
} }

68
libsolidity/Types.h

@ -25,7 +25,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <boost/assert.hpp> #include <libdevcore/Common.h>
#include <libsolidity/ASTForward.h> #include <libsolidity/ASTForward.h>
#include <libsolidity/Token.h> #include <libsolidity/Token.h>
@ -36,6 +36,9 @@ namespace solidity
// @todo realMxN, string<N>, mapping // @todo realMxN, string<N>, mapping
/**
* Abstract base class that forms the root of the type hierarchy.
*/
class Type: private boost::noncopyable class Type: private boost::noncopyable
{ {
public: public:
@ -44,15 +47,19 @@ public:
INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE
}; };
//! factory functions that convert an AST TypeName to a Type. ///@{
///@name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type.
static std::shared_ptr<Type> fromElementaryTypeName(Token::Value _typeToken); static std::shared_ptr<Type> fromElementaryTypeName(Token::Value _typeToken);
static std::shared_ptr<Type> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); static std::shared_ptr<Type> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static std::shared_ptr<Type> fromMapping(Mapping const& _typeName); static std::shared_ptr<Type> fromMapping(Mapping const& _typeName);
/// @}
/// Auto-detect the proper type for a literal
static std::shared_ptr<Type> forLiteral(Literal const& _literal); static std::shared_ptr<Type> forLiteral(Literal const& _literal);
virtual Category getCategory() const = 0; virtual Category getCategory() const = 0;
virtual bool isImplicitlyConvertibleTo(Type const&) const { return false; } virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
return isImplicitlyConvertibleTo(_convertTo); return isImplicitlyConvertibleTo(_convertTo);
@ -60,9 +67,16 @@ public:
virtual bool acceptsBinaryOperator(Token::Value) const { return false; } virtual bool acceptsBinaryOperator(Token::Value) const { return false; }
virtual bool acceptsUnaryOperator(Token::Value) const { return false; } virtual bool acceptsUnaryOperator(Token::Value) const { return false; }
virtual bool operator==(Type const& _other) const { return getCategory() == _other.getCategory(); }
virtual bool operator!=(Type const& _other) const { return !this->operator ==(_other); }
virtual std::string toString() const = 0; virtual std::string toString() const = 0;
virtual bytes literalToBigEndian(Literal const&) const { return NullBytes; }
}; };
/**
* Any kind of integer type including hash and address.
*/
class IntegerType: public Type class IntegerType: public Type
{ {
public: public:
@ -81,7 +95,10 @@ public:
virtual bool acceptsBinaryOperator(Token::Value _operator) const override; virtual bool acceptsBinaryOperator(Token::Value _operator) const override;
virtual bool acceptsUnaryOperator(Token::Value _operator) const override; virtual bool acceptsUnaryOperator(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override; virtual std::string toString() const override;
virtual bytes literalToBigEndian(Literal const& _literal) const override;
int getNumBits() const { return m_bits; } int getNumBits() const { return m_bits; }
bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; } bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; }
@ -93,14 +110,13 @@ private:
Modifier m_modifier; Modifier m_modifier;
}; };
/**
* The boolean type.
*/
class BoolType: public Type class BoolType: public Type
{ {
public: public:
virtual Category getCategory() const { return Category::BOOL; } virtual Category getCategory() const { return Category::BOOL; }
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override
{
return _convertTo.getCategory() == Category::BOOL;
}
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool acceptsBinaryOperator(Token::Value _operator) const override virtual bool acceptsBinaryOperator(Token::Value _operator) const override
{ {
@ -110,15 +126,21 @@ public:
{ {
return _operator == Token::NOT || _operator == Token::DELETE; return _operator == Token::NOT || _operator == Token::DELETE;
} }
virtual std::string toString() const override { return "bool"; } virtual std::string toString() const override { return "bool"; }
virtual bytes literalToBigEndian(Literal const& _literal) const override;
}; };
/**
* The type of a contract instance, there is one distinct type for each contract definition.
*/
class ContractType: public Type class ContractType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::CONTRACT; } virtual Category getCategory() const override { return Category::CONTRACT; }
ContractType(ContractDefinition const& _contract): m_contract(_contract) {} ContractType(ContractDefinition const& _contract): m_contract(_contract) {}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const;
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override { return "contract{...}"; } virtual std::string toString() const override { return "contract{...}"; }
@ -126,17 +148,20 @@ private:
ContractDefinition const& m_contract; ContractDefinition const& m_contract;
}; };
/**
* The type of a struct instance, there is one distinct type per struct definition.
*/
class StructType: public Type class StructType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::STRUCT; } virtual Category getCategory() const override { return Category::STRUCT; }
StructType(StructDefinition const& _struct): m_struct(_struct) {} StructType(StructDefinition const& _struct): m_struct(_struct) {}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const;
virtual bool acceptsUnaryOperator(Token::Value _operator) const override virtual bool acceptsUnaryOperator(Token::Value _operator) const override
{ {
return _operator == Token::DELETE; return _operator == Token::DELETE;
} }
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override { return "struct{...}"; } virtual std::string toString() const override { return "struct{...}"; }
@ -144,6 +169,9 @@ private:
StructDefinition const& m_struct; StructDefinition const& m_struct;
}; };
/**
* The type of a function, there is one distinct type per function definition.
*/
class FunctionType: public Type class FunctionType: public Type
{ {
public: public:
@ -154,10 +182,15 @@ public:
virtual std::string toString() const override { return "function(...)returns(...)"; } virtual std::string toString() const override { return "function(...)returns(...)"; }
virtual bool operator==(Type const& _other) const override;
private: private:
FunctionDefinition const& m_function; FunctionDefinition const& m_function;
}; };
/**
* The type of a mapping, there is one distinct type per key/value type pair.
*/
class MappingType: public Type class MappingType: public Type
{ {
public: public:
@ -165,19 +198,30 @@ public:
MappingType() {} MappingType() {}
virtual std::string toString() const override { return "mapping(...=>...)"; } virtual std::string toString() const override { return "mapping(...=>...)"; }
virtual bool operator==(Type const& _other) const override;
private: private:
//@todo std::shared_ptr<Type const> m_keyType;
std::shared_ptr<Type const> m_valueType;
}; };
//@todo should be changed into "empty anonymous struct" /**
* The void type, can only be implicitly used as the type that is returned by functions without
* return parameters.
*/
class VoidType: public Type class VoidType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::VOID; } virtual Category getCategory() const override { return Category::VOID; }
VoidType() {} VoidType() {}
virtual std::string toString() const override { return "void"; } virtual std::string toString() const override { return "void"; }
}; };
/**
* The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example
* of a TypeType.
*/
class TypeType: public Type class TypeType: public Type
{ {
public: public:
@ -186,6 +230,8 @@ public:
std::shared_ptr<Type const> const& getActualType() const { return m_actualType; } std::shared_ptr<Type const> const& getActualType() const { return m_actualType; }
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
private: private:

43
solc/main.cpp

@ -31,6 +31,7 @@
#include <libsolidity/ASTPrinter.h> #include <libsolidity/ASTPrinter.h>
#include <libsolidity/NameAndTypeResolver.h> #include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
using namespace dev; using namespace dev;
@ -55,6 +56,37 @@ void version()
exit(0); exit(0);
} }
/**
* Helper class that extracts the first expression in an AST.
*/
class FirstExpressionExtractor: private ASTVisitor
{
public:
FirstExpressionExtractor(ASTNode& _node): m_expression(nullptr) { _node.accept(*this); }
Expression* getExpression() const { return m_expression; }
private:
virtual bool visit(Expression& _expression) override { return checkExpression(_expression); }
virtual bool visit(Assignment& _expression) override { return checkExpression(_expression); }
virtual bool visit(UnaryOperation& _expression) override { return checkExpression(_expression); }
virtual bool visit(BinaryOperation& _expression) override { return checkExpression(_expression); }
virtual bool visit(FunctionCall& _expression) override { return checkExpression(_expression); }
virtual bool visit(MemberAccess& _expression) override { return checkExpression(_expression); }
virtual bool visit(IndexAccess& _expression) override { return checkExpression(_expression); }
virtual bool visit(PrimaryExpression& _expression) override { return checkExpression(_expression); }
virtual bool visit(Identifier& _expression) override { return checkExpression(_expression); }
virtual bool visit(ElementaryTypeNameExpression& _expression) override { return checkExpression(_expression); }
virtual bool visit(Literal& _expression) override { return checkExpression(_expression); }
bool checkExpression(Expression& _expression)
{
if (m_expression == nullptr)
m_expression = &_expression;
return false;
}
private:
Expression* m_expression;
};
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
std::string infile; std::string infile;
@ -113,5 +145,16 @@ int main(int argc, char** argv)
std::cout << "Syntax tree for the contract:" << std::endl; std::cout << "Syntax tree for the contract:" << std::endl;
dev::solidity::ASTPrinter printer(ast, sourceCode); dev::solidity::ASTPrinter printer(ast, sourceCode);
printer.print(std::cout); printer.print(std::cout);
FirstExpressionExtractor extractor(*ast);
CompilerContext context;
ExpressionCompiler compiler(context);
compiler.compile(*extractor.getExpression());
bytes instructions = compiler.getAssembledBytecode();
// debug
std::cout << "Bytecode for the first expression: " << std::endl;
std::cout << eth::disassemble(instructions) << std::endl;
return 0; return 0;
} }

229
test/solidityCompiler.cpp

@ -0,0 +1,229 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Unit tests for the name and type resolution of the solidity parser.
*/
#include <string>
#include <libdevcore/Log.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/AST.h>
#include <boost/test/unit_test.hpp>
namespace dev
{
namespace solidity
{
namespace test
{
namespace
{
/**
* Helper class that extracts the first expression in an AST.
*/
class FirstExpressionExtractor: private ASTVisitor
{
public:
FirstExpressionExtractor(ASTNode& _node): m_expression(nullptr) { _node.accept(*this); }
Expression* getExpression() const { return m_expression; }
private:
virtual bool visit(Expression& _expression) override { return checkExpression(_expression); }
virtual bool visit(Assignment& _expression) override { return checkExpression(_expression); }
virtual bool visit(UnaryOperation& _expression) override { return checkExpression(_expression); }
virtual bool visit(BinaryOperation& _expression) override { return checkExpression(_expression); }
virtual bool visit(FunctionCall& _expression) override { return checkExpression(_expression); }
virtual bool visit(MemberAccess& _expression) override { return checkExpression(_expression); }
virtual bool visit(IndexAccess& _expression) override { return checkExpression(_expression); }
virtual bool visit(PrimaryExpression& _expression) override { return checkExpression(_expression); }
virtual bool visit(Identifier& _expression) override { return checkExpression(_expression); }
virtual bool visit(ElementaryTypeNameExpression& _expression) override { return checkExpression(_expression); }
virtual bool visit(Literal& _expression) override { return checkExpression(_expression); }
bool checkExpression(Expression& _expression)
{
if (m_expression == nullptr)
m_expression = &_expression;
return false;
}
private:
Expression* m_expression;
};
bytes compileFirstExpression(std::string const& _sourceCode)
{
Parser parser;
ASTPointer<ContractDefinition> contract;
BOOST_REQUIRE_NO_THROW(contract = parser.parse(std::make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver;
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
FirstExpressionExtractor extractor(*contract);
BOOST_REQUIRE(extractor.getExpression() != nullptr);
CompilerContext context;
ExpressionCompiler compiler(context);
compiler.compile(*extractor.getExpression());
bytes instructions = compiler.getAssembledBytecode();
// debug
//std::cout << eth::disassemble(instructions) << std::endl;
return instructions;
}
} // end anonymous namespace
BOOST_AUTO_TEST_SUITE(SolidityExpressionCompiler)
BOOST_AUTO_TEST_CASE(literal_true)
{
char const* sourceCode = "contract test {\n"
" function f() { var x = true; }"
"}\n";
bytes code = compileFirstExpression(sourceCode);
bytes expectation({byte(eth::Instruction::PUSH1), 0x1});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
BOOST_AUTO_TEST_CASE(literal_false)
{
char const* sourceCode = "contract test {\n"
" function f() { var x = false; }"
"}\n";
bytes code = compileFirstExpression(sourceCode);
bytes expectation({byte(eth::Instruction::PUSH1), 0x0});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
BOOST_AUTO_TEST_CASE(int_literal)
{
char const* sourceCode = "contract test {\n"
" function f() { var x = 0x12345678901234567890; }"
"}\n";
bytes code = compileFirstExpression(sourceCode);
bytes expectation({byte(eth::Instruction::PUSH10), 0x12, 0x34, 0x56, 0x78, 0x90,
0x12, 0x34, 0x56, 0x78, 0x90});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
BOOST_AUTO_TEST_CASE(comparison)
{
char const* sourceCode = "contract test {\n"
" function f() { var x = (0x10aa < 0x11aa) != true; }"
"}\n";
bytes code = compileFirstExpression(sourceCode);
bytes expectation({byte(eth::Instruction::PUSH2), 0x10, 0xaa,
byte(eth::Instruction::PUSH2), 0x11, 0xaa,
byte(eth::Instruction::GT),
byte(eth::Instruction::PUSH1), 0x1,
byte(eth::Instruction::EQ),
byte(eth::Instruction::NOT)});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
BOOST_AUTO_TEST_CASE(short_circuiting)
{
char const* sourceCode = "contract test {\n"
" function f() { var x = (10 + 8 >= 4 || 2 != 9) != true; }"
"}\n";
bytes code = compileFirstExpression(sourceCode);
bytes expectation({byte(eth::Instruction::PUSH1), 0xa,
byte(eth::Instruction::PUSH1), 0x8,
byte(eth::Instruction::ADD),
byte(eth::Instruction::PUSH1), 0x4,
byte(eth::Instruction::GT),
byte(eth::Instruction::NOT), // after this we have 10 + 8 >= 4
byte(eth::Instruction::DUP1),
byte(eth::Instruction::PUSH1), 0x14,
byte(eth::Instruction::JUMPI), // short-circuit if it is true
byte(eth::Instruction::PUSH1), 0x2,
byte(eth::Instruction::PUSH1), 0x9,
byte(eth::Instruction::EQ),
byte(eth::Instruction::NOT), // after this we have 2 != 9
byte(eth::Instruction::JUMPDEST),
byte(eth::Instruction::PUSH1), 0x1,
byte(eth::Instruction::EQ),
byte(eth::Instruction::NOT)});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
BOOST_AUTO_TEST_CASE(arithmetics)
{
char const* sourceCode = "contract test {\n"
" function f() { var x = (1 * (2 / (3 % (4 + (5 - (6 | (7 & (8 ^ 9)))))))); }"
"}\n";
bytes code = compileFirstExpression(sourceCode);
bytes expectation({byte(eth::Instruction::PUSH1), 0x1,
byte(eth::Instruction::PUSH1), 0x2,
byte(eth::Instruction::PUSH1), 0x3,
byte(eth::Instruction::PUSH1), 0x4,
byte(eth::Instruction::PUSH1), 0x5,
byte(eth::Instruction::PUSH1), 0x6,
byte(eth::Instruction::PUSH1), 0x7,
byte(eth::Instruction::PUSH1), 0x8,
byte(eth::Instruction::PUSH1), 0x9,
byte(eth::Instruction::XOR),
byte(eth::Instruction::AND),
byte(eth::Instruction::OR),
byte(eth::Instruction::SWAP1),
byte(eth::Instruction::SUB),
byte(eth::Instruction::ADD),
byte(eth::Instruction::MOD),
byte(eth::Instruction::DIV),
byte(eth::Instruction::MUL)});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
BOOST_AUTO_TEST_CASE(unary_operators)
{
char const* sourceCode = "contract test {\n"
" function f() { var x = !(~+-(--(++1++)--) == 2); }"
"}\n";
bytes code = compileFirstExpression(sourceCode);
bytes expectation({byte(eth::Instruction::PUSH1), 0x1,
byte(eth::Instruction::PUSH1), 0x1,
byte(eth::Instruction::ADD),
byte(eth::Instruction::PUSH1), 0x1,
byte(eth::Instruction::SWAP1),
byte(eth::Instruction::SUB),
byte(eth::Instruction::PUSH1), 0x0,
byte(eth::Instruction::SUB),
byte(eth::Instruction::NOT),
byte(eth::Instruction::PUSH1), 0x2,
byte(eth::Instruction::EQ),
byte(eth::Instruction::ISZERO)});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
BOOST_AUTO_TEST_SUITE_END()
}
}
} // end namespaces

2
test/solidityNameAndTypeResolution.cpp

@ -38,7 +38,7 @@ namespace test
namespace namespace
{ {
void parseTextAndResolveNames(const std::string& _source) void parseTextAndResolveNames(std::string const& _source)
{ {
Parser parser; Parser parser;
ASTPointer<ContractDefinition> contract = parser.parse( ASTPointer<ContractDefinition> contract = parser.parse(

2
test/solidityParser.cpp

@ -37,7 +37,7 @@ namespace test
namespace namespace
{ {
ASTPointer<ASTNode> parseText(const std::string& _source) ASTPointer<ASTNode> parseText(std::string const& _source)
{ {
Parser parser; Parser parser;
return parser.parse(std::make_shared<Scanner>(CharStream(_source))); return parser.parse(std::make_shared<Scanner>(CharStream(_source)));

8
test/vm.cpp

@ -535,7 +535,9 @@ void doTests(json_spirit::mValue& v, bool _fillin)
auto argc = boost::unit_test::framework::master_test_suite().argc; auto argc = boost::unit_test::framework::master_test_suite().argc;
auto argv = boost::unit_test::framework::master_test_suite().argv; auto argv = boost::unit_test::framework::master_test_suite().argv;
auto useJit = argc >= 2 && std::string(argv[1]) == "--jit"; auto useJit = false;
for (auto i = 0; i < argc && !useJit; ++i)
useJit |= std::string(argv[i]) == "--jit";
auto vmKind = useJit ? VMFace::JIT : VMFace::Interpreter; auto vmKind = useJit ? VMFace::JIT : VMFace::Interpreter;
dev::test::FakeExtVM fev; dev::test::FakeExtVM fev;
@ -585,8 +587,8 @@ void doTests(json_spirit::mValue& v, bool _fillin)
cnote << "Execution time: " cnote << "Execution time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(testDuration).count() << std::chrono::duration_cast<std::chrono::milliseconds>(testDuration).count()
<< " ms"; << " ms";
break;
} }
break;
} }
auto gas = vm->gas(); auto gas = vm->gas();
@ -826,7 +828,7 @@ BOOST_AUTO_TEST_CASE(vmSystemOperationsTest)
BOOST_AUTO_TEST_CASE(userDefinedFile) BOOST_AUTO_TEST_CASE(userDefinedFile)
{ {
if (boost::unit_test::framework::master_test_suite().argc == 2) if (boost::unit_test::framework::master_test_suite().argc >= 2)
{ {
string filename = boost::unit_test::framework::master_test_suite().argv[1]; string filename = boost::unit_test::framework::master_test_suite().argv[1];
int currentVerbosity = g_logVerbosity; int currentVerbosity = g_logVerbosity;

12
test/vmArithmeticTestFiller.json

@ -1319,7 +1319,7 @@
}, },
"neg0": { "bnot0": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -1347,7 +1347,7 @@
} }
}, },
"neg1": { "bnot1": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -1375,7 +1375,7 @@
} }
}, },
"neg2": { "bnot2": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -1403,7 +1403,7 @@
} }
}, },
"neg3": { "bnot3": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -1431,7 +1431,7 @@
} }
}, },
"neg4": { "bnot4": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",
@ -1459,7 +1459,7 @@
} }
}, },
"neg5": { "bnot5": {
"env" : { "env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0", "currentNumber" : "0",

336
test/vmBitwiseLogicOperationTestFiller.json

@ -1224,6 +1224,342 @@
"gasPrice" : "100000000000000", "gasPrice" : "100000000000000",
"gas" : "10000" "gas" : "10000"
} }
},
"signextend_bitIsSet": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "0x62122ff4600016600057",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"signextend_BitIsNotSet": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "0x62122f6a600016600057",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"signextend_BitIsSetInHigherByte": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "0x6212faf4600116600057",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"signextend_BitIsNotSetInHigherByte": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "0x62126af4600116600057",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"signextendInvalidByteNumber": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "0x62126af4605016600057",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"signextend_00": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (SIGNEXTEND 0 0) } ",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"signextend_BigByte_0": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (SIGNEXTEND 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0) } ",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"signextend_0_BigByte": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (SIGNEXTEND 0 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) } ",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"signextend_BigByteBigByte": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (SIGNEXTEND 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) } ",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"signextend_AlmostBiggestByte": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 0 ]] (SIGNEXTEND 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe) } ",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"signextend_bigBytePlus1": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "0x66f000000000000161ffff16600057",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
},
"signextend_BigBytePlus1_2": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "0x60ff68f0000000000000000116600057",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "1000000000000000000",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "10000"
}
} }
} }

Loading…
Cancel
Save