From e185d9a117465a0b792144a99554947aede00eaa Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 20 Feb 2015 21:59:21 +0100 Subject: [PATCH] Add EVMJIT. --- evmjit/.gitignore | 1 + evmjit/CMakeLists.txt | 44 + evmjit/LICENSE.md | 21 + evmjit/README.md | 43 + evmjit/evmcc/CMakeLists.txt | 18 + evmjit/evmcc/evmcc.cpp | 210 ++++ evmjit/evmcc/test/arith/addmod.evm | 1 + evmjit/evmcc/test/arith/addmod.lll | 12 + evmjit/evmcc/test/arith/arith1.evm | 1 + evmjit/evmcc/test/arith/arith1.lll | 37 + evmjit/evmcc/test/arith/arith_bnot.evm | 1 + evmjit/evmcc/test/arith/arith_bnot.lll | 14 + evmjit/evmcc/test/arith/div.evm | 1 + evmjit/evmcc/test/arith/div.lll | 10 + evmjit/evmcc/test/arith/fib1.evm | 1 + evmjit/evmcc/test/arith/fib1.lll | 57 ++ evmjit/evmcc/test/arith/mul.evm | 1 + evmjit/evmcc/test/arith/mul.lll | 13 + evmjit/evmcc/test/arith/mulmod.evm | 1 + evmjit/evmcc/test/arith/mulmod.lll | 12 + evmjit/evmcc/test/except/badinst1.evm | 1 + evmjit/evmcc/test/ext/calldatacopy1.evm | 1 + evmjit/evmcc/test/ext/calldatacopy1.lll | 13 + evmjit/evmcc/test/ext/calldatacopy2.evm | 1 + evmjit/evmcc/test/ext/calldatacopy2.lll | 13 + evmjit/evmcc/test/ext/codecopy1.evm | 1 + evmjit/evmcc/test/ext/codecopy1.lll | 13 + evmjit/evmcc/test/ext/codecopy2.evm | 1 + evmjit/evmcc/test/ext/codecopy2.lll | 13 + evmjit/evmcc/test/ext/codecopy3.evm | 1 + evmjit/evmcc/test/ext/codecopy3.lll | 13 + evmjit/evmcc/test/ext/ext_test.evm | 1 + evmjit/evmcc/test/ext/ext_test.lll | 55 + evmjit/evmcc/test/ext/extcodecopy1.evm | 1 + evmjit/evmcc/test/ext/extcodecopy1.lll | 11 + evmjit/evmcc/test/ext/store_delete.evm | 1 + evmjit/evmcc/test/ext/store_delete.lll | 9 + evmjit/evmcc/test/ext/store_test.evm | 1 + evmjit/evmcc/test/ext/store_test.lll | 14 + evmjit/evmcc/test/jump/ackermann.ethel | 7 + evmjit/evmcc/test/jump/ackermann.evm | 1 + evmjit/evmcc/test/jump/badindirect1.evm | 1 + evmjit/evmcc/test/jump/badindirect1.lll | 9 + evmjit/evmcc/test/jump/badindirect2.evm | 1 + evmjit/evmcc/test/jump/badindirect2.lll | 12 + evmjit/evmcc/test/jump/badjump1.evm | 1 + evmjit/evmcc/test/jump/badjump1.lll | 6 + evmjit/evmcc/test/jump/badjump2.evm | 1 + evmjit/evmcc/test/jump/badjump2.lll | 9 + evmjit/evmcc/test/jump/call1.ethel | 5 + evmjit/evmcc/test/jump/call1.evm | 1 + evmjit/evmcc/test/jump/call2.ethel | 5 + evmjit/evmcc/test/jump/call2.evm | 1 + evmjit/evmcc/test/jump/fac.ethel | 5 + evmjit/evmcc/test/jump/fac.evm | 1 + evmjit/evmcc/test/jump/fac_tail.ethel | 5 + evmjit/evmcc/test/jump/fac_tail.evm | 1 + evmjit/evmcc/test/jump/fib1.ethel | 6 + evmjit/evmcc/test/jump/fib1.evm | 1 + evmjit/evmcc/test/jump/for1.evm | 1 + evmjit/evmcc/test/jump/for1.lll | 3 + evmjit/evmcc/test/jump/for2.evm | 1 + evmjit/evmcc/test/jump/for2.lll | 3 + evmjit/evmcc/test/jump/if1.ethel | 1 + evmjit/evmcc/test/jump/if1.evm | 1 + evmjit/evmcc/test/jump/if2.ethel | 1 + evmjit/evmcc/test/jump/if2.evm | 1 + evmjit/evmcc/test/jump/indirect1.evm | 1 + evmjit/evmcc/test/jump/indirect1.lll | 13 + evmjit/evmcc/test/jump/indirect2.evm | 1 + evmjit/evmcc/test/jump/indirect2.lll | 19 + evmjit/evmcc/test/jump/indirect3.evm | 1 + evmjit/evmcc/test/jump/indirect3.lll | 14 + evmjit/evmcc/test/jump/indirect4.evm | 1 + evmjit/evmcc/test/jump/indirect4.lll | 15 + evmjit/evmcc/test/jump/jump1.evm | 1 + evmjit/evmcc/test/jump/jump1.lll | 11 + evmjit/evmcc/test/jump/jump2.evm | 1 + evmjit/evmcc/test/jump/jump2.lll | 10 + evmjit/evmcc/test/jump/jump3.evm | 1 + evmjit/evmcc/test/jump/jump3.lll | 10 + evmjit/evmcc/test/jump/jump4.evm | 1 + evmjit/evmcc/test/jump/jump4.lll | 17 + evmjit/evmcc/test/jump/jump5.evm | 1 + evmjit/evmcc/test/jump/jump5.lll | 16 + evmjit/evmcc/test/jump/jump6.evm | 1 + evmjit/evmcc/test/jump/jump6.lll | 32 + evmjit/evmcc/test/jump/jumpi_at_the_end.evm | 1 + evmjit/evmcc/test/jump/jumpi_at_the_end.lll | 1 + evmjit/evmcc/test/jump/loop1.evm | 1 + evmjit/evmcc/test/jump/loop1.lll | 27 + evmjit/evmcc/test/jump/loop2.evm | 1 + evmjit/evmcc/test/jump/loop2.lll | 28 + evmjit/evmcc/test/jump/rec1.ethel | 4 + evmjit/evmcc/test/jump/rec1.evm | 1 + evmjit/evmcc/test/jump/when1.asm | 10 + evmjit/evmcc/test/jump/when1.evm | 1 + evmjit/evmcc/test/jump/when1.lll | 2 + evmjit/evmcc/test/kv.evm | 1 + evmjit/evmcc/test/kv.lll | 10 + evmjit/evmcc/test/mem/byte.evm | 1 + evmjit/evmcc/test/mem/byte.lll | 105 ++ evmjit/evmcc/test/mem/mem2.evm | 1 + evmjit/evmcc/test/mem/mem2.lll | 15 + evmjit/evmcc/test/mem/memtest1.evm | 1 + evmjit/evmcc/test/mem/memtest1.lll | 18 + evmjit/evmcc/test/mem/mstore1.evm | 1 + evmjit/evmcc/test/mem/mstore1.lll | 6 + evmjit/evmcc/test/ret/return1.evm | 1 + evmjit/evmcc/test/ret/return1.lll | 6 + evmjit/evmcc/test/ret/return2.evm | 1 + evmjit/evmcc/test/ret/return2.lll | 6 + evmjit/evmcc/test/ret/return_test.evm | 1 + evmjit/evmcc/test/ret/return_test.lll | 15 + evmjit/evmcc/test/stack/oos.evm | 1 + evmjit/evmcc/test/stack/oos.lll | 11 + evmjit/evmcc/test/stack/push_test.evm | 1 + evmjit/evmcc/test/stack/push_test.lll | 35 + evmjit/evmcc/test/stack/stack_test.evm | 1 + evmjit/evmcc/test/stack/stack_test.lll | 8 + evmjit/evmcc/test/stack/stackjump.evm | 1 + evmjit/evmcc/test/stack/stackjump.lll | 3 + evmjit/evmcc/test/stack/swap.evm | 1 + evmjit/evmcc/test/stack/swap.lll | 31 + evmjit/evmcc/test/stack/swapswap.evm | 1 + evmjit/evmcc/test/stack/swapswap.lll | 32 + evmjit/evmcc/test/stack/test.evm | 1 + .../test/vmtests/vmArithPerformanceTest.json | 260 +++++ .../evmcc/test/vmtests/vmPerformanceTest.json | 214 ++++ evmjit/evmcc/test/vmtests/vm_jump.json | 41 + evmjit/libevmjit-cpp/CMakeLists.txt | 24 + evmjit/libevmjit-cpp/Env.cpp | 117 +++ evmjit/libevmjit-cpp/JitVM.cpp | 83 ++ evmjit/libevmjit-cpp/JitVM.h | 26 + evmjit/libevmjit-cpp/Utils.h | 38 + evmjit/libevmjit/Arith256.cpp | 406 ++++++++ evmjit/libevmjit/Arith256.h | 47 + evmjit/libevmjit/BasicBlock.cpp | 381 +++++++ evmjit/libevmjit/BasicBlock.h | 126 +++ evmjit/libevmjit/BuildInfo.h.in | 10 + evmjit/libevmjit/CMakeLists.txt | 68 ++ evmjit/libevmjit/Cache.cpp | 120 +++ evmjit/libevmjit/Cache.h | 38 + evmjit/libevmjit/Common.h | 65 ++ evmjit/libevmjit/Compiler.cpp | 965 ++++++++++++++++++ evmjit/libevmjit/Compiler.h | 80 ++ evmjit/libevmjit/CompilerHelper.cpp | 51 + evmjit/libevmjit/CompilerHelper.h | 79 ++ evmjit/libevmjit/Endianness.cpp | 39 + evmjit/libevmjit/Endianness.h | 25 + evmjit/libevmjit/ExecStats.cpp | 97 ++ evmjit/libevmjit/ExecStats.h | 43 + evmjit/libevmjit/ExecutionEngine.cpp | 153 +++ evmjit/libevmjit/ExecutionEngine.h | 60 ++ evmjit/libevmjit/Ext.cpp | 192 ++++ evmjit/libevmjit/Ext.h | 81 ++ evmjit/libevmjit/GasMeter.cpp | 231 +++++ evmjit/libevmjit/GasMeter.h | 63 ++ evmjit/libevmjit/Instruction.cpp | 42 + evmjit/libevmjit/Instruction.h | 239 +++++ evmjit/libevmjit/Memory.cpp | 260 +++++ evmjit/libevmjit/Memory.h | 49 + evmjit/libevmjit/Runtime.cpp | 34 + evmjit/libevmjit/Runtime.h | 40 + evmjit/libevmjit/RuntimeData.h | 60 ++ evmjit/libevmjit/RuntimeManager.cpp | 242 +++++ evmjit/libevmjit/RuntimeManager.h | 60 ++ evmjit/libevmjit/Stack.cpp | 200 ++++ evmjit/libevmjit/Stack.h | 44 + evmjit/libevmjit/Type.cpp | 70 ++ evmjit/libevmjit/Type.h | 60 ++ evmjit/libevmjit/Utils.cpp | 13 + evmjit/libevmjit/Utils.h | 24 + evmjit/libevmjit/interface.cpp | 40 + evmjit/libevmjit/interface.h | 13 + .../preprocessor/llvm_includes_end.h | 5 + .../preprocessor/llvm_includes_start.h | 8 + 177 files changed, 7040 insertions(+) create mode 100644 evmjit/.gitignore create mode 100644 evmjit/CMakeLists.txt create mode 100644 evmjit/LICENSE.md create mode 100644 evmjit/README.md create mode 100644 evmjit/evmcc/CMakeLists.txt create mode 100644 evmjit/evmcc/evmcc.cpp create mode 100644 evmjit/evmcc/test/arith/addmod.evm create mode 100644 evmjit/evmcc/test/arith/addmod.lll create mode 100644 evmjit/evmcc/test/arith/arith1.evm create mode 100644 evmjit/evmcc/test/arith/arith1.lll create mode 100644 evmjit/evmcc/test/arith/arith_bnot.evm create mode 100644 evmjit/evmcc/test/arith/arith_bnot.lll create mode 100644 evmjit/evmcc/test/arith/div.evm create mode 100644 evmjit/evmcc/test/arith/div.lll create mode 100644 evmjit/evmcc/test/arith/fib1.evm create mode 100644 evmjit/evmcc/test/arith/fib1.lll create mode 100644 evmjit/evmcc/test/arith/mul.evm create mode 100644 evmjit/evmcc/test/arith/mul.lll create mode 100644 evmjit/evmcc/test/arith/mulmod.evm create mode 100644 evmjit/evmcc/test/arith/mulmod.lll create mode 100644 evmjit/evmcc/test/except/badinst1.evm create mode 100644 evmjit/evmcc/test/ext/calldatacopy1.evm create mode 100644 evmjit/evmcc/test/ext/calldatacopy1.lll create mode 100644 evmjit/evmcc/test/ext/calldatacopy2.evm create mode 100644 evmjit/evmcc/test/ext/calldatacopy2.lll create mode 100644 evmjit/evmcc/test/ext/codecopy1.evm create mode 100644 evmjit/evmcc/test/ext/codecopy1.lll create mode 100644 evmjit/evmcc/test/ext/codecopy2.evm create mode 100644 evmjit/evmcc/test/ext/codecopy2.lll create mode 100644 evmjit/evmcc/test/ext/codecopy3.evm create mode 100644 evmjit/evmcc/test/ext/codecopy3.lll create mode 100644 evmjit/evmcc/test/ext/ext_test.evm create mode 100644 evmjit/evmcc/test/ext/ext_test.lll create mode 100644 evmjit/evmcc/test/ext/extcodecopy1.evm create mode 100644 evmjit/evmcc/test/ext/extcodecopy1.lll create mode 100644 evmjit/evmcc/test/ext/store_delete.evm create mode 100644 evmjit/evmcc/test/ext/store_delete.lll create mode 100644 evmjit/evmcc/test/ext/store_test.evm create mode 100644 evmjit/evmcc/test/ext/store_test.lll create mode 100644 evmjit/evmcc/test/jump/ackermann.ethel create mode 100644 evmjit/evmcc/test/jump/ackermann.evm create mode 100644 evmjit/evmcc/test/jump/badindirect1.evm create mode 100644 evmjit/evmcc/test/jump/badindirect1.lll create mode 100644 evmjit/evmcc/test/jump/badindirect2.evm create mode 100644 evmjit/evmcc/test/jump/badindirect2.lll create mode 100644 evmjit/evmcc/test/jump/badjump1.evm create mode 100644 evmjit/evmcc/test/jump/badjump1.lll create mode 100644 evmjit/evmcc/test/jump/badjump2.evm create mode 100644 evmjit/evmcc/test/jump/badjump2.lll create mode 100644 evmjit/evmcc/test/jump/call1.ethel create mode 100644 evmjit/evmcc/test/jump/call1.evm create mode 100644 evmjit/evmcc/test/jump/call2.ethel create mode 100644 evmjit/evmcc/test/jump/call2.evm create mode 100644 evmjit/evmcc/test/jump/fac.ethel create mode 100644 evmjit/evmcc/test/jump/fac.evm create mode 100644 evmjit/evmcc/test/jump/fac_tail.ethel create mode 100644 evmjit/evmcc/test/jump/fac_tail.evm create mode 100644 evmjit/evmcc/test/jump/fib1.ethel create mode 100644 evmjit/evmcc/test/jump/fib1.evm create mode 100644 evmjit/evmcc/test/jump/for1.evm create mode 100644 evmjit/evmcc/test/jump/for1.lll create mode 100644 evmjit/evmcc/test/jump/for2.evm create mode 100644 evmjit/evmcc/test/jump/for2.lll create mode 100644 evmjit/evmcc/test/jump/if1.ethel create mode 100644 evmjit/evmcc/test/jump/if1.evm create mode 100644 evmjit/evmcc/test/jump/if2.ethel create mode 100644 evmjit/evmcc/test/jump/if2.evm create mode 100644 evmjit/evmcc/test/jump/indirect1.evm create mode 100644 evmjit/evmcc/test/jump/indirect1.lll create mode 100644 evmjit/evmcc/test/jump/indirect2.evm create mode 100644 evmjit/evmcc/test/jump/indirect2.lll create mode 100644 evmjit/evmcc/test/jump/indirect3.evm create mode 100644 evmjit/evmcc/test/jump/indirect3.lll create mode 100644 evmjit/evmcc/test/jump/indirect4.evm create mode 100644 evmjit/evmcc/test/jump/indirect4.lll create mode 100644 evmjit/evmcc/test/jump/jump1.evm create mode 100644 evmjit/evmcc/test/jump/jump1.lll create mode 100644 evmjit/evmcc/test/jump/jump2.evm create mode 100644 evmjit/evmcc/test/jump/jump2.lll create mode 100644 evmjit/evmcc/test/jump/jump3.evm create mode 100644 evmjit/evmcc/test/jump/jump3.lll create mode 100644 evmjit/evmcc/test/jump/jump4.evm create mode 100644 evmjit/evmcc/test/jump/jump4.lll create mode 100644 evmjit/evmcc/test/jump/jump5.evm create mode 100644 evmjit/evmcc/test/jump/jump5.lll create mode 100644 evmjit/evmcc/test/jump/jump6.evm create mode 100644 evmjit/evmcc/test/jump/jump6.lll create mode 100644 evmjit/evmcc/test/jump/jumpi_at_the_end.evm create mode 100644 evmjit/evmcc/test/jump/jumpi_at_the_end.lll create mode 100644 evmjit/evmcc/test/jump/loop1.evm create mode 100644 evmjit/evmcc/test/jump/loop1.lll create mode 100644 evmjit/evmcc/test/jump/loop2.evm create mode 100644 evmjit/evmcc/test/jump/loop2.lll create mode 100644 evmjit/evmcc/test/jump/rec1.ethel create mode 100644 evmjit/evmcc/test/jump/rec1.evm create mode 100644 evmjit/evmcc/test/jump/when1.asm create mode 100644 evmjit/evmcc/test/jump/when1.evm create mode 100644 evmjit/evmcc/test/jump/when1.lll create mode 100644 evmjit/evmcc/test/kv.evm create mode 100644 evmjit/evmcc/test/kv.lll create mode 100644 evmjit/evmcc/test/mem/byte.evm create mode 100644 evmjit/evmcc/test/mem/byte.lll create mode 100644 evmjit/evmcc/test/mem/mem2.evm create mode 100644 evmjit/evmcc/test/mem/mem2.lll create mode 100644 evmjit/evmcc/test/mem/memtest1.evm create mode 100644 evmjit/evmcc/test/mem/memtest1.lll create mode 100644 evmjit/evmcc/test/mem/mstore1.evm create mode 100644 evmjit/evmcc/test/mem/mstore1.lll create mode 100644 evmjit/evmcc/test/ret/return1.evm create mode 100644 evmjit/evmcc/test/ret/return1.lll create mode 100644 evmjit/evmcc/test/ret/return2.evm create mode 100644 evmjit/evmcc/test/ret/return2.lll create mode 100644 evmjit/evmcc/test/ret/return_test.evm create mode 100644 evmjit/evmcc/test/ret/return_test.lll create mode 100644 evmjit/evmcc/test/stack/oos.evm create mode 100644 evmjit/evmcc/test/stack/oos.lll create mode 100644 evmjit/evmcc/test/stack/push_test.evm create mode 100644 evmjit/evmcc/test/stack/push_test.lll create mode 100644 evmjit/evmcc/test/stack/stack_test.evm create mode 100644 evmjit/evmcc/test/stack/stack_test.lll create mode 100644 evmjit/evmcc/test/stack/stackjump.evm create mode 100644 evmjit/evmcc/test/stack/stackjump.lll create mode 100644 evmjit/evmcc/test/stack/swap.evm create mode 100644 evmjit/evmcc/test/stack/swap.lll create mode 100644 evmjit/evmcc/test/stack/swapswap.evm create mode 100644 evmjit/evmcc/test/stack/swapswap.lll create mode 100644 evmjit/evmcc/test/stack/test.evm create mode 100644 evmjit/evmcc/test/vmtests/vmArithPerformanceTest.json create mode 100644 evmjit/evmcc/test/vmtests/vmPerformanceTest.json create mode 100644 evmjit/evmcc/test/vmtests/vm_jump.json create mode 100644 evmjit/libevmjit-cpp/CMakeLists.txt create mode 100644 evmjit/libevmjit-cpp/Env.cpp create mode 100644 evmjit/libevmjit-cpp/JitVM.cpp create mode 100644 evmjit/libevmjit-cpp/JitVM.h create mode 100644 evmjit/libevmjit-cpp/Utils.h create mode 100644 evmjit/libevmjit/Arith256.cpp create mode 100644 evmjit/libevmjit/Arith256.h create mode 100644 evmjit/libevmjit/BasicBlock.cpp create mode 100644 evmjit/libevmjit/BasicBlock.h create mode 100644 evmjit/libevmjit/BuildInfo.h.in create mode 100644 evmjit/libevmjit/CMakeLists.txt create mode 100644 evmjit/libevmjit/Cache.cpp create mode 100644 evmjit/libevmjit/Cache.h create mode 100644 evmjit/libevmjit/Common.h create mode 100644 evmjit/libevmjit/Compiler.cpp create mode 100644 evmjit/libevmjit/Compiler.h create mode 100644 evmjit/libevmjit/CompilerHelper.cpp create mode 100644 evmjit/libevmjit/CompilerHelper.h create mode 100644 evmjit/libevmjit/Endianness.cpp create mode 100644 evmjit/libevmjit/Endianness.h create mode 100644 evmjit/libevmjit/ExecStats.cpp create mode 100644 evmjit/libevmjit/ExecStats.h create mode 100644 evmjit/libevmjit/ExecutionEngine.cpp create mode 100644 evmjit/libevmjit/ExecutionEngine.h create mode 100644 evmjit/libevmjit/Ext.cpp create mode 100644 evmjit/libevmjit/Ext.h create mode 100644 evmjit/libevmjit/GasMeter.cpp create mode 100644 evmjit/libevmjit/GasMeter.h create mode 100644 evmjit/libevmjit/Instruction.cpp create mode 100644 evmjit/libevmjit/Instruction.h create mode 100644 evmjit/libevmjit/Memory.cpp create mode 100644 evmjit/libevmjit/Memory.h create mode 100644 evmjit/libevmjit/Runtime.cpp create mode 100644 evmjit/libevmjit/Runtime.h create mode 100644 evmjit/libevmjit/RuntimeData.h create mode 100644 evmjit/libevmjit/RuntimeManager.cpp create mode 100644 evmjit/libevmjit/RuntimeManager.h create mode 100644 evmjit/libevmjit/Stack.cpp create mode 100644 evmjit/libevmjit/Stack.h create mode 100644 evmjit/libevmjit/Type.cpp create mode 100644 evmjit/libevmjit/Type.h create mode 100644 evmjit/libevmjit/Utils.cpp create mode 100644 evmjit/libevmjit/Utils.h create mode 100644 evmjit/libevmjit/interface.cpp create mode 100644 evmjit/libevmjit/interface.h create mode 100644 evmjit/libevmjit/preprocessor/llvm_includes_end.h create mode 100644 evmjit/libevmjit/preprocessor/llvm_includes_start.h diff --git a/evmjit/.gitignore b/evmjit/.gitignore new file mode 100644 index 000000000..84c048a73 --- /dev/null +++ b/evmjit/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/evmjit/CMakeLists.txt b/evmjit/CMakeLists.txt new file mode 100644 index 000000000..14fca2cde --- /dev/null +++ b/evmjit/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(evmjit) + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +set(CMAKE_AUTOMOC OFF) + +if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") +else() + set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-unknown-pragmas") +endif() + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + # Do not allow unresovled symbols in shared library (default on linux) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") +endif() + +# LLVM +if(LLVM_DIR OR APPLE) # local LLVM build + find_package(LLVM REQUIRED CONFIG) + message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") + message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + add_definitions(${LLVM_DEFINITIONS}) + # TODO: bitwriter is needed only for evmcc + llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen bitwriter) +else() + # Workaround for Ubuntu broken LLVM package + message(STATUS "Using llvm-3.5-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake") + execute_process(COMMAND llvm-config-3.5 --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS) + message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}") + set(LLVM_LIBS "-lLLVMBitWriter -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMX86AsmParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMExecutionEngine -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm") + add_definitions(-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS) + link_directories(/usr/lib/llvm-3.5/lib) +endif() + +add_subdirectory(libevmjit) + +if(EVMJIT_CPP) + add_subdirectory(libevmjit-cpp) +endif() + +if(EVMJIT_TOOLS) + add_subdirectory(evmcc) +endif() diff --git a/evmjit/LICENSE.md b/evmjit/LICENSE.md new file mode 100644 index 000000000..630157f98 --- /dev/null +++ b/evmjit/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Paweł Bylica + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/evmjit/README.md b/evmjit/README.md new file mode 100644 index 000000000..a480e83dc --- /dev/null +++ b/evmjit/README.md @@ -0,0 +1,43 @@ +# The Ethereum EVM JIT + +EVM JIT is a library for just-in-time compilation of Ethereum EVM code. +It can be used to substitute classic interpreter-like EVM Virtual Machine in Ethereum client. + +## Build + +### Linux / Ubuntu + +1. Install llvm-3.5-dev package + 1. For Ubuntu 14.04 using LLVM deb packages source: http://llvm.org/apt + 2. For Ubuntu 14.10 using Ubuntu packages +2. Build library with cmake + 1. `mkdir build && cd $_` + 2. `cmake .. && make` +3. Install library + 1. `sudo make install` + 2. `sudo ldconfig` + +### OSX + +1. Install llvm35 + 1. `brew install llvm35 --disable-shared --HEAD` +2. Build library with cmake + 1. `mkdir build && cd $_` + 2. `cmake -DLLVM_DIR=/usr/local/lib/llvm-3.5/share/llvm/cmake .. && make` +3. Install library + 1. `make install` (with admin rights?) + +### Windows + +Ask me. + +## Options + +Options to evmjit library can be passed by environmental variables, e.g. `EVMJIT_CACHE=0 testeth --jit`. + +Option | Default value | Description +------------- | ------------- | ---------------------------------------------- +EVMJIT_CACHE | 1 | Enables on disk cache for compiled EVM objects +EVMJIT_DUMP | 0 | Dumps generated LLVM module to standard output + + diff --git a/evmjit/evmcc/CMakeLists.txt b/evmjit/evmcc/CMakeLists.txt new file mode 100644 index 000000000..4ffbf5fb5 --- /dev/null +++ b/evmjit/evmcc/CMakeLists.txt @@ -0,0 +1,18 @@ +set(TARGET_NAME evmcc) + +set(SOURCES + evmcc.cpp +) +source_group("" FILES ${SOURCES}) + +add_executable(${TARGET_NAME} ${SOURCES}) +set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "tools") + +include_directories(../..) +include_directories(${LLVM_INCLUDE_DIRS}) +include_directories(${Boost_INCLUDE_DIRS}) + +target_link_libraries(${TARGET_NAME} ethereum) +target_link_libraries(${TARGET_NAME} ${Boost_PROGRAM_OPTIONS_LIBRARIES}) + +install(TARGETS ${TARGET_NAME} DESTINATION bin ) \ No newline at end of file diff --git a/evmjit/evmcc/evmcc.cpp b/evmjit/evmcc/evmcc.cpp new file mode 100644 index 000000000..e86f948f2 --- /dev/null +++ b/evmjit/evmcc/evmcc.cpp @@ -0,0 +1,210 @@ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +void parseProgramOptions(int _argc, char** _argv, boost::program_options::variables_map& _varMap) +{ + namespace opt = boost::program_options; + + opt::options_description explicitOpts("Allowed options"); + explicitOpts.add_options() + ("help,h", "show usage information") + ("compile,c", "compile the code to LLVM IR") + ("interpret,i", "compile the code to LLVM IR and execute") + ("gas,g", opt::value(), "set initial gas for execution") + ("disassemble,d", "dissassemble the code") + ("dump-cfg", "dump control flow graph to graphviz file") + ("dont-optimize", "turn off optimizations") + ("optimize-stack", "optimize stack use between basic blocks (default: on)") + ("rewrite-switch", "rewrite LLVM switch to branches (default: on)") + ("output-ll", opt::value(), "dump generated LLVM IR to file") + ("output-bc", opt::value(), "dump generated LLVM bitcode to file") + ("show-logs", "output LOG statements to stderr") + ("verbose,V", "enable verbose output"); + + opt::options_description implicitOpts("Input files"); + implicitOpts.add_options() + ("input-file", opt::value(), "input file"); + + opt::options_description allOpts(""); + allOpts.add(explicitOpts).add(implicitOpts); + + opt::positional_options_description inputOpts; + inputOpts.add("input-file", 1); + + const char* errorMsg = nullptr; + try + { + auto parser = opt::command_line_parser(_argc, _argv).options(allOpts).positional(inputOpts); + opt::store(parser.run(), _varMap); + opt::notify(_varMap); + } + catch (boost::program_options::error& err) + { + errorMsg = err.what(); + } + + if (!errorMsg && _varMap.count("input-file") == 0) + errorMsg = "missing input file name"; + + if (_varMap.count("disassemble") == 0 + && _varMap.count("compile") == 0 + && _varMap.count("interpret") == 0) + { + errorMsg = "at least one of -c, -i, -d is required"; + } + + if (errorMsg || _varMap.count("help")) + { + if (errorMsg) + std::cerr << "Error: " << errorMsg << std::endl; + + std::cout << "Usage: " << _argv[0] << " input-file " << std::endl + << explicitOpts << std::endl; + std::exit(errorMsg ? 1 : 0); + } +} + +int main(int argc, char** argv) +{ + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram X(argc, argv); + + boost::program_options::variables_map options; + parseProgramOptions(argc, argv, options); + + auto inputFile = options["input-file"].as(); + std::ifstream ifs(inputFile); + if (!ifs.is_open()) + { + std::cerr << "cannot open input file " << inputFile << std::endl; + exit(1); + } + + std::string src((std::istreambuf_iterator(ifs)), + (std::istreambuf_iterator())); + + boost::algorithm::trim(src); + + using namespace dev; + + bytes bytecode = fromHex(src); + + if (options.count("disassemble")) + { + std::string assembly = eth::disassemble(bytecode); + std::cout << assembly << std::endl; + } + + if (options.count("compile") || options.count("interpret")) + { + size_t initialGas = 10000; + + if (options.count("gas")) + initialGas = options["gas"].as(); + + auto compilationStartTime = std::chrono::high_resolution_clock::now(); + + eth::jit::Compiler::Options compilerOptions; + compilerOptions.dumpCFG = options.count("dump-cfg") > 0; + bool optimize = options.count("dont-optimize") == 0; + compilerOptions.optimizeStack = optimize || options.count("optimize-stack") > 0; + compilerOptions.rewriteSwitchToBranches = optimize || options.count("rewrite-switch") > 0; + + auto compiler = eth::jit::Compiler(compilerOptions); + auto module = compiler.compile(bytecode, "main"); + + auto compilationEndTime = std::chrono::high_resolution_clock::now(); + + module->dump(); + + if (options.count("output-ll")) + { + auto outputFile = options["output-ll"].as(); + std::ofstream ofs(outputFile); + if (!ofs.is_open()) + { + std::cerr << "cannot open output file " << outputFile << std::endl; + exit(1); + } + llvm::raw_os_ostream ros(ofs); + module->print(ros, nullptr); + ofs.close(); + } + + if (options.count("output-bc")) + { + auto outputFile = options["output-bc"].as(); + std::ofstream ofs(outputFile); + if (!ofs.is_open()) + { + std::cerr << "cannot open output file " << outputFile << std::endl; + exit(1); + } + llvm::raw_os_ostream ros(ofs); + llvm::WriteBitcodeToFile(module.get(), ros); + ros.flush(); + ofs.close(); + } + + if (options.count("verbose")) + { + std::cerr << "*** Compilation time: " + << std::chrono::duration_cast(compilationEndTime - compilationStartTime).count() + << std::endl; + } + + if (options.count("interpret")) + { + using namespace eth::jit; + + ExecutionEngine engine; + eth::jit::u256 gas = initialGas; + + // Create random runtime data + RuntimeData data; + data.set(RuntimeData::Gas, gas); + data.set(RuntimeData::Address, (u160)Address(1122334455667788)); + data.set(RuntimeData::Caller, (u160)Address(0xfacefacefaceface)); + data.set(RuntimeData::Origin, (u160)Address(101010101010101010)); + data.set(RuntimeData::CallValue, 0xabcd); + data.set(RuntimeData::CallDataSize, 3); + data.set(RuntimeData::GasPrice, 1003); + data.set(RuntimeData::CoinBase, (u160)Address(101010101010101015)); + data.set(RuntimeData::TimeStamp, 1005); + data.set(RuntimeData::Number, 1006); + data.set(RuntimeData::Difficulty, 16); + data.set(RuntimeData::GasLimit, 1008); + data.set(RuntimeData::CodeSize, bytecode.size()); + data.callData = (uint8_t*)"abc"; + data.code = bytecode.data(); + + // BROKEN: env_* functions must be implemented & RuntimeData struct created + // TODO: Do not compile module again + auto result = engine.run(bytecode, &data, nullptr); + return static_cast(result); + } + } + + return 0; +} diff --git a/evmjit/evmcc/test/arith/addmod.evm b/evmjit/evmcc/test/arith/addmod.evm new file mode 100644 index 000000000..4ca71e065 --- /dev/null +++ b/evmjit/evmcc/test/arith/addmod.evm @@ -0,0 +1 @@ +60646107b760271460005560006001f2 diff --git a/evmjit/evmcc/test/arith/addmod.lll b/evmjit/evmcc/test/arith/addmod.lll new file mode 100644 index 000000000..11a6b2cb9 --- /dev/null +++ b/evmjit/evmcc/test/arith/addmod.lll @@ -0,0 +1,12 @@ +;; Should return (1975 + 39) `mod` 100 = 14 = 0x0e +(asm +100 +1975 +39 +ADDMOD +0 +MSTORE8 +0 +1 +RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/arith/arith1.evm b/evmjit/evmcc/test/arith/arith1.evm new file mode 100644 index 000000000..c7a029f52 --- /dev/null +++ b/evmjit/evmcc/test/arith/arith1.evm @@ -0,0 +1 @@ +60016001900160070260050160029004600490066021900560150160030260059007600303600960110860005460086000f2 diff --git a/evmjit/evmcc/test/arith/arith1.lll b/evmjit/evmcc/test/arith/arith1.lll new file mode 100644 index 000000000..4757a7420 --- /dev/null +++ b/evmjit/evmcc/test/arith/arith1.lll @@ -0,0 +1,37 @@ + +(asm +1 +1 +SWAP1 +ADD ;; 2 +7 +MUL ;; 14 +5 +ADD ;; 19 +2 +SWAP1 +DIV ;; 9 +4 +SWAP1 +MOD ;; 1 +33 +SWAP1 +SDIV;; 0 +21 +ADD ;; 21 +3 +MUL ;; 63 +5 +SWAP1 +SMOD;; 3 +3 +SUB ;; 0 +9 +17 +EXP ;; 17^9 +0 +MSTORE +8 +0 +RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/arith/arith_bnot.evm b/evmjit/evmcc/test/arith/arith_bnot.evm new file mode 100644 index 000000000..4cfaf8f55 --- /dev/null +++ b/evmjit/evmcc/test/arith/arith_bnot.evm @@ -0,0 +1 @@ +6201e2406000546000530960005460206000f2 diff --git a/evmjit/evmcc/test/arith/arith_bnot.lll b/evmjit/evmcc/test/arith/arith_bnot.lll new file mode 100644 index 000000000..a83b05a9a --- /dev/null +++ b/evmjit/evmcc/test/arith/arith_bnot.lll @@ -0,0 +1,14 @@ + +(asm +123456 +0 +MSTORE +0 +MLOAD +BNOT +0 +MSTORE +32 +0 +RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/arith/div.evm b/evmjit/evmcc/test/arith/div.evm new file mode 100644 index 000000000..b68d5d202 --- /dev/null +++ b/evmjit/evmcc/test/arith/div.evm @@ -0,0 +1 @@ +60027ffedcba9876543210fedcba9876543210fedcba9876543210fedcba98765432100460005460206000f2 diff --git a/evmjit/evmcc/test/arith/div.lll b/evmjit/evmcc/test/arith/div.lll new file mode 100644 index 000000000..72c22bfdc --- /dev/null +++ b/evmjit/evmcc/test/arith/div.lll @@ -0,0 +1,10 @@ +(asm +0x2 +0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210 +DIV +0 +MSTORE +32 +0 +RETURN +) diff --git a/evmjit/evmcc/test/arith/fib1.evm b/evmjit/evmcc/test/arith/fib1.evm new file mode 100644 index 000000000..4c141314e --- /dev/null +++ b/evmjit/evmcc/test/arith/fib1.evm @@ -0,0 +1 @@ +60016001818101818101818101818101818101818101818101818101818101818101818101818101818101818101818101818101818101 diff --git a/evmjit/evmcc/test/arith/fib1.lll b/evmjit/evmcc/test/arith/fib1.lll new file mode 100644 index 000000000..286bed275 --- /dev/null +++ b/evmjit/evmcc/test/arith/fib1.lll @@ -0,0 +1,57 @@ +;; Fibbonacci unrolled + +(asm +1 +1 +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +DUP2 +DUP2 +ADD +) \ No newline at end of file diff --git a/evmjit/evmcc/test/arith/mul.evm b/evmjit/evmcc/test/arith/mul.evm new file mode 100644 index 000000000..7e8afd268 --- /dev/null +++ b/evmjit/evmcc/test/arith/mul.evm @@ -0,0 +1 @@ +7001234567890abcdef0fedcba09876543217001234567890abcdef0fedcba09876543217001234567890abcdef0fedcba0987654321020260005460206000f2 diff --git a/evmjit/evmcc/test/arith/mul.lll b/evmjit/evmcc/test/arith/mul.lll new file mode 100644 index 000000000..b0fa343bb --- /dev/null +++ b/evmjit/evmcc/test/arith/mul.lll @@ -0,0 +1,13 @@ +(asm +0x1234567890abcdef0fedcba0987654321 +0x1234567890abcdef0fedcba0987654321 +0x1234567890abcdef0fedcba0987654321 +MUL +MUL +0 +MSTORE +32 +0 +RETURN +;; 47d0817e4167b1eb4f9fc722b133ef9d7d9a6fb4c2c1c442d000107a5e419561 +) diff --git a/evmjit/evmcc/test/arith/mulmod.evm b/evmjit/evmcc/test/arith/mulmod.evm new file mode 100644 index 000000000..e34a06154 --- /dev/null +++ b/evmjit/evmcc/test/arith/mulmod.evm @@ -0,0 +1 @@ +6064601b60251560005560006001f2 diff --git a/evmjit/evmcc/test/arith/mulmod.lll b/evmjit/evmcc/test/arith/mulmod.lll new file mode 100644 index 000000000..5e87f0843 --- /dev/null +++ b/evmjit/evmcc/test/arith/mulmod.lll @@ -0,0 +1,12 @@ +;; Should return (27 * 37) `mod` 100 = 99 = 0x63 +(asm +100 +27 +37 +MULMOD +0 +MSTORE8 +0 +1 +RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/except/badinst1.evm b/evmjit/evmcc/test/except/badinst1.evm new file mode 100644 index 000000000..69aadac5e --- /dev/null +++ b/evmjit/evmcc/test/except/badinst1.evm @@ -0,0 +1 @@ +4a diff --git a/evmjit/evmcc/test/ext/calldatacopy1.evm b/evmjit/evmcc/test/ext/calldatacopy1.evm new file mode 100644 index 000000000..f20019651 --- /dev/null +++ b/evmjit/evmcc/test/ext/calldatacopy1.evm @@ -0,0 +1 @@ +60326000600a37600053600a6014f2 diff --git a/evmjit/evmcc/test/ext/calldatacopy1.lll b/evmjit/evmcc/test/ext/calldatacopy1.lll new file mode 100644 index 000000000..3d2ae0a78 --- /dev/null +++ b/evmjit/evmcc/test/ext/calldatacopy1.lll @@ -0,0 +1,13 @@ +(asm +50 ;; byte count +0 ;; source index in calldata array +10 ;; dest index in memory +CALLDATACOPY + +0 +MLOAD ;; to dump memory + +10 +20 +RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/ext/calldatacopy2.evm b/evmjit/evmcc/test/ext/calldatacopy2.evm new file mode 100644 index 000000000..e8eea8da7 --- /dev/null +++ b/evmjit/evmcc/test/ext/calldatacopy2.evm @@ -0,0 +1 @@ +606464e8d4a510006000376000536000600af2 diff --git a/evmjit/evmcc/test/ext/calldatacopy2.lll b/evmjit/evmcc/test/ext/calldatacopy2.lll new file mode 100644 index 000000000..6bbea48d8 --- /dev/null +++ b/evmjit/evmcc/test/ext/calldatacopy2.lll @@ -0,0 +1,13 @@ +(asm +100 ;; byte count +1000000000000 ;; source index in calldata array +0 ;; dest index in memory +CALLDATACOPY + +0 +MLOAD ;; to dump memory + +0 +10 +RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/ext/codecopy1.evm b/evmjit/evmcc/test/ext/codecopy1.evm new file mode 100644 index 000000000..d286f9232 --- /dev/null +++ b/evmjit/evmcc/test/ext/codecopy1.evm @@ -0,0 +1 @@ +60146000600a39600053600a6014f2 diff --git a/evmjit/evmcc/test/ext/codecopy1.lll b/evmjit/evmcc/test/ext/codecopy1.lll new file mode 100644 index 000000000..85a02b5d7 --- /dev/null +++ b/evmjit/evmcc/test/ext/codecopy1.lll @@ -0,0 +1,13 @@ +(asm +20 ;; byte count +0 ;; source index in code array +10 ;; dest index in memory +CODECOPY + +0 +MLOAD ;; to dump memory + +10 +20 +RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/ext/codecopy2.evm b/evmjit/evmcc/test/ext/codecopy2.evm new file mode 100644 index 000000000..71cd92525 --- /dev/null +++ b/evmjit/evmcc/test/ext/codecopy2.evm @@ -0,0 +1 @@ +606464e8d4a510006000396000536000600af2 diff --git a/evmjit/evmcc/test/ext/codecopy2.lll b/evmjit/evmcc/test/ext/codecopy2.lll new file mode 100644 index 000000000..dcbbcaa46 --- /dev/null +++ b/evmjit/evmcc/test/ext/codecopy2.lll @@ -0,0 +1,13 @@ +(asm +100 ;; byte count +1000000000000 ;; source index in code array +0 ;; dest index in memory +CODECOPY + +0 +MLOAD ;; to dump memory + +0 +10 +RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/ext/codecopy3.evm b/evmjit/evmcc/test/ext/codecopy3.evm new file mode 100644 index 000000000..e4b6a9253 --- /dev/null +++ b/evmjit/evmcc/test/ext/codecopy3.evm @@ -0,0 +1 @@ +3860006000396000536000600af2 diff --git a/evmjit/evmcc/test/ext/codecopy3.lll b/evmjit/evmcc/test/ext/codecopy3.lll new file mode 100644 index 000000000..80d9982c6 --- /dev/null +++ b/evmjit/evmcc/test/ext/codecopy3.lll @@ -0,0 +1,13 @@ +(asm +CODESIZE ;; byte count +0 ;; source index in code array +0 ;; dest index in memory +CODECOPY + +0 +MLOAD ;; to dump memory + +0 +10 +RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/ext/ext_test.evm b/evmjit/evmcc/test/ext/ext_test.evm new file mode 100644 index 000000000..580bd9675 --- /dev/null +++ b/evmjit/evmcc/test/ext/ext_test.evm @@ -0,0 +1 @@ +5a3031333234363a4041424344455a36600035602635601335387f1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff600054602060006000f06020600060206000600030610bb8f1600053611000545b60200260002030ff60016002f2 diff --git a/evmjit/evmcc/test/ext/ext_test.lll b/evmjit/evmcc/test/ext/ext_test.lll new file mode 100644 index 000000000..3287ae95f --- /dev/null +++ b/evmjit/evmcc/test/ext/ext_test.lll @@ -0,0 +1,55 @@ + +(asm +PC +ADDRESS +BALANCE +CALLER +ORIGIN +CALLVALUE +CALLDATASIZE +GASPRICE +PREVHASH +COINBASE +TIMESTAMP +NUMBER +DIFFICULTY +GASLIMIT +PC +CALLDATASIZE +0 +CALLDATALOAD +38 +CALLDATALOAD +19 +CALLDATALOAD +CODESIZE +0x1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff +0 +MSTORE +32 +0 +0 +CREATE +32 +0 +32 +0 +0 +ADDRESS +3000 +CALL +0 +MLOAD +4096 +MSTORE +MSIZE +32 +MUL +0 +SHA3 +ADDRESS +SUICIDE +1 +2 +RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/ext/extcodecopy1.evm b/evmjit/evmcc/test/ext/extcodecopy1.evm new file mode 100644 index 000000000..6132b52d8 --- /dev/null +++ b/evmjit/evmcc/test/ext/extcodecopy1.evm @@ -0,0 +1 @@ +60c86000600a303c60005360006020f2 diff --git a/evmjit/evmcc/test/ext/extcodecopy1.lll b/evmjit/evmcc/test/ext/extcodecopy1.lll new file mode 100644 index 000000000..c37054574 --- /dev/null +++ b/evmjit/evmcc/test/ext/extcodecopy1.lll @@ -0,0 +1,11 @@ +(asm +200 ;; byte count +0 ;; source index in code array +10 ;; dest index in memory +ADDRESS +EXTCODECOPY + +0 MLOAD ;; to dump memory + +0 32 RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/ext/store_delete.evm b/evmjit/evmcc/test/ext/store_delete.evm new file mode 100644 index 000000000..d6acae03d --- /dev/null +++ b/evmjit/evmcc/test/ext/store_delete.evm @@ -0,0 +1 @@ +6104d26063576000606357 diff --git a/evmjit/evmcc/test/ext/store_delete.lll b/evmjit/evmcc/test/ext/store_delete.lll new file mode 100644 index 000000000..3d8f0f23a --- /dev/null +++ b/evmjit/evmcc/test/ext/store_delete.lll @@ -0,0 +1,9 @@ + +(asm +1234 +99 +SSTORE +0 +99 +SSTORE +) \ No newline at end of file diff --git a/evmjit/evmcc/test/ext/store_test.evm b/evmjit/evmcc/test/ext/store_test.evm new file mode 100644 index 000000000..54c9419b5 --- /dev/null +++ b/evmjit/evmcc/test/ext/store_test.evm @@ -0,0 +1 @@ +607b607c60015760005760015660005603 diff --git a/evmjit/evmcc/test/ext/store_test.lll b/evmjit/evmcc/test/ext/store_test.lll new file mode 100644 index 000000000..c40471c40 --- /dev/null +++ b/evmjit/evmcc/test/ext/store_test.lll @@ -0,0 +1,14 @@ + +(asm +123 +124 +1 +SSTORE +0 +SSTORE +1 +SLOAD +0 +SLOAD +SUB +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/ackermann.ethel b/evmjit/evmcc/test/jump/ackermann.ethel new file mode 100644 index 000000000..971fd2b8d --- /dev/null +++ b/evmjit/evmcc/test/jump/ackermann.ethel @@ -0,0 +1,7 @@ +let A m n = + if m == 0 then n+1 + else if n == 0 then A (m-1) 1 + else A (m-1) (A (m) (n-1)) + +return A 3 8 + diff --git a/evmjit/evmcc/test/jump/ackermann.evm b/evmjit/evmcc/test/jump/ackermann.evm new file mode 100644 index 000000000..964844045 --- /dev/null +++ b/evmjit/evmcc/test/jump/ackermann.evm @@ -0,0 +1 @@ +6009600360086012585d60005460206000f26000820e6047596000810e603859603460018303603084600185036012585d6012585d60445860436001830360016012585d604b5860018101905090509058 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/badindirect1.evm b/evmjit/evmcc/test/jump/badindirect1.evm new file mode 100644 index 000000000..b2a8aad67 --- /dev/null +++ b/evmjit/evmcc/test/jump/badindirect1.evm @@ -0,0 +1 @@ +601b602502585d diff --git a/evmjit/evmcc/test/jump/badindirect1.lll b/evmjit/evmcc/test/jump/badindirect1.lll new file mode 100644 index 000000000..d6291be68 --- /dev/null +++ b/evmjit/evmcc/test/jump/badindirect1.lll @@ -0,0 +1,9 @@ +;; Indirect jump out of code + +(asm +27 +37 +MUL +JUMP +JUMPDEST +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/badindirect2.evm b/evmjit/evmcc/test/jump/badindirect2.evm new file mode 100644 index 000000000..22217523d --- /dev/null +++ b/evmjit/evmcc/test/jump/badindirect2.evm @@ -0,0 +1 @@ +60016003600302596000600058 diff --git a/evmjit/evmcc/test/jump/badindirect2.lll b/evmjit/evmcc/test/jump/badindirect2.lll new file mode 100644 index 000000000..53a6294f7 --- /dev/null +++ b/evmjit/evmcc/test/jump/badindirect2.lll @@ -0,0 +1,12 @@ +;; Indirect jump into data + +(asm +1 ;; 0 +3 +3 +MUL ;; 6 +JUMPI ;; 7 +0 ;; 8 +0 +JUMP +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/badjump1.evm b/evmjit/evmcc/test/jump/badjump1.evm new file mode 100644 index 000000000..5c11a8661 --- /dev/null +++ b/evmjit/evmcc/test/jump/badjump1.evm @@ -0,0 +1 @@ +6103e758 diff --git a/evmjit/evmcc/test/jump/badjump1.lll b/evmjit/evmcc/test/jump/badjump1.lll new file mode 100644 index 000000000..1834a62ef --- /dev/null +++ b/evmjit/evmcc/test/jump/badjump1.lll @@ -0,0 +1,6 @@ +;; Direct jump out of code. + +(asm +999 +JUMP +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/badjump2.evm b/evmjit/evmcc/test/jump/badjump2.evm new file mode 100644 index 000000000..900a1c15a --- /dev/null +++ b/evmjit/evmcc/test/jump/badjump2.evm @@ -0,0 +1 @@ +6004586000600058 diff --git a/evmjit/evmcc/test/jump/badjump2.lll b/evmjit/evmcc/test/jump/badjump2.lll new file mode 100644 index 000000000..ce61276d7 --- /dev/null +++ b/evmjit/evmcc/test/jump/badjump2.lll @@ -0,0 +1,9 @@ +;; Direct jump into data + +(asm +4 ;; 0 0-3 +JUMP ;; 2 +0 ;; 3 3-4 +0 ;; 5 4-7 +JUMP ;; 6 +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/call1.ethel b/evmjit/evmcc/test/jump/call1.ethel new file mode 100644 index 000000000..414ad0124 --- /dev/null +++ b/evmjit/evmcc/test/jump/call1.ethel @@ -0,0 +1,5 @@ +let f n = n + 1 + +return f 2 + + diff --git a/evmjit/evmcc/test/jump/call1.evm b/evmjit/evmcc/test/jump/call1.evm new file mode 100644 index 000000000..252aaf778 --- /dev/null +++ b/evmjit/evmcc/test/jump/call1.evm @@ -0,0 +1 @@ +600760026010585d60005460206000f28060010190509058 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/call2.ethel b/evmjit/evmcc/test/jump/call2.ethel new file mode 100644 index 000000000..bdeb9b734 --- /dev/null +++ b/evmjit/evmcc/test/jump/call2.ethel @@ -0,0 +1,5 @@ +let f a b = a + b + +return f 2 3 + + diff --git a/evmjit/evmcc/test/jump/call2.evm b/evmjit/evmcc/test/jump/call2.evm new file mode 100644 index 000000000..6832e044d --- /dev/null +++ b/evmjit/evmcc/test/jump/call2.evm @@ -0,0 +1 @@ +6009600260036012585d60005460206000f2818101905090509058 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/fac.ethel b/evmjit/evmcc/test/jump/fac.ethel new file mode 100644 index 000000000..8bfe94dd6 --- /dev/null +++ b/evmjit/evmcc/test/jump/fac.ethel @@ -0,0 +1,5 @@ +let fac n = + if n == 0 then 1 + else n * fac (n-1) + +return fac 60 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/fac.evm b/evmjit/evmcc/test/jump/fac.evm new file mode 100644 index 000000000..04cd3e4f4 --- /dev/null +++ b/evmjit/evmcc/test/jump/fac.evm @@ -0,0 +1 @@ +6007603c6010585d60005460206000f26000810e6026596020600182036010585d8102602858600190509058 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/fac_tail.ethel b/evmjit/evmcc/test/jump/fac_tail.ethel new file mode 100644 index 000000000..9ce5ecac7 --- /dev/null +++ b/evmjit/evmcc/test/jump/fac_tail.ethel @@ -0,0 +1,5 @@ +let fac a n = + if n == 0 then a + else fac (a*n) (n-1) + +return fac 1 60 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/fac_tail.evm b/evmjit/evmcc/test/jump/fac_tail.evm new file mode 100644 index 000000000..8384d94e4 --- /dev/null +++ b/evmjit/evmcc/test/jump/fac_tail.evm @@ -0,0 +1 @@ +60096001603c6012585d60005460206000f26000810e6029596025818302600183036012585d602a5881905090509058 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/fib1.ethel b/evmjit/evmcc/test/jump/fib1.ethel new file mode 100644 index 000000000..81b869f41 --- /dev/null +++ b/evmjit/evmcc/test/jump/fib1.ethel @@ -0,0 +1,6 @@ +let fib n = + if n < 3 then 1 + else fib (n-1) + fib (n-2) + +return fib 10 + diff --git a/evmjit/evmcc/test/jump/fib1.evm b/evmjit/evmcc/test/jump/fib1.evm new file mode 100644 index 000000000..5042a192f --- /dev/null +++ b/evmjit/evmcc/test/jump/fib1.evm @@ -0,0 +1 @@ +6007600a6010585d60005460206000f26003810a602f596020600282036010585d602a600183036010585d01603158600190509058 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/for1.evm b/evmjit/evmcc/test/jump/for1.evm new file mode 100644 index 000000000..f8e65cbf2 --- /dev/null +++ b/evmjit/evmcc/test/jump/for1.evm @@ -0,0 +1 @@ +600a60805460006080530b0f60255960a0536080530160a054600160805303608054600558 diff --git a/evmjit/evmcc/test/jump/for1.lll b/evmjit/evmcc/test/jump/for1.lll new file mode 100644 index 000000000..419fc9b54 --- /dev/null +++ b/evmjit/evmcc/test/jump/for1.lll @@ -0,0 +1,3 @@ +(for [i]:10 (> @i 0) [i](- @i 1) + [j](+ @i @j) +) diff --git a/evmjit/evmcc/test/jump/for2.evm b/evmjit/evmcc/test/jump/for2.evm new file mode 100644 index 000000000..628297778 --- /dev/null +++ b/evmjit/evmcc/test/jump/for2.evm @@ -0,0 +1 @@ +6000608054600a6080530a0f60255960a0536080530160a054600160805301608054600558 diff --git a/evmjit/evmcc/test/jump/for2.lll b/evmjit/evmcc/test/jump/for2.lll new file mode 100644 index 000000000..de17d65ac --- /dev/null +++ b/evmjit/evmcc/test/jump/for2.lll @@ -0,0 +1,3 @@ +(for [i]:0 (< @i 10) [i](+ @i 1) + [j](+ @i @j) +) diff --git a/evmjit/evmcc/test/jump/if1.ethel b/evmjit/evmcc/test/jump/if1.ethel new file mode 100644 index 000000000..85c3e126b --- /dev/null +++ b/evmjit/evmcc/test/jump/if1.ethel @@ -0,0 +1 @@ +return if 0 then 1 else 2 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/if1.evm b/evmjit/evmcc/test/jump/if1.evm new file mode 100644 index 000000000..51fbe04bd --- /dev/null +++ b/evmjit/evmcc/test/jump/if1.evm @@ -0,0 +1 @@ +60006300000010596002630000001258600160005460206000f2 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/if2.ethel b/evmjit/evmcc/test/jump/if2.ethel new file mode 100644 index 000000000..2a58d6365 --- /dev/null +++ b/evmjit/evmcc/test/jump/if2.ethel @@ -0,0 +1 @@ +return if 1 then 1 else 2 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/if2.evm b/evmjit/evmcc/test/jump/if2.evm new file mode 100644 index 000000000..6d823b374 --- /dev/null +++ b/evmjit/evmcc/test/jump/if2.evm @@ -0,0 +1 @@ +60016300000010596002630000001258600160005460206000f2 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/indirect1.evm b/evmjit/evmcc/test/jump/indirect1.evm new file mode 100644 index 000000000..ab6928304 --- /dev/null +++ b/evmjit/evmcc/test/jump/indirect1.evm @@ -0,0 +1 @@ +600460030158005d6001600054 diff --git a/evmjit/evmcc/test/jump/indirect1.lll b/evmjit/evmcc/test/jump/indirect1.lll new file mode 100644 index 000000000..1ee7dc347 --- /dev/null +++ b/evmjit/evmcc/test/jump/indirect1.lll @@ -0,0 +1,13 @@ +;; Indirect JUMP + +(asm +4 ;; 0 +3 ;; 2 +ADD ;; 4 +JUMP ;; 5 +STOP ;; 6 +JUMPDEST ;; 7 +1 +0 +MSTORE +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/indirect2.evm b/evmjit/evmcc/test/jump/indirect2.evm new file mode 100644 index 000000000..e9697eaa1 --- /dev/null +++ b/evmjit/evmcc/test/jump/indirect2.evm @@ -0,0 +1 @@ +600860060158005d6001600054005d600260005400 diff --git a/evmjit/evmcc/test/jump/indirect2.lll b/evmjit/evmcc/test/jump/indirect2.lll new file mode 100644 index 000000000..f2f068630 --- /dev/null +++ b/evmjit/evmcc/test/jump/indirect2.lll @@ -0,0 +1,19 @@ +;; Indirect JUMP + +(asm +8 ;; 0 +6 ;; 2 +ADD ;; 4 +JUMP ;; 5 --> 14 +STOP ;; 6 +JUMPDEST ;; 7 +1 ;; 8 +0 ;; 10 +MSTORE ;; 12 +STOP ;; 13 +JUMPDEST ;; 14 +2 +0 +MSTORE +STOP +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/indirect3.evm b/evmjit/evmcc/test/jump/indirect3.evm new file mode 100644 index 000000000..1fb0a356c --- /dev/null +++ b/evmjit/evmcc/test/jump/indirect3.evm @@ -0,0 +1 @@ +6001600460050159005d6001600054 diff --git a/evmjit/evmcc/test/jump/indirect3.lll b/evmjit/evmcc/test/jump/indirect3.lll new file mode 100644 index 000000000..d6a679f9a --- /dev/null +++ b/evmjit/evmcc/test/jump/indirect3.lll @@ -0,0 +1,14 @@ +;; Indirect JUMP + +(asm +1 ;; 0 +4 ;; 2 +5 ;; 4 +ADD ;; 6 +JUMPI ;; 7 +STOP ;; 8 +JUMPDEST ;; 9 +1 +0 +MSTORE +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/indirect4.evm b/evmjit/evmcc/test/jump/indirect4.evm new file mode 100644 index 000000000..f0e31a8f4 --- /dev/null +++ b/evmjit/evmcc/test/jump/indirect4.evm @@ -0,0 +1 @@ +60006007600501596001600054005d00 diff --git a/evmjit/evmcc/test/jump/indirect4.lll b/evmjit/evmcc/test/jump/indirect4.lll new file mode 100644 index 000000000..7fbe0b833 --- /dev/null +++ b/evmjit/evmcc/test/jump/indirect4.lll @@ -0,0 +1,15 @@ +;; Indirect JUMP + +(asm +0 ;; 0 +7 ;; 2 +5 ;; 4 +ADD ;; 6 +JUMPI ;; 7 +1 ;; 8 +0 ;; 9 +MSTORE ;; 10 +STOP ;; 11 +JUMPDEST ;; 12 +STOP +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/jump1.evm b/evmjit/evmcc/test/jump/jump1.evm new file mode 100644 index 000000000..0df9b4036 --- /dev/null +++ b/evmjit/evmcc/test/jump/jump1.evm @@ -0,0 +1 @@ +600458006001600154 diff --git a/evmjit/evmcc/test/jump/jump1.lll b/evmjit/evmcc/test/jump/jump1.lll new file mode 100644 index 000000000..33119edb3 --- /dev/null +++ b/evmjit/evmcc/test/jump/jump1.lll @@ -0,0 +1,11 @@ +;; Direct JUMP. +;; output: memory[1] == 1 + +(asm +4 ;; 0 +JUMP ;; 2 +STOP ;; 3 +1 ;; 4 +1 ;; 6 +MSTORE ;; 8 +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/jump2.evm b/evmjit/evmcc/test/jump/jump2.evm new file mode 100644 index 000000000..35d75941d --- /dev/null +++ b/evmjit/evmcc/test/jump/jump2.evm @@ -0,0 +1 @@ +6008586001600154 diff --git a/evmjit/evmcc/test/jump/jump2.lll b/evmjit/evmcc/test/jump/jump2.lll new file mode 100644 index 000000000..a70d50ecb --- /dev/null +++ b/evmjit/evmcc/test/jump/jump2.lll @@ -0,0 +1,10 @@ +;; Direct JUMP to the end of code. +;; output: memory should have size 0. + +(asm +8 ;; 0 +JUMP ;; 2 +1 ;; 3 +1 ;; 5 +MSTORE ;; 7 +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/jump3.evm b/evmjit/evmcc/test/jump/jump3.evm new file mode 100644 index 000000000..599d4a764 --- /dev/null +++ b/evmjit/evmcc/test/jump/jump3.evm @@ -0,0 +1 @@ +602a586001600154 diff --git a/evmjit/evmcc/test/jump/jump3.lll b/evmjit/evmcc/test/jump/jump3.lll new file mode 100644 index 000000000..bc897e30c --- /dev/null +++ b/evmjit/evmcc/test/jump/jump3.lll @@ -0,0 +1,10 @@ +;; Direct JUMP past the end of code. +;; output: memory should have size 0. + +(asm +42 +JUMP +1 +1 +MSTORE +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/jump4.evm b/evmjit/evmcc/test/jump/jump4.evm new file mode 100644 index 000000000..41713f43e --- /dev/null +++ b/evmjit/evmcc/test/jump/jump4.evm @@ -0,0 +1 @@ +600b6009580000600558005d6001600154 diff --git a/evmjit/evmcc/test/jump/jump4.lll b/evmjit/evmcc/test/jump/jump4.lll new file mode 100644 index 000000000..131baee2d --- /dev/null +++ b/evmjit/evmcc/test/jump/jump4.lll @@ -0,0 +1,17 @@ +;; Direct JUMP. +;; output: memory[1] = 1 + +(asm +11 ;; 0 +9 ;; 2 +JUMP ;; 4 --> 9 +STOP ;; 5 +STOP ;; 6 +5 ;; 7 +JUMP ;; 9 --> 11 +STOP ;; 10 +JUMPDEST +1 ;; 11 +1 +MSTORE +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/jump5.evm b/evmjit/evmcc/test/jump/jump5.evm new file mode 100644 index 000000000..c36d9615b --- /dev/null +++ b/evmjit/evmcc/test/jump/jump5.evm @@ -0,0 +1 @@ +6005600e585d600160015400600f5800 diff --git a/evmjit/evmcc/test/jump/jump5.lll b/evmjit/evmcc/test/jump/jump5.lll new file mode 100644 index 000000000..d28b7d4ac --- /dev/null +++ b/evmjit/evmcc/test/jump/jump5.lll @@ -0,0 +1,16 @@ +;; Direct JUMP. +;; output: memory[1] = 1 + +(asm +5 ;; 0 +14 ;; 2 +JUMP ;; 4 --> 14 +JUMPDEST ;; 5 +1 ;; 6 +1 ;; 8 +MSTORE ;; 10 +STOP ;; 11 +15 ;; 12 +JUMP ;; 14 --> 5 +STOP ;; 15 +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/jump6.evm b/evmjit/evmcc/test/jump/jump6.evm new file mode 100644 index 000000000..029db7191 --- /dev/null +++ b/evmjit/evmcc/test/jump/jump6.evm @@ -0,0 +1 @@ +600358600f600d58006014600758005d6001600154005d600260025400 diff --git a/evmjit/evmcc/test/jump/jump6.lll b/evmjit/evmcc/test/jump/jump6.lll new file mode 100644 index 000000000..1116aa663 --- /dev/null +++ b/evmjit/evmcc/test/jump/jump6.lll @@ -0,0 +1,32 @@ +;; Direct JUMP. +;; output: memory[1] = 1 + +;; 0, 2 --> 3 .. 7 --> 13 -*-> 15 .. 19 + +(asm +3 ;; 0 +JUMP ;; 2 + +15 ;; 3 <- start +13 ;; 5 +JUMP ;; 7 <- b +STOP ;; 8 + +20 ;; 9 +7 ;; 11 + +JUMP ;; 13 <- a +STOP ;; 14 + +JUMPDEST ;; 15 <- c +1 ;; 16 +1 ;; 18 +MSTORE ;; 19 +STOP ;; 20 + +JUMPDEST ;; 21 <- d +2 ;; 22 +2 ;; 24 +MSTORE ;; 26 +STOP ;; 27 +) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/jumpi_at_the_end.evm b/evmjit/evmcc/test/jump/jumpi_at_the_end.evm new file mode 100644 index 000000000..2d7411761 --- /dev/null +++ b/evmjit/evmcc/test/jump/jumpi_at_the_end.evm @@ -0,0 +1 @@ +600a6000545d6000536001900380600054600659 diff --git a/evmjit/evmcc/test/jump/jumpi_at_the_end.lll b/evmjit/evmcc/test/jump/jumpi_at_the_end.lll new file mode 100644 index 000000000..263ada6a7 --- /dev/null +++ b/evmjit/evmcc/test/jump/jumpi_at_the_end.lll @@ -0,0 +1 @@ +(asm 10 0 MSTORE JUMPDEST 0 MLOAD 1 SWAP1 SUB DUP1 0 MSTORE 6 JUMPI) \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/loop1.evm b/evmjit/evmcc/test/jump/loop1.evm new file mode 100644 index 000000000..7724d6308 --- /dev/null +++ b/evmjit/evmcc/test/jump/loop1.evm @@ -0,0 +1 @@ +600a600181038060025960005460015460025400 diff --git a/evmjit/evmcc/test/jump/loop1.lll b/evmjit/evmcc/test/jump/loop1.lll new file mode 100644 index 000000000..0044ec1fb --- /dev/null +++ b/evmjit/evmcc/test/jump/loop1.lll @@ -0,0 +1,27 @@ +;; Produces 1 2 3 4 5 6 7 8 9 10 on the stack and exits + +(asm +10 + +;; 2 +1 +DUP2 +SUB +DUP1 +2 +JUMPI + +;; stack = 1 2 3 4 5 6 7 8 9 10 +0 +MSTORE +1 +MSTORE +2 +MSTORE +;;3 +;;MSTORE + +STOP +) + + diff --git a/evmjit/evmcc/test/jump/loop2.evm b/evmjit/evmcc/test/jump/loop2.evm new file mode 100644 index 000000000..faffa4e5b --- /dev/null +++ b/evmjit/evmcc/test/jump/loop2.evm @@ -0,0 +1 @@ +600a80600190038060025960005460015460025400 diff --git a/evmjit/evmcc/test/jump/loop2.lll b/evmjit/evmcc/test/jump/loop2.lll new file mode 100644 index 000000000..9996c52ba --- /dev/null +++ b/evmjit/evmcc/test/jump/loop2.lll @@ -0,0 +1,28 @@ +;; Produces 1 2 3 4 5 6 7 8 9 10 on the stack and exits + +(asm +10 + +;; 2 +DUP1 +1 +SWAP1 +SUB +DUP1 +2 +JUMPI + +;; stack = 1 2 3 4 5 6 7 8 9 10 +0 +MSTORE +1 +MSTORE +2 +MSTORE +;;3 +;;MSTORE + +STOP +) + + diff --git a/evmjit/evmcc/test/jump/rec1.ethel b/evmjit/evmcc/test/jump/rec1.ethel new file mode 100644 index 000000000..f83c8e81e --- /dev/null +++ b/evmjit/evmcc/test/jump/rec1.ethel @@ -0,0 +1,4 @@ +let f n = + if n == 0 then 2 else f (n-1) + +return f 10 diff --git a/evmjit/evmcc/test/jump/rec1.evm b/evmjit/evmcc/test/jump/rec1.evm new file mode 100644 index 000000000..2ae62aff6 --- /dev/null +++ b/evmjit/evmcc/test/jump/rec1.evm @@ -0,0 +1 @@ +6007600a6010585d60005460206000f26000810e6024596020600182036010585d602658600290509058 \ No newline at end of file diff --git a/evmjit/evmcc/test/jump/when1.asm b/evmjit/evmcc/test/jump/when1.asm new file mode 100644 index 000000000..01d41c266 --- /dev/null +++ b/evmjit/evmcc/test/jump/when1.asm @@ -0,0 +1,10 @@ +.code: + PUSH 1 + NOT + PUSH [tag0] + JUMPI + PUSH 13 + PUSH 128 + MSTORE +tag0: + diff --git a/evmjit/evmcc/test/jump/when1.evm b/evmjit/evmcc/test/jump/when1.evm new file mode 100644 index 000000000..303b02623 --- /dev/null +++ b/evmjit/evmcc/test/jump/when1.evm @@ -0,0 +1 @@ +60010f600b59600d608054 diff --git a/evmjit/evmcc/test/jump/when1.lll b/evmjit/evmcc/test/jump/when1.lll new file mode 100644 index 000000000..990a6e64a --- /dev/null +++ b/evmjit/evmcc/test/jump/when1.lll @@ -0,0 +1,2 @@ +(when (> 1 0) [i] 13) + \ No newline at end of file diff --git a/evmjit/evmcc/test/kv.evm b/evmjit/evmcc/test/kv.evm new file mode 100644 index 000000000..55141ea59 --- /dev/null +++ b/evmjit/evmcc/test/kv.evm @@ -0,0 +1 @@ +33604557602a8060106000396000f200604556330e0f602a59366080530a0f602a59602060805301356080533557604060805301608054600958 diff --git a/evmjit/evmcc/test/kv.lll b/evmjit/evmcc/test/kv.lll new file mode 100644 index 000000000..c62d9fa70 --- /dev/null +++ b/evmjit/evmcc/test/kv.lll @@ -0,0 +1,10 @@ +{ + [[69]] (caller) + (return 0 (lll + (when (= (caller) @@69) + (for {} (< @i (calldatasize)) [i](+ @i 64) + [[ (calldataload @i) ]] (calldataload (+ @i 32)) + ) + ) + 0)) +} diff --git a/evmjit/evmcc/test/mem/byte.evm b/evmjit/evmcc/test/mem/byte.evm new file mode 100644 index 000000000..ab63431ee --- /dev/null +++ b/evmjit/evmcc/test/mem/byte.evm @@ -0,0 +1 @@ +7f112233445566778899001122334455667788990011223344556677889900aabb6000137f112233445566778899001122334455667788990011223344556677889900aabb6001137f112233445566778899001122334455667788990011223344556677889900aabb6002137f112233445566778899001122334455667788990011223344556677889900aabb6003137f112233445566778899001122334455667788990011223344556677889900aabb6004137f112233445566778899001122334455667788990011223344556677889900aabb6005137f112233445566778899001122334455667788990011223344556677889900aabb6006137f112233445566778899001122334455667788990011223344556677889900aabb6007137f112233445566778899001122334455667788990011223344556677889900aabb6008137f112233445566778899001122334455667788990011223344556677889900aabb6009137f112233445566778899001122334455667788990011223344556677889900aabb600a137f112233445566778899001122334455667788990011223344556677889900aabb600b137f112233445566778899001122334455667788990011223344556677889900aabb600c137f112233445566778899001122334455667788990011223344556677889900aabb600d137f112233445566778899001122334455667788990011223344556677889900aabb600e137f112233445566778899001122334455667788990011223344556677889900aabb600f137f112233445566778899001122334455667788990011223344556677889900aabb6010137f112233445566778899001122334455667788990011223344556677889900aabb6011137f112233445566778899001122334455667788990011223344556677889900aabb6012137f112233445566778899001122334455667788990011223344556677889900aabb6013137f112233445566778899001122334455667788990011223344556677889900aabb6014137f112233445566778899001122334455667788990011223344556677889900aabb6015137f112233445566778899001122334455667788990011223344556677889900aabb6016137f112233445566778899001122334455667788990011223344556677889900aabb6017137f112233445566778899001122334455667788990011223344556677889900aabb6018137f112233445566778899001122334455667788990011223344556677889900aabb6019137f112233445566778899001122334455667788990011223344556677889900aabb601a137f112233445566778899001122334455667788990011223344556677889900aabb601b137f112233445566778899001122334455667788990011223344556677889900aabb601c137f112233445566778899001122334455667788990011223344556677889900aabb601d137f112233445566778899001122334455667788990011223344556677889900aabb601e137f112233445566778899001122334455667788990011223344556677889900aabb601f137f112233445566778899001122334455667788990011223344556677889900aabb6020137f112233445566778899001122334455667788990011223344556677889900aabb6107de13 diff --git a/evmjit/evmcc/test/mem/byte.lll b/evmjit/evmcc/test/mem/byte.lll new file mode 100644 index 000000000..95b0f99dc --- /dev/null +++ b/evmjit/evmcc/test/mem/byte.lll @@ -0,0 +1,105 @@ + +(asm +0x112233445566778899001122334455667788990011223344556677889900aabb +0 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +1 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +2 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +3 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +4 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +5 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +6 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +7 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +8 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +9 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +10 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +11 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +12 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +13 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +14 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +15 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +16 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +17 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +18 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +19 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +20 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +21 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +22 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +23 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +24 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +25 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +26 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +27 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +28 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +29 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +30 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +31 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +32 +BYTE +0x112233445566778899001122334455667788990011223344556677889900aabb +2014 +BYTE +) \ No newline at end of file diff --git a/evmjit/evmcc/test/mem/mem2.evm b/evmjit/evmcc/test/mem/mem2.evm new file mode 100644 index 000000000..49cc6e8b1 --- /dev/null +++ b/evmjit/evmcc/test/mem/mem2.evm @@ -0,0 +1 @@ +6001610d805b01556504409585d6df620493e05462061a80535b01 diff --git a/evmjit/evmcc/test/mem/mem2.lll b/evmjit/evmcc/test/mem/mem2.lll new file mode 100644 index 000000000..5345ee47c --- /dev/null +++ b/evmjit/evmcc/test/mem/mem2.lll @@ -0,0 +1,15 @@ + +(asm ;; [] +1 +3456 +MSIZE +ADD +MSTORE8 ;; [02] +4675432994527 +300000 +MSTORE +400000 +MLOAD +MSIZE +ADD +) \ No newline at end of file diff --git a/evmjit/evmcc/test/mem/memtest1.evm b/evmjit/evmcc/test/mem/memtest1.evm new file mode 100644 index 000000000..0506bf928 --- /dev/null +++ b/evmjit/evmcc/test/mem/memtest1.evm @@ -0,0 +1 @@ +6002600055600360015560005360015301600254 diff --git a/evmjit/evmcc/test/mem/memtest1.lll b/evmjit/evmcc/test/mem/memtest1.lll new file mode 100644 index 000000000..4b4389ad8 --- /dev/null +++ b/evmjit/evmcc/test/mem/memtest1.lll @@ -0,0 +1,18 @@ + +(asm ;; [] +2 +0 +MSTORE8 ;; [02] +3 +1 +MSTORE8 ;; [02 03] +0 +MLOAD ;; [2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] +1 +MLOAD ;; [2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + ;; 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] +ADD +2 +MSTORE ;; [2 3 5 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + ;; 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] +) \ No newline at end of file diff --git a/evmjit/evmcc/test/mem/mstore1.evm b/evmjit/evmcc/test/mem/mstore1.evm new file mode 100644 index 000000000..ba6141ab1 --- /dev/null +++ b/evmjit/evmcc/test/mem/mstore1.evm @@ -0,0 +1 @@ +6001600054 diff --git a/evmjit/evmcc/test/mem/mstore1.lll b/evmjit/evmcc/test/mem/mstore1.lll new file mode 100644 index 000000000..2d2ca32b5 --- /dev/null +++ b/evmjit/evmcc/test/mem/mstore1.lll @@ -0,0 +1,6 @@ + +(asm ;; [] +1 +0 +MSTORE ;; [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1] +) \ No newline at end of file diff --git a/evmjit/evmcc/test/ret/return1.evm b/evmjit/evmcc/test/ret/return1.evm new file mode 100644 index 000000000..8092cb007 --- /dev/null +++ b/evmjit/evmcc/test/ret/return1.evm @@ -0,0 +1 @@ +600160805460006080530b601b59600160005460206000f2602a58602760005460206000f26002608054 diff --git a/evmjit/evmcc/test/ret/return1.lll b/evmjit/evmcc/test/ret/return1.lll new file mode 100644 index 000000000..159d15ca3 --- /dev/null +++ b/evmjit/evmcc/test/ret/return1.lll @@ -0,0 +1,6 @@ +;; code should return 39 +;; i should remain 1 +{ + [i] 1 + ( if (> @i 0) { (return 39) [i] 2 } (return 1) ) +} \ No newline at end of file diff --git a/evmjit/evmcc/test/ret/return2.evm b/evmjit/evmcc/test/ret/return2.evm new file mode 100644 index 000000000..e29da7664 --- /dev/null +++ b/evmjit/evmcc/test/ret/return2.evm @@ -0,0 +1 @@ +6001620f4240f2 diff --git a/evmjit/evmcc/test/ret/return2.lll b/evmjit/evmcc/test/ret/return2.lll new file mode 100644 index 000000000..f5ee68f6e --- /dev/null +++ b/evmjit/evmcc/test/ret/return2.lll @@ -0,0 +1,6 @@ + +(asm +1 +1000000 +RETURN ;; return 1 byte from index 1M +) \ No newline at end of file diff --git a/evmjit/evmcc/test/ret/return_test.evm b/evmjit/evmcc/test/ret/return_test.evm new file mode 100644 index 000000000..977cf7c19 --- /dev/null +++ b/evmjit/evmcc/test/ret/return_test.evm @@ -0,0 +1 @@ +60016064546002608454600360a45460606064f2 diff --git a/evmjit/evmcc/test/ret/return_test.lll b/evmjit/evmcc/test/ret/return_test.lll new file mode 100644 index 000000000..c87a2d812 --- /dev/null +++ b/evmjit/evmcc/test/ret/return_test.lll @@ -0,0 +1,15 @@ + +(asm +1 +100 +MSTORE +2 +132 +MSTORE +3 +164 +MSTORE +96 +100 +RETURN +) \ No newline at end of file diff --git a/evmjit/evmcc/test/stack/oos.evm b/evmjit/evmcc/test/stack/oos.evm new file mode 100644 index 000000000..ea2f1c890 --- /dev/null +++ b/evmjit/evmcc/test/stack/oos.evm @@ -0,0 +1 @@ +60018194505050509150 diff --git a/evmjit/evmcc/test/stack/oos.lll b/evmjit/evmcc/test/stack/oos.lll new file mode 100644 index 000000000..5394b06ba --- /dev/null +++ b/evmjit/evmcc/test/stack/oos.lll @@ -0,0 +1,11 @@ +(asm ;; x . v y z +1 ;; 1 x . v y z +DUP2 ;; x 1 x . v y z +SWAP5 ;; y 1 x . v x z +POP ;; 1 x . v x z +POP ;; x . v x z +POP ;; . v x z +POP ;; v x z +SWAP2 ;; z x v +POP ;; x v +) diff --git a/evmjit/evmcc/test/stack/push_test.evm b/evmjit/evmcc/test/stack/push_test.evm new file mode 100644 index 000000000..d624cee1d --- /dev/null +++ b/evmjit/evmcc/test/stack/push_test.evm @@ -0,0 +1 @@ +60656107d26204a0c763026921f4640bc5588eb165372d0f1dca6e661ba1d901961c71670c55f7bc23038e3868056bc75e2d630fffff69021e19e0c9bab24000016a085d1c6e8050f0ea1c71bd6b0688be36543f3c36e638e37a6c03d41f73d55d0d482ae55555376dc76810d0fe03c91964d31c71c6f46e615dd0360c07d931663b14e38e38b16f2da3f99955a3adcf27ebb1caaaaaaa6e7014ccba6a8bb1ed35bd86bf065c71c71c2b7109491c5d4781b79c9009de6bfb8e38e38de8720414a0f6fdec81304d4c563e740bffffffffa573118427b3b4a05bc8a8a4de8459868000000000017406eb15e7331e727940d4ac54b7cdca1c71c71c71bd750567a91c9fefc96ebaa626a22f98c5e638e38e38e37a76032abd16c5b68006e15d5aa307e383f4e55555555555377701a6427bdc4f0d58eab5f48a3ec67f64e21c71c71c71c6f478080dd0a0c9b9ff2c2a0c740b06853a0a980ee38e38e38e38b17903c679cb5e8f2f9cb3b5d6652b0e7334f746faaaaaaaaaaaaa6e7a01b873815917ebb2bf3b890a1af495d6235bae3c71c71c71c71c2b7b07ae4cca96e1a55dfa49c85ad3c3e60e426b92fb8e38e38e38e38de87c036018bf074e292bcc7d6c8bea0f9699443046178bffffffffffffffa57d0e7d34c64a9c85d4460dbbca87196b61618a4bd2168000000000000000017e05b901f48a5b994d6572502bc4ea43140486666416aa1c71c71c71c71c71bd7f047889870c178fc477414ea231d70467a388fffe31b4e638e38e38e38e38e37a diff --git a/evmjit/evmcc/test/stack/push_test.lll b/evmjit/evmcc/test/stack/push_test.lll new file mode 100644 index 000000000..832daaec1 --- /dev/null +++ b/evmjit/evmcc/test/stack/push_test.lll @@ -0,0 +1,35 @@ + +(asm +101 ;; PUSH1 +2002 ;; PUSH2 +303303 ;; PUSH3 +40444404 ;; PUSH4 +50555555505 ;; PUSH5 +60666666666606 +7777777777777777 +888888888888888888 +99999999999999999999 +10000000000000000000001 +10111111111111111111111101 +2022222222222222222222222202 +303333333333333333333333333303 +4044444444444444444444444444444404 +505555555555555555555555555555555505 +60666666666666666666666666666666666606 +7077777777777777777777777777777777777707 +808888888888888888888888888888888888888808 +90999999999999999999999999999999999999999909 +100000000000000000000000000000000000000000000001 +10111111111111111111111111111111111111111111111101 +2022222222222222222222222222222222222222222222222202 +303333333333333333333333333333333333333333333333333303 +40444444444444444444444444444444444444444444444444444404 +50555555555555555555555555555555555555555555555555555555505 +6066666666666666666666666666666666666666666666666666666666606 +707777777777777777777777777777777777777777777777777777777777707 +808888888888888888888888888888888888888888888888888888888888888808 +90999999999999999999999999999999999999999999999999999999999999999909 +100000000000000000000000000000000000000000000000000000000000000000000001 +10111111111111111111111111111111111111111111111111111111111111111111111101 +2022222222222222222222222222222222222222222222222222222222222222222222222202 ;; PUSH32 +) \ No newline at end of file diff --git a/evmjit/evmcc/test/stack/stack_test.evm b/evmjit/evmcc/test/stack/stack_test.evm new file mode 100644 index 000000000..02417c967 --- /dev/null +++ b/evmjit/evmcc/test/stack/stack_test.evm @@ -0,0 +1 @@ +65372d0f1dca6e661925338e3e5c2b808280848184505050505050506104576108ae81819290 diff --git a/evmjit/evmcc/test/stack/stack_test.lll b/evmjit/evmcc/test/stack/stack_test.lll new file mode 100644 index 000000000..fdf83594c --- /dev/null +++ b/evmjit/evmcc/test/stack/stack_test.lll @@ -0,0 +1,8 @@ + +(asm +123 +SSTORE +SLOAD +123 +SUB +) \ No newline at end of file diff --git a/evmjit/evmcc/test/stack/stackjump.evm b/evmjit/evmcc/test/stack/stackjump.evm new file mode 100644 index 000000000..baddec42e --- /dev/null +++ b/evmjit/evmcc/test/stack/stackjump.evm @@ -0,0 +1 @@ +600460066009601358600a036000545b6000f260005401600958 \ No newline at end of file diff --git a/evmjit/evmcc/test/stack/stackjump.lll b/evmjit/evmcc/test/stack/stackjump.lll new file mode 100644 index 000000000..f5da5e733 --- /dev/null +++ b/evmjit/evmcc/test/stack/stackjump.lll @@ -0,0 +1,3 @@ +(asm +0x4 0x6 0x9 0x13 JUMP 0xa SUB 0x0 MSTORE MSIZE 0x0 RETURN 0x0 MSTORE ADD 0x9 JUMP +) diff --git a/evmjit/evmcc/test/stack/swap.evm b/evmjit/evmcc/test/stack/swap.evm new file mode 100644 index 000000000..d17f0ee09 --- /dev/null +++ b/evmjit/evmcc/test/stack/swap.evm @@ -0,0 +1 @@ +600560026001600c59505000906001601559505000036000546001601ff2 diff --git a/evmjit/evmcc/test/stack/swap.lll b/evmjit/evmcc/test/stack/swap.lll new file mode 100644 index 000000000..90dee585d --- /dev/null +++ b/evmjit/evmcc/test/stack/swap.lll @@ -0,0 +1,31 @@ +(asm +5 ;; 0 +2 ;; 2 +1 ;; 4 +12 ;; 6 +JUMPI ;; 8 + +POP ;; 9 +POP ;; 10 +STOP ;; 11 + +;; stack = 2,1 +SWAP1 ;; 12 +1 ;; 13 +21 ;; 15 +JUMPI ;; 17 + +POP ;; 18 +POP ;; 19 +STOP ;; 20 + +;; stack = 1,2 +SUB ;; 21 +0 +MSTORE +1 +31 +RETURN ;; returns 03 +) + + diff --git a/evmjit/evmcc/test/stack/swapswap.evm b/evmjit/evmcc/test/stack/swapswap.evm new file mode 100644 index 000000000..fb4f1bf75 --- /dev/null +++ b/evmjit/evmcc/test/stack/swapswap.evm @@ -0,0 +1 @@ +600260056001600c5950500090906001601659505000036000546001601ff2 diff --git a/evmjit/evmcc/test/stack/swapswap.lll b/evmjit/evmcc/test/stack/swapswap.lll new file mode 100644 index 000000000..1fedf726e --- /dev/null +++ b/evmjit/evmcc/test/stack/swapswap.lll @@ -0,0 +1,32 @@ +(asm +2 ;; 0 +5 ;; 2 +1 ;; 4 +12 ;; 6 +JUMPI ;; 8 + +POP ;; 9 +POP ;; 10 +STOP ;; 11 + +;; stack = 2,1 +SWAP1 ;; 12 +SWAP1 ;; 13 +1 ;; 14 +22 ;; 16 +JUMPI ;; 18 + +POP ;; 19 +POP ;; 20 +STOP ;; 21 + +;; stack = 2,1 +SUB ;; 22 +0 +MSTORE +1 +31 +RETURN ;; returns 03 +) + + diff --git a/evmjit/evmcc/test/stack/test.evm b/evmjit/evmcc/test/stack/test.evm new file mode 100644 index 000000000..ea2f1c890 --- /dev/null +++ b/evmjit/evmcc/test/stack/test.evm @@ -0,0 +1 @@ +60018194505050509150 diff --git a/evmjit/evmcc/test/vmtests/vmArithPerformanceTest.json b/evmjit/evmcc/test/vmtests/vmArithPerformanceTest.json new file mode 100644 index 000000000..d9017517f --- /dev/null +++ b/evmjit/evmcc/test/vmtests/vmArithPerformanceTest.json @@ -0,0 +1,260 @@ +{ + "arith-1" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x600a60005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "data" : "0x", + "gas" : "1000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "999538", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x600a60005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x600a60005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "nonce" : "0", + "storage" : { } + } + } + } + + , + + "arith-2" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x606460005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "data" : "0x", + "gas" : "1000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "995488", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x606460005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x606460005260005160105760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600556", + "nonce" : "0", + "storage" : { } + } + } + } + + , + + "arith-3" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x6103e860005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "data" : "0x", + "gas" : "1000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "954988", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6103e860005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6103e860005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "nonce" : "0", + "storage" : { } + } + } + } + + , + + "arith-4" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x61271060005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "data" : "0x", + "gas" : "1000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "549988", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x61271060005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x61271060005260005160115760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600656", + "nonce" : "0", + "storage" : { } + } + } + } + + + , + + + "arith-5" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x620186a060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "data" : "0x", + "gas" : "10000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "5499988", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620186a060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620186a060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "nonce" : "0", + "storage" : { } + } + } + } + +, + + "arith-6" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "100000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x620f424060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "data" : "0x", + "gas" : "100000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "54999988", + "out" : "0x0000000000000000000000000000000000000000000000000000001b9c636491", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620f424060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620f424060005260005160125760206020f26001600190016007026005016002900460049006602190056015016003026005900760030360090160110a60205260005160019003600052600756", + "nonce" : "0", + "storage" : { } + } + } + } + +} diff --git a/evmjit/evmcc/test/vmtests/vmPerformanceTest.json b/evmjit/evmcc/test/vmtests/vmPerformanceTest.json new file mode 100644 index 000000000..604e45993 --- /dev/null +++ b/evmjit/evmcc/test/vmtests/vmPerformanceTest.json @@ -0,0 +1,214 @@ +{ + "mulmodloop" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x60015b68010000000000000000908060010109600356", + "data" : "0x", + "gas" : "10000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "0", + "out" : "0x0", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x60015b68010000000000000000908060010109600356", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x60015b68010000000000000000908060010109600356", + "nonce" : "0", + "storage" : { } + } + } + }, + + + "for-1e06" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "100000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x620f42406080525b6000608051111560285760a0516080510160a0526001608051036080526007565b", + "data" : "0x", + "gas" : "30000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "6999982", + "out" : "0x00", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620f42406080525b6000608051111560285760a0516080510160a0526001608051036080526007565b", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x620f42406080525b6000608051111560285760a0516080510160a0526001608051036080526007565b", + "nonce" : "0", + "storage" : { } + } + } + }, + + "fib25" : { + "callcreates" : [ + ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "100000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x6007601e6010565b60005260206000f25b600381106030576021600282036010565b602b600183036010565b016033565b60015b90509056", + "data" : "0x", + "gas" : "40000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "5886377", + "out" : "0x00000000000000000000000000000000000000000000000000000000000cb228", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6007601e6010565b60005260206000f25b600381106030576021600282036010565b602b600183036010565b016033565b60015b90509056", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6007601e6010565b60005260206000f25b600381106030576021600282036010565b602b600183036010565b016033565b60015b90509056", + "nonce" : "0", + "storage" : { + } + } + } + }, + + "ackermann37" : { + "callcreates" : [ + ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "20000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x6009600360076012565b60005260206000f25b60008214604a5760008114603957603560018303603184600185036012565b6012565b6046565b60456001830360016012565b5b604f565b600181015b905090509056", + "data" : "0x", + "gas" : "20000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "913456", + "out" : "0x00000000000000000000000000000000000000000000000000000000000003fd", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6009600360076012565b60005260206000f25b60008214604a5760008114603957603560018303603184600185036012565b6012565b6046565b60456001830360016012565b5b604f565b600181015b905090509056", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6009600360076012565b60005260206000f25b60008214604a5760008114603957603560018303603184600185036012565b6012565b6046565b60456001830360016012565b5b604f565b600181015b905090509056", + "nonce" : "0", + "storage" : { + } + } + } + }, + + "jumptable100" : { + "callcreates" : [ + ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "0x6103e85b60019003806000146104605761001a600160005256005b610025600260005256005b610030600360005256005b61003b600460005256005b610046600560005256005b610051600660005256005b61005c600760005256005b610067600860005256005b610072600960005256005b61007d600a60005256005b610088600b60005256005b610093600c60005256005b61009e600d60005256005b6100a9600e60005256005b6100b4600f60005256005b6100bf601060005256005b6100ca601160005256005b6100d5601260005256005b6100e0601360005256005b6100eb601460005256005b6100f6601560005256005b610101601660005256005b61010c601760005256005b610117601860005256005b610122601960005256005b61012d601a60005256005b610138601b60005256005b610143601c60005256005b61014e601d60005256005b610159601e60005256005b610164601f60005256005b61016f602060005256005b61017a602160005256005b610185602260005256005b610190602360005256005b61019b602460005256005b6101a6602560005256005b6101b1602660005256005b6101bc602760005256005b6101c7602860005256005b6101d2602960005256005b6101dd602a60005256005b6101e8602b60005256005b6101f3602c60005256005b6101fe602d60005256005b610209602e60005256005b610214602f60005256005b61021f603060005256005b61022a603160005256005b610235603260005256005b610240603360005256005b61024b603460005256005b610256603560005256005b610261603660005256005b61026c603760005256005b610277603860005256005b610282603960005256005b61028d603a60005256005b610298603b60005256005b6102a3603c60005256005b6102ae603d60005256005b6102b9603e60005256005b6102c4603f60005256005b6102cf604060005256005b6102da604160005256005b6102e5604260005256005b6102f0604360005256005b6102fb604460005256005b610306604560005256005b610311604660005256005b61031c604760005256005b610327604860005256005b610332604960005256005b61033d604a60005256005b610348604b60005256005b610353604c60005256005b61035e604d60005256005b610369604e60005256005b610374604f60005256005b61037f605060005256005b61038a605160005256005b610395605260005256005b6103a0605360005256005b6103ab605460005256005b6103b6605560005256005b6103c1605660005256005b6103cc605760005256005b6103d7605860005256005b6103e2605960005256005b6103ed605a60005256005b6103f8605b60005256005b610403605c60005256005b61040e605d60005256005b610419605e60005256005b610424605f60005256005b61042f606060005256005b61043a606160005256005b610445606260005256005b610450606360005256005b61045b606460005256005b610003565b60206000f2", + "data" : "0x", + "gas" : "1000000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "389596", + "out" : "0x0000000000000000000000000000000000000000000000000000000000000064", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6103e85b60019003806000146104605761001a600160005256005b610025600260005256005b610030600360005256005b61003b600460005256005b610046600560005256005b610051600660005256005b61005c600760005256005b610067600860005256005b610072600960005256005b61007d600a60005256005b610088600b60005256005b610093600c60005256005b61009e600d60005256005b6100a9600e60005256005b6100b4600f60005256005b6100bf601060005256005b6100ca601160005256005b6100d5601260005256005b6100e0601360005256005b6100eb601460005256005b6100f6601560005256005b610101601660005256005b61010c601760005256005b610117601860005256005b610122601960005256005b61012d601a60005256005b610138601b60005256005b610143601c60005256005b61014e601d60005256005b610159601e60005256005b610164601f60005256005b61016f602060005256005b61017a602160005256005b610185602260005256005b610190602360005256005b61019b602460005256005b6101a6602560005256005b6101b1602660005256005b6101bc602760005256005b6101c7602860005256005b6101d2602960005256005b6101dd602a60005256005b6101e8602b60005256005b6101f3602c60005256005b6101fe602d60005256005b610209602e60005256005b610214602f60005256005b61021f603060005256005b61022a603160005256005b610235603260005256005b610240603360005256005b61024b603460005256005b610256603560005256005b610261603660005256005b61026c603760005256005b610277603860005256005b610282603960005256005b61028d603a60005256005b610298603b60005256005b6102a3603c60005256005b6102ae603d60005256005b6102b9603e60005256005b6102c4603f60005256005b6102cf604060005256005b6102da604160005256005b6102e5604260005256005b6102f0604360005256005b6102fb604460005256005b610306604560005256005b610311604660005256005b61031c604760005256005b610327604860005256005b610332604960005256005b61033d604a60005256005b610348604b60005256005b610353604c60005256005b61035e604d60005256005b610369604e60005256005b610374604f60005256005b61037f605060005256005b61038a605160005256005b610395605260005256005b6103a0605360005256005b6103ab605460005256005b6103b6605560005256005b6103c1605660005256005b6103cc605760005256005b6103d7605860005256005b6103e2605960005256005b6103ed605a60005256005b6103f8605b60005256005b610403605c60005256005b61040e605d60005256005b610419605e60005256005b610424605f60005256005b61042f606060005256005b61043a606160005256005b610445606260005256005b610450606360005256005b61045b606460005256005b610003565b60206000f2", + "nonce" : "0", + "storage" : { + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "0x6103e85b60019003806000146104605761001a600160005256005b610025600260005256005b610030600360005256005b61003b600460005256005b610046600560005256005b610051600660005256005b61005c600760005256005b610067600860005256005b610072600960005256005b61007d600a60005256005b610088600b60005256005b610093600c60005256005b61009e600d60005256005b6100a9600e60005256005b6100b4600f60005256005b6100bf601060005256005b6100ca601160005256005b6100d5601260005256005b6100e0601360005256005b6100eb601460005256005b6100f6601560005256005b610101601660005256005b61010c601760005256005b610117601860005256005b610122601960005256005b61012d601a60005256005b610138601b60005256005b610143601c60005256005b61014e601d60005256005b610159601e60005256005b610164601f60005256005b61016f602060005256005b61017a602160005256005b610185602260005256005b610190602360005256005b61019b602460005256005b6101a6602560005256005b6101b1602660005256005b6101bc602760005256005b6101c7602860005256005b6101d2602960005256005b6101dd602a60005256005b6101e8602b60005256005b6101f3602c60005256005b6101fe602d60005256005b610209602e60005256005b610214602f60005256005b61021f603060005256005b61022a603160005256005b610235603260005256005b610240603360005256005b61024b603460005256005b610256603560005256005b610261603660005256005b61026c603760005256005b610277603860005256005b610282603960005256005b61028d603a60005256005b610298603b60005256005b6102a3603c60005256005b6102ae603d60005256005b6102b9603e60005256005b6102c4603f60005256005b6102cf604060005256005b6102da604160005256005b6102e5604260005256005b6102f0604360005256005b6102fb604460005256005b610306604560005256005b610311604660005256005b61031c604760005256005b610327604860005256005b610332604960005256005b61033d604a60005256005b610348604b60005256005b610353604c60005256005b61035e604d60005256005b610369604e60005256005b610374604f60005256005b61037f605060005256005b61038a605160005256005b610395605260005256005b6103a0605360005256005b6103ab605460005256005b6103b6605560005256005b6103c1605660005256005b6103cc605760005256005b6103d7605860005256005b6103e2605960005256005b6103ed605a60005256005b6103f8605b60005256005b610403605c60005256005b61040e605d60005256005b610419605e60005256005b610424605f60005256005b61042f606060005256005b61043a606160005256005b610445606260005256005b610450606360005256005b61045b606460005256005b610003565b60206000f2", + "nonce" : "0", + "storage" : { + } + } + } + }, + +} diff --git a/evmjit/evmcc/test/vmtests/vm_jump.json b/evmjit/evmcc/test/vmtests/vm_jump.json new file mode 100644 index 000000000..6b63edeae --- /dev/null +++ b/evmjit/evmcc/test/vmtests/vm_jump.json @@ -0,0 +1,41 @@ +{ + "jumpi_at_the_end" : { + "callcreates" : [ ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : "1", + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "code" : "(asm 10 0 MSTORE JUMPDEST 0 MLOAD 1 SWAP1 SUB DUP1 0 MSTORE 6 JUMPI)", + "data" : "0x", + "gas" : "1000", + "gasPrice" : "100000000000000", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "1000000000000000000" + }, + "gas" : "895", + "out" : "0x0", + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "(asm 10 0 MSTORE JUMPDEST 0 MLOAD 1 SWAP1 SUB DUP1 0 MSTORE 6 JUMPI)", + "nonce" : "0", + "storage" : {} + } + }, + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "1000000000000000000", + "code" : "(asm 10 0 MSTORE JUMPDEST 0 MLOAD 1 SWAP1 SUB DUP1 0 MSTORE 6 JUMPI)", + "nonce" : "0", + "storage" : {} + } + } + } +} diff --git a/evmjit/libevmjit-cpp/CMakeLists.txt b/evmjit/libevmjit-cpp/CMakeLists.txt new file mode 100644 index 000000000..53448332b --- /dev/null +++ b/evmjit/libevmjit-cpp/CMakeLists.txt @@ -0,0 +1,24 @@ +set(TARGET_NAME evmjit-cpp) + +# Boost +find_package(Boost REQUIRED) + +set(SOURCES + Env.cpp + JitVM.cpp JitVM.h +) +source_group("" FILES ${SOURCES}) + +if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # add PIC for archive +endif() + +add_library(${TARGET_NAME} ${SOURCES}) +set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") + +include_directories(../..) +include_directories(${LLVM_INCLUDE_DIRS}) +include_directories(${Boost_INCLUDE_DIRS}) + +target_link_libraries(${TARGET_NAME} evmjit) diff --git a/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp new file mode 100644 index 000000000..11882d79d --- /dev/null +++ b/evmjit/libevmjit-cpp/Env.cpp @@ -0,0 +1,117 @@ + +#pragma GCC diagnostic ignored "-Wconversion" +#include +#include +#include + +#include "Utils.h" + +extern "C" +{ + #ifdef _MSC_VER + #define EXPORT __declspec(dllexport) + #else + #define EXPORT + #endif + + using namespace dev; + using namespace dev::eth; + using jit::i256; + + EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) + { + auto index = llvm2eth(*_index); + auto value = _env->store(index); // Interface uses native endianness + *o_value = eth2llvm(value); + } + + EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value) + { + auto index = llvm2eth(*_index); + auto value = llvm2eth(*_value); + + if (value == 0 && _env->store(index) != 0) // If delete + _env->sub.refunds += c_sstoreRefundGas; // Increase refund counter + + _env->setStore(index, value); // Interface uses native endianness + } + + EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value) + { + auto u = _env->balance(right160(*_address)); + *o_value = eth2llvm(u); + } + + EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash) + { + *o_hash = _env->blockhash(llvm2eth(*_number)); + } + + EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) + { + auto endowment = llvm2eth(*_endowment); + if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) + { + _env->subBalance(endowment); + u256 gas = *io_gas; + h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight); + *io_gas = static_cast(gas); + *o_address = address; + } + else + *o_address = {}; + } + + EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) + { + auto value = llvm2eth(*_value); + if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) + { + _env->subBalance(value); + auto receiveAddress = right160(*_receiveAddress); + auto inRef = bytesConstRef{_inBeg, _inSize}; + auto outRef = bytesRef{_outBeg, _outSize}; + auto codeAddress = right160(*_codeAddress); + u256 gas = *io_gas; + auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, {}, {}, codeAddress); + *io_gas = static_cast(gas); + return ret; + } + + return false; + } + + EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash) + { + auto hash = sha3({_begin, _size}); + *o_hash = hash; + } + + EXPORT byte const* env_extcode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size) + { + auto addr = right160(*_addr256); + auto& code = _env->codeAt(addr); + *o_size = code.size(); + return code.data(); + } + + EXPORT void env_log(ExtVMFace* _env, byte* _beg, uint64_t _size, h256* _topic1, h256* _topic2, h256* _topic3, h256* _topic4) + { + dev::h256s topics; + + if (_topic1) + topics.push_back(*_topic1); + + if (_topic2) + topics.push_back(*_topic2); + + if (_topic3) + topics.push_back(*_topic3); + + if (_topic4) + topics.push_back(*_topic4); + + _env->log(std::move(topics), {_beg, _size}); + } +} + diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp new file mode 100644 index 000000000..55dcd94f8 --- /dev/null +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -0,0 +1,83 @@ + +#pragma GCC diagnostic ignored "-Wconversion" +#include "JitVM.h" +#include +#include +#include +#include +#include "Utils.h" + +namespace dev +{ +namespace eth +{ + +extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below + +bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) +{ + using namespace jit; + + auto rejected = false; + // TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope + rejected |= m_gas > std::numeric_limits::max(); // Do not accept requests with gas > 2^63 (int64 max) + rejected |= _ext.gasPrice > std::numeric_limits::max(); + rejected |= _ext.currentBlock.number > std::numeric_limits::max(); + rejected |= _ext.currentBlock.timestamp > std::numeric_limits::max(); + + if (rejected) + { + UNTESTED; + std::cerr << "Rejected\n"; + VMFactory::setKind(VMKind::Interpreter); + m_fallbackVM = VMFactory::create(m_gas); + VMFactory::setKind(VMKind::JIT); + return m_fallbackVM->go(_ext, _onOp, _step); + } + + m_data.gas = static_cast(m_gas); + m_data.gasPrice = static_cast(_ext.gasPrice); + m_data.callData = _ext.data.data(); + m_data.callDataSize = _ext.data.size(); + m_data.address = eth2llvm(fromAddress(_ext.myAddress)); + m_data.caller = eth2llvm(fromAddress(_ext.caller)); + m_data.origin = eth2llvm(fromAddress(_ext.origin)); + m_data.callValue = eth2llvm(_ext.value); + m_data.coinBase = eth2llvm(fromAddress(_ext.currentBlock.coinbaseAddress)); + m_data.difficulty = eth2llvm(_ext.currentBlock.difficulty); + m_data.gasLimit = eth2llvm(_ext.currentBlock.gasLimit); + m_data.number = static_cast(_ext.currentBlock.number); + m_data.timestamp = static_cast(_ext.currentBlock.timestamp); + m_data.code = _ext.code.data(); + m_data.codeSize = _ext.code.size(); + m_data.codeHash = eth2llvm(sha3(_ext.code)); + + auto env = reinterpret_cast(&_ext); + auto exitCode = m_engine.run(&m_data, env); + switch (exitCode) + { + case ReturnCode::Suicide: + _ext.suicide(right160(llvm2eth(m_data.address))); + break; + + case ReturnCode::BadJumpDestination: + BOOST_THROW_EXCEPTION(BadJumpDestination()); + case ReturnCode::OutOfGas: + BOOST_THROW_EXCEPTION(OutOfGas()); + case ReturnCode::StackTooSmall: + BOOST_THROW_EXCEPTION(StackTooSmall()); + case ReturnCode::BadInstruction: + BOOST_THROW_EXCEPTION(BadInstruction()); + case ReturnCode::LinkerWorkaround: // never happens + env_sload(); // but forces linker to include env_* JIT callback functions + break; + default: + break; + } + + m_gas = m_data.gas; // TODO: Remove m_gas field + return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)}; +} + +} +} diff --git a/evmjit/libevmjit-cpp/JitVM.h b/evmjit/libevmjit-cpp/JitVM.h new file mode 100644 index 000000000..58caa3648 --- /dev/null +++ b/evmjit/libevmjit-cpp/JitVM.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +namespace dev +{ +namespace eth +{ + +class JitVM: public VMFace +{ + virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + +private: + friend class VMFactory; + explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} + + jit::RuntimeData m_data; + jit::ExecutionEngine m_engine; + std::unique_ptr m_fallbackVM; ///< VM used in case of input data rejected by JIT +}; + + +} +} diff --git a/evmjit/libevmjit-cpp/Utils.h b/evmjit/libevmjit-cpp/Utils.h new file mode 100644 index 000000000..ac796920b --- /dev/null +++ b/evmjit/libevmjit-cpp/Utils.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +namespace dev +{ +namespace eth +{ + +inline u256 llvm2eth(jit::i256 _i) +{ + u256 u = 0; + u |= _i.d; + u <<= 64; + u |= _i.c; + u <<= 64; + u |= _i.b; + u <<= 64; + u |= _i.a; + return u; +} + +inline jit::i256 eth2llvm(u256 _u) +{ + jit::i256 i; + u256 mask = 0xFFFFFFFFFFFFFFFF; + i.a = static_cast(_u & mask); + _u >>= 64; + i.b = static_cast(_u & mask); + _u >>= 64; + i.c = static_cast(_u & mask); + _u >>= 64; + i.d = static_cast(_u & mask); + return i; +} + +} +} diff --git a/evmjit/libevmjit/Arith256.cpp b/evmjit/libevmjit/Arith256.cpp new file mode 100644 index 000000000..ddf06b463 --- /dev/null +++ b/evmjit/libevmjit/Arith256.cpp @@ -0,0 +1,406 @@ +#include "Arith256.h" + +#include +#include + +#include "preprocessor/llvm_includes_start.h" +#include +#include "preprocessor/llvm_includes_end.h" + +#include "Type.h" +#include "Endianness.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Arith256::Arith256(llvm::IRBuilder<>& _builder) : + CompilerHelper(_builder) +{} + +void Arith256::debug(llvm::Value* _value, char _c) +{ + if (!m_debug) + { + llvm::Type* argTypes[] = {Type::Word, m_builder.getInt8Ty()}; + m_debug = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "debug", getModule()); + } + createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_c)}); +} + +llvm::Function* Arith256::getMulFunc() +{ + auto& func = m_mul; + if (!func) + { + llvm::Type* argTypes[] = {Type::Word, Type::Word}; + func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule()); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + + InsertPointGuard guard{m_builder}; + auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func); + m_builder.SetInsertPoint(bb); + auto i64 = Type::Size; + auto i128 = m_builder.getIntNTy(128); + auto i256 = Type::Word; + auto x_lo = m_builder.CreateTrunc(x, i64, "x.lo"); + auto y_lo = m_builder.CreateTrunc(y, i64, "y.lo"); + auto x_mi = m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(64)), i64); + auto y_mi = m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(64)), i64); + auto x_hi = m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128); + auto y_hi = m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128); + + auto t1 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_lo, i128)); + auto t2 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_mi, i128)); + auto t3 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), y_hi); + auto t4 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_lo, i128)); + auto t5 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_mi, i128)); + auto t6 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), y_hi); + auto t7 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_lo, i128)); + auto t8 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_mi, i128)); + + auto p = m_builder.CreateZExt(t1, i256); + p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i256), Constant::get(64))); + p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i256), Constant::get(128))); + p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i256), Constant::get(64))); + p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t5, i256), Constant::get(128))); + p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t6, i256), Constant::get(192))); + p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t7, i256), Constant::get(128))); + p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t8, i256), Constant::get(192))); + m_builder.CreateRet(p); + } + return func; +} + +llvm::Function* Arith256::getMul512Func() +{ + auto& func = m_mul512; + if (!func) + { + auto i512 = m_builder.getIntNTy(512); + llvm::Type* argTypes[] = {Type::Word, Type::Word}; + func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule()); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + + InsertPointGuard guard{m_builder}; + auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func); + m_builder.SetInsertPoint(bb); + auto i128 = m_builder.getIntNTy(128); + auto i256 = Type::Word; + auto x_lo = m_builder.CreateZExt(m_builder.CreateTrunc(x, i128, "x.lo"), i256); + auto y_lo = m_builder.CreateZExt(m_builder.CreateTrunc(y, i128, "y.lo"), i256); + auto x_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256); + auto y_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256); + + auto t1 = createCall(getMulFunc(), {x_lo, y_lo}); + auto t2 = createCall(getMulFunc(), {x_lo, y_hi}); + auto t3 = createCall(getMulFunc(), {x_hi, y_lo}); + auto t4 = createCall(getMulFunc(), {x_hi, y_hi}); + + auto p = m_builder.CreateZExt(t1, i512); + p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i512), m_builder.getIntN(512, 128))); + p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i512), m_builder.getIntN(512, 128))); + p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i512), m_builder.getIntN(512, 256))); + m_builder.CreateRet(p); + } + return func; +} + +llvm::Function* Arith256::getDivFunc(llvm::Type* _type) +{ + auto& func = _type == Type::Word ? m_div : m_div512; + + if (!func) + { + // Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research + // The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder + + llvm::Type* argTypes[] = {_type, _type}; + auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef{argTypes}); + auto funcName = _type == Type::Word ? "div" : "div512"; + func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule()); + + auto zero = llvm::ConstantInt::get(_type, 0); + auto one = llvm::ConstantInt::get(_type, 1); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto yArg = x->getNextNode(); + yArg->setName("y"); + + InsertPointGuard guard{m_builder}; + + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func); + auto mainBB = llvm::BasicBlock::Create(m_builder.getContext(), "Main", func); + auto loopBB = llvm::BasicBlock::Create(m_builder.getContext(), "Loop", func); + auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", func); + auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func); + + m_builder.SetInsertPoint(entryBB); + auto yNonZero = m_builder.CreateICmpNE(yArg, zero); + auto yLEx = m_builder.CreateICmpULE(yArg, x); + auto r0 = m_builder.CreateSelect(yNonZero, x, zero, "r0"); + m_builder.CreateCondBr(m_builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB); + + m_builder.SetInsertPoint(mainBB); + auto ctlzIntr = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, _type); + // both y and r are non-zero + auto yLz = m_builder.CreateCall2(ctlzIntr, yArg, m_builder.getInt1(true), "y.lz"); + auto rLz = m_builder.CreateCall2(ctlzIntr, r0, m_builder.getInt1(true), "r.lz"); + auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0"); + auto shlBy0 = m_builder.CreateICmpEQ(i0, zero); + auto y0 = m_builder.CreateShl(yArg, i0); + if (_type == m_builder.getIntNTy(512)) // Workaround for shl bug for long shifts + { + const auto treshold = m_builder.getIntN(512, 128); + auto highShift = m_builder.CreateICmpUGT(i0, treshold); + auto s = m_builder.CreateNUWSub(i0, treshold); + auto yhs = m_builder.CreateShl(yArg, treshold); + yhs = m_builder.CreateShl(yhs, s); + y0 = m_builder.CreateSelect(highShift, yhs, y0); + } + y0 = m_builder.CreateSelect(shlBy0, yArg, y0, "y0"); // Workaround for LLVM bug: shl by 0 produces wrong result + m_builder.CreateBr(loopBB); + + m_builder.SetInsertPoint(loopBB); + auto yPhi = m_builder.CreatePHI(_type, 2, "y.phi"); + auto rPhi = m_builder.CreatePHI(_type, 2, "r.phi"); + auto iPhi = m_builder.CreatePHI(_type, 2, "i.phi"); + auto qPhi = m_builder.CreatePHI(_type, 2, "q.phi"); + auto rUpdate = m_builder.CreateNUWSub(rPhi, yPhi); + auto qUpdate = m_builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0 + auto rGEy = m_builder.CreateICmpUGE(rPhi, yPhi); + auto r1 = m_builder.CreateSelect(rGEy, rUpdate, rPhi, "r1"); + auto q1 = m_builder.CreateSelect(rGEy, qUpdate, qPhi, "q"); + auto iZero = m_builder.CreateICmpEQ(iPhi, zero); + m_builder.CreateCondBr(iZero, returnBB, continueBB); + + m_builder.SetInsertPoint(continueBB); + auto i2 = m_builder.CreateNUWSub(iPhi, one); + auto q2 = m_builder.CreateShl(q1, one); + auto y2 = m_builder.CreateLShr(yPhi, one); + m_builder.CreateBr(loopBB); + + yPhi->addIncoming(y0, mainBB); + yPhi->addIncoming(y2, continueBB); + rPhi->addIncoming(r0, mainBB); + rPhi->addIncoming(r1, continueBB); + iPhi->addIncoming(i0, mainBB); + iPhi->addIncoming(i2, continueBB); + qPhi->addIncoming(zero, mainBB); + qPhi->addIncoming(q2, continueBB); + + m_builder.SetInsertPoint(returnBB); + auto qRet = m_builder.CreatePHI(_type, 2, "q.ret"); + qRet->addIncoming(zero, entryBB); + qRet->addIncoming(q1, loopBB); + auto rRet = m_builder.CreatePHI(_type, 2, "r.ret"); + rRet->addIncoming(r0, entryBB); + rRet->addIncoming(r1, loopBB); + auto ret = m_builder.CreateInsertValue(llvm::UndefValue::get(retType), qRet, 0, "ret0"); + ret = m_builder.CreateInsertValue(ret, rRet, 1, "ret"); + m_builder.CreateRet(ret); + } + return func; +} + +llvm::Function* Arith256::getExpFunc() +{ + if (!m_exp) + { + llvm::Type* argTypes[] = {Type::Word, Type::Word}; + m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "exp", getModule()); + + auto base = &m_exp->getArgumentList().front(); + base->setName("base"); + auto exponent = base->getNextNode(); + exponent->setName("exponent"); + + InsertPointGuard guard{m_builder}; + + // while (e != 0) { + // if (e % 2 == 1) + // r *= b; + // b *= b; + // e /= 2; + // } + + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", m_exp); + auto headerBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopHeader", m_exp); + auto bodyBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopBody", m_exp); + auto updateBB = llvm::BasicBlock::Create(m_builder.getContext(), "ResultUpdate", m_exp); + auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", m_exp); + auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp); + + m_builder.SetInsertPoint(entryBB); + m_builder.CreateBr(headerBB); + + m_builder.SetInsertPoint(headerBB); + auto r = m_builder.CreatePHI(Type::Word, 2, "r"); + auto b = m_builder.CreatePHI(Type::Word, 2, "b"); + auto e = m_builder.CreatePHI(Type::Word, 2, "e"); + auto eNonZero = m_builder.CreateICmpNE(e, Constant::get(0), "e.nonzero"); + m_builder.CreateCondBr(eNonZero, bodyBB, returnBB); + + m_builder.SetInsertPoint(bodyBB); + auto eOdd = m_builder.CreateICmpNE(m_builder.CreateAnd(e, Constant::get(1)), Constant::get(0), "e.isodd"); + m_builder.CreateCondBr(eOdd, updateBB, continueBB); + + m_builder.SetInsertPoint(updateBB); + auto r0 = createCall(getMulFunc(), {r, b}); + m_builder.CreateBr(continueBB); + + m_builder.SetInsertPoint(continueBB); + auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); + r1->addIncoming(r, bodyBB); + r1->addIncoming(r0, updateBB); + auto b1 = createCall(getMulFunc(), {b, b}); + auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1"); + m_builder.CreateBr(headerBB); + + r->addIncoming(Constant::get(1), entryBB); + r->addIncoming(r1, continueBB); + b->addIncoming(base, entryBB); + b->addIncoming(b1, continueBB); + e->addIncoming(exponent, entryBB); + e->addIncoming(e1, continueBB); + + m_builder.SetInsertPoint(returnBB); + m_builder.CreateRet(r); + } + return m_exp; +} + +llvm::Function* Arith256::getAddModFunc() +{ + if (!m_addmod) + { + auto i512Ty = m_builder.getIntNTy(512); + llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word}; + m_addmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "addmod", getModule()); + + auto x = &m_addmod->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + auto mod = y->getNextNode(); + mod->setName("m"); + + InsertPointGuard guard{m_builder}; + + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_addmod); + m_builder.SetInsertPoint(entryBB); + auto x512 = m_builder.CreateZExt(x, i512Ty, "x512"); + auto y512 = m_builder.CreateZExt(y, i512Ty, "y512"); + auto m512 = m_builder.CreateZExt(mod, i512Ty, "m512"); + auto s = m_builder.CreateAdd(x512, y512, "s"); + auto d = createCall(getDivFunc(i512Ty), {s, m512}); + auto r = m_builder.CreateExtractValue(d, 1, "r"); + m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word)); + } + return m_addmod; +} + +llvm::Function* Arith256::getMulModFunc() +{ + if (!m_mulmod) + { + llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word}; + m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule()); + + auto i512Ty = m_builder.getIntNTy(512); + auto x = &m_mulmod->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + auto mod = y->getNextNode(); + mod->setName("mod"); + + InsertPointGuard guard{m_builder}; + + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod); + m_builder.SetInsertPoint(entryBB); + auto p = createCall(getMul512Func(), {x, y}); + auto m = m_builder.CreateZExt(mod, i512Ty, "m"); + auto d = createCall(getDivFunc(i512Ty), {p, m}); + auto r = m_builder.CreateExtractValue(d, 1, "r"); + r = m_builder.CreateTrunc(r, Type::Word); + m_builder.CreateRet(r); + } + return m_mulmod; +} + +llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2) +{ + return createCall(getMulFunc(), {_arg1, _arg2}); +} + +std::pair Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) +{ + auto div = m_builder.CreateExtractValue(createCall(getDivFunc(Type::Word), {_arg1, _arg2}), 0, "div"); + auto mod = m_builder.CreateExtractValue(createCall(getDivFunc(Type::Word), {_arg1, _arg2}), 1, "mod"); + return std::make_pair(div, mod); +} + +std::pair Arith256::sdiv(llvm::Value* _x, llvm::Value* _y) +{ + auto xIsNeg = m_builder.CreateICmpSLT(_x, Constant::get(0)); + auto xNeg = m_builder.CreateSub(Constant::get(0), _x); + auto xAbs = m_builder.CreateSelect(xIsNeg, xNeg, _x); + + auto yIsNeg = m_builder.CreateICmpSLT(_y, Constant::get(0)); + auto yNeg = m_builder.CreateSub(Constant::get(0), _y); + auto yAbs = m_builder.CreateSelect(yIsNeg, yNeg, _y); + + auto res = div(xAbs, yAbs); + + // the reminder has the same sign as dividend + auto rNeg = m_builder.CreateSub(Constant::get(0), res.second); + res.second = m_builder.CreateSelect(xIsNeg, rNeg, res.second); + + auto qNeg = m_builder.CreateSub(Constant::get(0), res.first); + auto xyOpposite = m_builder.CreateXor(xIsNeg, yIsNeg); + res.first = m_builder.CreateSelect(xyOpposite, qNeg, res.first); + + return res; +} + +llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) +{ + return createCall(getExpFunc(), {_arg1, _arg2}); +} + +llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) +{ + return createCall(getAddModFunc(), {_arg1, _arg2, _arg3}); +} + +llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) +{ + return createCall(getMulModFunc(), {_arg1, _arg2, _arg3}); +} + + +} +} +} + +extern "C" +{ + EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z) + { + std::cerr << "DEBUG " << std::dec << z << ": " //<< d << c << b << a + << " [" << std::hex << std::setfill('0') << std::setw(16) << d << std::setw(16) << c << std::setw(16) << b << std::setw(16) << a << "]\n"; + } +} diff --git a/evmjit/libevmjit/Arith256.h b/evmjit/libevmjit/Arith256.h new file mode 100644 index 000000000..2513ca568 --- /dev/null +++ b/evmjit/libevmjit/Arith256.h @@ -0,0 +1,47 @@ +#pragma once + +#include "CompilerHelper.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +class Arith256 : public CompilerHelper +{ +public: + Arith256(llvm::IRBuilder<>& _builder); + + llvm::Value* mul(llvm::Value* _arg1, llvm::Value* _arg2); + std::pair div(llvm::Value* _arg1, llvm::Value* _arg2); + std::pair sdiv(llvm::Value* _arg1, llvm::Value* _arg2); + llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); + llvm::Value* mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); + llvm::Value* addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); + + void debug(llvm::Value* _value, char _c); + +private: + llvm::Function* getMulFunc(); + llvm::Function* getMul512Func(); + llvm::Function* getDivFunc(llvm::Type* _type); + llvm::Function* getExpFunc(); + llvm::Function* getAddModFunc(); + llvm::Function* getMulModFunc(); + + llvm::Function* m_mul = nullptr; + llvm::Function* m_mul512 = nullptr; + llvm::Function* m_div = nullptr; + llvm::Function* m_div512 = nullptr; + llvm::Function* m_exp = nullptr; + llvm::Function* m_addmod = nullptr; + llvm::Function* m_mulmod = nullptr; + llvm::Function* m_debug = nullptr; +}; + + +} +} +} diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp new file mode 100644 index 000000000..c9e71be9a --- /dev/null +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -0,0 +1,381 @@ +#include "BasicBlock.h" + +#include + +#include "preprocessor/llvm_includes_start.h" +#include +#include +#include +#include +#include +#include "preprocessor/llvm_includes_end.h" + +#include "Type.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +static const char* jumpDestName = "JmpDst."; +static const char* basicBlockName = "Instr."; + +BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : + m_firstInstrIdx{_firstInstrIdx}, + m_begin(_begin), + m_end(_end), + m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {isJumpDest ? jumpDestName : basicBlockName, std::to_string(_firstInstrIdx)}, _mainFunc)), + m_stack(*this), + m_builder(_builder), + m_isJumpDest(isJumpDest) +{} + +BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : + m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)), + m_stack(*this), + m_builder(_builder), + m_isJumpDest(isJumpDest) +{} + +BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) : + m_bblock(_owner) +{} + +void BasicBlock::LocalStack::push(llvm::Value* _value) +{ + assert(_value->getType() == Type::Word); + m_bblock.m_currentStack.push_back(_value); + m_bblock.m_tosOffset += 1; +} + +llvm::Value* BasicBlock::LocalStack::pop() +{ + auto result = get(0); + + if (m_bblock.m_currentStack.size() > 0) + m_bblock.m_currentStack.pop_back(); + + m_bblock.m_tosOffset -= 1; + return result; +} + +/** + * Pushes a copy of _index-th element (tos is 0-th elem). + */ +void BasicBlock::LocalStack::dup(size_t _index) +{ + auto val = get(_index); + push(val); +} + +/** + * Swaps tos with _index-th element (tos is 0-th elem). + * _index must be > 0. + */ +void BasicBlock::LocalStack::swap(size_t _index) +{ + assert(_index > 0); + auto val = get(_index); + auto tos = get(0); + set(_index, tos); + set(0, val); +} + +std::vector::iterator BasicBlock::LocalStack::getItemIterator(size_t _index) +{ + auto& currentStack = m_bblock.m_currentStack; + if (_index < currentStack.size()) + return currentStack.end() - _index - 1; + + // Need to map more elements from the EVM stack + auto nNewItems = 1 + _index - currentStack.size(); + currentStack.insert(currentStack.begin(), nNewItems, nullptr); + + return currentStack.end() - _index - 1; +} + +llvm::Value* BasicBlock::LocalStack::get(size_t _index) +{ + auto& initialStack = m_bblock.m_initialStack; + auto itemIter = getItemIterator(_index); + + if (*itemIter == nullptr) + { + // Need to fetch a new item from the EVM stack + assert(static_cast(_index) >= m_bblock.m_tosOffset); + size_t initialIdx = _index - m_bblock.m_tosOffset; + if (initialIdx >= initialStack.size()) + { + auto nNewItems = 1 + initialIdx - initialStack.size(); + initialStack.insert(initialStack.end(), nNewItems, nullptr); + } + + assert(initialStack[initialIdx] == nullptr); + // Create a dummy value. + std::string name = "get_" + std::to_string(_index); + initialStack[initialIdx] = m_bblock.m_builder.CreatePHI(Type::Word, 0, std::move(name)); + *itemIter = initialStack[initialIdx]; + } + + return *itemIter; +} + +void BasicBlock::LocalStack::set(size_t _index, llvm::Value* _word) +{ + auto itemIter = getItemIterator(_index); + *itemIter = _word; +} + + + + + +void BasicBlock::synchronizeLocalStack(Stack& _evmStack) +{ + auto blockTerminator = m_llvmBB->getTerminator(); + assert(blockTerminator != nullptr); + m_builder.SetInsertPoint(blockTerminator); + + auto currIter = m_currentStack.begin(); + auto endIter = m_currentStack.end(); + + // Update (emit set()) changed values + for (int idx = (int)m_currentStack.size() - 1 - m_tosOffset; + currIter < endIter && idx >= 0; + ++currIter, --idx) + { + assert(static_cast(idx) < m_initialStack.size()); + if (*currIter != m_initialStack[idx]) // value needs update + _evmStack.set(static_cast(idx), *currIter); + } + + if (m_tosOffset < 0) + { + // Pop values + _evmStack.pop(static_cast(-m_tosOffset)); + } + + // Push new values + for (; currIter < endIter; ++currIter) + { + assert(*currIter != nullptr); + _evmStack.push(*currIter); + } + + // Emit get() for all (used) values from the initial stack + for (size_t idx = 0; idx < m_initialStack.size(); ++idx) + { + auto val = m_initialStack[idx]; + if (val == nullptr) + continue; + + llvm::PHINode* phi = llvm::cast(val); + // Insert call to get() just before the PHI node and replace + // the uses of PHI with the uses of this new instruction. + m_builder.SetInsertPoint(phi); + auto newVal = _evmStack.get(idx); // OPT: Value may be never user but we need to check stack heigth + // It is probably a good idea to keep heigth as a local variable accesible by LLVM directly + phi->replaceAllUsesWith(newVal); + phi->eraseFromParent(); + } + + // Reset the stack + m_initialStack.erase(m_initialStack.begin(), m_initialStack.end()); + m_currentStack.erase(m_currentStack.begin(), m_currentStack.end()); + m_tosOffset = 0; +} + +void BasicBlock::linkLocalStacks(std::vector basicBlocks, llvm::IRBuilder<>& _builder) +{ + struct BBInfo + { + BasicBlock& bblock; + std::vector predecessors; + size_t inputItems; + size_t outputItems; + std::vector phisToRewrite; + + BBInfo(BasicBlock& _bblock) : + bblock(_bblock), + predecessors(), + inputItems(0), + outputItems(0) + { + auto& initialStack = bblock.m_initialStack; + for (auto it = initialStack.begin(); + it != initialStack.end() && *it != nullptr; + ++it, ++inputItems); + + //if (bblock.localStack().m_tosOffset > 0) + // outputItems = bblock.localStack().m_tosOffset; + auto& exitStack = bblock.m_currentStack; + for (auto it = exitStack.rbegin(); + it != exitStack.rend() && *it != nullptr; + ++it, ++outputItems); + } + }; + + std::map cfg; + + // Create nodes in cfg + for (auto bb : basicBlocks) + cfg.emplace(bb->llvm(), *bb); + + // Create edges in cfg: for each bb info fill the list + // of predecessor infos. + for (auto& pair : cfg) + { + auto bb = pair.first; + auto& info = pair.second; + + for (auto predIt = llvm::pred_begin(bb); predIt != llvm::pred_end(bb); ++predIt) + { + auto predInfoEntry = cfg.find(*predIt); + if (predInfoEntry != cfg.end()) + info.predecessors.push_back(&predInfoEntry->second); + } + } + + // Iteratively compute inputs and outputs of each block, until reaching fixpoint. + bool valuesChanged = true; + while (valuesChanged) + { + if (getenv("EVMCC_DEBUG_BLOCKS")) + { + for (auto& pair : cfg) + std::cerr << pair.second.bblock.llvm()->getName().str() + << ": in " << pair.second.inputItems + << ", out " << pair.second.outputItems + << "\n"; + } + + valuesChanged = false; + for (auto& pair : cfg) + { + auto& info = pair.second; + + if (info.predecessors.empty()) + info.inputItems = 0; // no consequences for other blocks, so leave valuesChanged false + + for (auto predInfo : info.predecessors) + { + if (predInfo->outputItems < info.inputItems) + { + info.inputItems = predInfo->outputItems; + valuesChanged = true; + } + else if (predInfo->outputItems > info.inputItems) + { + predInfo->outputItems = info.inputItems; + valuesChanged = true; + } + } + } + } + + // Propagate values between blocks. + for (auto& entry : cfg) + { + auto& info = entry.second; + auto& bblock = info.bblock; + + llvm::BasicBlock::iterator fstNonPhi(bblock.llvm()->getFirstNonPHI()); + auto phiIter = bblock.m_initialStack.begin(); + for (size_t index = 0; index < info.inputItems; ++index, ++phiIter) + { + assert(llvm::isa(*phiIter)); + auto phi = llvm::cast(*phiIter); + + for (auto predIt : info.predecessors) + { + auto& predExitStack = predIt->bblock.m_currentStack; + auto value = *(predExitStack.end() - 1 - index); + phi->addIncoming(value, predIt->bblock.llvm()); + } + + // Move phi to the front + if (llvm::BasicBlock::iterator(phi) != bblock.llvm()->begin()) + { + phi->removeFromParent(); + _builder.SetInsertPoint(bblock.llvm(), bblock.llvm()->begin()); + _builder.Insert(phi); + } + } + + // The items pulled directly from predecessors block must be removed + // from the list of items that has to be popped from the initial stack. + auto& initialStack = bblock.m_initialStack; + initialStack.erase(initialStack.begin(), initialStack.begin() + info.inputItems); + // Initial stack shrinks, so the size difference grows: + bblock.m_tosOffset += (int)info.inputItems; + } + + // We must account for the items that were pushed directly to successor + // blocks and thus should not be on the list of items to be pushed onto + // to EVM stack + for (auto& entry : cfg) + { + auto& info = entry.second; + auto& bblock = info.bblock; + + auto& exitStack = bblock.m_currentStack; + exitStack.erase(exitStack.end() - info.outputItems, exitStack.end()); + bblock.m_tosOffset -= (int)info.outputItems; // FIXME: Fix types + } +} + +void BasicBlock::dump() +{ + dump(std::cerr, false); +} + +void BasicBlock::dump(std::ostream& _out, bool _dotOutput) +{ + llvm::raw_os_ostream out(_out); + + out << (_dotOutput ? "" : "Initial stack:\n"); + for (auto val : m_initialStack) + { + if (val == nullptr) + out << " ?"; + else if (llvm::isa(val)) + out << *val; + else + out << " " << *val; + + out << (_dotOutput ? "\\l" : "\n"); + } + + out << (_dotOutput ? "| " : "Instructions:\n"); + for (auto ins = m_llvmBB->begin(); ins != m_llvmBB->end(); ++ins) + out << *ins << (_dotOutput ? "\\l" : "\n"); + + if (! _dotOutput) + out << "Current stack (offset = " << m_tosOffset << "):\n"; + else + out << "|"; + + for (auto val = m_currentStack.rbegin(); val != m_currentStack.rend(); ++val) + { + if (*val == nullptr) + out << " ?"; + else if (llvm::isa(*val)) + out << **val; + else + out << " " << **val; + out << (_dotOutput ? "\\l" : "\n"); + } + + if (! _dotOutput) + out << " ...\n----------------------------------------\n"; +} + + + + +} +} +} + diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h new file mode 100644 index 000000000..7469b7b69 --- /dev/null +++ b/evmjit/libevmjit/BasicBlock.h @@ -0,0 +1,126 @@ +#pragma once + +#include + +#include "Common.h" +#include "Stack.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +using instr_idx = uint64_t; + +class BasicBlock +{ +public: + class LocalStack + { + public: + /// Pushes value on stack + void push(llvm::Value* _value); + + /// Pops and returns top value + llvm::Value* pop(); + + /// Duplicates _index'th value on stack + void dup(size_t _index); + + /// Swaps _index'th value on stack with a value on stack top. + /// @param _index Index of value to be swaped. Must be > 0. + void swap(size_t _index); + + private: + LocalStack(BasicBlock& _owner); + LocalStack(LocalStack const&) = delete; + void operator=(LocalStack const&) = delete; + friend BasicBlock; + + /// Gets _index'th value from top (counting from 0) + llvm::Value* get(size_t _index); + + /// Sets _index'th value from top (counting from 0) + void set(size_t _index, llvm::Value* _value); + + std::vector::iterator getItemIterator(size_t _index); + + private: + BasicBlock& m_bblock; + }; + + explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); + explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); + + BasicBlock(const BasicBlock&) = delete; + BasicBlock& operator=(const BasicBlock&) = delete; + + llvm::BasicBlock* llvm() { return m_llvmBB; } + + instr_idx firstInstrIdx() const { return m_firstInstrIdx; } + code_iterator begin() const { return m_begin; } + code_iterator end() const { return m_end; } + + bool isJumpDest() const { return m_isJumpDest; } + + llvm::Value* getJumpTarget() const { return m_jumpTarget; } + void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; } + + LocalStack& localStack() { return m_stack; } + + /// Optimization: propagates values between local stacks in basic blocks + /// to avoid excessive pushing/popping on the EVM stack. + static void linkLocalStacks(std::vector _basicBlocks, llvm::IRBuilder<>& _builder); + + /// Synchronize current local stack with the EVM stack. + void synchronizeLocalStack(Stack& _evmStack); + + /// Prints local stack and block instructions to stderr. + /// Useful for calling in a debugger session. + void dump(); + void dump(std::ostream& os, bool _dotOutput = false); + +private: + instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block + code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block + code_iterator const m_end = {}; ///< Iterator pointing code end of the block + + llvm::BasicBlock* const m_llvmBB; + + /// Basic black state vector (stack) - current/end values and their positions on stack + /// @internal Must be AFTER m_llvmBB + LocalStack m_stack; + + llvm::IRBuilder<>& m_builder; + + /// This stack contains LLVM values that correspond to items found at + /// the EVM stack when the current basic block starts executing. + /// Location 0 corresponds to the top of the EVM stack, location 1 is + /// the item below the top and so on. The stack grows as the code + /// accesses more items on the EVM stack but once a value is put on + /// the stack, it will never be replaced. + std::vector m_initialStack; + + /// This stack tracks the contents of the EVM stack as the basic block + /// executes. It may grow on both sides, as the code pushes items on + /// top of the stack or changes existing items. + std::vector m_currentStack; + + /// How many items higher is the current stack than the initial one. + /// May be negative. + int m_tosOffset = 0; + + /// Is the basic block a valid jump destination. + /// JUMPDEST is the first instruction of the basic block. + bool const m_isJumpDest = false; + + /// If block finishes with dynamic jump target index is stored here + llvm::Value* m_jumpTarget = nullptr; +}; + +} +} +} + diff --git a/evmjit/libevmjit/BuildInfo.h.in b/evmjit/libevmjit/BuildInfo.h.in new file mode 100644 index 000000000..204b4d89b --- /dev/null +++ b/evmjit/libevmjit/BuildInfo.h.in @@ -0,0 +1,10 @@ + +#define EVMJIT_VERSION "${EVMJIT_VERSION}" +#define EVMJIT_VERSION_MAJOR ${EVMJIT_VERSION_MAJOR} +#define EVMJIT_VERSION_MINOR ${EVMJIT_VERSION_MINOR} +#define EVMJIT_VERSION_PATCH ${EVMJIT_VERSION_PATCH} +#define EVMJIT_VERSION_PRERELEASE "${EVMJIT_VERSION_PRERELEASE}" +#define EVMJIT_VERSION_FULL "${EVMJIT_VERSION_FULL}" + +#define LLVM_VERSION "${LLVM_PACKAGE_VERSION}" +#define LLVM_ASSERTIONS "${LLVM_ENABLE_ASSERTIONS}" diff --git a/evmjit/libevmjit/CMakeLists.txt b/evmjit/libevmjit/CMakeLists.txt new file mode 100644 index 000000000..943c64e42 --- /dev/null +++ b/evmjit/libevmjit/CMakeLists.txt @@ -0,0 +1,68 @@ +set(TARGET_NAME evmjit) + +file(GLOB SOURCES "*.cpp") +file(GLOB HEADERS "*.h") +set(INTERFACE_HEADERS interface.h) +source_group("" FILES ${HEADERS}) +source_group("" FILES ${SOURCES}) + +if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +endif() + + +set(EVMJIT_VERSION "0.0.0") +set(EVMJIT_VERSION_MAJOR 0) +set(EVMJIT_VERSION_MINOR 0) +set(EVMJIT_VERSION_PATCH 0) +set(EVMJIT_VERSION_FULL "v0.0.0-nogit") + +find_package(Git) +if(GIT_FOUND) + execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --dirty --always --match v* + OUTPUT_VARIABLE EVMJIT_VERSION_FULL OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +if(${EVMJIT_VERSION_FULL} MATCHES "^v[0-9]+\\.[0-9]+") + string(SUBSTRING ${EVMJIT_VERSION_FULL} 1 -1 EVMJIT_VERSION_FULL) # skip "v" + string(REPLACE "-" ";" VERSION_COMPONENTS ${EVMJIT_VERSION_FULL}) + list(LENGTH VERSION_COMPONENTS NUM_VERSION_COMPONENTS) + list(GET VERSION_COMPONENTS 0 EVMJIT_VERSION) + string(REPLACE "." ";" VERSION_NUMBERS ${EVMJIT_VERSION}) + list(LENGTH VERSION_NUMBERS NUM_VERSION_NUMBERS) + list(GET VERSION_NUMBERS 0 EVMJIT_VERSION_MAJOR) + list(GET VERSION_NUMBERS 1 EVMJIT_VERSION_MINOR) + if(${NUM_VERSION_NUMBERS} GREATER 2) + list(GET VERSION_NUMBERS 2 EVMJIT_VERSION_PATCH) # patch number is optional + endif() + if(${NUM_VERSION_COMPONENTS} GREATER 1) + list(GET VERSION_COMPONENTS 1 VERSION_PRERELEASE_CANDIDATE) + string(REGEX MATCH "^[a-zA-Z]+.*" EVMJIT_VERSION_PRERELEASE ${VERSION_PRERELEASE_CANDIDATE}) # prerelease starts with letter + endif() +endif() + +if(${EVMJIT_VERSION_MAJOR} EQUAL 0) + set(EVMJIT_SOVERSION "0.${EVMJIT_VERSION_MINOR}") +else() + set(EVMJIT_SOVERSION ${EVMJIT_VERSION_MAJOR}) +endif() + +configure_file(BuildInfo.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen/BuildInfo.gen.h) + +message(STATUS "EVM JIT version: ${EVMJIT_VERSION_MAJOR}.${EVMJIT_VERSION_MINOR}.${EVMJIT_VERSION_PATCH} ${EVMJIT_VERSION_PRERELEASE} (${EVMJIT_VERSION_FULL})") + +add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS} gen/BuildInfo.gen.h) +set_target_properties(${TARGET_NAME} PROPERTIES + VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION} + FOLDER "libs") + +include_directories(${LLVM_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen) + +target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS}) + +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") + +install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin) +#install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME}) \ No newline at end of file diff --git a/evmjit/libevmjit/Cache.cpp b/evmjit/libevmjit/Cache.cpp new file mode 100644 index 000000000..fe226eefb --- /dev/null +++ b/evmjit/libevmjit/Cache.cpp @@ -0,0 +1,120 @@ +#include "Cache.h" + +#include +#include + +#include "preprocessor/llvm_includes_start.h" +#include +#include +#include +#include +#include +#include +#include "preprocessor/llvm_includes_end.h" + +#include "ExecutionEngine.h" +#include "Utils.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +//#define CACHE_LOG std::cerr << "CACHE " +#define CACHE_LOG std::ostream(nullptr) + +namespace +{ + llvm::MemoryBuffer* g_lastObject; + ExecutionEngineListener* g_listener; +} + +ObjectCache* Cache::getObjectCache(ExecutionEngineListener* _listener) +{ + static ObjectCache objectCache; + g_listener = _listener; + return &objectCache; +} + +std::unique_ptr Cache::getObject(std::string const& id) +{ + if (g_listener) + g_listener->stateChanged(ExecState::CacheLoad); + + CACHE_LOG << id << ": search\n"; + if (!CHECK(!g_lastObject)) + g_lastObject = nullptr; + + llvm::SmallString<256> cachePath; + llvm::sys::path::system_temp_directory(false, cachePath); + llvm::sys::path::append(cachePath, "evm_objs", id); + +#if defined(__GNUC__) && !defined(NDEBUG) + llvm::sys::fs::file_status st; + auto err = llvm::sys::fs::status(cachePath.str(), st); + if (err) + return nullptr; + auto mtime = st.getLastModificationTime().toEpochTime(); + + std::tm tm; + strptime(__DATE__ __TIME__, " %b %d %Y %H:%M:%S", &tm); + auto btime = (uint64_t)std::mktime(&tm); + if (btime > mtime) + return nullptr; +#endif + + if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) + g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); + else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) + std::cerr << r.getError().message(); // TODO: Add log + + if (g_lastObject) // if object found create fake module + { + CACHE_LOG << id << ": found\n"; + auto&& context = llvm::getGlobalContext(); + auto module = std::unique_ptr(new llvm::Module(id, context)); + auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(context), {}, false); + auto mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get()); + auto bb = llvm::BasicBlock::Create(context, {}, mainFunc); + bb->getInstList().push_back(new llvm::UnreachableInst{context}); + return module; + } + CACHE_LOG << id << ": not found\n"; + return nullptr; +} + + +void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) +{ + if (g_listener) + g_listener->stateChanged(ExecState::CacheWrite); + + auto&& id = _module->getModuleIdentifier(); + llvm::SmallString<256> cachePath; + llvm::sys::path::system_temp_directory(false, cachePath); + llvm::sys::path::append(cachePath, "evm_objs"); + + if (llvm::sys::fs::create_directory(cachePath.str())) + return; // TODO: Add log + + llvm::sys::path::append(cachePath, id); + + CACHE_LOG << id << ": write\n"; + std::string error; + llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None); + cacheFile << _object->getBuffer(); +} + +llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module) +{ + CACHE_LOG << _module->getModuleIdentifier() << ": use\n"; + auto o = g_lastObject; + g_lastObject = nullptr; + return o; +} + +} +} +} diff --git a/evmjit/libevmjit/Cache.h b/evmjit/libevmjit/Cache.h new file mode 100644 index 000000000..e8f01d38d --- /dev/null +++ b/evmjit/libevmjit/Cache.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ +class ExecutionEngineListener; + +class ObjectCache : public llvm::ObjectCache +{ +public: + /// notifyObjectCompiled - Provides a pointer to compiled code for Module M. + virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) final override; + + /// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that + /// contains the object which corresponds with Module M, or 0 if an object is + /// not available. The caller owns both the MemoryBuffer returned by this + /// and the memory it references. + virtual llvm::MemoryBuffer* getObject(llvm::Module const* _module) final override; +}; + + +class Cache +{ +public: + static ObjectCache* getObjectCache(ExecutionEngineListener* _listener); + static std::unique_ptr getObject(std::string const& id); +}; + +} +} +} diff --git a/evmjit/libevmjit/Common.h b/evmjit/libevmjit/Common.h new file mode 100644 index 000000000..62731292f --- /dev/null +++ b/evmjit/libevmjit/Common.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include + +#ifdef _MSC_VER +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +using byte = uint8_t; +using bytes = std::vector; +using bytes_ref = std::tuple; +using code_iterator = byte const*; + +struct NoteChannel {}; // FIXME: Use some log library? + +enum class ReturnCode +{ + // Success codes + Stop = 0, + Return = 1, + Suicide = 2, + + // Standard error codes + OutOfGas = -1, + StackTooSmall = -2, + BadJumpDestination = -3, + BadInstruction = -4, + Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected + + // Internal error codes + LLVMConfigError = -101, + LLVMCompileError = -102, + LLVMLinkError = -103, + + UnexpectedException = -111, + + LinkerWorkaround = -299, +}; + +/// Representation of 256-bit value binary compatible with LLVM i256 +struct i256 +{ + uint64_t a = 0; + uint64_t b = 0; + uint64_t c = 0; + uint64_t d = 0; +}; +static_assert(sizeof(i256) == 32, "Wrong i265 size"); + +#define UNTESTED assert(false) + +} +} +} diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp new file mode 100644 index 000000000..de48e8ef9 --- /dev/null +++ b/evmjit/libevmjit/Compiler.cpp @@ -0,0 +1,965 @@ +#include "Compiler.h" + +#include +#include +#include +#include + +#include "preprocessor/llvm_includes_start.h" +#include +#include +#include +#include +#include +#include +#include +#include "preprocessor/llvm_includes_end.h" + +#include "Instruction.h" +#include "Type.h" +#include "Memory.h" +#include "Stack.h" +#include "Ext.h" +#include "GasMeter.h" +#include "Utils.h" +#include "Endianness.h" +#include "Arith256.h" +#include "RuntimeManager.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Compiler::Compiler(Options const& _options): + m_options(_options), + m_builder(llvm::getGlobalContext()) +{ + Type::init(m_builder.getContext()); +} + +void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd) +{ + /// Helper function that skips push data and finds next iterator (can be the end) + auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end) + { + static const auto push1 = static_cast(Instruction::PUSH1); + static const auto push32 = static_cast(Instruction::PUSH32); + size_t offset = 1; + if (*_curr >= push1 && *_curr <= push32) + offset += std::min(*_curr - push1 + 1, (_end - _curr) - 1); + return _curr + offset; + }; + + auto begin = _codeBegin; // begin of current block + bool nextJumpDest = false; + for (auto curr = begin, next = begin; curr != _codeEnd; curr = next) + { + next = skipPushDataAndGetNext(curr, _codeEnd); + + bool isEnd = false; + switch (Instruction(*curr)) + { + case Instruction::JUMP: + case Instruction::JUMPI: + case Instruction::RETURN: + case Instruction::STOP: + case Instruction::SUICIDE: + isEnd = true; + break; + + case Instruction::JUMPDEST: + nextJumpDest = true; + break; + + default: + break; + } + + assert(next <= _codeEnd); + if (next == _codeEnd || Instruction(*next) == Instruction::JUMPDEST) + isEnd = true; + + if (isEnd) + { + auto beginIdx = begin - _codeBegin; + m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), + std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, m_builder, nextJumpDest)); + nextJumpDest = false; + begin = next; + } + } +} + +llvm::BasicBlock* Compiler::getJumpTableBlock() +{ + if (!m_jumpTableBlock) + { + m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true)); + InsertPointGuard g{m_builder}; + m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); + auto dest = m_builder.CreatePHI(Type::Word, 8, "target"); + auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock()); + for (auto&& p : m_basicBlocks) + { + if (p.second.isJumpDest()) + switchInstr->addCase(Constant::get(p.first), p.second.llvm()); + } + } + return m_jumpTableBlock->llvm(); +} + +llvm::BasicBlock* Compiler::getBadJumpBlock() +{ + if (!m_badJumpBlock) + { + m_badJumpBlock.reset(new BasicBlock("BadJump", m_mainFunc, m_builder, true)); + InsertPointGuard g{m_builder}; + m_builder.SetInsertPoint(m_badJumpBlock->llvm()); + m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination)); + } + return m_badJumpBlock->llvm(); +} + +std::unique_ptr Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id) +{ + auto compilationStartTime = std::chrono::high_resolution_clock::now(); + auto module = std::unique_ptr(new llvm::Module(_id, m_builder.getContext())); + + // Create main function + auto mainFuncType = llvm::FunctionType::get(Type::MainReturn, Type::RuntimePtr, false); + m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get()); + m_mainFunc->getArgumentList().front().setName("rt"); + + // Create entry basic block + auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc); + m_builder.SetInsertPoint(entryBlock); + + auto jmpBufWords = m_builder.CreateAlloca(Type::BytePtr, m_builder.getInt64(3), "jmpBuf.words"); + auto frameaddress = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::frameaddress); + auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp"); + m_builder.CreateStore(fp, jmpBufWords); + auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave); + auto sp = m_builder.CreateCall(stacksave, "sp"); + auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp"); + m_builder.CreateStore(sp, jmpBufSp); + auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp); + auto jmpBuf = m_builder.CreateBitCast(jmpBufWords, Type::BytePtr, "jmpBuf"); + auto r = m_builder.CreateCall(setjmp, jmpBuf); + auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0)); + + createBasicBlocks(_begin, _end); + + // Init runtime structures. + RuntimeManager runtimeManager(m_builder, jmpBuf, _begin, _end); + GasMeter gasMeter(m_builder, runtimeManager); + Memory memory(runtimeManager, gasMeter); + Ext ext(runtimeManager, memory); + Stack stack(m_builder, runtimeManager); + Arith256 arith(m_builder); + + // TODO: Create Stop basic block on demand + m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); + auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc); + + auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm(); + auto expectTrue = llvm::MDBuilder{m_builder.getContext()}.createBranchWeights(1, 0); + m_builder.CreateCondBr(normalFlow, firstBB, abortBB, expectTrue); + + for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt) + { + auto& basicBlock = basicBlockPairIt->second; + auto iterCopy = basicBlockPairIt; + ++iterCopy; + auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; + compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock); + } + + // Code for special blocks: + // TODO: move to separate function. + m_builder.SetInsertPoint(m_stopBB); + m_builder.CreateRet(Constant::get(ReturnCode::Stop)); + + m_builder.SetInsertPoint(abortBB); + m_builder.CreateRet(Constant::get(ReturnCode::OutOfGas)); + + removeDeadBlocks(); + + // Link jump table target index + if (m_jumpTableBlock) + { + auto phi = llvm::cast(&m_jumpTableBlock->llvm()->getInstList().front()); + for (auto predIt = llvm::pred_begin(m_jumpTableBlock->llvm()); predIt != llvm::pred_end(m_jumpTableBlock->llvm()); ++predIt) + { + BasicBlock* pred = nullptr; + for (auto&& p : m_basicBlocks) + { + if (p.second.llvm() == *predIt) + { + pred = &p.second; + break; + } + } + + phi->addIncoming(pred->getJumpTarget(), pred->llvm()); + } + } + + dumpCFGifRequired("blocks-init.dot"); + + if (m_options.optimizeStack) + { + std::vector blockList; + for (auto& entry : m_basicBlocks) + blockList.push_back(&entry.second); + + if (m_jumpTableBlock) + blockList.push_back(m_jumpTableBlock.get()); + + BasicBlock::linkLocalStacks(blockList, m_builder); + + dumpCFGifRequired("blocks-opt.dot"); + } + + for (auto& entry : m_basicBlocks) + entry.second.synchronizeLocalStack(stack); + if (m_jumpTableBlock) + m_jumpTableBlock->synchronizeLocalStack(stack); + + dumpCFGifRequired("blocks-sync.dot"); + + if (m_jumpTableBlock && m_options.rewriteSwitchToBranches) + { + llvm::FunctionPassManager fpManager(module.get()); + fpManager.add(llvm::createLowerSwitchPass()); + fpManager.doInitialization(); + fpManager.run(*m_mainFunc); + } + + auto compilationEndTime = std::chrono::high_resolution_clock::now(); + clog(JIT) << "JIT: " << std::chrono::duration_cast(compilationEndTime - compilationStartTime).count(); + return module; +} + + +void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager, + Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock) +{ + if (!_nextBasicBlock) // this is the last block in the code + _nextBasicBlock = m_stopBB; + + m_builder.SetInsertPoint(_basicBlock.llvm()); + auto& stack = _basicBlock.localStack(); + + for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it) + { + auto inst = Instruction(*it); + + _gasMeter.count(inst); + + switch (inst) + { + + case Instruction::ADD: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto result = m_builder.CreateAdd(lhs, rhs); + stack.push(result); + break; + } + + case Instruction::SUB: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto result = m_builder.CreateSub(lhs, rhs); + stack.push(result); + break; + } + + case Instruction::MUL: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = _arith.mul(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::DIV: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = _arith.div(lhs, rhs); + stack.push(res.first); + break; + } + + case Instruction::SDIV: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = _arith.sdiv(lhs, rhs); + stack.push(res.first); + break; + } + + case Instruction::MOD: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = _arith.div(lhs, rhs); + stack.push(res.second); + break; + } + + case Instruction::SMOD: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = _arith.sdiv(lhs, rhs); + stack.push(res.second); + break; + } + + case Instruction::EXP: + { + auto base = stack.pop(); + auto exponent = stack.pop(); + _gasMeter.countExp(exponent); + auto ret = _arith.exp(base, exponent); + stack.push(ret); + break; + } + + case Instruction::NOT: + { + auto value = stack.pop(); + auto ret = m_builder.CreateXor(value, Constant::get(-1), "bnot"); + stack.push(ret); + break; + } + + case Instruction::LT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpULT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::GT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpUGT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::SLT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpSLT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::SGT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpSGT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::EQ: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpEQ(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::ISZERO: + { + auto top = stack.pop(); + auto iszero = m_builder.CreateICmpEQ(top, Constant::get(0), "iszero"); + auto result = m_builder.CreateZExt(iszero, Type::Word); + stack.push(result); + break; + } + + case Instruction::AND: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = m_builder.CreateAnd(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::OR: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = m_builder.CreateOr(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::XOR: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = m_builder.CreateXor(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::BYTE: + { + const auto byteNum = stack.pop(); + auto value = stack.pop(); + + value = Endianness::toBE(m_builder, value); + auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); + auto safeByteNum = m_builder.CreateZExt(m_builder.CreateTrunc(byteNum, m_builder.getIntNTy(5)), Type::lowPrecision); // Trim index, large values can crash + auto byte = m_builder.CreateExtractElement(bytes, safeByteNum, "byte"); + value = m_builder.CreateZExt(byte, Type::Word); + + auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32)); + value = m_builder.CreateSelect(byteNumValid, value, Constant::get(0)); + stack.push(value); + break; + } + + case Instruction::ADDMOD: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto mod = stack.pop(); + auto res = _arith.addmod(lhs, rhs, mod); + stack.push(res); + break; + } + + case Instruction::MULMOD: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto mod = stack.pop(); + auto res = _arith.mulmod(lhs, rhs, mod); + stack.push(res); + break; + } + + case Instruction::SIGNEXTEND: + { + auto idx = stack.pop(); + auto word = stack.pop(); + + auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32"); + auto k32 = m_builder.CreateZExt(k32_, Type::lowPrecision); + auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8"); + + // test for word >> (k * 8 + 7) + auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos"); + auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word); + auto bittester = m_builder.CreateShl(Constant::get(1), bitposEx); + auto bitresult = m_builder.CreateAnd(word, bittester); + auto bittest = m_builder.CreateICmpUGT(bitresult, Constant::get(0)); + // FIXME: The following does not work - LLVM bug, report! + //auto bitval = m_builder.CreateLShr(word, bitpos, "bitval"); + //auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest"); + + auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx); + auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); + + auto negmask = m_builder.CreateXor(mask, llvm::ConstantInt::getAllOnesValue(Type::Word), "negmask"); + auto val1 = m_builder.CreateOr(word, negmask); + auto val0 = m_builder.CreateAnd(word, mask); + + auto kInRange = m_builder.CreateICmpULE(idx, llvm::ConstantInt::get(Type::Word, 30)); + auto result = m_builder.CreateSelect(kInRange, + m_builder.CreateSelect(bittest, val1, val0), + word); + stack.push(result); + break; + } + + case Instruction::SHA3: + { + auto inOff = stack.pop(); + auto inSize = stack.pop(); + _memory.require(inOff, inSize); + _gasMeter.countSha3Data(inSize); + auto hash = _ext.sha3(inOff, inSize); + stack.push(hash); + break; + } + + case Instruction::POP: + { + auto val = stack.pop(); + static_cast(val); + // Generate a dummy use of val to make sure that a get(0) will be emitted at this point, + // so that StackTooSmall will be thrown + // m_builder.CreateICmpEQ(val, val, "dummy"); + break; + } + + case Instruction::ANY_PUSH: + { + auto value = readPushData(it, _basicBlock.end()); + stack.push(Constant::get(value)); + break; + } + + case Instruction::ANY_DUP: + { + auto index = static_cast(inst) - static_cast(Instruction::DUP1); + stack.dup(index); + break; + } + + case Instruction::ANY_SWAP: + { + auto index = static_cast(inst) - static_cast(Instruction::SWAP1) + 1; + stack.swap(index); + break; + } + + case Instruction::MLOAD: + { + auto addr = stack.pop(); + auto word = _memory.loadWord(addr); + stack.push(word); + break; + } + + case Instruction::MSTORE: + { + auto addr = stack.pop(); + auto word = stack.pop(); + _memory.storeWord(addr, word); + break; + } + + case Instruction::MSTORE8: + { + auto addr = stack.pop(); + auto word = stack.pop(); + _memory.storeByte(addr, word); + break; + } + + case Instruction::MSIZE: + { + auto word = _memory.getSize(); + stack.push(word); + break; + } + + case Instruction::SLOAD: + { + auto index = stack.pop(); + auto value = _ext.sload(index); + stack.push(value); + break; + } + + case Instruction::SSTORE: + { + auto index = stack.pop(); + auto value = stack.pop(); + _gasMeter.countSStore(_ext, index, value); + _ext.sstore(index, value); + break; + } + + case Instruction::JUMP: + case Instruction::JUMPI: + { + llvm::BasicBlock* targetBlock = nullptr; + auto target = stack.pop(); + if (auto constant = llvm::dyn_cast(target)) + { + auto&& c = constant->getValue(); + auto targetIdx = c.getActiveBits() <= 64 ? c.getZExtValue() : -1; + auto it = m_basicBlocks.find(targetIdx); + targetBlock = (it != m_basicBlocks.end() && it->second.isJumpDest()) ? it->second.llvm() : getBadJumpBlock(); + } + + // TODO: Improve; check for constants + if (inst == Instruction::JUMP) + { + if (targetBlock) + { + m_builder.CreateBr(targetBlock); + } + else + { + _basicBlock.setJumpTarget(target); + m_builder.CreateBr(getJumpTableBlock()); + } + } + else // JUMPI + { + auto val = stack.pop(); + auto zero = Constant::get(0); + auto cond = m_builder.CreateICmpNE(val, zero, "nonzero"); + + if (targetBlock) + { + m_builder.CreateCondBr(cond, targetBlock, _nextBasicBlock); + } + else + { + _basicBlock.setJumpTarget(target); + m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock); + } + } + break; + } + + case Instruction::JUMPDEST: + { + // Nothing to do + break; + } + + case Instruction::PC: + { + auto value = Constant::get(it - _basicBlock.begin() + _basicBlock.firstInstrIdx()); + stack.push(value); + break; + } + + case Instruction::GAS: + { + _gasMeter.commitCostBlock(); + stack.push(m_builder.CreateZExt(_runtimeManager.getGas(), Type::Word)); + break; + } + + case Instruction::ADDRESS: + case Instruction::CALLER: + case Instruction::ORIGIN: + case Instruction::CALLVALUE: + case Instruction::GASPRICE: + case Instruction::COINBASE: + case Instruction::DIFFICULTY: + case Instruction::GASLIMIT: + case Instruction::NUMBER: + case Instruction::TIMESTAMP: + { + // Pushes an element of runtime data on stack + auto value = _runtimeManager.get(inst); + value = m_builder.CreateZExt(value, Type::Word); + stack.push(value); + break; + } + + case Instruction::CODESIZE: + // TODO: Use constant + stack.push(_runtimeManager.getCodeSize()); + break; + + case Instruction::CALLDATASIZE: + stack.push(_runtimeManager.getCallDataSize()); + break; + + case Instruction::BLOCKHASH: + { + auto number = stack.pop(); + auto hash = _ext.blockhash(number); + stack.push(hash); + break; + } + + case Instruction::BALANCE: + { + auto address = stack.pop(); + auto value = _ext.balance(address); + stack.push(value); + break; + } + + case Instruction::EXTCODESIZE: + { + auto addr = stack.pop(); + auto codeRef = _ext.extcode(addr); + stack.push(codeRef.size); + break; + } + + case Instruction::CALLDATACOPY: + { + auto destMemIdx = stack.pop(); + auto srcIdx = stack.pop(); + auto reqBytes = stack.pop(); + + auto srcPtr = _runtimeManager.getCallData(); + auto srcSize = _runtimeManager.getCallDataSize(); + + _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); + break; + } + + case Instruction::CODECOPY: + { + auto destMemIdx = stack.pop(); + auto srcIdx = stack.pop(); + auto reqBytes = stack.pop(); + + auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, feature #80814234 + auto srcSize = _runtimeManager.getCodeSize(); + + _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); + break; + } + + case Instruction::EXTCODECOPY: + { + auto addr = stack.pop(); + auto destMemIdx = stack.pop(); + auto srcIdx = stack.pop(); + auto reqBytes = stack.pop(); + + auto codeRef = _ext.extcode(addr); + + _memory.copyBytes(codeRef.ptr, codeRef.size, srcIdx, destMemIdx, reqBytes); + break; + } + + case Instruction::CALLDATALOAD: + { + auto index = stack.pop(); + auto value = _ext.calldataload(index); + stack.push(value); + break; + } + + case Instruction::CREATE: + { + auto endowment = stack.pop(); + auto initOff = stack.pop(); + auto initSize = stack.pop(); + _memory.require(initOff, initSize); + + _gasMeter.commitCostBlock(); + auto address = _ext.create(endowment, initOff, initSize); + stack.push(address); + break; + } + + case Instruction::CALL: + case Instruction::CALLCODE: + { + auto callGas256 = stack.pop(); + auto codeAddress = stack.pop(); + auto value = stack.pop(); + auto inOff = stack.pop(); + auto inSize = stack.pop(); + auto outOff = stack.pop(); + auto outSize = stack.pop(); + + _gasMeter.commitCostBlock(); + + // 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(inOff, inSize); + + auto receiveAddress = codeAddress; + if (inst == Instruction::CALLCODE) + receiveAddress = _runtimeManager.get(RuntimeData::Address); + + auto gas = _runtimeManager.getGas(); + _gasMeter.count(callGas256); + auto callGas = m_builder.CreateTrunc(callGas256, Type::Gas); + auto gasLeft = m_builder.CreateNSWSub(gas, callGas); + _runtimeManager.setGas(callGas); + auto ret = _ext.call(receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress); + _gasMeter.giveBack(gasLeft); + stack.push(ret); + break; + } + + case Instruction::RETURN: + { + auto index = stack.pop(); + auto size = stack.pop(); + + _memory.require(index, size); + _runtimeManager.registerReturnData(index, size); + + m_builder.CreateRet(Constant::get(ReturnCode::Return)); + break; + } + + case Instruction::SUICIDE: + { + _runtimeManager.registerSuicide(stack.pop()); + m_builder.CreateRet(Constant::get(ReturnCode::Suicide)); + break; + } + + + case Instruction::STOP: + { + m_builder.CreateRet(Constant::get(ReturnCode::Stop)); + break; + } + + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + { + auto beginIdx = stack.pop(); + auto numBytes = stack.pop(); + _memory.require(beginIdx, numBytes); + + // This will commit the current cost block + _gasMeter.countLogData(numBytes); + + std::array topics{{}}; + auto numTopics = static_cast(inst) - static_cast(Instruction::LOG0); + for (size_t i = 0; i < numTopics; ++i) + topics[i] = stack.pop(); + + _ext.log(beginIdx, numBytes, topics); + break; + } + + default: // Invalid instruction - abort + m_builder.CreateRet(Constant::get(ReturnCode::BadInstruction)); + it = _basicBlock.end() - 1; // finish block compilation + } + } + + _gasMeter.commitCostBlock(); + + // Block may have no terminator if the next instruction is a jump destination. + if (!_basicBlock.llvm()->getTerminator()) + m_builder.CreateBr(_nextBasicBlock); +} + + + +void Compiler::removeDeadBlocks() +{ + // Remove dead basic blocks + auto sthErased = false; + do + { + sthErased = false; + for (auto it = m_basicBlocks.begin(); it != m_basicBlocks.end();) + { + auto llvmBB = it->second.llvm(); + if (llvm::pred_begin(llvmBB) == llvm::pred_end(llvmBB)) + { + llvmBB->eraseFromParent(); + m_basicBlocks.erase(it++); + sthErased = true; + } + else + ++it; + } + } + while (sthErased); + + if (m_jumpTableBlock && llvm::pred_begin(m_jumpTableBlock->llvm()) == llvm::pred_end(m_jumpTableBlock->llvm())) + { + m_jumpTableBlock->llvm()->eraseFromParent(); + m_jumpTableBlock.reset(); + } + + if (m_badJumpBlock && llvm::pred_begin(m_badJumpBlock->llvm()) == llvm::pred_end(m_badJumpBlock->llvm())) + { + m_badJumpBlock->llvm()->eraseFromParent(); + m_badJumpBlock.reset(); + } +} + +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" + << " node [shape=record, fontname=Courier, fontsize=10];\n" + << " entry [share=record, label=\"entry block\"];\n"; + + std::vector blocks; + for (auto& pair : m_basicBlocks) + blocks.push_back(&pair.second); + if (m_jumpTableBlock) + blocks.push_back(m_jumpTableBlock.get()); + if (m_badJumpBlock) + blocks.push_back(m_badJumpBlock.get()); + + // std::map phiNodesPerBlock; + + // Output nodes + for (auto bb : blocks) + { + std::string blockName = bb->llvm()->getName(); + + std::ostringstream oss; + bb->dump(oss, true); + + _out << " \"" << blockName << "\" [shape=record, label=\" { " << blockName << "|" << oss.str() << "} \"];\n"; + } + + // Output edges + for (auto bb : blocks) + { + std::string blockName = bb->llvm()->getName(); + + auto end = llvm::pred_end(bb->llvm()); + for (llvm::pred_iterator it = llvm::pred_begin(bb->llvm()); it != end; ++it) + { + _out << " \"" << (*it)->getName().str() << "\" -> \"" << blockName << "\" [" + << ((m_jumpTableBlock.get() && *it == m_jumpTableBlock.get()->llvm()) ? "style = dashed, " : "") + << "];\n"; + } + } + + _out << "}\n"; +} + +void Compiler::dump() +{ + for (auto& entry : m_basicBlocks) + entry.second.dump(); + if (m_jumpTableBlock != nullptr) + m_jumpTableBlock->dump(); +} + +} +} +} + diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h new file mode 100644 index 000000000..c9795fb99 --- /dev/null +++ b/evmjit/libevmjit/Compiler.h @@ -0,0 +1,80 @@ +#pragma once + +#include "Common.h" +#include "BasicBlock.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +class Compiler +{ +public: + + struct Options + { + /// Optimize stack operations between basic blocks + bool optimizeStack = true; + + /// Rewrite switch instructions to sequences of branches + bool rewriteSwitchToBranches = true; + + /// Dump CFG as a .dot file for graphviz + bool dumpCFG = false; + }; + + using ProgramCounter = uint64_t; + + Compiler(Options const& _options); + + std::unique_ptr compile(code_iterator _begin, code_iterator _end, std::string const& _id); + +private: + + void createBasicBlocks(code_iterator _begin, code_iterator _end); + + void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock); + + llvm::BasicBlock* getJumpTableBlock(); + + llvm::BasicBlock* getBadJumpBlock(); + + void removeDeadBlocks(); + + /// 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(); + + /// Compiler options + Options const& m_options; + + /// Helper class for generating IR + llvm::IRBuilder<> m_builder; + + /// Maps a program counter pc to a basic block that starts at pc (if any). + std::map m_basicBlocks; + + /// Stop basic block - terminates execution with STOP code (0) + llvm::BasicBlock* m_stopBB = nullptr; + + /// Block with a jump table. + std::unique_ptr m_jumpTableBlock; + + /// Destination for invalid jumps + std::unique_ptr m_badJumpBlock; + + /// Main program function + llvm::Function* m_mainFunc = nullptr; +}; + +} +} +} diff --git a/evmjit/libevmjit/CompilerHelper.cpp b/evmjit/libevmjit/CompilerHelper.cpp new file mode 100644 index 000000000..5c8ee8574 --- /dev/null +++ b/evmjit/libevmjit/CompilerHelper.cpp @@ -0,0 +1,51 @@ +#include "CompilerHelper.h" + +#include "preprocessor/llvm_includes_start.h" +#include +#include "preprocessor/llvm_includes_end.h" + +#include "RuntimeManager.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +CompilerHelper::CompilerHelper(llvm::IRBuilder<>& _builder) : + m_builder(_builder) +{} + +llvm::Module* CompilerHelper::getModule() +{ + assert(m_builder.GetInsertBlock()); + assert(m_builder.GetInsertBlock()->getParent()); // BB must be in a function + return m_builder.GetInsertBlock()->getParent()->getParent(); +} + +llvm::Function* CompilerHelper::getMainFunction() +{ + // TODO: Rename or change semantics of getMainFunction() function + assert(m_builder.GetInsertBlock()); + auto mainFunc = m_builder.GetInsertBlock()->getParent(); + assert(mainFunc); + if (mainFunc == &mainFunc->getParent()->getFunctionList().front()) // Main function is the first one in module + return mainFunc; + return nullptr; +} + +llvm::CallInst* CompilerHelper::createCall(llvm::Function* _func, std::initializer_list const& _args) +{ + return getBuilder().CreateCall(_func, {_args.begin(), _args.size()}); +} + + +RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager): + CompilerHelper(_runtimeManager.getBuilder()), + m_runtimeManager(_runtimeManager) +{} + +} +} +} diff --git a/evmjit/libevmjit/CompilerHelper.h b/evmjit/libevmjit/CompilerHelper.h new file mode 100644 index 000000000..cd6d09a58 --- /dev/null +++ b/evmjit/libevmjit/CompilerHelper.h @@ -0,0 +1,79 @@ +#pragma once + +#include "preprocessor/llvm_includes_start.h" +#include +#include "preprocessor/llvm_includes_end.h" + + +namespace dev +{ +namespace eth +{ +namespace jit +{ +class RuntimeManager; + +/// Base class for compiler helpers like Memory, GasMeter, etc. +class CompilerHelper +{ +protected: + CompilerHelper(llvm::IRBuilder<>& _builder); + + CompilerHelper(const CompilerHelper&) = delete; + CompilerHelper& operator=(CompilerHelper) = delete; + + /// Reference to the IR module being compiled + llvm::Module* getModule(); + + /// Reference to the main module function + llvm::Function* getMainFunction(); + + /// Reference to parent compiler IR builder + llvm::IRBuilder<>& m_builder; + llvm::IRBuilder<>& getBuilder() { return m_builder; } + + llvm::CallInst* createCall(llvm::Function* _func, std::initializer_list const& _args); + + friend class RuntimeHelper; +}; + + +/// Compiler helper that depends on runtime data +class RuntimeHelper : public CompilerHelper +{ +protected: + RuntimeHelper(RuntimeManager& _runtimeManager); + + RuntimeManager& getRuntimeManager() { return m_runtimeManager; } + +private: + RuntimeManager& m_runtimeManager; +}; + + +/// Saves the insert point of the IR builder and restores it when destructed +struct InsertPointGuard +{ + InsertPointGuard(llvm::IRBuilder<>& _builder) : + m_builder(_builder), + m_insertBB(m_builder.GetInsertBlock()), + m_insertPt(m_builder.GetInsertPoint()) + {} + + InsertPointGuard(const InsertPointGuard&) = delete; + void operator=(InsertPointGuard) = delete; + + ~InsertPointGuard() + { + m_builder.SetInsertPoint(m_insertBB, m_insertPt); + } + +private: + llvm::IRBuilder<>& m_builder; + llvm::BasicBlock* m_insertBB; + llvm::BasicBlock::iterator m_insertPt; +}; + +} +} +} diff --git a/evmjit/libevmjit/Endianness.cpp b/evmjit/libevmjit/Endianness.cpp new file mode 100644 index 000000000..38f71560c --- /dev/null +++ b/evmjit/libevmjit/Endianness.cpp @@ -0,0 +1,39 @@ +#include "Endianness.h" + +#include "preprocessor/llvm_includes_start.h" +#include +#include "preprocessor/llvm_includes_end.h" + +#include "Type.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +llvm::Value* Endianness::bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word) +{ + union tester + { + unsigned int x; + unsigned char isLE; + }; + + if (tester{1}.isLE) + { + // FIXME: Disabled because of problems with BYTE + //if (auto constant = llvm::dyn_cast(_word)) + // return _builder.getInt(constant->getValue().byteSwap()); + + // OPT: Cache func declaration? + auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::Word); + return _builder.CreateCall(bswapFunc, _word); + } + return _word; +} + +} +} +} diff --git a/evmjit/libevmjit/Endianness.h b/evmjit/libevmjit/Endianness.h new file mode 100644 index 000000000..19fd8fc58 --- /dev/null +++ b/evmjit/libevmjit/Endianness.h @@ -0,0 +1,25 @@ +#pragma once + +#include "preprocessor/llvm_includes_start.h" +#include +#include "preprocessor/llvm_includes_end.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +struct Endianness +{ + static llvm::Value* toBE(llvm::IRBuilder<>& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } + static llvm::Value* toNative(llvm::IRBuilder<>& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } + +private: + static llvm::Value* bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word); +}; + +} +} +} diff --git a/evmjit/libevmjit/ExecStats.cpp b/evmjit/libevmjit/ExecStats.cpp new file mode 100644 index 000000000..684f6d39a --- /dev/null +++ b/evmjit/libevmjit/ExecStats.cpp @@ -0,0 +1,97 @@ +#include "ExecStats.h" + +#include +#include +#include + +#include "Utils.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +void ExecStats::stateChanged(ExecState _state) +{ + if (!CHECK(m_state != ExecState::Finished)) + return; + + auto now = clock::now(); + if (_state != ExecState::Started) + { + assert(time[(int)m_state] == ExecStats::duration::zero()); + time[(int)m_state] = now - m_tp; + } + m_state = _state; + m_tp = now; +} + +namespace +{ +struct StatsAgg +{ + using unit = std::chrono::microseconds; + ExecStats::duration tot = ExecStats::duration::zero(); + ExecStats::duration min = ExecStats::duration::max(); + ExecStats::duration max = ExecStats::duration::zero(); + size_t count = 0; + + void update(ExecStats::duration _d) + { + ++count; + tot += _d; + min = _d < min ? _d : min; + max = _d > max ? _d : max; + } + + void output(char const* _name, std::ostream& _os) + { + auto avg = tot / count; + _os << std::setfill(' ') + << std::setw(12) << std::left << _name + << std::setw(10) << std::right << std::chrono::duration_cast(tot).count() + << std::setw(10) << std::right << std::chrono::duration_cast(avg).count() + << std::setw(10) << std::right << std::chrono::duration_cast(min).count() + << std::setw(10) << std::right << std::chrono::duration_cast(max).count() + << std::endl; + } +}; + +char const* getExecStateName(ExecState _state) +{ + switch (_state) + { + case ExecState::Started: return "Start"; + case ExecState::CacheLoad: return "CacheLoad"; + case ExecState::CacheWrite: return "CacheWrite"; + case ExecState::Compilation: return "Compilation"; + case ExecState::CodeGen: return "CodeGen"; + case ExecState::Execution: return "Execution"; + case ExecState::Return: return "Return"; + case ExecState::Finished: return "Finish"; + } + return nullptr; +} +} + +StatsCollector::~StatsCollector() +{ + if (stats.empty()) + return; + + std::cout << " [us] total avg min max\n"; + for (int i = 0; i < (int)ExecState::Finished; ++i) + { + StatsAgg agg; + for (auto&& s : stats) + agg.update(s->time[i]); + + agg.output(getExecStateName(ExecState(i)), std::cout); + } +} + +} +} +} diff --git a/evmjit/libevmjit/ExecStats.h b/evmjit/libevmjit/ExecStats.h new file mode 100644 index 000000000..498e21341 --- /dev/null +++ b/evmjit/libevmjit/ExecStats.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include "ExecutionEngine.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +class ExecStats : public ExecutionEngineListener +{ +public: + using clock = std::chrono::high_resolution_clock; + using duration = clock::duration; + using time_point = clock::time_point; + + std::string id; + duration time[(int)ExecState::Finished] = {}; + + void stateChanged(ExecState _state) override; + +private: + ExecState m_state = {}; + time_point m_tp = {}; + +}; + + +class StatsCollector +{ +public: + std::vector> stats; + + ~StatsCollector(); +}; + +} +} +} diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp new file mode 100644 index 000000000..1d2ff91b1 --- /dev/null +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -0,0 +1,153 @@ +#include "ExecutionEngine.h" + +#include +#include // env options +#include + +#include "preprocessor/llvm_includes_start.h" +#include +#include +#include +#include +#include +#include +#include +#include "preprocessor/llvm_includes_end.h" + +#include "Runtime.h" +#include "Compiler.h" +#include "Cache.h" +#include "ExecStats.h" +#include "Utils.h" +#include "BuildInfo.gen.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +namespace +{ +using EntryFuncPtr = ReturnCode(*)(Runtime*); + +std::string codeHash(i256 const& _hash) +{ + static const auto size = sizeof(_hash); + static const auto hexChars = "0123456789abcdef"; + std::string str; + str.resize(size * 2); + auto outIt = str.rbegin(); // reverse for BE + auto& arr = *(std::array*)&_hash; + for (auto b : arr) + { + *(outIt++) = hexChars[b & 0xf]; + *(outIt++) = hexChars[b >> 4]; + } + return str; +} + +bool getEnvOption(char const* _name, bool _default) +{ + auto var = std::getenv(_name); + if (!var) + return _default; + return std::strtol(var, nullptr, 10) != 0; +} + +bool showInfo() +{ + auto show = getEnvOption("EVMJIT_INFO", false); + if (show) + { + std::cout << "The Ethereum EVM JIT " EVMJIT_VERSION_FULL " LLVM " LLVM_VERSION << std::endl; + } + return show; +} + +} + +ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) +{ + static auto debugDumpModule = getEnvOption("EVMJIT_DUMP", false); + static auto objectCacheEnabled = getEnvOption("EVMJIT_CACHE", true); + static auto statsCollectingEnabled = getEnvOption("EVMJIT_STATS", false); + static auto infoShown = showInfo(); + (void) infoShown; + + std::unique_ptr listener{new ExecStats}; + listener->stateChanged(ExecState::Started); + + auto objectCache = objectCacheEnabled ? Cache::getObjectCache(listener.get()) : nullptr; + + static std::unique_ptr ee; + if (!ee) + { + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + + auto module = std::unique_ptr(new llvm::Module({}, llvm::getGlobalContext())); + llvm::EngineBuilder builder(module.get()); + builder.setEngineKind(llvm::EngineKind::JIT); + builder.setUseMCJIT(true); + builder.setOptLevel(llvm::CodeGenOpt::None); + + auto triple = llvm::Triple(llvm::sys::getProcessTriple()); + if (triple.getOS() == llvm::Triple::OSType::Win32) + triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format + module->setTargetTriple(triple.str()); + + ee.reset(builder.create()); + if (!CHECK(ee)) + return ReturnCode::LLVMConfigError; + module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module + ee->setObjectCache(objectCache); + } + + static StatsCollector statsCollector; + + auto mainFuncName = codeHash(_data->codeHash); + Runtime runtime(_data, _env); // TODO: I don't know why but it must be created before getFunctionAddress() calls + + auto entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); + if (!entryFuncPtr) + { + auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr; + if (!module) + { + listener->stateChanged(ExecState::Compilation); + assert(_data->code || !_data->codeSize); //TODO: Is it good idea to execute empty code? + module = Compiler({}).compile(_data->code, _data->code + _data->codeSize, mainFuncName); + } + if (debugDumpModule) + module->dump(); + + ee->addModule(module.get()); + module.release(); + listener->stateChanged(ExecState::CodeGen); + entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); + } + if (!CHECK(entryFuncPtr)) + return ReturnCode::LLVMLinkError; + + listener->stateChanged(ExecState::Execution); + auto returnCode = entryFuncPtr(&runtime); + listener->stateChanged(ExecState::Return); + + if (returnCode == ReturnCode::Return) + { + returnData = runtime.getReturnData(); // Save reference to return data + std::swap(m_memory, runtime.getMemory()); // Take ownership of memory + } + listener->stateChanged(ExecState::Finished); + + if (statsCollectingEnabled) + statsCollector.stats.push_back(std::move(listener)); + + return returnCode; +} + +} +} +} diff --git a/evmjit/libevmjit/ExecutionEngine.h b/evmjit/libevmjit/ExecutionEngine.h new file mode 100644 index 000000000..4c2965e58 --- /dev/null +++ b/evmjit/libevmjit/ExecutionEngine.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +#include "RuntimeData.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +enum class ExecState +{ + Started, + CacheLoad, + CacheWrite, + Compilation, + CodeGen, + Execution, + Return, + Finished +}; + +class ExecutionEngineListener +{ +public: + ExecutionEngineListener() = default; + ExecutionEngineListener(ExecutionEngineListener const&) = delete; + ExecutionEngineListener& operator=(ExecutionEngineListener) = delete; + virtual ~ExecutionEngineListener() {} + + virtual void executionStarted() {} + virtual void executionEnded() {} + + virtual void stateChanged(ExecState) {} +}; + +class ExecutionEngine +{ +public: + ExecutionEngine() = default; + ExecutionEngine(ExecutionEngine const&) = delete; + ExecutionEngine& operator=(ExecutionEngine) = delete; + + EXPORT ReturnCode run(RuntimeData* _data, Env* _env); + + /// Reference to returned data (RETURN opcode used) + bytes_ref returnData; + +private: + /// After execution, if RETURN used, memory is moved there + /// to allow client copy the returned data + bytes m_memory; +}; + +} +} +} diff --git a/evmjit/libevmjit/Ext.cpp b/evmjit/libevmjit/Ext.cpp new file mode 100644 index 000000000..38deef214 --- /dev/null +++ b/evmjit/libevmjit/Ext.cpp @@ -0,0 +1,192 @@ +#include "Ext.h" + +#include "preprocessor/llvm_includes_start.h" +#include +#include "preprocessor/llvm_includes_end.h" + +#include "RuntimeManager.h" +#include "Memory.h" +#include "Type.h" +#include "Endianness.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): + RuntimeHelper(_runtimeManager), + m_memoryMan(_memoryMan) +{ + m_funcs = decltype(m_funcs)(); + m_argAllocas = decltype(m_argAllocas)(); + m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); +} + + +using FuncDesc = std::tuple; + +llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list const& _argsTypes) +{ + return llvm::FunctionType::get(_returnType, llvm::ArrayRef{_argsTypes.begin(), _argsTypes.size()}, false); +} + +std::array::value> const& getEnvFuncDescs() +{ + static std::array::value> descs{{ + FuncDesc{"env_sload", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})}, + FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})}, + FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})}, + FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, + FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})}, + }}; + + return descs; +} + +llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module) +{ + auto&& desc = getEnvFuncDescs()[static_cast(_id)]; + return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module); +} + +llvm::Value* Ext::getArgAlloca() +{ + auto& a = m_argAllocas[m_argCounter]; + if (!a) + { + InsertPointGuard g{getBuilder()}; + auto allocaIt = getMainFunction()->front().begin(); + std::advance(allocaIt, m_argCounter); // Skip already created allocas + getBuilder().SetInsertPoint(allocaIt); + a = getBuilder().CreateAlloca(Type::Word, nullptr, {"a.", std::to_string(m_argCounter)}); + } + ++m_argCounter; + return a; +} + +llvm::Value* Ext::byPtr(llvm::Value* _value) +{ + auto a = getArgAlloca(); + getBuilder().CreateStore(_value, a); + return a; +} + +llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list const& _args) +{ + auto& func = m_funcs[static_cast(_funcId)]; + if (!func) + func = createFunc(_funcId, getModule()); + + m_argCounter = 0; + return getBuilder().CreateCall(func, {_args.begin(), _args.size()}); +} + +llvm::Value* Ext::sload(llvm::Value* _index) +{ + auto ret = getArgAlloca(); + createCall(EnvFunc::sload, {getRuntimeManager().getEnvPtr(), byPtr(_index), ret}); // Uses native endianness + return m_builder.CreateLoad(ret); +} + +void Ext::sstore(llvm::Value* _index, llvm::Value* _value) +{ + createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness +} + +llvm::Value* Ext::calldataload(llvm::Value* _index) +{ + auto ret = getArgAlloca(); + createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret}); + ret = m_builder.CreateLoad(ret); + return Endianness::toNative(m_builder, ret); +} + +llvm::Value* Ext::balance(llvm::Value* _address) +{ + auto address = Endianness::toBE(m_builder, _address); + auto ret = getArgAlloca(); + createCall(EnvFunc::balance, {getRuntimeManager().getEnvPtr(), byPtr(address), ret}); + return m_builder.CreateLoad(ret); +} + +llvm::Value* Ext::blockhash(llvm::Value* _number) +{ + auto hash = getArgAlloca(); + createCall(EnvFunc::blockhash, {getRuntimeManager().getEnvPtr(), byPtr(_number), hash}); + hash = m_builder.CreateLoad(hash); + return Endianness::toNative(getBuilder(), hash); +} + +llvm::Value* Ext::create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize) +{ + auto ret = getArgAlloca(); + auto begin = m_memoryMan.getBytePtr(_initOff); + auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size"); + createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(_endowment), begin, size, ret}); + llvm::Value* address = m_builder.CreateLoad(ret); + address = Endianness::toNative(m_builder, address); + return address; +} + +llvm::Value* Ext::call(llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress) +{ + auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress); + auto inBeg = m_memoryMan.getBytePtr(_inOff); + auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size"); + auto outBeg = m_memoryMan.getBytePtr(_outOff); + auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size"); + auto codeAddress = Endianness::toBE(m_builder, _codeAddress); + auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)}); + return m_builder.CreateZExt(ret, Type::Word, "ret"); +} + +llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) +{ + auto begin = m_memoryMan.getBytePtr(_inOff); + auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size"); + auto ret = getArgAlloca(); + createCall(EnvFunc::sha3, {begin, size, ret}); + llvm::Value* hash = m_builder.CreateLoad(ret); + hash = Endianness::toNative(m_builder, hash); + return hash; +} + +MemoryRef Ext::extcode(llvm::Value* _addr) +{ + auto addr = Endianness::toBE(m_builder, _addr); + auto code = createCall(EnvFunc::extcode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size}); + auto codeSize = m_builder.CreateLoad(m_size); + auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word); + return {code, codeSize256}; +} + +void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array const& _topics) +{ + auto begin = m_memoryMan.getBytePtr(_memIdx); + auto size = m_builder.CreateTrunc(_numBytes, Type::Size, "size"); + llvm::Value* args[] = {getRuntimeManager().getEnvPtr(), begin, size, getArgAlloca(), getArgAlloca(), getArgAlloca(), getArgAlloca()}; + + auto topicArgPtr = &args[3]; + for (auto&& topic : _topics) + { + if (topic) + m_builder.CreateStore(Endianness::toBE(m_builder, topic), *topicArgPtr); + else + *topicArgPtr = llvm::ConstantPointerNull::get(Type::WordPtr); + ++topicArgPtr; + } + + createCall(EnvFunc::log, {args[0], args[1], args[2], args[3], args[4], args[5], args[6]}); // TODO: use std::initializer_list<> +} + +} +} +} diff --git a/evmjit/libevmjit/Ext.h b/evmjit/libevmjit/Ext.h new file mode 100644 index 000000000..1c0c0fc56 --- /dev/null +++ b/evmjit/libevmjit/Ext.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include "CompilerHelper.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + class Memory; + +struct MemoryRef +{ + llvm::Value* ptr; + llvm::Value* size; +}; + +template +struct sizeOf +{ + static const size_t value = static_cast(_EnumT::_size); +}; + +enum class EnvFunc +{ + sload, + sstore, + sha3, + balance, + create, + call, + log, + blockhash, + extcode, + calldataload, // Helper function, not client Env interface + + _size +}; + +class Ext : public RuntimeHelper +{ +public: + Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan); + + llvm::Value* sload(llvm::Value* _index); + void sstore(llvm::Value* _index, llvm::Value* _value); + + llvm::Value* balance(llvm::Value* _address); + llvm::Value* calldataload(llvm::Value* _index); + llvm::Value* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); + llvm::Value* call(llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress); + llvm::Value* blockhash(llvm::Value* _number); + + llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); + MemoryRef extcode(llvm::Value* _addr); + + void log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array const& _topics); + +private: + Memory& m_memoryMan; + + llvm::Value* m_size; + llvm::Value* m_data = nullptr; + + std::array::value> m_funcs; + std::array m_argAllocas; + size_t m_argCounter = 0; + + llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list const& _args); + llvm::Value* getArgAlloca(); + llvm::Value* byPtr(llvm::Value* _value); +}; + + +} +} +} + diff --git a/evmjit/libevmjit/GasMeter.cpp b/evmjit/libevmjit/GasMeter.cpp new file mode 100644 index 000000000..ca21714e0 --- /dev/null +++ b/evmjit/libevmjit/GasMeter.cpp @@ -0,0 +1,231 @@ +#include "GasMeter.h" + +#include "preprocessor/llvm_includes_start.h" +#include +#include "preprocessor/llvm_includes_end.h" + +#include "Ext.h" +#include "RuntimeManager.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +namespace // Helper functions +{ + +int64_t const c_stepGas = 1; +int64_t const c_balanceGas = 20; +int64_t const c_sha3Gas = 10; +int64_t const c_sha3WordGas = 10; +int64_t const c_sloadGas = 20; +int64_t const c_sstoreSetGas = 300; +int64_t const c_sstoreResetGas = 100; +int64_t const c_sstoreRefundGas = 100; +int64_t const c_createGas = 100; +int64_t const c_createDataGas = 5; +int64_t const c_callGas = 20; +int64_t const c_expGas = 1; +int64_t const c_expByteGas = 1; +int64_t const c_memoryGas = 1; +int64_t const c_txDataZeroGas = 1; +int64_t const c_txDataNonZeroGas = 5; +int64_t const c_txGas = 500; +int64_t const c_logGas = 32; +int64_t const c_logDataGas = 1; +int64_t const c_logTopicGas = 32; +int64_t const c_copyGas = 1; + +int64_t getStepCost(Instruction inst) +{ + switch (inst) + { + default: // Assumes instruction code is valid + return c_stepGas; + + case Instruction::STOP: + case Instruction::SUICIDE: + case Instruction::SSTORE: // Handle cost of SSTORE separately in GasMeter::countSStore() + return 0; + + case Instruction::EXP: return c_expGas; + + case Instruction::SLOAD: return c_sloadGas; + + case Instruction::SHA3: return c_sha3Gas; + + case Instruction::BALANCE: return c_balanceGas; + + case Instruction::CALL: + case Instruction::CALLCODE: return c_callGas; + + case Instruction::CREATE: return c_createGas; + + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + { + auto numTopics = static_cast(inst) - static_cast(Instruction::LOG0); + return c_logGas + numTopics * c_logTopicGas; + } + } +} + +} + +GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager) : + CompilerHelper(_builder), + m_runtimeManager(_runtimeManager) +{ + auto module = getModule(); + + llvm::Type* gasCheckArgs[] = {Type::RuntimePtr, Type::Gas}; + m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", module); + InsertPointGuard guard(m_builder); + + auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc); + auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc); + auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc); + + auto rt = &m_gasCheckFunc->getArgumentList().front(); + rt->setName("rt"); + auto cost = rt->getNextNode(); + cost->setName("cost"); + + m_builder.SetInsertPoint(checkBB); + auto gas = m_runtimeManager.getGas(); + gas = m_builder.CreateNSWSub(gas, cost, "gasUpdated"); + auto isOutOfGas = m_builder.CreateICmpSLT(gas, m_builder.getInt64(0), "isOutOfGas"); // gas < 0, with gas == 0 we can still do 0 cost instructions + m_builder.CreateCondBr(isOutOfGas, outOfGasBB, updateBB); + + m_builder.SetInsertPoint(outOfGasBB); + m_runtimeManager.abort(); + m_builder.CreateUnreachable(); + + m_builder.SetInsertPoint(updateBB); + m_runtimeManager.setGas(gas); + m_builder.CreateRetVoid(); +} + +void GasMeter::count(Instruction _inst) +{ + if (!m_checkCall) + { + // Create gas check call with mocked block cost at begining of current cost-block + m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Gas)}); + } + + m_blockCost += getStepCost(_inst); +} + +void GasMeter::count(llvm::Value* _cost) +{ + if (_cost->getType() == Type::Word) + { + auto gasMax256 = m_builder.CreateZExt(Constant::gasMax, Type::Word); + auto tooHigh = m_builder.CreateICmpUGT(_cost, gasMax256, "costTooHigh"); + auto cost64 = m_builder.CreateTrunc(_cost, Type::Gas); + _cost = m_builder.CreateSelect(tooHigh, Constant::gasMax, cost64, "cost"); + } + + assert(_cost->getType() == Type::Gas); + createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost}); +} + +void GasMeter::countExp(llvm::Value* _exponent) +{ + // Additional cost is 1 per significant byte of exponent + // lz - leading zeros + // cost = ((256 - lz) + 7) / 8 + + // OPT: Can gas update be done in exp algorithm? + + auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); + auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false)); + auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz"); + auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits"); + auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8)); + count(sigBytes); +} + +void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue) +{ + auto oldValue = _ext.sload(_index); + auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero"); + auto newValueIsZero = m_builder.CreateICmpEQ(_newValue, Constant::get(0), "newValueIsZero"); + auto oldValueIsntZero = m_builder.CreateICmpNE(oldValue, Constant::get(0), "oldValueIsntZero"); + auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero"); + auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert"); + auto isDelete = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDelete"); + auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(c_sstoreSetGas), m_builder.getInt64(c_sstoreResetGas), "cost"); + cost = m_builder.CreateSelect(isDelete, m_builder.getInt64(0), cost, "cost"); + count(cost); +} + +void GasMeter::countLogData(llvm::Value* _dataLength) +{ + assert(m_checkCall); + assert(m_blockCost > 0); // LOGn instruction is already counted + static_assert(c_logDataGas == 1, "Log data gas cost has changed. Update GasMeter."); + count(_dataLength); +} + +void GasMeter::countSha3Data(llvm::Value* _dataLength) +{ + assert(m_checkCall); + assert(m_blockCost > 0); // SHA3 instruction is already counted + + // TODO: This round ups to 32 happens in many places + static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter"); + auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::Gas); + auto words64 = m_builder.CreateUDiv(m_builder.CreateNUWAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32)); + auto cost64 = m_builder.CreateNUWMul(getBuilder().getInt64(c_sha3WordGas), words64); + count(cost64); +} + +void GasMeter::giveBack(llvm::Value* _gas) +{ + assert(_gas->getType() == Type::Gas); + m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas)); +} + +void GasMeter::commitCostBlock() +{ + // If any uncommited block + if (m_checkCall) + { + if (m_blockCost == 0) // Do not check 0 + { + m_checkCall->eraseFromParent(); // Remove the gas check call + m_checkCall = nullptr; + return; + } + + m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas check call + m_checkCall = nullptr; // End cost-block + m_blockCost = 0; + } + assert(m_blockCost == 0); +} + +void GasMeter::countMemory(llvm::Value* _additionalMemoryInWords) +{ + static_assert(c_memoryGas == 1, "Memory gas cost has changed. Update GasMeter."); + count(_additionalMemoryInWords); +} + +void GasMeter::countCopy(llvm::Value* _copyWords) +{ + static_assert(c_copyGas == 1, "Copy gas cost has changed. Update GasMeter."); + count(_copyWords); +} + +} +} +} + diff --git a/evmjit/libevmjit/GasMeter.h b/evmjit/libevmjit/GasMeter.h new file mode 100644 index 000000000..4056cd64d --- /dev/null +++ b/evmjit/libevmjit/GasMeter.h @@ -0,0 +1,63 @@ +#pragma once + +#include "CompilerHelper.h" +#include "Instruction.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ +class RuntimeManager; + +class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper +{ +public: + GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager); + + /// Count step cost of instruction + void count(Instruction _inst); + + /// Count additional cost + void count(llvm::Value* _cost); + + /// Calculate & count gas cost for SSTORE instruction + void countSStore(class Ext& _ext, llvm::Value* _index, llvm::Value* _newValue); + + /// Calculate & count additional gas cost for EXP instruction + void countExp(llvm::Value* _exponent); + + /// Count gas cost of LOG data + void countLogData(llvm::Value* _dataLength); + + /// Count gas cost of SHA3 data + void countSha3Data(llvm::Value* _dataLength); + + /// Finalize cost-block by checking gas needed for the block before the block + void commitCostBlock(); + + /// Give back an amount of gas not used by a call + void giveBack(llvm::Value* _gas); + + /// Generate code that checks the cost of additional memory used by program + void countMemory(llvm::Value* _additionalMemoryInWords); + + /// Count addional gas cost for memory copy + void countCopy(llvm::Value* _copyWords); + +private: + /// Cumulative gas cost of a block of instructions + /// @TODO Handle overflow + int64_t m_blockCost = 0; + + llvm::CallInst* m_checkCall = nullptr; + llvm::Function* m_gasCheckFunc = nullptr; + + RuntimeManager& m_runtimeManager; +}; + +} +} +} + diff --git a/evmjit/libevmjit/Instruction.cpp b/evmjit/libevmjit/Instruction.cpp new file mode 100644 index 000000000..f70b020f8 --- /dev/null +++ b/evmjit/libevmjit/Instruction.cpp @@ -0,0 +1,42 @@ +#include "Instruction.h" + +#include "preprocessor/llvm_includes_start.h" +#include +#include "preprocessor/llvm_includes_end.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +llvm::APInt readPushData(code_iterator& _curr, code_iterator _end) +{ + auto pushInst = *_curr; + assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); + auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; + llvm::APInt value(256, 0); + ++_curr; // Point the data + for (decltype(numBytes) i = 0; i < numBytes; ++i) + { + byte b = (_curr != _end) ? *_curr++ : 0; + value <<= 8; + value |= b; + } + --_curr; // Point the last real byte read + return value; +} + +void skipPushData(code_iterator& _curr, code_iterator _end) +{ + auto pushInst = *_curr; + assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); + auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; + --_end; + for (decltype(numBytes) i = 0; i < numBytes && _curr < _end; ++i, ++_curr) {} +} + +} +} +} diff --git a/evmjit/libevmjit/Instruction.h b/evmjit/libevmjit/Instruction.h new file mode 100644 index 000000000..6785213d6 --- /dev/null +++ b/evmjit/libevmjit/Instruction.h @@ -0,0 +1,239 @@ +#pragma once + +#include "Common.h" + +namespace llvm +{ + class APInt; +} + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +/// Virtual machine bytecode instruction. +enum class Instruction: uint8_t +{ + STOP = 0x00, ///< halts execution + ADD, ///< addition operation + MUL, ///< mulitplication operation + SUB, ///< subtraction operation + DIV, ///< integer division operation + SDIV, ///< signed integer division operation + MOD, ///< modulo remainder operation + SMOD, ///< signed modulo remainder operation + ADDMOD, ///< unsigned modular addition + MULMOD, ///< unsigned modular multiplication + EXP, ///< exponential operation + SIGNEXTEND, ///< extend length of signed integer + + LT = 0x10, ///< less-than comparision + GT, ///< greater-than comparision + SLT, ///< signed less-than comparision + SGT, ///< signed greater-than comparision + EQ, ///< equality comparision + ISZERO, ///< simple not operator + AND, ///< bitwise AND operation + OR, ///< bitwise OR operation + XOR, ///< bitwise XOR operation + NOT, ///< bitwise NOT opertation + BYTE, ///< retrieve single byte from word + + SHA3 = 0x20, ///< compute SHA3-256 hash + + ADDRESS = 0x30, ///< get address of currently executing account + BALANCE, ///< get balance of the given account + ORIGIN, ///< get execution origination address + CALLER, ///< get caller address + CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution + CALLDATALOAD, ///< get input data of current environment + CALLDATASIZE, ///< get size of input data in current environment + CALLDATACOPY, ///< copy input data in current environment to memory + CODESIZE, ///< get size of code running in current environment + CODECOPY, ///< copy code running in current environment to memory + GASPRICE, ///< get price of gas in current environment + EXTCODESIZE, ///< get external code size (from another contract) + EXTCODECOPY, ///< copy external code (from another contract) + + BLOCKHASH = 0x40, ///< get hash of most recent complete block + COINBASE, ///< get the block's coinbase address + TIMESTAMP, ///< get the block's timestamp + NUMBER, ///< get the block's number + DIFFICULTY, ///< get the block's difficulty + GASLIMIT, ///< get the block's gas limit + + POP = 0x50, ///< remove item from stack + MLOAD, ///< load word from memory + MSTORE, ///< save word to memory + MSTORE8, ///< save byte to memory + SLOAD, ///< load word from storage + SSTORE, ///< save word to storage + JUMP, ///< alter the program counter + JUMPI, ///< conditionally alter the program counter + PC, ///< get the program counter + MSIZE, ///< get the size of active memory + GAS, ///< get the amount of available gas + JUMPDEST, ///< set a potential jump destination + + PUSH1 = 0x60, ///< place 1 byte item on stack + PUSH2, ///< place 2 byte item on stack + PUSH3, ///< place 3 byte item on stack + PUSH4, ///< place 4 byte item on stack + PUSH5, ///< place 5 byte item on stack + PUSH6, ///< place 6 byte item on stack + PUSH7, ///< place 7 byte item on stack + PUSH8, ///< place 8 byte item on stack + PUSH9, ///< place 9 byte item on stack + PUSH10, ///< place 10 byte item on stack + PUSH11, ///< place 11 byte item on stack + PUSH12, ///< place 12 byte item on stack + PUSH13, ///< place 13 byte item on stack + PUSH14, ///< place 14 byte item on stack + PUSH15, ///< place 15 byte item on stack + PUSH16, ///< place 16 byte item on stack + PUSH17, ///< place 17 byte item on stack + PUSH18, ///< place 18 byte item on stack + PUSH19, ///< place 19 byte item on stack + PUSH20, ///< place 20 byte item on stack + PUSH21, ///< place 21 byte item on stack + PUSH22, ///< place 22 byte item on stack + PUSH23, ///< place 23 byte item on stack + PUSH24, ///< place 24 byte item on stack + PUSH25, ///< place 25 byte item on stack + PUSH26, ///< place 26 byte item on stack + PUSH27, ///< place 27 byte item on stack + PUSH28, ///< place 28 byte item on stack + PUSH29, ///< place 29 byte item on stack + PUSH30, ///< place 30 byte item on stack + PUSH31, ///< place 31 byte item on stack + PUSH32, ///< place 32 byte item on stack + + DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack + DUP2, ///< copies the second highest item in the stack to the top of the stack + DUP3, ///< copies the third highest item in the stack to the top of the stack + DUP4, ///< copies the 4th highest item in the stack to the top of the stack + DUP5, ///< copies the 5th highest item in the stack to the top of the stack + DUP6, ///< copies the 6th highest item in the stack to the top of the stack + DUP7, ///< copies the 7th highest item in the stack to the top of the stack + DUP8, ///< copies the 8th highest item in the stack to the top of the stack + DUP9, ///< copies the 9th highest item in the stack to the top of the stack + DUP10, ///< copies the 10th highest item in the stack to the top of the stack + DUP11, ///< copies the 11th highest item in the stack to the top of the stack + DUP12, ///< copies the 12th highest item in the stack to the top of the stack + DUP13, ///< copies the 13th highest item in the stack to the top of the stack + DUP14, ///< copies the 14th highest item in the stack to the top of the stack + DUP15, ///< copies the 15th highest item in the stack to the top of the stack + DUP16, ///< copies the 16th highest item in the stack to the top of the stack + + SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack + SWAP2, ///< swaps the highest and third highest value on the stack + SWAP3, ///< swaps the highest and 4th highest value on the stack + SWAP4, ///< swaps the highest and 5th highest value on the stack + SWAP5, ///< swaps the highest and 6th highest value on the stack + SWAP6, ///< swaps the highest and 7th highest value on the stack + SWAP7, ///< swaps the highest and 8th highest value on the stack + SWAP8, ///< swaps the highest and 9th highest value on the stack + SWAP9, ///< swaps the highest and 10th highest value on the stack + SWAP10, ///< swaps the highest and 11th highest value on the stack + SWAP11, ///< swaps the highest and 12th highest value on the stack + SWAP12, ///< swaps the highest and 13th highest value on the stack + SWAP13, ///< swaps the highest and 14th highest value on the stack + SWAP14, ///< swaps the highest and 15th highest value on the stack + SWAP15, ///< swaps the highest and 16th highest value on the stack + SWAP16, ///< swaps the highest and 17th highest value on the stack + + LOG0 = 0xa0, ///< Makes a log entry; no topics. + LOG1, ///< Makes a log entry; 1 topic. + LOG2, ///< Makes a log entry; 2 topics. + LOG3, ///< Makes a log entry; 3 topics. + LOG4, ///< Makes a log entry; 4 topics. + + CREATE = 0xf0, ///< create a new account with associated code + CALL, ///< message-call into an account + CALLCODE, ///< message-call with another account's code only + RETURN, ///< halt execution returning output data + SUICIDE = 0xff ///< halt execution and register account for later deletion +}; + +/// Reads PUSH data from pointed fragment of bytecode and constructs number out of it +/// Reading out of bytecode means reading 0 +/// @param _curr is updated and points the last real byte read +llvm::APInt readPushData(code_iterator& _curr, code_iterator _end); + +/// Skips PUSH data in pointed fragment of bytecode. +/// @param _curr is updated and points the last real byte skipped +void skipPushData(code_iterator& _curr, code_iterator _end); + +#define ANY_PUSH 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 + +#define ANY_DUP DUP1: \ + case Instruction::DUP2: \ + case Instruction::DUP3: \ + case Instruction::DUP4: \ + case Instruction::DUP5: \ + case Instruction::DUP6: \ + case Instruction::DUP7: \ + case Instruction::DUP8: \ + case Instruction::DUP9: \ + case Instruction::DUP10: \ + case Instruction::DUP11: \ + case Instruction::DUP12: \ + case Instruction::DUP13: \ + case Instruction::DUP14: \ + case Instruction::DUP15: \ + case Instruction::DUP16 + +#define ANY_SWAP SWAP1: \ + case Instruction::SWAP2: \ + case Instruction::SWAP3: \ + case Instruction::SWAP4: \ + case Instruction::SWAP5: \ + case Instruction::SWAP6: \ + case Instruction::SWAP7: \ + case Instruction::SWAP8: \ + case Instruction::SWAP9: \ + case Instruction::SWAP10: \ + case Instruction::SWAP11: \ + case Instruction::SWAP12: \ + case Instruction::SWAP13: \ + case Instruction::SWAP14: \ + case Instruction::SWAP15: \ + case Instruction::SWAP16 + +} +} +} diff --git a/evmjit/libevmjit/Memory.cpp b/evmjit/libevmjit/Memory.cpp new file mode 100644 index 000000000..647c5f26a --- /dev/null +++ b/evmjit/libevmjit/Memory.cpp @@ -0,0 +1,260 @@ +#include "Memory.h" + +#include "preprocessor/llvm_includes_start.h" +#include +#include "preprocessor/llvm_includes_end.h" + +#include "Type.h" +#include "Runtime.h" +#include "GasMeter.h" +#include "Endianness.h" +#include "RuntimeManager.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): + RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed + m_gasMeter(_gasMeter) +{} + +llvm::Function* Memory::getRequireFunc() +{ + auto& func = m_require; + if (!func) + { + llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; + func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); + auto rt = func->arg_begin(); + rt->setName("rt"); + auto offset = rt->getNextNode(); + offset->setName("offset"); + auto size = offset->getNextNode(); + size->setName("size"); + + llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr}; + auto resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule()); + llvm::AttrBuilder attrBuilder; + attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly); + resize->setAttributes(llvm::AttributeSet::get(resize->getContext(), 1, attrBuilder)); + + auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); + auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); + auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); + auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); + + InsertPointGuard guard(m_builder); // Restores insert point at function exit + + // BB "Pre": Ignore checks with size 0 + m_builder.SetInsertPoint(preBB); + auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0)); + m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB); + + // BB "Check" + m_builder.SetInsertPoint(checkBB); + auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word); + auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); + auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); + auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); + auto rtPtr = getRuntimeManager().getRuntimePtr(); + auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); + auto currSize = m_builder.CreateLoad(sizePtr, "currSize"); + auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall"); + auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); + m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights? + + // BB "Resize" + m_builder.SetInsertPoint(resizeBB); + // Check gas first + uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res"); + auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0); + auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2"); + auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow"); + wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired); + wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq"); + 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 newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); + m_gasMeter.countMemory(newWords); + // Resize + m_builder.CreateStore(sizeRequired, sizePtr); + auto newData = m_builder.CreateCall2(resize, rt, sizePtr, "newData"); + auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); + m_builder.CreateStore(newData, dataPtr); + m_builder.CreateBr(returnBB); + + // BB "Return" + m_builder.SetInsertPoint(returnBB); + m_builder.CreateRetVoid(); + } + return func; +} + +llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) +{ + auto isWord = _valueType == Type::Word; + + llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType}; + llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word}; + auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; + auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false); + auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); + + InsertPointGuard guard(m_builder); // Restores insert point at function exit + + m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); + auto rt = func->arg_begin(); + rt->setName("rt"); + auto index = rt->getNextNode(); + index->setName("index"); + + auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; + this->require(index, Constant::get(valueSize)); + auto ptr = getBytePtr(index); + if (isWord) + ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); + if (_isStore) + { + llvm::Value* value = index->getNextNode(); + value->setName("value"); + if (isWord) + value = Endianness::toBE(m_builder, value); + m_builder.CreateStore(value, ptr); + m_builder.CreateRetVoid(); + } + else + { + llvm::Value* ret = m_builder.CreateLoad(ptr); + ret = Endianness::toNative(m_builder, ret); + m_builder.CreateRet(ret); + } + + return func; +} + +llvm::Function* Memory::getLoadWordFunc() +{ + auto& func = m_loadWord; + if (!func) + func = createFunc(false, Type::Word, m_gasMeter); + return func; +} + +llvm::Function* Memory::getStoreWordFunc() +{ + auto& func = m_storeWord; + if (!func) + func = createFunc(true, Type::Word, m_gasMeter); + return func; +} + +llvm::Function* Memory::getStoreByteFunc() +{ + auto& func = m_storeByte; + if (!func) + func = createFunc(true, Type::Byte, m_gasMeter); + return func; +} + + +llvm::Value* Memory::loadWord(llvm::Value* _addr) +{ + return createCall(getLoadWordFunc(), {getRuntimeManager().getRuntimePtr(), _addr}); +} + +void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) +{ + createCall(getStoreWordFunc(), {getRuntimeManager().getRuntimePtr(), _addr, _word}); +} + +void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) +{ + auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); + createCall(getStoreByteFunc(), {getRuntimeManager().getRuntimePtr(), _addr, byte}); +} + +llvm::Value* Memory::getData() +{ + auto rtPtr = getRuntimeManager().getRuntimePtr(); + auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); + return m_builder.CreateLoad(dataPtr, "data"); +} + +llvm::Value* Memory::getSize() +{ + auto rtPtr = getRuntimeManager().getRuntimePtr(); + auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); + return m_builder.CreateLoad(sizePtr, "size"); +} + +llvm::Value* Memory::getBytePtr(llvm::Value* _index) +{ + auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 + return m_builder.CreateGEP(getData(), idx, "ptr"); +} + +void Memory::require(llvm::Value* _offset, llvm::Value* _size) +{ + if (auto constant = llvm::dyn_cast(_size)) + { + if (!constant->getValue()) + return; + } + createCall(getRequireFunc(), {getRuntimeManager().getRuntimePtr(), _offset, _size}); +} + +void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, + llvm::Value* _destMemIdx, llvm::Value* _reqBytes) +{ + require(_destMemIdx, _reqBytes); + + // Additional copy cost + // TODO: This round ups to 32 happens in many places + auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Gas); + auto copyWords = m_builder.CreateUDiv(m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32)); + m_gasMeter.countCopy(copyWords); + + // Algorithm: + // isOutsideData = idx256 >= size256 + // idx64 = trunc idx256 + // size64 = trunc size256 + // dataLeftSize = size64 - idx64 // safe if not isOutsideData + // reqBytes64 = trunc _reqBytes // require() handles large values + // bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min + // bytesToCopy = select(isOutsideData, 0, bytesToCopy0) + + auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); + auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size); + auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size); + auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); + auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize); + auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes); + auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner); + + auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); + auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64 + auto dst = m_builder.CreateGEP(getData(), dstIdx, "dst"); + m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); +} + +} +} +} + + +extern "C" +{ + using namespace dev::eth::jit; + + EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR + { + auto size = _size->a; // Trunc to 64-bit + auto& memory = _rt->getMemory(); + memory.resize(size); + return memory.data(); + } +} diff --git a/evmjit/libevmjit/Memory.h b/evmjit/libevmjit/Memory.h new file mode 100644 index 000000000..e8edce735 --- /dev/null +++ b/evmjit/libevmjit/Memory.h @@ -0,0 +1,49 @@ +#pragma once + +#include "CompilerHelper.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ +class GasMeter; + +class Memory : public RuntimeHelper +{ +public: + Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter); + + llvm::Value* loadWord(llvm::Value* _addr); + void storeWord(llvm::Value* _addr, llvm::Value* _word); + void storeByte(llvm::Value* _addr, llvm::Value* _byte); + llvm::Value* getData(); + llvm::Value* getSize(); + llvm::Value* getBytePtr(llvm::Value* _index); + void copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIndex, + llvm::Value* _destMemIdx, llvm::Value* _byteCount); + + /// Requires the amount of memory to for data defined by offset and size. And counts gas fee for that memory. + void require(llvm::Value* _offset, llvm::Value* _size); + +private: + GasMeter& m_gasMeter; + + llvm::Function* createFunc(bool _isStore, llvm::Type* _type, GasMeter& _gasMeter); + + llvm::Function* getRequireFunc(); + llvm::Function* getLoadWordFunc(); + llvm::Function* getStoreWordFunc(); + llvm::Function* getStoreByteFunc(); + + llvm::Function* m_require = nullptr; + llvm::Function* m_loadWord = nullptr; + llvm::Function* m_storeWord = nullptr; + llvm::Function* m_storeByte = nullptr; +}; + +} +} +} + diff --git a/evmjit/libevmjit/Runtime.cpp b/evmjit/libevmjit/Runtime.cpp new file mode 100644 index 000000000..69937368c --- /dev/null +++ b/evmjit/libevmjit/Runtime.cpp @@ -0,0 +1,34 @@ +#include "Runtime.h" + +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Runtime::Runtime(RuntimeData* _data, Env* _env) : + m_data(*_data), + m_env(*_env) +{} + +bytes_ref Runtime::getReturnData() const +{ + auto data = m_data.callData; + auto size = static_cast(m_data.callDataSize); + + if (data < m_memory.data() || data >= m_memory.data() + m_memory.size() || size == 0) + { + assert(size == 0); // data can be an invalid pointer only if size is 0 + m_data.callData = nullptr; + return {}; + } + + return bytes_ref{data, size}; +} + +} +} +} diff --git a/evmjit/libevmjit/Runtime.h b/evmjit/libevmjit/Runtime.h new file mode 100644 index 000000000..82be4a0c8 --- /dev/null +++ b/evmjit/libevmjit/Runtime.h @@ -0,0 +1,40 @@ +#pragma once + +#include "RuntimeData.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +using StackImpl = std::vector; +using MemoryImpl = bytes; + +class Runtime +{ +public: + Runtime(RuntimeData* _data, Env* _env); + + Runtime(const Runtime&) = delete; + Runtime& operator=(const Runtime&) = delete; + + StackImpl& getStack() { return m_stack; } + MemoryImpl& getMemory() { return m_memory; } + + bytes_ref getReturnData() const; + +private: + RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract. + Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract. + void* m_currJmpBuf = nullptr; ///< Pointer to jump buffer. Expected by compiled contract. + byte* m_memoryData = nullptr; + i256 m_memorySize; + StackImpl m_stack; + MemoryImpl m_memory; +}; + +} +} +} diff --git a/evmjit/libevmjit/RuntimeData.h b/evmjit/libevmjit/RuntimeData.h new file mode 100644 index 000000000..cc081cc58 --- /dev/null +++ b/evmjit/libevmjit/RuntimeData.h @@ -0,0 +1,60 @@ +#pragma once + +#include "Common.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +struct RuntimeData +{ + enum Index + { + Gas, + GasPrice, + CallData, + CallDataSize, + Address, + Caller, + Origin, + CallValue, + CoinBase, + Difficulty, + GasLimit, + Number, + Timestamp, + Code, + CodeSize, + + SuicideDestAddress = Address, ///< Suicide balance destination address + ReturnData = CallData, ///< Return data pointer (set only in case of RETURN) + ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN) + }; + + int64_t gas = 0; + int64_t gasPrice = 0; + byte const* callData = nullptr; + uint64_t callDataSize = 0; + i256 address; + i256 caller; + i256 origin; + i256 callValue; + i256 coinBase; + i256 difficulty; + i256 gasLimit; + uint64_t number = 0; + int64_t timestamp = 0; + byte const* code = nullptr; + uint64_t codeSize = 0; + i256 codeHash; +}; + +/// VM Environment (ExtVM) opaque type +struct Env; + +} +} +} diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp new file mode 100644 index 000000000..0b5762fa2 --- /dev/null +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -0,0 +1,242 @@ +#include "RuntimeManager.h" + +#include "preprocessor/llvm_includes_start.h" +#include +#include "preprocessor/llvm_includes_end.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +llvm::StructType* RuntimeManager::getRuntimeDataType() +{ + static llvm::StructType* type = nullptr; + if (!type) + { + llvm::Type* elems[] = + { + Type::Size, // gas + Type::Size, // gasPrice + Type::BytePtr, // callData + Type::Size, // callDataSize + Type::Word, // address + Type::Word, // caller + Type::Word, // origin + Type::Word, // callValue + Type::Word, // coinBase + Type::Word, // difficulty + Type::Word, // gasLimit + Type::Size, // blockNumber + Type::Size, // blockTimestamp + Type::BytePtr, // code + Type::Size, // codeSize + }; + type = llvm::StructType::create(elems, "RuntimeData"); + } + return type; +} + +llvm::StructType* RuntimeManager::getRuntimeType() +{ + static llvm::StructType* type = nullptr; + if (!type) + { + llvm::Type* elems[] = + { + Type::RuntimeDataPtr, // data + Type::EnvPtr, // Env* + Type::BytePtr, // jmpbuf + Type::BytePtr, // memory data + Type::Word, // memory size + }; + type = llvm::StructType::create(elems, "Runtime"); + } + return type; +} + +namespace +{ +llvm::Twine getName(RuntimeData::Index _index) +{ + switch (_index) + { + default: return "data"; + case RuntimeData::Address: return "address"; + case RuntimeData::Caller: return "caller"; + case RuntimeData::Origin: return "origin"; + case RuntimeData::CallValue: return "callvalue"; + case RuntimeData::GasPrice: return "gasprice"; + case RuntimeData::CoinBase: return "coinbase"; + case RuntimeData::Difficulty: return "difficulty"; + case RuntimeData::GasLimit: return "gaslimit"; + case RuntimeData::CallData: return "callData"; + case RuntimeData::Code: return "code"; + case RuntimeData::CodeSize: return "code"; + case RuntimeData::CallDataSize: return "callDataSize"; + case RuntimeData::Gas: return "gas"; + case RuntimeData::Number: return "number"; + case RuntimeData::Timestamp: return "timestamp"; + } +} +} + +RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf, code_iterator _codeBegin, code_iterator _codeEnd): + CompilerHelper(_builder), + m_jmpBuf(_jmpBuf), + m_codeBegin(_codeBegin), + m_codeEnd(_codeEnd) +{ + m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); + + // save jmpBuf to be used in helper functions + auto ptr = m_builder.CreateStructGEP(getRuntimePtr(), 2); + m_builder.CreateStore(m_jmpBuf, ptr, "jmpBufExt"); + + // Unpack data + auto rtPtr = getRuntimePtr(); + m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); + assert(m_dataPtr->getType() == Type::RuntimeDataPtr); + m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env"); + assert(m_envPtr->getType() == Type::EnvPtr); +} + +llvm::Value* RuntimeManager::getRuntimePtr() +{ + // Expect first argument of a function to be a pointer to Runtime + auto func = m_builder.GetInsertBlock()->getParent(); + auto rtPtr = &func->getArgumentList().front(); + assert(rtPtr->getType() == Type::RuntimePtr); + return rtPtr; +} + +llvm::Value* RuntimeManager::getDataPtr() +{ + if (getMainFunction()) + return m_dataPtr; + + auto rtPtr = getRuntimePtr(); + auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); + assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo()); + return dataPtr; +} + +llvm::Value* RuntimeManager::getEnvPtr() +{ + assert(getMainFunction()); // Available only in main function + return m_envPtr; +} + +llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) +{ + auto ptr = getBuilder().CreateStructGEP(getDataPtr(), _index); + assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType()); + return ptr; +} + +llvm::Value* RuntimeManager::get(RuntimeData::Index _index) +{ + return getBuilder().CreateLoad(getPtr(_index), getName(_index)); +} + +void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value) +{ + auto ptr = getPtr(_index); + assert(ptr->getType() == _value->getType()->getPointerTo()); + getBuilder().CreateStore(_value, ptr); +} + +void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size) +{ + auto memPtr = getBuilder().CreateStructGEP(getRuntimePtr(), 3); + auto mem = getBuilder().CreateLoad(memPtr, "memory"); + auto idx = m_builder.CreateTrunc(_offset, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 // TODO: Report bug & fix to LLVM + auto returnDataPtr = getBuilder().CreateGEP(mem, idx); + set(RuntimeData::ReturnData, returnDataPtr); + + auto size64 = getBuilder().CreateTrunc(_size, Type::Size); + set(RuntimeData::ReturnDataSize, size64); +} + +void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress) +{ + set(RuntimeData::SuicideDestAddress, _balanceAddress); +} + +void RuntimeManager::abort(llvm::Value* _jmpBuf) +{ + auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); + createCall(longjmp, {_jmpBuf}); +} + +llvm::Value* RuntimeManager::get(Instruction _inst) +{ + switch (_inst) + { + default: assert(false); return nullptr; + case Instruction::ADDRESS: return get(RuntimeData::Address); + case Instruction::CALLER: return get(RuntimeData::Caller); + case Instruction::ORIGIN: return get(RuntimeData::Origin); + case Instruction::CALLVALUE: return get(RuntimeData::CallValue); + case Instruction::GASPRICE: return get(RuntimeData::GasPrice); + case Instruction::COINBASE: return get(RuntimeData::CoinBase); + case Instruction::DIFFICULTY: return get(RuntimeData::Difficulty); + case Instruction::GASLIMIT: return get(RuntimeData::GasLimit); + case Instruction::NUMBER: return get(RuntimeData::Number); + case Instruction::TIMESTAMP: return get(RuntimeData::Timestamp); + } +} + +llvm::Value* RuntimeManager::getCallData() +{ + return get(RuntimeData::CallData); +} + +llvm::Value* RuntimeManager::getCode() +{ + // OPT Check what is faster + //return get(RuntimeData::Code); + return m_builder.CreateGlobalStringPtr({reinterpret_cast(m_codeBegin), static_cast(m_codeEnd - m_codeBegin)}, "code"); +} + +llvm::Value* RuntimeManager::getCodeSize() +{ + return Constant::get(m_codeEnd - m_codeBegin); +} + +llvm::Value* RuntimeManager::getCallDataSize() +{ + auto value = get(RuntimeData::CallDataSize); + assert(value->getType() == Type::Size); + return getBuilder().CreateZExt(value, Type::Word); +} + +llvm::Value* RuntimeManager::getJmpBufExt() +{ + auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 2); + return getBuilder().CreateLoad(ptr, "jmpBufExt"); +} + +llvm::Value* RuntimeManager::getGas() +{ + auto gas = get(RuntimeData::Gas); + assert(gas->getType() == Type::Gas); + return gas; +} + +llvm::Value* RuntimeManager::getGasPtr() +{ + return getPtr(RuntimeData::Gas); +} + +void RuntimeManager::setGas(llvm::Value* _gas) +{ + assert(_gas->getType() == Type::Gas); + set(RuntimeData::Gas, _gas); +} + +} +} +} diff --git a/evmjit/libevmjit/RuntimeManager.h b/evmjit/libevmjit/RuntimeManager.h new file mode 100644 index 000000000..30c69ec88 --- /dev/null +++ b/evmjit/libevmjit/RuntimeManager.h @@ -0,0 +1,60 @@ +#pragma once + +#include "CompilerHelper.h" +#include "Type.h" +#include "RuntimeData.h" +#include "Instruction.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +class RuntimeManager: public CompilerHelper +{ +public: + RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf, code_iterator _codeBegin, code_iterator _codeEnd); + + llvm::Value* getRuntimePtr(); + llvm::Value* getDataPtr(); + llvm::Value* getEnvPtr(); + + llvm::Value* get(RuntimeData::Index _index); + llvm::Value* get(Instruction _inst); + llvm::Value* getGas(); + llvm::Value* getGasPtr(); + llvm::Value* getCallData(); + llvm::Value* getCode(); + llvm::Value* getCodeSize(); + llvm::Value* getCallDataSize(); + llvm::Value* getJmpBuf() { return m_jmpBuf; } + void setGas(llvm::Value* _gas); + + void registerReturnData(llvm::Value* _index, llvm::Value* _size); + void registerSuicide(llvm::Value* _balanceAddress); + + void abort(llvm::Value* _jmpBuf); + void abort() { abort(getJmpBufExt()); } + + static llvm::StructType* getRuntimeType(); + static llvm::StructType* getRuntimeDataType(); + +private: + llvm::Value* getPtr(RuntimeData::Index _index); + void set(RuntimeData::Index _index, llvm::Value* _value); + llvm::Value* getJmpBufExt(); + + llvm::Function* m_longjmp = nullptr; + llvm::Value* const m_jmpBuf; + llvm::Value* m_dataPtr = nullptr; + llvm::Value* m_envPtr = nullptr; + + code_iterator m_codeBegin = {}; + code_iterator m_codeEnd = {}; +}; + +} +} +} diff --git a/evmjit/libevmjit/Stack.cpp b/evmjit/libevmjit/Stack.cpp new file mode 100644 index 000000000..81a954991 --- /dev/null +++ b/evmjit/libevmjit/Stack.cpp @@ -0,0 +1,200 @@ +#include "Stack.h" + +#include "preprocessor/llvm_includes_start.h" +#include +#include "preprocessor/llvm_includes_end.h" + +#include "RuntimeManager.h" +#include "Runtime.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager): + CompilerHelper(_builder), + m_runtimeManager(_runtimeManager) +{ + m_arg = m_builder.CreateAlloca(Type::Word, nullptr, "stack.arg"); + + using namespace llvm; + using Linkage = GlobalValue::LinkageTypes; + + auto module = getModule(); + + llvm::Type* pushArgTypes[] = {Type::RuntimePtr, Type::WordPtr}; + m_push = Function::Create(FunctionType::get(Type::Void, pushArgTypes, false), Linkage::ExternalLinkage, "stack_push", module); + + llvm::Type* getSetArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr}; + m_set = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_set", module); +} + +llvm::Function* Stack::getPopFunc() +{ + auto& func = m_pop; + if (!func) + { + llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr}; + func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.pop", getModule()); + llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size}; + auto extPopFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_pop", getModule()); + + auto rt = &func->getArgumentList().front(); + rt->setName("rt"); + auto index = rt->getNextNode(); + index->setName("index"); + auto jmpBuf = index->getNextNode(); + jmpBuf->setName("jmpBuf"); + + InsertPointGuard guard{m_builder}; + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func); + auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func); + auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func); + + m_builder.SetInsertPoint(entryBB); + auto ok = createCall(extPopFunc, {rt, index}); + m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight + + m_builder.SetInsertPoint(underflowBB); + m_runtimeManager.abort(jmpBuf); + m_builder.CreateUnreachable(); + + m_builder.SetInsertPoint(returnBB); + m_builder.CreateRetVoid(); + } + return func; +} + +llvm::Function* Stack::getGetFunc() +{ + auto& func = m_get; + if (!func) + { + llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr}; + func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), llvm::Function::ExternalLinkage, "stack.get", getModule()); + llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size}; + auto extGetFunc = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_get", getModule()); + + auto rt = &func->getArgumentList().front(); + rt->setName("rt"); + auto index = rt->getNextNode(); + index->setName("index"); + auto jmpBuf = index->getNextNode(); + jmpBuf->setName("jmpBuf"); + + InsertPointGuard guard{m_builder}; + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func); + auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func); + auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func); + + m_builder.SetInsertPoint(entryBB); + auto valuePtr = createCall(extGetFunc, {rt, index}); + auto ok = m_builder.CreateICmpNE(valuePtr, llvm::ConstantPointerNull::get(Type::WordPtr)); + m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight + + m_builder.SetInsertPoint(underflowBB); + m_runtimeManager.abort(jmpBuf); + m_builder.CreateUnreachable(); + + m_builder.SetInsertPoint(returnBB); + m_builder.CreateRet(valuePtr); + } + return func; +} + +llvm::Value* Stack::get(size_t _index) +{ + auto valuePtr = createCall(getGetFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_index), m_runtimeManager.getJmpBuf()}); + return m_builder.CreateLoad(valuePtr); +} + +void Stack::set(size_t _index, llvm::Value* _value) +{ + m_builder.CreateStore(_value, m_arg); + m_builder.CreateCall3(m_set, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _index, false), m_arg); +} + +void Stack::pop(size_t _count) +{ + createCall(getPopFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_count), m_runtimeManager.getJmpBuf()}); +} + +void Stack::push(llvm::Value* _value) +{ + m_builder.CreateStore(_value, m_arg); + m_builder.CreateCall2(m_push, m_runtimeManager.getRuntimePtr(), m_arg); +} + + +size_t Stack::maxStackSize = 0; + +} +} +} + +extern "C" +{ + using namespace dev::eth::jit; + + EXPORT bool stack_pop(Runtime* _rt, uint64_t _count) + { + auto& stack = _rt->getStack(); + if (stack.size() < _count) + return false; + + stack.erase(stack.end() - _count, stack.end()); + return true; + } + + EXPORT void stack_push(Runtime* _rt, i256 const* _word) + { + auto& stack = _rt->getStack(); + stack.push_back(*_word); + + if (stack.size() > Stack::maxStackSize) + Stack::maxStackSize = stack.size(); + } + + EXPORT i256* stack_get(Runtime* _rt, uint64_t _index) + { + auto& stack = _rt->getStack(); + return _index < stack.size() ? &*(stack.rbegin() + _index) : nullptr; + } + + EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256 const* _word) + { + auto& stack = _rt->getStack(); + assert(_index < stack.size()); + if (_index >= stack.size()) + return; + + *(stack.rbegin() + _index) = *_word; + } + + EXPORT void ext_calldataload(RuntimeData* _rtData, i256* _index, byte* o_value) + { + // It asumes all indexes are less than 2^64 + + auto index = _index->a; + if (_index->b || _index->c || _index->d) // if bigger that 2^64 + index = std::numeric_limits::max(); // set max to fill with 0 leter + + auto data = _rtData->callData; + auto size = _rtData->callDataSize; + for (auto i = 0; i < 32; ++i) + { + if (index < size) + { + o_value[i] = data[index]; + ++index; // increment only if in range + } + else + o_value[i] = 0; + } + } + +} // extern "C" + diff --git a/evmjit/libevmjit/Stack.h b/evmjit/libevmjit/Stack.h new file mode 100644 index 000000000..4b6fa374f --- /dev/null +++ b/evmjit/libevmjit/Stack.h @@ -0,0 +1,44 @@ +#pragma once + +#include "CompilerHelper.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ +class RuntimeManager; + +class Stack : public CompilerHelper +{ +public: + Stack(llvm::IRBuilder<>& builder, RuntimeManager& runtimeManager); + + llvm::Value* get(size_t _index); + void set(size_t _index, llvm::Value* _value); + void pop(size_t _count); + void push(llvm::Value* _value); + + static size_t maxStackSize; + +private: + llvm::Function* getPopFunc(); + llvm::Function* getGetFunc(); + + RuntimeManager& m_runtimeManager; + + llvm::Function* m_pop = nullptr; + llvm::Function* m_push; + llvm::Function* m_get = nullptr; + llvm::Function* m_set; + + llvm::Value* m_arg; +}; + + +} +} +} + + diff --git a/evmjit/libevmjit/Type.cpp b/evmjit/libevmjit/Type.cpp new file mode 100644 index 000000000..8e2bc13fc --- /dev/null +++ b/evmjit/libevmjit/Type.cpp @@ -0,0 +1,70 @@ +#include "Type.h" +#include "RuntimeManager.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +llvm::IntegerType* Type::Word; +llvm::PointerType* Type::WordPtr; +llvm::IntegerType* Type::lowPrecision; +llvm::IntegerType* Type::Bool; +llvm::IntegerType* Type::Size; +llvm::IntegerType* Type::Gas; +llvm::PointerType* Type::GasPtr; +llvm::IntegerType* Type::Byte; +llvm::PointerType* Type::BytePtr; +llvm::Type* Type::Void; +llvm::IntegerType* Type::MainReturn; +llvm::PointerType* Type::EnvPtr; +llvm::PointerType* Type::RuntimeDataPtr; +llvm::PointerType* Type::RuntimePtr; +llvm::ConstantInt* Constant::gasMax; + +void Type::init(llvm::LLVMContext& _context) +{ + if (!Word) // Do init only once + { + Word = llvm::Type::getIntNTy(_context, 256); + WordPtr = Word->getPointerTo(); + lowPrecision = llvm::Type::getInt64Ty(_context); + // TODO: Size should be architecture-dependent + Bool = llvm::Type::getInt1Ty(_context); + Size = llvm::Type::getInt64Ty(_context); + Gas = Size; + GasPtr = Gas->getPointerTo(); + Byte = llvm::Type::getInt8Ty(_context); + BytePtr = Byte->getPointerTo(); + Void = llvm::Type::getVoidTy(_context); + MainReturn = llvm::Type::getInt32Ty(_context); + + EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo(); + RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo(); + RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo(); + + Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits::max()); + } +} + +llvm::ConstantInt* Constant::get(int64_t _n) +{ + return llvm::ConstantInt::getSigned(Type::Word, _n); +} + +llvm::ConstantInt* Constant::get(llvm::APInt const& _n) +{ + return llvm::ConstantInt::get(Type::Word->getContext(), _n); +} + +llvm::ConstantInt* Constant::get(ReturnCode _returnCode) +{ + return llvm::ConstantInt::get(Type::MainReturn, static_cast(_returnCode)); +} + +} +} +} + diff --git a/evmjit/libevmjit/Type.h b/evmjit/libevmjit/Type.h new file mode 100644 index 000000000..b8a4a09eb --- /dev/null +++ b/evmjit/libevmjit/Type.h @@ -0,0 +1,60 @@ +#pragma once + +#include "preprocessor/llvm_includes_start.h" +#include +#include +#include "preprocessor/llvm_includes_end.h" + +#include "Common.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +struct Type +{ + static llvm::IntegerType* Word; + static llvm::PointerType* WordPtr; + + /// Type for doing low precision arithmetics where 256-bit precision is not supported by native target + /// @TODO: Use 64-bit for now. In 128-bit compiler-rt library functions are required + static llvm::IntegerType* lowPrecision; + + static llvm::IntegerType* Bool; + static llvm::IntegerType* Size; + static llvm::IntegerType* Gas; + static llvm::PointerType* GasPtr; + + static llvm::IntegerType* Byte; + static llvm::PointerType* BytePtr; + + static llvm::Type* Void; + + /// Main function return type + static llvm::IntegerType* MainReturn; + + static llvm::PointerType* EnvPtr; + static llvm::PointerType* RuntimeDataPtr; + static llvm::PointerType* RuntimePtr; + + static void init(llvm::LLVMContext& _context); +}; + +struct Constant +{ + static llvm::ConstantInt* gasMax; + + /// Returns word-size constant + static llvm::ConstantInt* get(int64_t _n); + static llvm::ConstantInt* get(llvm::APInt const& _n); + + static llvm::ConstantInt* get(ReturnCode _returnCode); +}; + +} +} +} + diff --git a/evmjit/libevmjit/Utils.cpp b/evmjit/libevmjit/Utils.cpp new file mode 100644 index 000000000..bf3bf93b3 --- /dev/null +++ b/evmjit/libevmjit/Utils.cpp @@ -0,0 +1,13 @@ +#include "Utils.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + + +} +} +} diff --git a/evmjit/libevmjit/Utils.h b/evmjit/libevmjit/Utils.h new file mode 100644 index 000000000..aad975f5b --- /dev/null +++ b/evmjit/libevmjit/Utils.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include "Common.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +struct JIT: public NoteChannel { static const char* name() { return "JIT"; } }; + +//#define clog(CHANNEL) std::cerr +#define clog(CHANNEL) std::ostream(nullptr) + +// The same as assert, but expression is always evaluated and result returned +#define CHECK(expr) (assert(expr), expr) + +} +} +} diff --git a/evmjit/libevmjit/interface.cpp b/evmjit/libevmjit/interface.cpp new file mode 100644 index 000000000..645f3d150 --- /dev/null +++ b/evmjit/libevmjit/interface.cpp @@ -0,0 +1,40 @@ +#include "ExecutionEngine.h" + +extern "C" +{ + +using namespace dev::eth::jit; + +#ifdef _MSC_VER +#define _ALLOW_KEYWORD_MACROS +#define noexcept throw() +#endif + +EXPORT void* evmjit_create() noexcept +{ + // TODO: Make sure ExecutionEngine constructor does not throw + return new(std::nothrow) ExecutionEngine; +} + +EXPORT void evmjit_destroy(ExecutionEngine* _engine) noexcept +{ + delete _engine; +} + +EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept +{ + if (!_engine || !_data) + return static_cast(ReturnCode::UnexpectedException); + + try + { + auto returnCode = _engine->run(_data, _env); + return static_cast(returnCode); + } + catch(...) + { + return static_cast(ReturnCode::UnexpectedException); + } +} + +} diff --git a/evmjit/libevmjit/interface.h b/evmjit/libevmjit/interface.h new file mode 100644 index 000000000..4f4d56610 --- /dev/null +++ b/evmjit/libevmjit/interface.h @@ -0,0 +1,13 @@ + +#ifdef __cplusplus +extern "C" { +#endif + +void* evmjit_create(); +int evmjit_run(void* _jit, void* _data, void* _env); +void evmjit_destroy(void* _jit); + + +#ifdef __cplusplus +} +#endif diff --git a/evmjit/libevmjit/preprocessor/llvm_includes_end.h b/evmjit/libevmjit/preprocessor/llvm_includes_end.h new file mode 100644 index 000000000..023c8021e --- /dev/null +++ b/evmjit/libevmjit/preprocessor/llvm_includes_end.h @@ -0,0 +1,5 @@ +#if defined(_MSC_VER) + #pragma warning(pop) +#else + #pragma GCC diagnostic pop +#endif diff --git a/evmjit/libevmjit/preprocessor/llvm_includes_start.h b/evmjit/libevmjit/preprocessor/llvm_includes_start.h new file mode 100644 index 000000000..bf34ade99 --- /dev/null +++ b/evmjit/libevmjit/preprocessor/llvm_includes_start.h @@ -0,0 +1,8 @@ +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable: 4267 4244 4800) +#else + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-parameter" + #pragma GCC diagnostic ignored "-Wconversion" +#endif