From ca49fe4897e53507fddbedd3024ee5ad09c0066b Mon Sep 17 00:00:00 2001 From: artur-zawlocki Date: Thu, 2 Oct 2014 11:00:28 +0200 Subject: [PATCH 1/5] initial implementation of JUMP/JUMPI (untested) --- evmcc/Compiler.cpp | 171 ++++++++++++++++++++++++++++++++++++++++++++- evmcc/Compiler.h | 16 +++++ 2 files changed, 184 insertions(+), 3 deletions(-) diff --git a/evmcc/Compiler.cpp b/evmcc/Compiler.cpp index 2a13e4a64..87c96a20d 100644 --- a/evmcc/Compiler.cpp +++ b/evmcc/Compiler.cpp @@ -39,6 +39,117 @@ Compiler::Compiler() Types.WordLowPrecision = llvm::Type::getIntNTy(context, 64); } +llvm::BasicBlock* Compiler::getOrCreateBasicBlockAtPC(ProgramCounter pc) +{ + llvm::BasicBlock* block = nullptr; + auto blockIter = basicBlocks.find(pc); + if (blockIter == basicBlocks.cend()) + { + // Create a basic block at targetPC. + std::ostringstream oss; + oss << "instr." << pc; + block = llvm::BasicBlock::Create(llvm::getGlobalContext(), oss.str()); + basicBlocks[pc] = block; + } + else + { + block = blockIter->second; + } + return block; +} + +void Compiler::createBasicBlocks(const dev::bytes& bytecode) +{ + for (auto curr = bytecode.cbegin(); curr != bytecode.cend(); ++curr) + { + using dev::eth::Instruction; + + auto inst = static_cast(*curr); + switch (inst) + { + case Instruction::PUSH1: + case Instruction::PUSH2: + case Instruction::PUSH3: + case Instruction::PUSH4: + case Instruction::PUSH5: + case Instruction::PUSH6: + case Instruction::PUSH7: + case Instruction::PUSH8: + case Instruction::PUSH9: + case Instruction::PUSH10: + case Instruction::PUSH11: + case Instruction::PUSH12: + case Instruction::PUSH13: + case Instruction::PUSH14: + case Instruction::PUSH15: + case Instruction::PUSH16: + case Instruction::PUSH17: + case Instruction::PUSH18: + case Instruction::PUSH19: + case Instruction::PUSH20: + case Instruction::PUSH21: + case Instruction::PUSH22: + case Instruction::PUSH23: + case Instruction::PUSH24: + case Instruction::PUSH25: + case Instruction::PUSH26: + case Instruction::PUSH27: + case Instruction::PUSH28: + case Instruction::PUSH29: + case Instruction::PUSH30: + case Instruction::PUSH31: + case Instruction::PUSH32: + { + auto numBytes = static_cast(inst) - static_cast(Instruction::PUSH1) + 1; + auto next = curr + numBytes + 1; + if (next == bytecode.cend()) + break; + + auto nextInst = static_cast(*next); + + if (nextInst == Instruction::JUMP || nextInst == Instruction::JUMPI) + { + // Compute target PC of the jump. + dev::u256 val = 0; + for (auto iter = curr + 1; iter < next; ++iter) + { + val <<= 8; + val |= *iter; + } + + // Create a block for the JUMP target. + ProgramCounter targetPC = val.convert_to(); + auto targetBlock = getOrCreateBasicBlockAtPC(targetPC); + + ProgramCounter jumpPC = (next - bytecode.cbegin()); + jumpTargets[jumpPC] = targetBlock; + + // Create a block following the JUMP. + if (next + 1 < bytecode.cend()) + { + ProgramCounter nextPC = (next + 1 - bytecode.cbegin()); + getOrCreateBasicBlockAtPC(nextPC); + } + + curr += 1; // skip over JUMP + } + + curr += numBytes; + break; + } + + case Instruction::JUMP: + case Instruction::JUMPI: + { + std::cerr << "JUMP/JUMPI at " << (curr - bytecode.cbegin()) << " not preceded by PUSH\n"; + std::exit(1); + } + + default: + break; + } + } +} std::unique_ptr Compiler::compile(const dev::bytes& bytecode) { @@ -52,23 +163,77 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) auto mainFuncType = FunctionType::get(llvm::Type::getInt32Ty(context), false); auto mainFunc = Function::Create(mainFuncType, Function::ExternalLinkage, "main", module.get()); - auto entryBlock = BasicBlock::Create(context, "entry", mainFunc); - builder.SetInsertPoint(entryBlock); - // Init stack and memory auto stack = Stack(builder, module.get()); auto memory = Memory(builder, module.get()); auto ext = Ext(builder); + // Create the basic blocks. + auto entryBlock = BasicBlock::Create(context, "entry", mainFunc); + basicBlocks[0] = entryBlock; + createBasicBlocks(bytecode); + + BasicBlock* currentBlock = nullptr; + for (auto pc = bytecode.cbegin(); pc != bytecode.cend(); ++pc) { using dev::eth::Instruction; + ProgramCounter currentPC = pc - bytecode.cbegin(); + + auto blockIter = basicBlocks.find(currentPC); + if (blockIter != basicBlocks.end()) + { + auto nextBlock = blockIter->second; + if (currentBlock != nullptr) + { + // Terminate the current block by jumping to the next one. + builder.CreateBr(nextBlock); + } + // Insert the next block into the main function. + if (nextBlock != entryBlock) + mainFunc->getBasicBlockList().push_back(nextBlock); + builder.SetInsertPoint(nextBlock); + currentBlock = nextBlock; + } + + assert(currentBlock != nullptr); + auto inst = static_cast(*pc); switch (inst) { + case Instruction::JUMP: + { + // The target address is computed at compile time, + // just pop it without looking... + stack.pop(); + + auto targetBlock = jumpTargets[currentPC]; + builder.CreateBr(targetBlock); + + currentBlock = nullptr; + break; + } + + case Instruction::JUMPI: + { + assert(pc + 1 < bytecode.cend()); + + // The target address is computed at compile time, + // just pop it without looking... + stack.pop(); + + auto cond = stack.pop(); + auto targetBlock = jumpTargets[currentPC]; + auto followBlock = basicBlocks[currentPC + 1]; + builder.CreateCondBr(cond, targetBlock, followBlock); + + currentBlock = nullptr; + break; + } + case Instruction::ADD: { auto lhs = stack.pop(); diff --git a/evmcc/Compiler.h b/evmcc/Compiler.h index 5a07e685d..6e9fd8443 100644 --- a/evmcc/Compiler.h +++ b/evmcc/Compiler.h @@ -12,10 +12,26 @@ class Compiler { public: + using ProgramCounter = uint64_t; + Compiler(); std::unique_ptr compile(const dev::bytes& bytecode); +private: + + llvm::BasicBlock* getOrCreateBasicBlockAtPC(ProgramCounter pc); + void createBasicBlocks(const dev::bytes& bytecode); + + /** + * Maps a program counter pc to a basic block which starts at pc (if any). + */ + std::map basicBlocks; + + /** + * Maps a pc at which there is a JUMP or JUMPI to the target block of the jump. + */ + std::map jumpTargets; }; } From d11127c94d8900b84983339eb0d1d487a2643621 Mon Sep 17 00:00:00 2001 From: artur-zawlocki Date: Thu, 2 Oct 2014 11:14:09 +0200 Subject: [PATCH 2/5] Added Module* constructor argument to Memory and Ext. --- evmcc/Compiler.cpp | 11 +++++------ evmcc/Ext.cpp | 5 ++--- evmcc/Ext.h | 4 ++-- evmcc/Memory.cpp | 4 +--- evmcc/Memory.h | 2 +- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/evmcc/Compiler.cpp b/evmcc/Compiler.cpp index 0dae87213..6c5229056 100644 --- a/evmcc/Compiler.cpp +++ b/evmcc/Compiler.cpp @@ -166,17 +166,16 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) auto mainFuncType = FunctionType::get(builder.getInt64Ty(), false); auto mainFunc = Function::Create(mainFuncType, Function::ExternalLinkage, "main", module.get()); - // Init stack and memory - auto stack = Stack(builder, module.get()); - auto memory = Memory(builder); - - auto ext = Ext(builder); - // Create the basic blocks. auto entryBlock = BasicBlock::Create(context, "entry", mainFunc); basicBlocks[0] = entryBlock; createBasicBlocks(bytecode); + // Init runtime structures. + auto stack = Stack(builder, module.get()); + auto memory = Memory(builder, module.get()); + auto ext = Ext(builder, module.get()); + auto userRet = false; auto finished = false; diff --git a/evmcc/Ext.cpp b/evmcc/Ext.cpp index 0c38f73d3..e3bb17af6 100644 --- a/evmcc/Ext.cpp +++ b/evmcc/Ext.cpp @@ -43,10 +43,9 @@ struct ExtData const byte* calldata; }; -Ext::Ext(llvm::IRBuilder<>& _builder) +Ext::Ext(llvm::IRBuilder<>& _builder, llvm::Module* module) : m_builder(_builder) { - auto module = m_builder.GetInsertBlock()->getParent()->getParent(); auto&& ctx = _builder.getContext(); auto i256Ty = m_builder.getIntNTy(256); @@ -148,4 +147,4 @@ EXPORT void ext_calldataload(i256* _index, i256* _value) } -} \ No newline at end of file +} diff --git a/evmcc/Ext.h b/evmcc/Ext.h index a579dca81..8c08acdfe 100644 --- a/evmcc/Ext.h +++ b/evmcc/Ext.h @@ -13,7 +13,7 @@ namespace evmcc class Ext { public: - Ext(llvm::IRBuilder<>& _builder); + Ext(llvm::IRBuilder<>& _builder, llvm::Module* module); static void init(std::unique_ptr _ext); llvm::Value* store(llvm::Value* _index); @@ -43,4 +43,4 @@ private: }; -} \ No newline at end of file +} diff --git a/evmcc/Memory.cpp b/evmcc/Memory.cpp index 2a1a0f727..fd7727b59 100644 --- a/evmcc/Memory.cpp +++ b/evmcc/Memory.cpp @@ -26,13 +26,11 @@ using MemoryImpl = dev::bytes; static MemoryImpl* evmccrt_memory; -Memory::Memory(llvm::IRBuilder<>& _builder) +Memory::Memory(llvm::IRBuilder<>& _builder, llvm::Module* module) : m_builder(_builder) { auto voidTy = m_builder.getVoidTy(); auto i64Ty = m_builder.getInt64Ty(); - auto module = _builder.GetInsertBlock()->getParent()->getParent(); - auto memRequireTy = llvm::FunctionType::get(m_builder.getInt8PtrTy(), i64Ty, false); m_memRequire = llvm::Function::Create(memRequireTy, diff --git a/evmcc/Memory.h b/evmcc/Memory.h index b36386325..87ce27ed0 100644 --- a/evmcc/Memory.h +++ b/evmcc/Memory.h @@ -10,7 +10,7 @@ namespace evmcc class Memory { public: - Memory(llvm::IRBuilder<>& _builder); + Memory(llvm::IRBuilder<>& _builder, llvm::Module* module); static const dev::bytes& init(); From 0d283dfa7c62811400e219353a39731fd3892c7c Mon Sep 17 00:00:00 2001 From: artur-zawlocki Date: Thu, 2 Oct 2014 11:45:54 +0200 Subject: [PATCH 3/5] fixed implementation of JUMPI (cond casted to bool) --- evmcc/Compiler.cpp | 76 ++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/evmcc/Compiler.cpp b/evmcc/Compiler.cpp index 6c5229056..1ce211e98 100644 --- a/evmcc/Compiler.cpp +++ b/evmcc/Compiler.cpp @@ -169,6 +169,7 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) // Create the basic blocks. auto entryBlock = BasicBlock::Create(context, "entry", mainFunc); basicBlocks[0] = entryBlock; + builder.SetInsertPoint(entryBlock); createBasicBlocks(bytecode); // Init runtime structures. @@ -179,7 +180,7 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) auto userRet = false; auto finished = false; - BasicBlock* currentBlock = nullptr; + BasicBlock* currentBlock = entryBlock; for (auto pc = bytecode.cbegin(); pc != bytecode.cend(); ++pc) { @@ -188,17 +189,13 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) ProgramCounter currentPC = pc - bytecode.cbegin(); auto blockIter = basicBlocks.find(currentPC); - if (blockIter != basicBlocks.end()) + if (currentPC > 0 && blockIter != basicBlocks.end()) { auto nextBlock = blockIter->second; - if (currentBlock != nullptr) - { - // Terminate the current block by jumping to the next one. - builder.CreateBr(nextBlock); - } + // Terminate the current block by jumping to the next one. + builder.CreateBr(nextBlock); // Insert the next block into the main function. - if (nextBlock != entryBlock) - mainFunc->getBasicBlockList().push_back(nextBlock); + mainFunc->getBasicBlockList().push_back(nextBlock); builder.SetInsertPoint(nextBlock); currentBlock = nextBlock; } @@ -209,36 +206,6 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) switch (inst) { - case Instruction::JUMP: - { - // The target address is computed at compile time, - // just pop it without looking... - stack.pop(); - - auto targetBlock = jumpTargets[currentPC]; - builder.CreateBr(targetBlock); - - currentBlock = nullptr; - break; - } - - case Instruction::JUMPI: - { - assert(pc + 1 < bytecode.cend()); - - // The target address is computed at compile time, - // just pop it without looking... - stack.pop(); - - auto cond = stack.pop(); - auto targetBlock = jumpTargets[currentPC]; - auto followBlock = basicBlocks[currentPC + 1]; - builder.CreateCondBr(cond, targetBlock, followBlock); - - currentBlock = nullptr; - break; - } - case Instruction::ADD: { auto lhs = stack.pop(); @@ -464,6 +431,37 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) break; } + case Instruction::JUMP: + { + // The target address is computed at compile time, + // just pop it without looking... + stack.pop(); + + auto targetBlock = jumpTargets[currentPC]; + builder.CreateBr(targetBlock); + + currentBlock = nullptr; + break; + } + + case Instruction::JUMPI: + { + assert(pc + 1 < bytecode.cend()); + + // The target address is computed at compile time, + // just pop it without looking... + stack.pop(); + + auto top = stack.pop(); + auto cond = builder.CreateTrunc(top, builder.getInt1Ty(), "cond"); + auto targetBlock = jumpTargets[currentPC]; + auto followBlock = basicBlocks[currentPC + 1]; + builder.CreateCondBr(cond, targetBlock, followBlock); + + currentBlock = nullptr; + break; + } + case Instruction::ADDRESS: { auto value = ext.address(); From d843ec660ad0300e6a63d8a7930dcb5abf05984f Mon Sep 17 00:00:00 2001 From: artur-zawlocki Date: Thu, 2 Oct 2014 12:22:47 +0200 Subject: [PATCH 4/5] fixes for JUMP/JUMPI (generating final basic block) implementation of NOT --- evmcc/Compiler.cpp | 35 +++++++++++++++++++++++++++++++---- evmcc/bytecode/if1.evm | 1 + evmcc/lll/if1.asm | 21 +++++++++++++++++++++ evmcc/lll/if1.lll | 5 +++++ 4 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 evmcc/bytecode/if1.evm create mode 100644 evmcc/lll/if1.asm create mode 100644 evmcc/lll/if1.lll diff --git a/evmcc/Compiler.cpp b/evmcc/Compiler.cpp index 1ce211e98..d5f39e053 100644 --- a/evmcc/Compiler.cpp +++ b/evmcc/Compiler.cpp @@ -60,6 +60,8 @@ llvm::BasicBlock* Compiler::getOrCreateBasicBlockAtPC(ProgramCounter pc) void Compiler::createBasicBlocks(const dev::bytes& bytecode) { + getOrCreateBasicBlockAtPC(0); + for (auto curr = bytecode.cbegin(); curr != bytecode.cend(); ++curr) { using dev::eth::Instruction; @@ -168,7 +170,6 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) // Create the basic blocks. auto entryBlock = BasicBlock::Create(context, "entry", mainFunc); - basicBlocks[0] = entryBlock; builder.SetInsertPoint(entryBlock); createBasicBlocks(bytecode); @@ -189,11 +190,12 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) ProgramCounter currentPC = pc - bytecode.cbegin(); auto blockIter = basicBlocks.find(currentPC); - if (currentPC > 0 && blockIter != basicBlocks.end()) + if (blockIter != basicBlocks.end()) { auto nextBlock = blockIter->second; // Terminate the current block by jumping to the next one. - builder.CreateBr(nextBlock); + if (currentBlock != nullptr) + builder.CreateBr(nextBlock); // Insert the next block into the main function. mainFunc->getBasicBlockList().push_back(nextBlock); builder.SetInsertPoint(nextBlock); @@ -284,6 +286,16 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) break; } + case Instruction::NOT: + { + auto top = stack.pop(); + auto zero = ConstantInt::get(Types.word256, 0); + auto nonzero = builder.CreateICmpNE(top, zero, "nonzero"); + auto result = builder.CreateZExt(nonzero, Types.word256); + stack.push(result); + break; + } + case Instruction::POP: { stack.pop(); @@ -453,7 +465,8 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) stack.pop(); auto top = stack.pop(); - auto cond = builder.CreateTrunc(top, builder.getInt1Ty(), "cond"); + auto zero = ConstantInt::get(Types.word256, 0); + auto cond = builder.CreateICmpNE(top, zero, "nonzero"); auto targetBlock = jumpTargets[currentPC]; auto followBlock = basicBlocks[currentPC + 1]; builder.CreateCondBr(cond, targetBlock, followBlock); @@ -551,6 +564,20 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) } } + // Generate final basic block (may be jumped to). + auto finalPC = bytecode.size(); + auto it = basicBlocks.find(finalPC); + if (it != basicBlocks.end()) + { + auto finalBlock = it->second; + + if (currentBlock != nullptr) + builder.CreateBr(finalBlock); + + mainFunc->getBasicBlockList().push_back(finalBlock); + builder.SetInsertPoint(finalBlock); + } + if (!userRet) builder.CreateRet(builder.getInt64(0)); diff --git a/evmcc/bytecode/if1.evm b/evmcc/bytecode/if1.evm new file mode 100644 index 000000000..ee9009294 --- /dev/null +++ b/evmcc/bytecode/if1.evm @@ -0,0 +1 @@ +600160805460006080530b6016596003608054601b586002608054 diff --git a/evmcc/lll/if1.asm b/evmcc/lll/if1.asm new file mode 100644 index 000000000..4a938adce --- /dev/null +++ b/evmcc/lll/if1.asm @@ -0,0 +1,21 @@ +.code: + PUSH 1 + PUSH 128 + MSTORE + PUSH 0 + PUSH 128 + MLOAD + GT + PUSH [tag0] + JUMPI + PUSH 3 + PUSH 128 + MSTORE + PUSH [tag1] + JUMP +tag0: + PUSH 2 + PUSH 128 + MSTORE +tag1: + diff --git a/evmcc/lll/if1.lll b/evmcc/lll/if1.lll new file mode 100644 index 000000000..a807b697d --- /dev/null +++ b/evmcc/lll/if1.lll @@ -0,0 +1,5 @@ +{ + [i] 1 + + ( if (> @i 0) [i] 2 [i] 3 ) +} \ No newline at end of file From 01cc09a2796e08b054d8bace30dea1d0fa1551ad Mon Sep 17 00:00:00 2001 From: artur-zawlocki Date: Thu, 2 Oct 2014 12:36:12 +0200 Subject: [PATCH 5/5] fixed implementation of NOT --- evmcc/Compiler.cpp | 4 ++-- evmcc/bytecode/when1.evm | 1 + evmcc/lll/when1.asm | 10 ++++++++++ evmcc/lll/when1.lll | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 evmcc/bytecode/when1.evm create mode 100644 evmcc/lll/when1.asm create mode 100644 evmcc/lll/when1.lll diff --git a/evmcc/Compiler.cpp b/evmcc/Compiler.cpp index d5f39e053..f327f86ed 100644 --- a/evmcc/Compiler.cpp +++ b/evmcc/Compiler.cpp @@ -290,8 +290,8 @@ std::unique_ptr Compiler::compile(const dev::bytes& bytecode) { auto top = stack.pop(); auto zero = ConstantInt::get(Types.word256, 0); - auto nonzero = builder.CreateICmpNE(top, zero, "nonzero"); - auto result = builder.CreateZExt(nonzero, Types.word256); + auto iszero = builder.CreateICmpEQ(top, zero, "iszero"); + auto result = builder.CreateZExt(iszero, Types.word256); stack.push(result); break; } diff --git a/evmcc/bytecode/when1.evm b/evmcc/bytecode/when1.evm new file mode 100644 index 000000000..303b02623 --- /dev/null +++ b/evmcc/bytecode/when1.evm @@ -0,0 +1 @@ +60010f600b59600d608054 diff --git a/evmcc/lll/when1.asm b/evmcc/lll/when1.asm new file mode 100644 index 000000000..01d41c266 --- /dev/null +++ b/evmcc/lll/when1.asm @@ -0,0 +1,10 @@ +.code: + PUSH 1 + NOT + PUSH [tag0] + JUMPI + PUSH 13 + PUSH 128 + MSTORE +tag0: + diff --git a/evmcc/lll/when1.lll b/evmcc/lll/when1.lll new file mode 100644 index 000000000..990a6e64a --- /dev/null +++ b/evmcc/lll/when1.lll @@ -0,0 +1,2 @@ +(when (> 1 0) [i] 13) + \ No newline at end of file