├── .clang-format ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── appveyor.yml ├── circle.yml ├── cmake └── ProjectLLVM.cmake ├── docker ├── Dockerfile └── test.sh ├── include └── evmjit.h ├── libevmjit ├── Arith256.cpp ├── Arith256.h ├── Array.cpp ├── Array.h ├── BasicBlock.cpp ├── BasicBlock.h ├── BuildInfo.h.in ├── CMakeLists.txt ├── Cache.cpp ├── Cache.h ├── Common.h ├── Compiler.cpp ├── Compiler.h ├── CompilerHelper.cpp ├── CompilerHelper.h ├── Endianness.cpp ├── Endianness.h ├── ExecStats.cpp ├── ExecStats.h ├── Ext.cpp ├── Ext.h ├── GasMeter.cpp ├── GasMeter.h ├── Instruction.cpp ├── Instruction.h ├── JIT.cpp ├── JIT.h ├── Memory.cpp ├── Memory.h ├── Optimizer.cpp ├── Optimizer.h ├── RuntimeManager.cpp ├── RuntimeManager.h ├── Type.cpp ├── Type.h ├── Utils.cpp ├── Utils.h └── preprocessor │ ├── llvm_includes_end.h │ └── llvm_includes_start.h ├── scripts ├── build.sh └── install_cmake.sh ├── tests ├── CMakeLists.txt └── test-evmjit-standalone.c └── wercker.yml /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Chromium 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: DontAlign 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: true 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: false 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: Inline 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: true 19 | AlwaysBreakTemplateDeclarations: true 20 | BinPackArguments: true 21 | BinPackParameters: true 22 | BreakBeforeBinaryOperators: None 23 | BreakBeforeBraces: Custom 24 | BraceWrapping: 25 | AfterClass: true 26 | AfterControlStatement: true 27 | AfterEnum: true 28 | AfterFunction: true 29 | AfterNamespace: true 30 | AfterObjCDeclaration: true 31 | AfterStruct: true 32 | AfterUnion: true 33 | BeforeCatch: true 34 | BeforeElse: true 35 | IndentBraces: false 36 | SplitEmptyFunction: false 37 | BreakBeforeTernaryOperators: false 38 | BreakConstructorInitializers: BeforeColon 39 | ColumnLimit: 100 40 | CommentPragmas: '^ IWYU pragma:' 41 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 42 | ConstructorInitializerIndentWidth: 2 43 | ContinuationIndentWidth: 4 44 | Cpp11BracedListStyle: true 45 | DerivePointerAlignment: false 46 | DisableFormat: false 47 | ExperimentalAutoDetectBinPacking: false 48 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 49 | IncludeCategories: 50 | - Regex: '^".*' 51 | Priority: 1 52 | - Regex: '^' 55 | Priority: 2 56 | - Regex: '^<.*' 57 | Priority: 99 58 | - Regex: '.*' 59 | Priority: 4 60 | IndentCaseLabels: false 61 | IndentWidth: 4 62 | IndentWrappedFunctionNames: false 63 | KeepEmptyLinesAtTheStartOfBlocks: false 64 | MacroBlockBegin: '' 65 | MacroBlockEnd: '' 66 | MaxEmptyLinesToKeep: 2 67 | NamespaceIndentation: None 68 | ObjCBlockIndentWidth: 2 69 | ObjCSpaceAfterProperty: false 70 | ObjCSpaceBeforeProtocolList: false 71 | PenaltyBreakAssignment: 1 72 | PenaltyBreakBeforeFirstCallParameter: 1 73 | PenaltyBreakComment: 50 74 | PenaltyBreakFirstLessLess: 120 75 | PenaltyBreakString: 1000 76 | PenaltyExcessCharacter: 1000000 77 | PenaltyReturnTypeOnItsOwnLine: 200 78 | PointerAlignment: Left 79 | ReflowComments: true 80 | SortIncludes: true 81 | SpaceAfterCStyleCast: false 82 | SpaceBeforeAssignmentOperators: true 83 | SpaceBeforeParens: ControlStatements 84 | SpaceInEmptyParentheses: false 85 | SpacesBeforeTrailingComments: 2 86 | SpacesInAngles: false 87 | SpacesInContainerLiterals: true 88 | SpacesInCStyleCastParentheses: false 89 | SpacesInParentheses: false 90 | SpacesInSquareBrackets: false 91 | Standard: Auto 92 | TabWidth: 4 93 | UseTab: Never 94 | ... 95 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # CLion files 2 | /.idea/ 3 | 4 | # Convenient binary output and build dirs 5 | /bin/ 6 | /build/ 7 | /deps/ 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "evmc"] 2 | path = evmc 3 | url = https://github.com/ethereum/evmc 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | branches: 3 | only: 4 | - master 5 | - develop 6 | os: 7 | - linux 8 | - osx 9 | dist: trusty 10 | osx_image: xcode8.2 # OSX 10.12, cmake 3.5 11 | env: 12 | global: 13 | secure: "BMDgV5REY3QSGGtixMyqg8AU6iS5ABGWgrdH7IXyTuFISHjRGrATNXU+JDh4K7YjCuRaPTUj4YPyj2koDuvcYMXaf9w/1Ov6DGH/oHiKybF+D5MABoNMC9G7mhpyaXvCVxjxJmn0EZo2J/0bwTjBhv0tBsHed9opMD6J9sWv5oo=" 14 | matrix: 15 | - BUILD_TYPE=Release 16 | - BUILD_TYPE=Debug 17 | cache: 18 | ccache: true 19 | directories: 20 | - $TRAVIS_BUILD_DIR/deps 21 | 22 | script: 23 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then scripts/install_cmake.sh; fi 24 | - scripts/build.sh 25 | - cd build && ctest 26 | - cd .. 27 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.0) 2 | 3 | cmake_policy(SET CMP0042 OLD) # Fix MACOSX_RPATH. 4 | cmake_policy(SET CMP0048 NEW) # Allow VERSION argument in project(). 5 | if (POLICY CMP0054) 6 | cmake_policy(SET CMP0054 NEW) # No longer implicitly dereference variables. 7 | endif() 8 | 9 | set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo) 10 | 11 | project(EVMJIT VERSION 0.9.0.2 LANGUAGES CXX C) 12 | 13 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") 14 | 15 | message(STATUS "EVM JIT ${EVMJIT_VERSION_MAJOR}.${EVMJIT_VERSION_MINOR}.${EVMJIT_VERSION_PATCH}") 16 | 17 | if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64|AMD64") 18 | message(FATAL_ERROR "Target ${CMAKE_SYSTEM_PROCESSOR} not supported -- EVM JIT works only on x86_64 architecture") 19 | endif() 20 | 21 | option(EVMJIT_EXAMPLES "Generate build targets for the EVMJIT examples" OFF) 22 | option(EVMJIT_TESTS "Create targets for CTest" OFF) 23 | 24 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 25 | 26 | if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 27 | # Always use Release variant of C++ runtime. 28 | # We don't want to provide Debug variants of all dependencies. Some default 29 | # flags set by CMake must be tweaked. 30 | string(REPLACE "/MDd" "/MD" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) 31 | string(REPLACE "/D_DEBUG" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) 32 | string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) 33 | set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS OFF) 34 | else() 35 | set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}") 36 | endif() 37 | 38 | if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT SANITIZE) 39 | # Do not allow unresolved symbols in shared library (default on linux) 40 | # unless sanitizer is used (sanity checks produce unresolved function calls) 41 | set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") 42 | endif() 43 | 44 | include(ProjectLLVM) 45 | configure_llvm_project() 46 | 47 | add_subdirectory(evmc) 48 | 49 | add_subdirectory(libevmjit) 50 | 51 | if (EVMJIT_TESTS) 52 | enable_testing() 53 | add_subdirectory(tests) 54 | endif() 55 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Paweł Bylica 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Ethereum EVM JIT 2 | 3 | [![Join the chat at https://gitter.im/ethereum/evmjit](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/evmjit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | EVM JIT is a library for just-in-time compilation of Ethereum EVM code. 6 | It can be used to substitute classic interpreter-like EVM Virtual Machine in Ethereum client. 7 | 8 | ## Maintainers 9 | 10 | **NOTE: This project is not maintained. Do not use it for anything important.** 11 | 12 | Looking for maintainers! [Please state your interest here.](https://github.com/ethereum/evmjit/issues/184) 13 | 14 | ## Build 15 | 16 | The EVMJIT project uses **CMake** tool to configure the build and depends only on the LLVM library. 17 | LLVM installation is not needed, because CMake will download and build LLVM from source. 18 | However, LLVM requires **Python** interpreter to be built. 19 | 20 | ```sh 21 | git submodule update --init --recursive 22 | mkdir build 23 | cd build 24 | cmake .. 25 | cmake --build . --config RelWithDebInfo 26 | ``` 27 | 28 | ## Options 29 | 30 | Options to evmjit library can be passed by environmental variable, e.g. `EVMJIT="-help" testeth --jit`. 31 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | branches: 3 | only: 4 | - develop 5 | configuration: 6 | - Debug 7 | - Release 8 | cache: 9 | - deps -> cmake/ProjectLLVM.cmake 10 | before_build: | 11 | if not exist build mkdir build 12 | cd build 13 | cmake -G "Visual Studio 14 2015 Win64" .. 14 | build: 15 | project: c:/projects/evmjit/build/evmjit.sln 16 | parallel: true 17 | verbosity: minimal 18 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | defaults: 2 | 3 | git-update-submodules: &git-update-submodules 4 | run: 5 | name: "Update git submodules" 6 | command: git submodule update --init 7 | 8 | environment-info: &environment-info 9 | run: 10 | name: "Environment info" 11 | command: | 12 | echo CXX: $CXX 13 | $CXX --version 14 | 15 | install-dependencies: &install-dependencies 16 | run: 17 | name: "Install dependencies" 18 | command: | 19 | if [ $(uname) = Darwin ]; then 20 | HOMEBREW_NO_AUTO_UPDATE=1 brew install -q cmake ninja leveldb 21 | fi 22 | 23 | configure: &configure 24 | run: 25 | name: "Configure" 26 | # Build "out-of-source" to have better coverage report 27 | # (ninja is using relative paths otherwise). 28 | working_directory: ~/build 29 | command: > 30 | cmake ../project 31 | -DCMAKE_BUILD_TYPE=$BUILD_TYPE 32 | -DEVMJIT_TESTS=ON 33 | -DEVMJIT_EXAMPLES=ON 34 | $CMAKE_OPTIONS 35 | 36 | build: &build 37 | run: 38 | name: "Build" 39 | working_directory: ~/build 40 | command: cmake --build . -- -j $BUILD_PARALLEL_JOBS 41 | 42 | test: &test 43 | run: 44 | name: "Test" 45 | working_directory: ~/build 46 | command: | 47 | ctest --output-on-failure -j $TEST_PARALLEL_JOBS 48 | 49 | save-deps-cache: &save-deps-cache 50 | cache-save: 51 | name: "Save dependencies cache" 52 | key: &deps-cache-key deps-2-{{arch}}-{{checksum "cmake/ProjectLLVM.cmake"}} 53 | paths: 54 | - deps 55 | 56 | restore-deps-cache: &restore-deps-cache 57 | cache-restore: 58 | name: "Restore dependencies cache" 59 | key: *deps-cache-key 60 | 61 | default-steps: &default-steps 62 | - checkout 63 | - *git-update-submodules 64 | - *environment-info 65 | - *install-dependencies 66 | - *configure 67 | - *restore-deps-cache 68 | - *build 69 | - *save-deps-cache 70 | - *test 71 | 72 | 73 | version: 2 74 | jobs: 75 | 76 | linux-clang: 77 | environment: 78 | - CXX: clang++ 79 | - CC: clang 80 | - BUILD_PARALLEL_JOBS: 3 81 | - TEST_PARALLEL_JOBS: 1 82 | docker: 83 | - image: ethereum/cpp-build-env 84 | steps: *default-steps 85 | 86 | linux-gcc-debug: 87 | environment: 88 | - BUILD_TYPE: Debug 89 | - CXX: g++ 90 | - CC: gcc 91 | - CMAKE_OPTIONS: -DLLVM_DIR=/usr/lib/llvm-5.0/cmake 92 | - BUILD_PARALLEL_JOBS: 4 93 | - TEST_PARALLEL_JOBS: 1 94 | docker: 95 | - image: ethereum/cpp-build-env 96 | steps: *default-steps 97 | 98 | macos-xcode93: 99 | environment: 100 | - CXX: clang++ 101 | - BUILD_PARALLEL_JOBS: 4 102 | - TEST_PARALLEL_JOBS: 1 103 | macos: 104 | xcode: "9.3.0" 105 | steps: *default-steps 106 | 107 | workflows: 108 | version: 2 109 | cpp-ethereum: 110 | jobs: 111 | - macos-xcode93 112 | - linux-clang 113 | - linux-gcc-debug -------------------------------------------------------------------------------- /cmake/ProjectLLVM.cmake: -------------------------------------------------------------------------------- 1 | 2 | # Configures LLVM dependency 3 | # 4 | # This function handles everything needed to setup LLVM project. 5 | # By default it downloads and builds LLVM from source. 6 | # In case LLVM_DIR variable is set it tries to use the pointed pre-built 7 | # LLVM package. LLVM_DIR should point LLVM's shared cmake files to be used 8 | # by find_package(... CONFIG) function. 9 | # 10 | # Creates a target representing all required LLVM libraries and include path. 11 | function(configure_llvm_project) 12 | if (LLVM_DIR) 13 | find_package(LLVM REQUIRED CONFIG) 14 | llvm_map_components_to_libnames(LIBS mcjit ipo x86codegen) 15 | 16 | # To create a fake imported library later on we need to know the 17 | # location of some library 18 | list(GET LIBS 0 MAIN_LIB) 19 | get_property(CONFIGS TARGET ${MAIN_LIB} PROPERTY IMPORTED_CONFIGURATIONS) 20 | list(GET CONFIGS 0 CONFIG) # Just get the first one. Usually there is only one. 21 | if (CONFIG) 22 | get_property(MAIN_LIB TARGET ${MAIN_LIB} PROPERTY IMPORTED_LOCATION_${CONFIG}) 23 | else() 24 | set(CONFIG Unknown) 25 | get_property(MAIN_LIB TARGET ${MAIN_LIB} PROPERTY IMPORTED_LOCATION) 26 | endif() 27 | message(STATUS "LLVM ${LLVM_VERSION} (${CONFIG}; ${LLVM_ENABLE_ASSERTIONS}; ${LLVM_DIR})") 28 | if (NOT EXISTS ${MAIN_LIB}) 29 | # Add some diagnostics to detect issues before building. 30 | message(FATAL_ERROR "LLVM library not found: ${MAIN_LIB}") 31 | endif() 32 | else() 33 | # List of required LLVM libs. 34 | # Generated with `llvm-config --libs mcjit ipo x86codegen` 35 | # Only used here locally to setup the "llvm" imported target 36 | set(LIBS 37 | LLVMMCJIT 38 | LLVMX86CodeGen LLVMGlobalISel LLVMX86Desc LLVMX86Info LLVMMCDisassembler 39 | LLVMX86AsmPrinter LLVMX86Utils LLVMSelectionDAG LLVMAsmPrinter LLVMDebugInfoCodeView 40 | LLVMDebugInfoMSF LLVMCodeGen LLVMipo LLVMInstrumentation LLVMVectorize LLVMScalarOpts 41 | LLVMLinker LLVMIRReader LLVMAsmParser LLVMInstCombine LLVMTransformUtils LLVMBitWriter 42 | LLVMExecutionEngine LLVMTarget LLVMAnalysis LLVMProfileData LLVMRuntimeDyld LLVMObject 43 | LLVMMCParser LLVMBitReader LLVMMC LLVMCore LLVMBinaryFormat LLVMSupport LLVMDemangle 44 | ) 45 | 46 | # System libs that LLVM depend on. 47 | # See `llvm-config --system-libs` 48 | if (APPLE) 49 | set(SYSTEM_LIBS pthread) 50 | elseif (UNIX) 51 | set(SYSTEM_LIBS pthread dl) 52 | endif() 53 | 54 | if (${CMAKE_GENERATOR} STREQUAL "Unix Makefiles") 55 | set(BUILD_COMMAND $(MAKE)) 56 | else() 57 | set(BUILD_COMMAND cmake --build --config Release) 58 | endif() 59 | 60 | include(ExternalProject) 61 | ExternalProject_Add(llvm 62 | PREFIX ${CMAKE_SOURCE_DIR}/deps 63 | URL http://llvm.org/releases/5.0.0/llvm-5.0.0.src.tar.xz 64 | URL_HASH SHA256=e35dcbae6084adcf4abb32514127c5eabd7d63b733852ccdb31e06f1373136da 65 | DOWNLOAD_NO_PROGRESS TRUE 66 | BINARY_DIR ${CMAKE_SOURCE_DIR}/deps # Build directly to install dir to avoid copy. 67 | CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release 68 | -DCMAKE_INSTALL_PREFIX= 69 | -DLLVM_ENABLE_TERMINFO=OFF # Disable terminal color support 70 | -DLLVM_ENABLE_ZLIB=OFF # Disable compression support -- not needed at all 71 | -DLLVM_TARGETS_TO_BUILD=X86 72 | -DLLVM_INCLUDE_TOOLS=OFF 73 | -DLLVM_INCLUDE_EXAMPLES=OFF 74 | -DLLVM_INCLUDE_TESTS=OFF 75 | LOG_CONFIGURE TRUE 76 | BUILD_COMMAND ${BUILD_COMMAND} 77 | INSTALL_COMMAND cmake --build --config Release --target install 78 | LOG_INSTALL TRUE 79 | EXCLUDE_FROM_ALL TRUE 80 | ) 81 | 82 | ExternalProject_Get_Property(llvm INSTALL_DIR) 83 | set(LLVM_LIBRARY_DIRS ${INSTALL_DIR}/lib) 84 | set(LLVM_INCLUDE_DIRS ${INSTALL_DIR}/include) 85 | file(MAKE_DIRECTORY ${LLVM_INCLUDE_DIRS}) # Must exists. 86 | 87 | foreach(LIB ${LIBS}) 88 | list(APPEND LIBFILES "${LLVM_LIBRARY_DIRS}/${CMAKE_STATIC_LIBRARY_PREFIX}${LIB}${CMAKE_STATIC_LIBRARY_SUFFIX}") 89 | endforeach() 90 | 91 | # Pick one of the libraries to be the main one. It does not matter which one 92 | # but the imported target requires the IMPORTED_LOCATION property. 93 | list(GET LIBFILES 0 MAIN_LIB) 94 | list(REMOVE_AT LIBFILES 0) 95 | set(LIBS ${LIBFILES} ${SYSTEM_LIBS}) 96 | endif() 97 | 98 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 99 | # Clang needs this to build LLVM. Weird that the GCC does not. 100 | set(DEFINES __STDC_LIMIT_MACROS __STDC_CONSTANT_MACROS) 101 | endif() 102 | 103 | # Create the target representing 104 | add_library(LLVM::JIT STATIC IMPORTED) 105 | set_property(TARGET LLVM::JIT PROPERTY IMPORTED_CONFIGURATIONS Release) 106 | set_property(TARGET LLVM::JIT PROPERTY IMPORTED_LOCATION_RELEASE ${MAIN_LIB}) 107 | set_property(TARGET LLVM::JIT PROPERTY INTERFACE_COMPILE_DEFINITIONS ${DEFINES}) 108 | set_property(TARGET LLVM::JIT PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${LLVM_INCLUDE_DIRS}) 109 | set_property(TARGET LLVM::JIT PROPERTY INTERFACE_LINK_LIBRARIES ${LIBS}) 110 | if (TARGET llvm) 111 | add_dependencies(LLVM::JIT llvm) 112 | endif() 113 | endfunction() 114 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | RUN echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.9 main" >> /etc/apt/sources.list \ 4 | && apt-get update \ 5 | && apt-get install -y --no-install-recommends --allow-unauthenticated \ 6 | cmake \ 7 | g++ \ 8 | make \ 9 | llvm-3.9-dev \ 10 | zlib1g-dev \ 11 | && rm -rf /var/lib/apt/lists/* 12 | -------------------------------------------------------------------------------- /docker/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | SRC_DIR=$(dirname $(pwd)) 5 | 6 | CONTAINER=$(docker run -d -v $SRC_DIR:/src:ro chfast/cpp-ethereum-dev tail -f /dev/null) 7 | 8 | docker exec $CONTAINER sh -c 'mkdir build && cd build && cmake /src -DLLVM_DIR=/usr/lib/llvm-3.9/lib/cmake/llvm' 9 | docker exec $CONTAINER cmake --build /build 10 | 11 | docker kill $CONTAINER 12 | -------------------------------------------------------------------------------- /include/evmjit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef _MSC_VER 6 | #ifdef evmjit_EXPORTS 7 | #define EXPORT __declspec(dllexport) 8 | #else 9 | #define EXPORT 10 | #endif 11 | 12 | #else 13 | #define EXPORT __attribute__ ((visibility ("default"))) 14 | #endif 15 | 16 | #if __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | /// Create EVMJIT instance. 21 | /// 22 | /// @return The EVMJIT instance. 23 | EXPORT struct evmc_instance* evmjit_create(void); 24 | 25 | #if __cplusplus 26 | } 27 | #endif 28 | -------------------------------------------------------------------------------- /libevmjit/Arith256.cpp: -------------------------------------------------------------------------------- 1 | #include "Arith256.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "preprocessor/llvm_includes_start.h" 7 | #include 8 | #include 9 | #include "preprocessor/llvm_includes_end.h" 10 | 11 | #include "Type.h" 12 | #include "Endianness.h" 13 | #include "Utils.h" 14 | 15 | namespace dev 16 | { 17 | namespace eth 18 | { 19 | namespace jit 20 | { 21 | 22 | Arith256::Arith256(IRBuilder& _builder) : 23 | CompilerHelper(_builder) 24 | {} 25 | 26 | void Arith256::debug(llvm::Value* _value, char _c, llvm::Module& _module, IRBuilder& _builder) 27 | { 28 | static const auto funcName = "debug"; 29 | auto func = _module.getFunction(funcName); 30 | if (!func) 31 | func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {Type::Word, _builder.getInt8Ty()}, false), llvm::Function::ExternalLinkage, funcName, &_module); 32 | 33 | _builder.CreateCall(func, {_builder.CreateZExtOrTrunc(_value, Type::Word), _builder.getInt8(_c)}); 34 | } 35 | 36 | namespace 37 | { 38 | llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) 39 | { 40 | // Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research 41 | // The following algorithm also handles divisor of value 0 returning 0 for both quotient and remainder 42 | 43 | auto retType = llvm::VectorType::get(_type, 2); 44 | auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); 45 | func->setDoesNotThrow(); 46 | func->setDoesNotAccessMemory(); 47 | 48 | auto zero = llvm::ConstantInt::get(_type, 0); 49 | auto one = llvm::ConstantInt::get(_type, 1); 50 | 51 | auto iter = func->arg_begin(); 52 | llvm::Argument* x = &(*iter++); 53 | x->setName("x"); 54 | llvm::Argument* y = &(*iter); 55 | y->setName("y"); 56 | 57 | auto entryBB = llvm::BasicBlock::Create(_module.getContext(), "Entry", func); 58 | auto mainBB = llvm::BasicBlock::Create(_module.getContext(), "Main", func); 59 | auto loopBB = llvm::BasicBlock::Create(_module.getContext(), "Loop", func); 60 | auto continueBB = llvm::BasicBlock::Create(_module.getContext(), "Continue", func); 61 | auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func); 62 | 63 | auto builder = IRBuilder{entryBB}; 64 | auto yLEx = builder.CreateICmpULE(y, x); 65 | auto r0 = x; 66 | builder.CreateCondBr(yLEx, mainBB, returnBB); 67 | 68 | builder.SetInsertPoint(mainBB); 69 | auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type); 70 | // both y and r are non-zero 71 | auto yLz = builder.CreateCall(ctlzIntr, {y, builder.getInt1(true)}, "y.lz"); 72 | auto rLz = builder.CreateCall(ctlzIntr, {r0, builder.getInt1(true)}, "r.lz"); 73 | auto i0 = builder.CreateNUWSub(yLz, rLz, "i0"); 74 | auto y0 = builder.CreateShl(y, i0); 75 | builder.CreateBr(loopBB); 76 | 77 | builder.SetInsertPoint(loopBB); 78 | auto yPhi = builder.CreatePHI(_type, 2, "y.phi"); 79 | auto rPhi = builder.CreatePHI(_type, 2, "r.phi"); 80 | auto iPhi = builder.CreatePHI(_type, 2, "i.phi"); 81 | auto qPhi = builder.CreatePHI(_type, 2, "q.phi"); 82 | auto rUpdate = builder.CreateNUWSub(rPhi, yPhi); 83 | auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0 84 | auto rGEy = builder.CreateICmpUGE(rPhi, yPhi); 85 | auto r1 = builder.CreateSelect(rGEy, rUpdate, rPhi, "r1"); 86 | auto q1 = builder.CreateSelect(rGEy, qUpdate, qPhi, "q"); 87 | auto iZero = builder.CreateICmpEQ(iPhi, zero); 88 | builder.CreateCondBr(iZero, returnBB, continueBB); 89 | 90 | builder.SetInsertPoint(continueBB); 91 | auto i2 = builder.CreateNUWSub(iPhi, one); 92 | auto q2 = builder.CreateShl(q1, one); 93 | auto y2 = builder.CreateLShr(yPhi, one); 94 | builder.CreateBr(loopBB); 95 | 96 | yPhi->addIncoming(y0, mainBB); 97 | yPhi->addIncoming(y2, continueBB); 98 | rPhi->addIncoming(r0, mainBB); 99 | rPhi->addIncoming(r1, continueBB); 100 | iPhi->addIncoming(i0, mainBB); 101 | iPhi->addIncoming(i2, continueBB); 102 | qPhi->addIncoming(zero, mainBB); 103 | qPhi->addIncoming(q2, continueBB); 104 | 105 | builder.SetInsertPoint(returnBB); 106 | auto qRet = builder.CreatePHI(_type, 2, "q.ret"); 107 | qRet->addIncoming(zero, entryBB); 108 | qRet->addIncoming(q1, loopBB); 109 | auto rRet = builder.CreatePHI(_type, 2, "r.ret"); 110 | rRet->addIncoming(r0, entryBB); 111 | rRet->addIncoming(r1, loopBB); 112 | auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0"); 113 | ret = builder.CreateInsertElement(ret, rRet, 1, "ret"); 114 | builder.CreateRet(ret); 115 | 116 | return func; 117 | } 118 | } 119 | 120 | llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module) 121 | { 122 | static const auto funcName = "evm.udivrem.i256"; 123 | if (auto func = _module.getFunction(funcName)) 124 | return func; 125 | 126 | return createUDivRemFunc(Type::Word, _module, funcName); 127 | } 128 | 129 | llvm::Function* Arith256::getUDivRem512Func(llvm::Module& _module) 130 | { 131 | static const auto funcName = "evm.udivrem.i512"; 132 | if (auto func = _module.getFunction(funcName)) 133 | return func; 134 | 135 | return createUDivRemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); 136 | } 137 | 138 | llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module) 139 | { 140 | static const auto funcName = "evm.udiv.i256"; 141 | if (auto func = _module.getFunction(funcName)) 142 | return func; 143 | 144 | auto udivremFunc = getUDivRem256Func(_module); 145 | 146 | auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); 147 | func->setDoesNotThrow(); 148 | func->setDoesNotAccessMemory(); 149 | 150 | auto iter = func->arg_begin(); 151 | llvm::Argument* x = &(*iter++); 152 | x->setName("x"); 153 | llvm::Argument* y = &(*iter); 154 | y->setName("y"); 155 | 156 | auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); 157 | auto builder = IRBuilder{bb}; 158 | auto udivrem = builder.CreateCall(udivremFunc, {x, y}); 159 | auto udiv = builder.CreateExtractElement(udivrem, uint64_t(0)); 160 | builder.CreateRet(udiv); 161 | 162 | return func; 163 | } 164 | 165 | namespace 166 | { 167 | llvm::Function* createURemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) 168 | { 169 | auto udivremFunc = _type == Type::Word ? Arith256::getUDivRem256Func(_module) : Arith256::getUDivRem512Func(_module); 170 | 171 | auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); 172 | func->setDoesNotThrow(); 173 | func->setDoesNotAccessMemory(); 174 | 175 | auto iter = func->arg_begin(); 176 | llvm::Argument* x = &(*iter++); 177 | x->setName("x"); 178 | llvm::Argument* y = &(*iter); 179 | y->setName("y"); 180 | 181 | auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); 182 | auto builder = IRBuilder{bb}; 183 | auto udivrem = builder.CreateCall(udivremFunc, {x, y}); 184 | auto r = builder.CreateExtractElement(udivrem, uint64_t(1)); 185 | builder.CreateRet(r); 186 | 187 | return func; 188 | } 189 | } 190 | 191 | llvm::Function* Arith256::getURem256Func(llvm::Module& _module) 192 | { 193 | static const auto funcName = "evm.urem.i256"; 194 | if (auto func = _module.getFunction(funcName)) 195 | return func; 196 | return createURemFunc(Type::Word, _module, funcName); 197 | } 198 | 199 | llvm::Function* Arith256::getURem512Func(llvm::Module& _module) 200 | { 201 | static const auto funcName = "evm.urem.i512"; 202 | if (auto func = _module.getFunction(funcName)) 203 | return func; 204 | return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); 205 | } 206 | 207 | llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module) 208 | { 209 | static const auto funcName = "evm.sdivrem.i256"; 210 | if (auto func = _module.getFunction(funcName)) 211 | return func; 212 | 213 | auto udivremFunc = getUDivRem256Func(_module); 214 | 215 | auto retType = llvm::VectorType::get(Type::Word, 2); 216 | auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); 217 | func->setDoesNotThrow(); 218 | func->setDoesNotAccessMemory(); 219 | 220 | auto iter = func->arg_begin(); 221 | llvm::Argument* x = &(*iter++); 222 | x->setName("x"); 223 | llvm::Argument* y = &(*iter); 224 | y->setName("y"); 225 | 226 | auto bb = llvm::BasicBlock::Create(_module.getContext(), "", func); 227 | auto builder = IRBuilder{bb}; 228 | auto xIsNeg = builder.CreateICmpSLT(x, Constant::get(0)); 229 | auto xNeg = builder.CreateSub(Constant::get(0), x); 230 | auto xAbs = builder.CreateSelect(xIsNeg, xNeg, x); 231 | 232 | auto yIsNeg = builder.CreateICmpSLT(y, Constant::get(0)); 233 | auto yNeg = builder.CreateSub(Constant::get(0), y); 234 | auto yAbs = builder.CreateSelect(yIsNeg, yNeg, y); 235 | 236 | auto res = builder.CreateCall(udivremFunc, {xAbs, yAbs}); 237 | auto qAbs = builder.CreateExtractElement(res, uint64_t(0)); 238 | auto rAbs = builder.CreateExtractElement(res, 1); 239 | 240 | // the remainder has the same sign as dividend 241 | auto rNeg = builder.CreateSub(Constant::get(0), rAbs); 242 | auto r = builder.CreateSelect(xIsNeg, rNeg, rAbs); 243 | 244 | auto qNeg = builder.CreateSub(Constant::get(0), qAbs); 245 | auto xyOpposite = builder.CreateXor(xIsNeg, yIsNeg); 246 | auto q = builder.CreateSelect(xyOpposite, qNeg, qAbs); 247 | 248 | auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), q, uint64_t(0)); 249 | ret = builder.CreateInsertElement(ret, r, 1); 250 | builder.CreateRet(ret); 251 | 252 | return func; 253 | } 254 | 255 | llvm::Function* Arith256::getSDiv256Func(llvm::Module& _module) 256 | { 257 | static const auto funcName = "evm.sdiv.i256"; 258 | if (auto func = _module.getFunction(funcName)) 259 | return func; 260 | 261 | auto sdivremFunc = getSDivRem256Func(_module); 262 | 263 | auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); 264 | func->setDoesNotThrow(); 265 | func->setDoesNotAccessMemory(); 266 | 267 | auto iter = func->arg_begin(); 268 | llvm::Argument* x = &(*iter++); 269 | x->setName("x"); 270 | llvm::Argument* y = &(*iter); 271 | y->setName("y"); 272 | 273 | auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); 274 | auto builder = IRBuilder{bb}; 275 | auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); 276 | auto q = builder.CreateExtractElement(sdivrem, uint64_t(0)); 277 | builder.CreateRet(q); 278 | 279 | return func; 280 | } 281 | 282 | llvm::Function* Arith256::getSRem256Func(llvm::Module& _module) 283 | { 284 | static const auto funcName = "evm.srem.i256"; 285 | if (auto func = _module.getFunction(funcName)) 286 | return func; 287 | 288 | auto sdivremFunc = getSDivRem256Func(_module); 289 | 290 | auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); 291 | func->setDoesNotThrow(); 292 | func->setDoesNotAccessMemory(); 293 | 294 | auto iter = func->arg_begin(); 295 | llvm::Argument* x = &(*iter++); 296 | x->setName("x"); 297 | llvm::Argument* y = &(*iter); 298 | y->setName("y"); 299 | 300 | auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); 301 | auto builder = IRBuilder{bb}; 302 | auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); 303 | auto r = builder.CreateExtractElement(sdivrem, uint64_t(1)); 304 | builder.CreateRet(r); 305 | 306 | return func; 307 | } 308 | 309 | llvm::Function* Arith256::getExpFunc() 310 | { 311 | if (!m_exp) 312 | { 313 | llvm::Type* argTypes[] = {Type::Word, Type::Word}; 314 | m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "exp", getModule()); 315 | m_exp->setDoesNotThrow(); 316 | m_exp->setDoesNotAccessMemory(); 317 | 318 | auto iter = m_exp->arg_begin(); 319 | llvm::Argument* base = &(*iter++); 320 | base->setName("base"); 321 | llvm::Argument* exponent = &(*iter); 322 | exponent->setName("exponent"); 323 | 324 | InsertPointGuard guard{m_builder}; 325 | 326 | // while (e != 0) { 327 | // if (e % 2 == 1) 328 | // r *= b; 329 | // b *= b; 330 | // e /= 2; 331 | // } 332 | 333 | auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", m_exp); 334 | auto headerBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopHeader", m_exp); 335 | auto bodyBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopBody", m_exp); 336 | auto updateBB = llvm::BasicBlock::Create(m_builder.getContext(), "ResultUpdate", m_exp); 337 | auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", m_exp); 338 | auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp); 339 | 340 | m_builder.SetInsertPoint(entryBB); 341 | m_builder.CreateBr(headerBB); 342 | 343 | m_builder.SetInsertPoint(headerBB); 344 | auto r = m_builder.CreatePHI(Type::Word, 2, "r"); 345 | auto b = m_builder.CreatePHI(Type::Word, 2, "b"); 346 | auto e = m_builder.CreatePHI(Type::Word, 2, "e"); 347 | auto eNonZero = m_builder.CreateICmpNE(e, Constant::get(0), "e.nonzero"); 348 | m_builder.CreateCondBr(eNonZero, bodyBB, returnBB); 349 | 350 | m_builder.SetInsertPoint(bodyBB); 351 | auto eOdd = m_builder.CreateICmpNE(m_builder.CreateAnd(e, Constant::get(1)), Constant::get(0), "e.isodd"); 352 | m_builder.CreateCondBr(eOdd, updateBB, continueBB); 353 | 354 | m_builder.SetInsertPoint(updateBB); 355 | auto r0 = m_builder.CreateMul(r, b); 356 | m_builder.CreateBr(continueBB); 357 | 358 | m_builder.SetInsertPoint(continueBB); 359 | auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); 360 | r1->addIncoming(r, bodyBB); 361 | r1->addIncoming(r0, updateBB); 362 | auto b1 = m_builder.CreateMul(b, b); 363 | auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1"); 364 | m_builder.CreateBr(headerBB); 365 | 366 | r->addIncoming(Constant::get(1), entryBB); 367 | r->addIncoming(r1, continueBB); 368 | b->addIncoming(base, entryBB); 369 | b->addIncoming(b1, continueBB); 370 | e->addIncoming(exponent, entryBB); 371 | e->addIncoming(e1, continueBB); 372 | 373 | m_builder.SetInsertPoint(returnBB); 374 | m_builder.CreateRet(r); 375 | } 376 | return m_exp; 377 | } 378 | 379 | llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) 380 | { 381 | // while (e != 0) { 382 | // if (e % 2 == 1) 383 | // r *= b; 384 | // b *= b; 385 | // e /= 2; 386 | // } 387 | 388 | if (auto c1 = llvm::dyn_cast(_arg1)) 389 | { 390 | if (auto c2 = llvm::dyn_cast(_arg2)) 391 | { 392 | auto b = c1->getValue(); 393 | auto e = c2->getValue(); 394 | auto r = llvm::APInt{256, 1}; 395 | while (e != 0) 396 | { 397 | if (e[0]) 398 | r *= b; 399 | b *= b; 400 | e = e.lshr(1); 401 | } 402 | return Constant::get(r); 403 | } 404 | } 405 | 406 | return m_builder.CreateCall(getExpFunc(), {_arg1, _arg2}); 407 | } 408 | 409 | } 410 | } 411 | } 412 | 413 | extern "C" 414 | { 415 | EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z) 416 | { 417 | DLOG(JIT) << "DEBUG " << std::dec << z << ": " //<< d << c << b << a 418 | << " [" << std::hex << std::setfill('0') << std::setw(16) << d << std::setw(16) << c << std::setw(16) << b << std::setw(16) << a << "]\n"; 419 | } 420 | } 421 | -------------------------------------------------------------------------------- /libevmjit/Arith256.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CompilerHelper.h" 4 | 5 | namespace dev 6 | { 7 | namespace eth 8 | { 9 | namespace jit 10 | { 11 | 12 | class Arith256 : public CompilerHelper 13 | { 14 | public: 15 | Arith256(IRBuilder& _builder); 16 | 17 | llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); 18 | 19 | static void debug(llvm::Value *_value, char _c, llvm::Module &_module, IRBuilder &_builder); 20 | 21 | static llvm::Function* getUDiv256Func(llvm::Module& _module); 22 | static llvm::Function* getURem256Func(llvm::Module& _module); 23 | static llvm::Function* getURem512Func(llvm::Module& _module); 24 | static llvm::Function* getUDivRem256Func(llvm::Module& _module); 25 | static llvm::Function* getSDiv256Func(llvm::Module& _module); 26 | static llvm::Function* getSRem256Func(llvm::Module& _module); 27 | static llvm::Function* getSDivRem256Func(llvm::Module& _module); 28 | static llvm::Function* getUDivRem512Func(llvm::Module& _module); 29 | 30 | private: 31 | llvm::Function* getExpFunc(); 32 | 33 | llvm::Function* m_exp = nullptr; 34 | }; 35 | 36 | 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /libevmjit/Array.cpp: -------------------------------------------------------------------------------- 1 | #include "Array.h" 2 | 3 | #include "preprocessor/llvm_includes_start.h" 4 | #include 5 | #include 6 | #include "preprocessor/llvm_includes_end.h" 7 | 8 | #include "RuntimeManager.h" 9 | #include "Utils.h" 10 | 11 | namespace dev 12 | { 13 | namespace eth 14 | { 15 | namespace jit 16 | { 17 | 18 | static const auto c_reallocStep = 1; 19 | 20 | llvm::Value* LazyFunction::call(IRBuilder& _builder, std::initializer_list const& _args, llvm::Twine const& _name) 21 | { 22 | if (!m_func) 23 | m_func = m_creator(); 24 | 25 | return _builder.CreateCall(m_func, {_args.begin(), _args.size()}, _name); 26 | } 27 | 28 | llvm::Function* Array::createArrayPushFunc() 29 | { 30 | llvm::Type* argTypes[] = {m_array->getType(), Type::Word}; 31 | auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.push", getModule()); 32 | func->setDoesNotThrow(); 33 | func->addAttribute(1, llvm::Attribute::NoCapture); 34 | 35 | auto iter = func->arg_begin(); 36 | llvm::Argument* arrayPtr = &(*iter++); 37 | arrayPtr->setName("arrayPtr"); 38 | llvm::Argument* value = &(*iter); 39 | value->setName("value"); 40 | 41 | InsertPointGuard guard{m_builder}; 42 | auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func); 43 | auto reallocBB = llvm::BasicBlock::Create(m_builder.getContext(), "Realloc", func); 44 | auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func); 45 | 46 | m_builder.SetInsertPoint(entryBB); 47 | auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); 48 | auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); 49 | auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); 50 | auto data = m_builder.CreateLoad(dataPtr, "data"); 51 | auto size = m_builder.CreateLoad(sizePtr, "size"); 52 | auto cap = m_builder.CreateLoad(capPtr, "cap"); 53 | auto reallocReq = m_builder.CreateICmpEQ(cap, size, "reallocReq"); 54 | m_builder.CreateCondBr(reallocReq, reallocBB, pushBB); 55 | 56 | m_builder.SetInsertPoint(reallocBB); 57 | auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap"); 58 | auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32 59 | auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes"); 60 | auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes"); 61 | auto newData = m_builder.CreateBitCast(newBytes, Type::WordPtr, "newData"); 62 | m_builder.CreateStore(newData, dataPtr); 63 | m_builder.CreateStore(newCap, capPtr); 64 | m_builder.CreateBr(pushBB); 65 | 66 | m_builder.SetInsertPoint(pushBB); 67 | auto dataPhi = m_builder.CreatePHI(Type::WordPtr, 2, "dataPhi"); 68 | dataPhi->addIncoming(data, entryBB); 69 | dataPhi->addIncoming(newData, reallocBB); 70 | auto newElemPtr = m_builder.CreateGEP(dataPhi, size, "newElemPtr"); 71 | m_builder.CreateStore(value, newElemPtr); 72 | auto newSize = m_builder.CreateNUWAdd(size, m_builder.getInt64(1), "newSize"); 73 | m_builder.CreateStore(newSize, sizePtr); 74 | m_builder.CreateRetVoid(); 75 | 76 | return func; 77 | } 78 | 79 | llvm::Function* Array::createArraySetFunc() 80 | { 81 | llvm::Type* argTypes[] = {m_array->getType(), Type::Size, Type::Word}; 82 | auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.set", getModule()); 83 | func->setDoesNotThrow(); 84 | func->addAttribute(1, llvm::Attribute::NoCapture); 85 | 86 | auto iter = func->arg_begin(); 87 | llvm::Argument* arrayPtr = &(*iter++); 88 | arrayPtr->setName("arrayPtr"); 89 | llvm::Argument* index = &(*iter++); 90 | index->setName("index"); 91 | llvm::Argument* value = &(*iter); 92 | value->setName("value"); 93 | 94 | InsertPointGuard guard{m_builder}; 95 | m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); 96 | auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); 97 | auto data = m_builder.CreateLoad(dataPtr, "data"); 98 | auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); 99 | m_builder.CreateStore(value, valuePtr); 100 | m_builder.CreateRetVoid(); 101 | return func; 102 | } 103 | 104 | llvm::Function* Array::createArrayGetFunc() 105 | { 106 | llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; 107 | auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "array.get", getModule()); 108 | func->setDoesNotThrow(); 109 | func->addAttribute(1, llvm::Attribute::NoCapture); 110 | 111 | auto iter = func->arg_begin(); 112 | llvm::Argument* arrayPtr = &(*iter++); 113 | arrayPtr->setName("arrayPtr"); 114 | llvm::Argument* index = &(*iter++); 115 | index->setName("index"); 116 | 117 | InsertPointGuard guard{m_builder}; 118 | m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); 119 | auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); 120 | auto data = m_builder.CreateLoad(dataPtr, "data"); 121 | auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); 122 | auto value = m_builder.CreateLoad(valuePtr, "value"); 123 | m_builder.CreateRet(value); 124 | return func; 125 | } 126 | 127 | llvm::Function* Array::createGetPtrFunc() 128 | { 129 | llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; 130 | auto func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), llvm::Function::PrivateLinkage, "array.getPtr", getModule()); 131 | func->setDoesNotThrow(); 132 | func->addAttribute(1, llvm::Attribute::NoCapture); 133 | 134 | auto iter = func->arg_begin(); 135 | llvm::Argument* arrayPtr = &(*iter++); 136 | arrayPtr->setName("arrayPtr"); 137 | llvm::Argument* index = &(*iter++); 138 | index->setName("index"); 139 | 140 | InsertPointGuard guard{m_builder}; 141 | m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); 142 | auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr"); 143 | auto data = m_builder.CreateLoad(dataPtr, "data"); 144 | auto bytePtr = m_builder.CreateGEP(data, index, "bytePtr"); 145 | auto wordPtr = m_builder.CreateBitCast(bytePtr, Type::WordPtr, "wordPtr"); 146 | m_builder.CreateRet(wordPtr); 147 | return func; 148 | } 149 | 150 | llvm::Function* Array::createFreeFunc() 151 | { 152 | auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, m_array->getType(), false), llvm::Function::PrivateLinkage, "array.free", getModule()); 153 | func->setDoesNotThrow(); 154 | func->addAttribute(1, llvm::Attribute::NoCapture); 155 | 156 | auto freeFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::BytePtr, false), llvm::Function::ExternalLinkage, "free", getModule()); 157 | freeFunc->setDoesNotThrow(); 158 | freeFunc->addAttribute(1, llvm::Attribute::NoCapture); 159 | 160 | llvm::Argument* arrayPtr{func->arg_begin()}; 161 | arrayPtr->setName("arrayPtr"); 162 | 163 | InsertPointGuard guard{m_builder}; 164 | m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); 165 | auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); 166 | auto data = m_builder.CreateLoad(dataPtr, "data"); 167 | auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem"); 168 | m_builder.CreateCall(freeFunc, mem); 169 | m_builder.CreateRetVoid(); 170 | return func; 171 | } 172 | 173 | llvm::Function* Array::getReallocFunc() 174 | { 175 | if (auto func = getModule()->getFunction("realloc")) 176 | return func; 177 | 178 | llvm::Type* reallocArgTypes[] = {Type::BytePtr, Type::Size}; 179 | auto reallocFunc = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, reallocArgTypes, false), llvm::Function::ExternalLinkage, "realloc", getModule()); 180 | reallocFunc->setDoesNotThrow(); 181 | reallocFunc->addAttribute(0, llvm::Attribute::NoAlias); 182 | reallocFunc->addAttribute(1, llvm::Attribute::NoCapture); 183 | return reallocFunc; 184 | } 185 | 186 | llvm::Function* Array::createExtendFunc() 187 | { 188 | llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; 189 | auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.extend", getModule()); 190 | func->setDoesNotThrow(); 191 | func->addAttribute(1, llvm::Attribute::NoCapture); 192 | 193 | auto iter = func->arg_begin(); 194 | llvm::Argument* arrayPtr = &(*iter++); 195 | arrayPtr->setName("arrayPtr"); 196 | llvm::Argument* newSize = &(*iter++); 197 | newSize->setName("newSize"); 198 | 199 | InsertPointGuard guard{m_builder}; 200 | m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); 201 | auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");// TODO: Use byte* in Array 202 | auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); 203 | auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); 204 | auto data = m_builder.CreateLoad(dataPtr, "data"); 205 | auto size = m_builder.CreateLoad(sizePtr, "size"); 206 | auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize"); 207 | auto newData = m_reallocFunc.call(m_builder, {data, newSize}, "newData"); // TODO: Check realloc result for null 208 | auto extPtr = m_builder.CreateGEP(newData, size, "extPtr"); 209 | m_builder.CreateMemSet(extPtr, m_builder.getInt8(0), extSize, 16); 210 | m_builder.CreateStore(newData, dataPtr); 211 | m_builder.CreateStore(newSize, sizePtr); 212 | m_builder.CreateStore(newSize, capPtr); 213 | m_builder.CreateRetVoid(); 214 | return func; 215 | } 216 | 217 | llvm::Type* Array::getType() 218 | { 219 | llvm::Type* elementTys[] = {Type::WordPtr, Type::Size, Type::Size}; 220 | static auto arrayTy = llvm::StructType::create(elementTys, "Array"); 221 | return arrayTy; 222 | } 223 | 224 | Array::Array(IRBuilder& _builder, char const* _name) : 225 | CompilerHelper(_builder) 226 | { 227 | m_array = m_builder.CreateAlloca(getType(), nullptr, _name); 228 | m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); 229 | } 230 | 231 | Array::Array(IRBuilder& _builder, llvm::Value* _array) : 232 | CompilerHelper(_builder), 233 | m_array(_array) 234 | { 235 | m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); 236 | } 237 | 238 | 239 | void Array::pop(llvm::Value* _count) 240 | { 241 | auto sizePtr = m_builder.CreateStructGEP(getType(), m_array, 1, "sizePtr"); 242 | auto size = m_builder.CreateLoad(sizePtr, "size"); 243 | auto newSize = m_builder.CreateNUWSub(size, _count, "newSize"); 244 | m_builder.CreateStore(newSize, sizePtr); 245 | } 246 | 247 | llvm::Value* Array::size(llvm::Value* _array) 248 | { 249 | auto sizePtr = m_builder.CreateStructGEP(getType(), _array ? _array : m_array, 1, "sizePtr"); 250 | return m_builder.CreateLoad(sizePtr, "array.size"); 251 | } 252 | 253 | void Array::extend(llvm::Value* _arrayPtr, llvm::Value* _size) 254 | { 255 | assert(_arrayPtr->getType() == m_array->getType()); 256 | assert(_size->getType() == Type::Size); 257 | m_extendFunc.call(m_builder, {_arrayPtr, _size}); 258 | } 259 | 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /libevmjit/Array.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "CompilerHelper.h" 6 | 7 | namespace dev 8 | { 9 | namespace eth 10 | { 11 | namespace jit 12 | { 13 | 14 | class LazyFunction 15 | { 16 | public: 17 | using Creator = std::function; 18 | 19 | LazyFunction(Creator _creator) : 20 | m_creator(_creator) 21 | {} 22 | 23 | llvm::Value* call(IRBuilder& _builder, std::initializer_list const& _args, llvm::Twine const& _name = ""); 24 | 25 | private: 26 | llvm::Function* m_func = nullptr; 27 | Creator m_creator; 28 | }; 29 | 30 | class Array : public CompilerHelper 31 | { 32 | public: 33 | Array(IRBuilder& _builder, char const* _name); 34 | Array(IRBuilder& _builder, llvm::Value* _array); 35 | 36 | void push(llvm::Value* _value) { m_pushFunc.call(m_builder, {m_array, _value}); } 37 | void set(llvm::Value* _index, llvm::Value* _value) { m_setFunc.call(m_builder, {m_array, _index, _value}); } 38 | llvm::Value* get(llvm::Value* _index) { return m_getFunc.call(m_builder, {m_array, _index}); } 39 | void pop(llvm::Value* _count); 40 | llvm::Value* size(llvm::Value* _array = nullptr); 41 | void free() { m_freeFunc.call(m_builder, {m_array}); } 42 | 43 | void extend(llvm::Value* _arrayPtr, llvm::Value* _size); 44 | llvm::Value* getPtr(llvm::Value* _arrayPtr, llvm::Value* _index) { return m_getPtrFunc.call(m_builder, {_arrayPtr, _index}); } 45 | 46 | llvm::Value* getPointerTo() const { return m_array; } 47 | 48 | static llvm::Type* getType(); 49 | 50 | private: 51 | llvm::Value* m_array = nullptr; 52 | 53 | llvm::Function* createArrayPushFunc(); 54 | llvm::Function* createArraySetFunc(); 55 | llvm::Function* createArrayGetFunc(); 56 | llvm::Function* createGetPtrFunc(); 57 | llvm::Function* createFreeFunc(); 58 | llvm::Function* createExtendFunc(); 59 | llvm::Function* getReallocFunc(); 60 | 61 | LazyFunction m_pushFunc = {[this](){ return createArrayPushFunc(); }}; 62 | LazyFunction m_setFunc = {[this](){ return createArraySetFunc(); }}; 63 | LazyFunction m_getPtrFunc = {[this](){ return createGetPtrFunc(); }}; 64 | LazyFunction m_getFunc = {[this](){ return createArrayGetFunc(); }}; 65 | LazyFunction m_freeFunc = {[this](){ return createFreeFunc(); }}; 66 | LazyFunction m_extendFunc = {[this](){ return createExtendFunc(); }}; 67 | LazyFunction m_reallocFunc = {[this](){ return getReallocFunc(); }}; 68 | }; 69 | 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /libevmjit/BasicBlock.cpp: -------------------------------------------------------------------------------- 1 | #include "BasicBlock.h" 2 | 3 | #include 4 | 5 | #include "preprocessor/llvm_includes_start.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "preprocessor/llvm_includes_end.h" 14 | 15 | #include "RuntimeManager.h" 16 | #include "Type.h" 17 | #include "Utils.h" 18 | 19 | namespace dev 20 | { 21 | namespace eth 22 | { 23 | namespace jit 24 | { 25 | 26 | BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc): 27 | m_firstInstrIdx{_firstInstrIdx}, 28 | m_begin(_begin), 29 | m_end(_end), 30 | m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {".", std::to_string(_firstInstrIdx)}, _mainFunc)) 31 | {} 32 | 33 | LocalStack::LocalStack(IRBuilder& _builder, RuntimeManager& _runtimeManager): 34 | CompilerHelper(_builder) 35 | { 36 | // Call stack.prepare. min, max, size args will be filled up in finalize(). 37 | auto undef = llvm::UndefValue::get(Type::Size); 38 | m_sp = m_builder.CreateCall(getStackPrepareFunc(), 39 | {_runtimeManager.getStackBase(), _runtimeManager.getStackSize(), undef, undef, undef, _runtimeManager.getJmpBuf()}, 40 | {"sp", m_builder.GetInsertBlock()->getName()}); 41 | } 42 | 43 | void LocalStack::push(llvm::Value* _value) 44 | { 45 | assert(_value->getType() == Type::Word); 46 | m_local.push_back(_value); 47 | m_maxSize = std::max(m_maxSize, size()); 48 | } 49 | 50 | llvm::Value* LocalStack::pop() 51 | { 52 | auto item = get(0); 53 | assert(!m_local.empty() || !m_input.empty()); 54 | 55 | if (m_local.size() > 0) 56 | m_local.pop_back(); 57 | else 58 | ++m_globalPops; 59 | 60 | m_minSize = std::min(m_minSize, size()); 61 | return item; 62 | } 63 | 64 | /// Copies the _index-th element of the local stack and pushes it back on the top. 65 | void LocalStack::dup(size_t _index) 66 | { 67 | auto val = get(_index); 68 | push(val); 69 | } 70 | 71 | /// Swaps the top element with the _index-th element of the local stack. 72 | void LocalStack::swap(size_t _index) 73 | { 74 | assert(_index > 0); ///< _index must not be 0. 75 | auto val = get(_index); 76 | auto tos = get(0); 77 | set(_index, tos); 78 | set(0, val); 79 | } 80 | 81 | llvm::Value* LocalStack::get(size_t _index) 82 | { 83 | if (_index < m_local.size()) 84 | return *(m_local.rbegin() + _index); // count from back 85 | 86 | auto idx = _index - m_local.size() + m_globalPops; 87 | if (idx >= m_input.size()) 88 | m_input.resize(idx + 1); 89 | auto& item = m_input[idx]; 90 | 91 | if (!item) 92 | { 93 | // Fetch an item from global stack 94 | ssize_t globalIdx = -static_cast(idx) - 1; 95 | auto slot = m_builder.CreateConstGEP1_64(m_sp, globalIdx); 96 | item = m_builder.CreateAlignedLoad(slot, 16); // TODO: Handle malloc alignment. Also for 32-bit systems. 97 | m_minSize = std::min(m_minSize, globalIdx); // remember required stack size 98 | } 99 | 100 | return item; 101 | } 102 | 103 | void LocalStack::set(size_t _index, llvm::Value* _word) 104 | { 105 | if (_index < m_local.size()) 106 | { 107 | *(m_local.rbegin() + _index) = _word; 108 | return; 109 | } 110 | 111 | auto idx = _index - m_local.size() + m_globalPops; 112 | assert(idx < m_input.size()); 113 | m_input[idx] = _word; 114 | } 115 | 116 | 117 | void LocalStack::finalize() 118 | { 119 | m_sp->setArgOperand(2, m_builder.getInt64(minSize())); 120 | m_sp->setArgOperand(3, m_builder.getInt64(maxSize())); 121 | m_sp->setArgOperand(4, m_builder.getInt64(size())); 122 | 123 | if (auto term = m_builder.GetInsertBlock()->getTerminator()) 124 | m_builder.SetInsertPoint(term); // Insert before terminator 125 | 126 | auto inputIt = m_input.rbegin(); 127 | auto localIt = m_local.begin(); 128 | for (auto globalIdx = -static_cast(m_input.size()); globalIdx < size(); ++globalIdx) 129 | { 130 | llvm::Value* item = nullptr; 131 | if (globalIdx < -m_globalPops) 132 | { 133 | item = *inputIt++; // update input items (might contain original value) 134 | if (!item) // some items are skipped 135 | continue; 136 | } 137 | else 138 | item = *localIt++; // store new items 139 | 140 | auto slot = m_builder.CreateConstGEP1_64(m_sp, globalIdx); 141 | m_builder.CreateAlignedStore(item, slot, 16); // TODO: Handle malloc alignment. Also for 32-bit systems. 142 | } 143 | } 144 | 145 | 146 | llvm::Function* LocalStack::getStackPrepareFunc() 147 | { 148 | static const auto c_funcName = "stack.prepare"; 149 | if (auto func = getModule()->getFunction(c_funcName)) 150 | return func; 151 | 152 | llvm::Type* argsTys[] = {Type::WordPtr, Type::Size->getPointerTo(), Type::Size, Type::Size, Type::Size, Type::BytePtr}; 153 | auto func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argsTys, false), llvm::Function::PrivateLinkage, c_funcName, getModule()); 154 | func->setDoesNotThrow(); 155 | func->addAttribute(1, llvm::Attribute::ReadNone); 156 | func->addAttribute(2, llvm::Attribute::NoAlias); 157 | func->addAttribute(2, llvm::Attribute::NoCapture); 158 | 159 | auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); 160 | auto updateBB = llvm::BasicBlock::Create(func->getContext(), "Update", func); 161 | auto outOfStackBB = llvm::BasicBlock::Create(func->getContext(), "OutOfStack", func); 162 | 163 | auto iter = func->arg_begin(); 164 | llvm::Argument* base = &(*iter++); 165 | base->setName("base"); 166 | llvm::Argument* sizePtr = &(*iter++); 167 | sizePtr->setName("size.ptr"); 168 | llvm::Argument* min = &(*iter++); 169 | min->setName("min"); 170 | llvm::Argument* max = &(*iter++); 171 | max->setName("max"); 172 | llvm::Argument* diff = &(*iter++); 173 | diff->setName("diff"); 174 | llvm::Argument* jmpBuf = &(*iter); 175 | jmpBuf->setName("jmpBuf"); 176 | 177 | InsertPointGuard guard{m_builder}; 178 | m_builder.SetInsertPoint(checkBB); 179 | auto sizeAlignment = getModule()->getDataLayout().getABITypeAlignment(Type::Size); 180 | auto size = m_builder.CreateAlignedLoad(sizePtr, sizeAlignment, "size"); 181 | auto minSize = m_builder.CreateAdd(size, min, "size.min", false, true); 182 | auto maxSize = m_builder.CreateAdd(size, max, "size.max", true, true); 183 | auto minOk = m_builder.CreateICmpSGE(minSize, m_builder.getInt64(0), "ok.min"); 184 | auto maxOk = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(RuntimeManager::stackSizeLimit), "ok.max"); 185 | auto ok = m_builder.CreateAnd(minOk, maxOk, "ok"); 186 | m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); 187 | 188 | m_builder.SetInsertPoint(updateBB); 189 | auto newSize = m_builder.CreateNSWAdd(size, diff, "size.next"); 190 | m_builder.CreateAlignedStore(newSize, sizePtr, sizeAlignment); 191 | auto sp = m_builder.CreateGEP(base, size, "sp"); 192 | m_builder.CreateRet(sp); 193 | 194 | m_builder.SetInsertPoint(outOfStackBB); 195 | auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); 196 | m_builder.CreateCall(longjmp, {jmpBuf}); 197 | m_builder.CreateUnreachable(); 198 | 199 | return func; 200 | } 201 | 202 | 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /libevmjit/BasicBlock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "Common.h" 6 | #include "CompilerHelper.h" 7 | 8 | namespace dev 9 | { 10 | namespace eth 11 | { 12 | namespace jit 13 | { 14 | using namespace evmjit; 15 | using instr_idx = uint64_t; 16 | 17 | class RuntimeManager; 18 | 19 | class LocalStack: public CompilerHelper 20 | { 21 | public: 22 | explicit LocalStack(IRBuilder& _builder, RuntimeManager& _runtimeManager); 23 | 24 | /// Pushes value on stack 25 | void push(llvm::Value* _value); 26 | 27 | /// Pops and returns top value 28 | llvm::Value* pop(); 29 | 30 | /// Duplicates _index'th value on stack 31 | void dup(size_t _index); 32 | 33 | /// Swaps _index'th value on stack with a value on stack top. 34 | /// @param _index Index of value to be swaped. Must be > 0. 35 | void swap(size_t _index); 36 | 37 | ssize_t size() const { return static_cast(m_local.size()) - m_globalPops; } 38 | ssize_t minSize() const { return m_minSize; } 39 | ssize_t maxSize() const { return m_maxSize; } 40 | 41 | /// Finalize local stack: check the requirements and update of the global stack. 42 | void finalize(); 43 | 44 | private: 45 | /// Gets _index'th value from top (counting from 0) 46 | llvm::Value* get(size_t _index); 47 | 48 | /// Sets _index'th value from top (counting from 0) 49 | void set(size_t _index, llvm::Value* _value); 50 | 51 | llvm::Function* getStackPrepareFunc(); 52 | 53 | /// Items fetched from global stack. First element matches the top of the global stack. 54 | /// Can contain nulls if some items has been skipped. 55 | std::vector m_input; 56 | 57 | /// Local stack items that has not been pushed to global stack. First item is just above global stack. 58 | std::vector m_local; 59 | 60 | llvm::CallInst* m_sp = nullptr; ///< Call to stack.prepare function which returns stack pointer for current basic block. 61 | 62 | ssize_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap. 63 | ssize_t m_minSize = 0; ///< Minimum reached local stack size. Can be negative. 64 | ssize_t m_maxSize = 0; ///< Maximum reached local stack size. 65 | }; 66 | 67 | class BasicBlock 68 | { 69 | public: 70 | explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc); 71 | 72 | llvm::BasicBlock* llvm() { return m_llvmBB; } 73 | 74 | instr_idx firstInstrIdx() const { return m_firstInstrIdx; } 75 | code_iterator begin() const { return m_begin; } 76 | code_iterator end() const { return m_end; } 77 | 78 | private: 79 | instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block 80 | code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block 81 | code_iterator const m_end = {}; ///< Iterator pointing code end of the block 82 | 83 | llvm::BasicBlock* const m_llvmBB; ///< Reference to the LLVM BasicBlock 84 | }; 85 | 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /libevmjit/BuildInfo.h.in: -------------------------------------------------------------------------------- 1 | 2 | #define EVMJIT_VERSION "${EVMJIT_VERSION}" 3 | #define EVMJIT_VERSION_MAJOR ${EVMJIT_VERSION_MAJOR} 4 | #define EVMJIT_VERSION_MINOR ${EVMJIT_VERSION_MINOR} 5 | #define EVMJIT_VERSION_PATCH ${EVMJIT_VERSION_PATCH} 6 | 7 | #define LLVM_VERSION "${LLVM_PACKAGE_VERSION}" 8 | #define LLVM_ASSERTIONS "${LLVM_ENABLE_ASSERTIONS}" 9 | #define LLVM_DEBUG ${LLVM_DEBUG} 10 | -------------------------------------------------------------------------------- /libevmjit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | get_filename_component(EVMJIT_INCLUDE_DIR ../include ABSOLUTE) 2 | 3 | set(SOURCES 4 | JIT.cpp JIT.h 5 | Arith256.cpp Arith256.h 6 | Array.cpp Array.h 7 | BasicBlock.cpp BasicBlock.h 8 | Cache.cpp Cache.h 9 | Common.h 10 | Compiler.cpp Compiler.h 11 | CompilerHelper.cpp CompilerHelper.h 12 | Endianness.cpp Endianness.h 13 | ExecStats.cpp ExecStats.h 14 | Ext.cpp Ext.h 15 | GasMeter.cpp GasMeter.h 16 | Instruction.cpp Instruction.h 17 | Memory.cpp Memory.h 18 | Optimizer.cpp Optimizer.h 19 | RuntimeManager.cpp RuntimeManager.h 20 | Type.cpp Type.h 21 | Utils.cpp Utils.h 22 | ) 23 | source_group("" FILES ${SOURCES}) 24 | 25 | if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 26 | else() 27 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fvisibility=hidden") 28 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 29 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs,ALL") # Do not export symbols from dependies, mostly LLVM libs 30 | endif() 31 | endif() 32 | 33 | if(EVMJIT_VERSION_MAJOR EQUAL 0) 34 | set(EVMJIT_SOVERSION "0.${EVMJIT_VERSION_MINOR}") 35 | else() 36 | set(EVMJIT_SOVERSION ${EVMJIT_VERSION_MAJOR}) 37 | endif() 38 | 39 | 40 | string(COMPARE EQUAL "${LLVM_ENABLE_ASSERTIONS}" "ON" LLVM_DEBUG) 41 | configure_file(BuildInfo.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen/BuildInfo.gen.h) 42 | 43 | add_library(evmjit ${SOURCES} gen/BuildInfo.gen.h) 44 | # Explicit dependency on llvm to download LLVM header files. 45 | add_dependencies(evmjit LLVM::JIT) 46 | get_target_property(LLVM_COMPILE_DEFINITIONS LLVM::JIT INTERFACE_COMPILE_DEFINITIONS) 47 | if (LLVM_COMPILE_DEFINITIONS) 48 | target_compile_definitions(evmjit PRIVATE ${LLVM_COMPILE_DEFINITIONS}) 49 | endif() 50 | get_target_property(LLVM_INCLUDE_DIRECTORIES LLVM::JIT INTERFACE_INCLUDE_DIRECTORIES) 51 | target_include_directories(evmjit SYSTEM PRIVATE ${LLVM_INCLUDE_DIRECTORIES}) 52 | target_include_directories(evmjit PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/gen) 53 | target_include_directories(evmjit PUBLIC ${EVMJIT_INCLUDE_DIR}) 54 | 55 | target_link_libraries(evmjit PRIVATE evmc LLVM::JIT) 56 | set_target_properties(evmjit PROPERTIES 57 | VERSION ${EVMJIT_VERSION} 58 | SOVERSION ${EVMJIT_SOVERSION} 59 | FOLDER "libs" 60 | ) 61 | 62 | include(GNUInstallDirs) 63 | install(TARGETS evmjit 64 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 65 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 66 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) 67 | install(DIRECTORY ${EVMJIT_INCLUDE_DIR}/ 68 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 69 | 70 | # This section configures evmjit-standalone -- a static library containing 71 | # evmjit and all its static dependencies. 72 | 73 | # This function tries to get a list of all static library dependencies of 74 | # a target. It probably does not handle all cases to be generic, but works for 75 | # LLVM case. 76 | function(get_link_libraries OUTPUT_LIST TARGET) 77 | get_target_property(IMPORTED ${TARGET} IMPORTED) 78 | list(APPEND VISITED_TARGETS ${TARGET}) 79 | if (IMPORTED) 80 | get_target_property(LIBS ${TARGET} INTERFACE_LINK_LIBRARIES) 81 | else() 82 | get_target_property(LIBS ${TARGET} LINK_LIBRARIES) 83 | endif() 84 | set(LIB_FILES "") 85 | foreach(LIB ${LIBS}) 86 | if (TARGET ${LIB}) 87 | get_target_property(type ${LIB} TYPE) 88 | if(type STREQUAL INTERFACE_LIBRARY) 89 | continue() # Skip interface libraries. 90 | endif() 91 | list(FIND VISITED_TARGETS ${LIB} VISITED) 92 | if (${VISITED} EQUAL -1) 93 | get_target_property(LIB_FILE ${LIB} LOCATION) 94 | get_link_libraries(LINK_LIB_FILES ${LIB}) 95 | list(APPEND LIB_FILES ${LIB_FILE} ${LINK_LIB_FILES}) 96 | endif() 97 | elseif (IS_ABSOLUTE ${LIB}) 98 | # Here LIB is a full path to a (hopefuly static) library. We don't 99 | # want system libs (usually dynamic) here. 100 | list(APPEND LIB_FILES ${LIB}) 101 | endif() 102 | endforeach() 103 | set(VISITED_TARGETS ${VISITED_TARGETS} PARENT_SCOPE) 104 | list(REMOVE_DUPLICATES LIB_FILES) 105 | set(${OUTPUT_LIST} ${LIB_FILES} PARENT_SCOPE) 106 | endfunction() 107 | 108 | # When building static lib add additional target evmjit-standalone -- 109 | # an archive containing all LLVM dependencies in a single file. 110 | get_target_property(_evmjit_type evmjit TYPE) 111 | if (_evmjit_type STREQUAL STATIC_LIBRARY) 112 | get_link_libraries(EVMJIT_LINK_LIBRARIES evmjit) 113 | set(EVMJIT_STANDALONE_FILE ${CMAKE_STATIC_LIBRARY_PREFIX}evmjit-standalone${CMAKE_STATIC_LIBRARY_SUFFIX}) 114 | if (MSVC) 115 | set(_dummy_file ${CMAKE_CURRENT_BINARY_DIR}/evmjit-standalone.c) 116 | if (NOT EXISTS ${_dummy_file}) 117 | file(WRITE ${_dummy_file} "// evmjit-standalone dummy file") 118 | endif() 119 | add_library(evmjit-standalone STATIC EXCLUDE_FROM_ALL ${_dummy_file}) 120 | add_dependencies(evmjit-standalone evmjit) 121 | string(REPLACE ";" " " FLAGS "${EVMJIT_LINK_LIBRARIES}") 122 | set(FLAGS "${CMAKE_CFG_INTDIR}/evmjit.lib ${FLAGS}") 123 | set_target_properties(evmjit-standalone PROPERTIES STATIC_LIBRARY_FLAGS "${FLAGS}") 124 | install(TARGETS evmjit-standalone 125 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 126 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 127 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) 128 | elseif (APPLE) 129 | add_custom_command(OUTPUT ${EVMJIT_STANDALONE_FILE} 130 | COMMAND libtool -static -o ${EVMJIT_STANDALONE_FILE} $ ${EVMJIT_LINK_LIBRARIES} 131 | VERBATIM) 132 | add_custom_target(evmjit-standalone DEPENDS ${EVMJIT_STANDALONE_FILE}) 133 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${EVMJIT_STANDALONE_FILE} DESTINATION ${CMAKE_INSTALL_LIBDIR} OPTIONAL) 134 | elseif (CMAKE_AR) 135 | # For platforms using ar a linker scripts is created and used to create 136 | # the standalone library. 137 | 138 | string(REPLACE ";" "\\naddlib " AR_SCRIPT ";${EVMJIT_LINK_LIBRARIES}") 139 | set(AR_SCRIPT "create ${EVMJIT_STANDALONE_FILE}\\naddlib $${AR_SCRIPT}\\nsave\\nend\\n") 140 | set(AR_SCRIPT_FILE ${CMAKE_STATIC_LIBRARY_PREFIX}evmjit-standalone.ar-script) 141 | 142 | # Generate the linker script. 143 | add_custom_command(OUTPUT ${AR_SCRIPT_FILE} 144 | COMMAND printf ${AR_SCRIPT} > ${AR_SCRIPT_FILE} 145 | DEPENDS $ 146 | VERBATIM) 147 | 148 | # Execute the script. 149 | add_custom_command(OUTPUT ${EVMJIT_STANDALONE_FILE} 150 | COMMAND ${CMAKE_AR} -M < ${AR_SCRIPT_FILE} 151 | MAIN_DEPENDENCY ${AR_SCRIPT_FILE} 152 | VERBATIM) 153 | 154 | add_custom_target(evmjit-standalone DEPENDS ${EVMJIT_STANDALONE_FILE}) 155 | 156 | # The "thin" library is also provided. It is smaller that the standalone one 157 | # but cannot be redistributed. 158 | set(EVMJIT_STANDALONE_THIN_FILE ${CMAKE_STATIC_LIBRARY_PREFIX}evmjit-standalone-thin${CMAKE_STATIC_LIBRARY_SUFFIX}) 159 | 160 | add_custom_command(OUTPUT ${EVMJIT_STANDALONE_THIN_FILE} 161 | COMMAND ${CMAKE_AR} cTq ${EVMJIT_STANDALONE_THIN_FILE} $ ${EVMJIT_LINK_LIBRARIES}) 162 | 163 | add_custom_target(evmjit-standalone-thin ALL DEPENDS ${EVMJIT_STANDALONE_THIN_FILE}) 164 | # FIXME: Looks it will be better to create evmjit-standalone as a library with costum steps instead of custom taget. 165 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${EVMJIT_STANDALONE_FILE} DESTINATION ${CMAKE_INSTALL_LIBDIR} OPTIONAL) 166 | endif() 167 | 168 | # Export the full path to evmjit-standalone library to be used in tests. 169 | # TODO: It would be easier if evmjit-standalone was a library target, but it 170 | # works for now. 171 | set(EVMJIT_STANDALONE_LIB ${CMAKE_CURRENT_BINARY_DIR}/${EVMJIT_STANDALONE_FILE} PARENT_SCOPE) 172 | endif() 173 | -------------------------------------------------------------------------------- /libevmjit/Cache.cpp: -------------------------------------------------------------------------------- 1 | #include "Cache.h" 2 | 3 | #include 4 | 5 | #include "preprocessor/llvm_includes_start.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "preprocessor/llvm_includes_end.h" 14 | 15 | #include "ExecStats.h" 16 | #include "Utils.h" 17 | 18 | namespace dev 19 | { 20 | namespace evmjit 21 | { 22 | 23 | namespace 24 | { 25 | /// The ABI version of jitted codes. It reflects how a generated code 26 | /// communicates with outside world. When this communication changes old 27 | /// cached code must be invalidated. 28 | const auto c_internalABIVersion = 4; 29 | 30 | using Guard = std::lock_guard; 31 | std::mutex x_cacheMutex; 32 | CacheMode g_mode; 33 | std::unique_ptr g_lastObject; 34 | JITListener* g_listener; 35 | 36 | std::string getVersionedCacheDir() 37 | { 38 | llvm::SmallString<256> path; 39 | llvm::sys::path::user_cache_directory(path, "ethereum", "evmjit", 40 | std::to_string(c_internalABIVersion)); 41 | return path.str(); 42 | } 43 | 44 | } 45 | 46 | ObjectCache* Cache::init(CacheMode _mode, JITListener* _listener) 47 | { 48 | DLOG(cache) << "Cache dir: " << getVersionedCacheDir() << "\n"; 49 | 50 | Guard g{x_cacheMutex}; 51 | 52 | g_mode = _mode; 53 | g_listener = _listener; 54 | 55 | if (g_mode == CacheMode::clear) 56 | { 57 | Cache::clear(); 58 | g_mode = CacheMode::off; 59 | } 60 | 61 | if (g_mode != CacheMode::off) 62 | { 63 | static ObjectCache objectCache; 64 | return &objectCache; 65 | } 66 | return nullptr; 67 | } 68 | 69 | void Cache::clear() 70 | { 71 | Guard g{x_cacheMutex}; 72 | 73 | auto cachePath = getVersionedCacheDir(); 74 | std::error_code err; 75 | for (auto it = llvm::sys::fs::directory_iterator{cachePath, err}; it != decltype(it){}; it.increment(err)) 76 | llvm::sys::fs::remove(it->path()); 77 | } 78 | 79 | void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache, 80 | llvm::LLVMContext& _llvmContext) 81 | { 82 | Guard g{x_cacheMutex}; 83 | 84 | // Disable listener 85 | auto listener = g_listener; 86 | g_listener = nullptr; 87 | 88 | auto cachePath = getVersionedCacheDir(); 89 | std::error_code err; 90 | for (auto it = llvm::sys::fs::directory_iterator{cachePath, err}; it != decltype(it){}; it.increment(err)) 91 | { 92 | auto name = it->path().substr(cachePath.size() + 1); 93 | if (auto module = getObject(name, _llvmContext)) 94 | { 95 | DLOG(cache) << "Preload: " << name << "\n"; 96 | _ee.addModule(std::move(module)); 97 | auto addr = _ee.getFunctionAddress(name); 98 | assert(addr); 99 | _funcCache[std::move(name)] = addr; 100 | } 101 | } 102 | 103 | g_listener = listener; 104 | } 105 | 106 | std::unique_ptr Cache::getObject(std::string const& id, llvm::LLVMContext& _llvmContext) 107 | { 108 | Guard g{x_cacheMutex}; 109 | 110 | if (g_mode != CacheMode::on && g_mode != CacheMode::read) 111 | return nullptr; 112 | 113 | // TODO: Disabled because is not thread-safe. 114 | //if (g_listener) 115 | // g_listener->stateChanged(ExecState::CacheLoad); 116 | 117 | DLOG(cache) << id << ": search\n"; 118 | if (!CHECK(!g_lastObject)) 119 | g_lastObject = nullptr; 120 | 121 | llvm::SmallString<256> cachePath{getVersionedCacheDir()}; 122 | llvm::sys::path::append(cachePath, id); 123 | 124 | if (auto r = llvm::MemoryBuffer::getFile(cachePath, -1, false)) 125 | g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); 126 | else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) 127 | DLOG(cache) << r.getError().message(); // TODO: Add warning log 128 | 129 | if (g_lastObject) // if object found create fake module 130 | { 131 | DLOG(cache) << id << ": found\n"; 132 | auto module = llvm::make_unique(id, _llvmContext); 133 | auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(_llvmContext), {}, false); 134 | auto mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get()); 135 | auto bb = llvm::BasicBlock::Create(_llvmContext, {}, mainFunc); 136 | bb->getInstList().push_back(new llvm::UnreachableInst{_llvmContext}); 137 | return module; 138 | } 139 | DLOG(cache) << id << ": not found\n"; 140 | return nullptr; 141 | } 142 | 143 | 144 | void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) 145 | { 146 | Guard g{x_cacheMutex}; 147 | 148 | // Only in "on" and "write" mode 149 | if (g_mode != CacheMode::on && g_mode != CacheMode::write) 150 | return; 151 | 152 | // TODO: Disabled because is not thread-safe. 153 | // if (g_listener) 154 | // g_listener->stateChanged(ExecState::CacheWrite); 155 | 156 | auto&& id = _module->getModuleIdentifier(); 157 | llvm::SmallString<256> cachePath{getVersionedCacheDir()}; 158 | if (auto err = llvm::sys::fs::create_directories(cachePath)) 159 | { 160 | DLOG(cache) << "Cannot create cache dir " << cachePath.str().str() << " (error: " << err.message() << "\n"; 161 | return; 162 | } 163 | 164 | llvm::sys::path::append(cachePath, id); 165 | 166 | DLOG(cache) << id << ": write\n"; 167 | std::error_code error; 168 | llvm::raw_fd_ostream cacheFile(cachePath, error, llvm::sys::fs::F_None); 169 | cacheFile << _object.getBuffer(); 170 | } 171 | 172 | std::unique_ptr ObjectCache::getObject(llvm::Module const* _module) 173 | { 174 | Guard g{x_cacheMutex}; 175 | 176 | DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; 177 | return std::move(g_lastObject); 178 | } 179 | 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /libevmjit/Cache.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "preprocessor/llvm_includes_start.h" 7 | #include 8 | #include 9 | #include "preprocessor/llvm_includes_end.h" 10 | 11 | namespace llvm 12 | { 13 | class ExecutionEngine; 14 | } 15 | 16 | namespace dev 17 | { 18 | namespace evmjit 19 | { 20 | class JITListener; 21 | 22 | enum class CacheMode 23 | { 24 | off, 25 | on, 26 | read, 27 | write, 28 | clear, 29 | preload 30 | }; 31 | 32 | class ObjectCache : public llvm::ObjectCache 33 | { 34 | public: 35 | /// notifyObjectCompiled - Provides a pointer to compiled code for Module M. 36 | virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) final override; 37 | 38 | /// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that 39 | /// contains the object which corresponds with Module M, or 0 if an object is 40 | /// not available. The caller owns both the MemoryBuffer returned by this 41 | /// and the memory it references. 42 | virtual std::unique_ptr getObject(llvm::Module const* _module) final override; 43 | }; 44 | 45 | 46 | class Cache 47 | { 48 | public: 49 | static ObjectCache* init(CacheMode _mode, JITListener* _listener); 50 | static std::unique_ptr getObject(std::string const& id, llvm::LLVMContext& _llvmContext); 51 | 52 | /// Clears cache storage 53 | static void clear(); 54 | 55 | /// Loads all available cached objects to ExecutionEngine 56 | static void preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache, 57 | llvm::LLVMContext& _llvmContext); 58 | }; 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /libevmjit/Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace dev 6 | { 7 | namespace evmjit 8 | { 9 | 10 | using byte = uint8_t; 11 | using code_iterator = byte const*; 12 | 13 | #define UNTESTED assert(false) 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /libevmjit/Compiler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "JIT.h" 4 | #include "BasicBlock.h" 5 | 6 | namespace dev 7 | { 8 | namespace eth 9 | { 10 | namespace jit 11 | { 12 | 13 | class Compiler 14 | { 15 | public: 16 | 17 | struct Options 18 | { 19 | /// Rewrite switch instructions to sequences of branches 20 | bool rewriteSwitchToBranches = true; 21 | 22 | /// Dump CFG as a .dot file for graphviz 23 | bool dumpCFG = false; 24 | }; 25 | 26 | Compiler(Options const& _options, evmc_revision _rev, bool _staticCall, llvm::LLVMContext& _llvmContext); 27 | 28 | std::unique_ptr compile(code_iterator _begin, code_iterator _end, std::string const& _id); 29 | 30 | private: 31 | 32 | std::vector createBasicBlocks(code_iterator _begin, code_iterator _end); 33 | 34 | void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter); 35 | 36 | void resolveJumps(); 37 | 38 | /// Compiler options 39 | Options const& m_options; 40 | 41 | /// EVM revision. 42 | evmc_revision m_rev; 43 | 44 | bool const m_staticCall = false; 45 | 46 | /// Helper class for generating IR 47 | IRBuilder m_builder; 48 | 49 | /// Block with a jump table. 50 | llvm::BasicBlock* m_jumpTableBB = nullptr; 51 | 52 | /// Main program function 53 | llvm::Function* m_mainFunc = nullptr; 54 | }; 55 | 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /libevmjit/CompilerHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "CompilerHelper.h" 2 | 3 | #include "preprocessor/llvm_includes_start.h" 4 | #include 5 | #include "preprocessor/llvm_includes_end.h" 6 | 7 | #include "RuntimeManager.h" 8 | 9 | namespace dev 10 | { 11 | namespace eth 12 | { 13 | namespace jit 14 | { 15 | 16 | CompilerHelper::CompilerHelper(IRBuilder& _builder) : 17 | m_builder(_builder) 18 | {} 19 | 20 | llvm::Module* CompilerHelper::getModule() 21 | { 22 | assert(m_builder.GetInsertBlock()); 23 | assert(m_builder.GetInsertBlock()->getParent()); // BB must be in a function 24 | return m_builder.GetInsertBlock()->getParent()->getParent(); 25 | } 26 | 27 | llvm::Function* CompilerHelper::getMainFunction() 28 | { 29 | // TODO: Rename or change semantics of getMainFunction() function 30 | assert(m_builder.GetInsertBlock()); 31 | auto mainFunc = m_builder.GetInsertBlock()->getParent(); 32 | assert(mainFunc); 33 | if (mainFunc == &mainFunc->getParent()->getFunctionList().front()) // Main function is the first one in module 34 | return mainFunc; 35 | return nullptr; 36 | } 37 | 38 | 39 | RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager): 40 | CompilerHelper(_runtimeManager.m_builder), 41 | m_runtimeManager(_runtimeManager) 42 | {} 43 | 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /libevmjit/CompilerHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "preprocessor/llvm_includes_start.h" 4 | #include 5 | #include "preprocessor/llvm_includes_end.h" 6 | 7 | 8 | namespace dev 9 | { 10 | namespace eth 11 | { 12 | namespace jit 13 | { 14 | class RuntimeManager; 15 | 16 | using IRBuilder = llvm::IRBuilder<>; 17 | 18 | /// Base class for compiler helpers like Memory, GasMeter, etc. 19 | class CompilerHelper 20 | { 21 | protected: 22 | CompilerHelper(IRBuilder& _builder); 23 | 24 | CompilerHelper(const CompilerHelper&) = delete; 25 | CompilerHelper& operator=(CompilerHelper) = delete; 26 | 27 | /// Reference to the IR module being compiled 28 | llvm::Module* getModule(); 29 | 30 | /// Reference to the main module function 31 | llvm::Function* getMainFunction(); 32 | 33 | /// Reference to parent compiler IR builder 34 | IRBuilder& m_builder; 35 | 36 | friend class RuntimeHelper; 37 | }; 38 | 39 | /// Compiler helper that depends on runtime data 40 | class RuntimeHelper : public CompilerHelper 41 | { 42 | protected: 43 | RuntimeHelper(RuntimeManager& _runtimeManager); 44 | 45 | RuntimeManager& getRuntimeManager() { return m_runtimeManager; } 46 | 47 | private: 48 | RuntimeManager& m_runtimeManager; 49 | }; 50 | 51 | struct InsertPointGuard 52 | { 53 | explicit InsertPointGuard(llvm::IRBuilderBase& _builder): m_builder(_builder), m_insertPoint(_builder.saveIP()) {} 54 | ~InsertPointGuard() { m_builder.restoreIP(m_insertPoint); } 55 | 56 | private: 57 | llvm::IRBuilderBase& m_builder; 58 | llvm::IRBuilderBase::InsertPoint m_insertPoint; 59 | }; 60 | 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /libevmjit/Endianness.cpp: -------------------------------------------------------------------------------- 1 | #include "Endianness.h" 2 | 3 | #include "preprocessor/llvm_includes_start.h" 4 | #include 5 | #include 6 | #include "preprocessor/llvm_includes_end.h" 7 | 8 | #include "Type.h" 9 | 10 | namespace dev 11 | { 12 | namespace eth 13 | { 14 | namespace jit 15 | { 16 | 17 | llvm::Value* Endianness::bswapIfLE(IRBuilder& _builder, llvm::Value* _value) 18 | { 19 | if (llvm::sys::IsLittleEndianHost) 20 | { 21 | if (auto constant = llvm::dyn_cast(_value)) 22 | return _builder.getInt(constant->getValue().byteSwap()); 23 | 24 | // OPT: Cache func declaration? 25 | auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, _value->getType()); 26 | return _builder.CreateCall(bswapFunc, _value); 27 | } 28 | return _value; 29 | } 30 | 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /libevmjit/Endianness.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CompilerHelper.h" 4 | 5 | namespace dev 6 | { 7 | namespace eth 8 | { 9 | namespace jit 10 | { 11 | 12 | struct Endianness 13 | { 14 | static llvm::Value* toBE(IRBuilder& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } 15 | static llvm::Value* toNative(IRBuilder& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } 16 | 17 | private: 18 | static llvm::Value* bswapIfLE(IRBuilder& _builder, llvm::Value* _word); 19 | }; 20 | 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /libevmjit/ExecStats.cpp: -------------------------------------------------------------------------------- 1 | #include "ExecStats.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Utils.h" 8 | 9 | namespace dev 10 | { 11 | namespace evmjit 12 | { 13 | 14 | void ExecStats::stateChanged(ExecState _state) 15 | { 16 | if (!CHECK(m_state != ExecState::Finished)) 17 | return; 18 | 19 | auto now = clock::now(); 20 | if (_state != ExecState::Started) 21 | { 22 | assert(time[(int)m_state] == ExecStats::duration::zero()); 23 | time[(int)m_state] = now - m_tp; 24 | } 25 | m_state = _state; 26 | m_tp = now; 27 | } 28 | 29 | namespace 30 | { 31 | struct StatsAgg 32 | { 33 | using unit = std::chrono::microseconds; 34 | ExecStats::duration tot = ExecStats::duration::zero(); 35 | ExecStats::duration min = ExecStats::duration::max(); 36 | ExecStats::duration max = ExecStats::duration::zero(); 37 | size_t count = 0; 38 | 39 | void update(ExecStats::duration _d) 40 | { 41 | ++count; 42 | tot += _d; 43 | min = _d < min ? _d : min; 44 | max = _d > max ? _d : max; 45 | } 46 | 47 | void output(char const* _name, std::ostream& _os) 48 | { 49 | auto avg = tot / count; 50 | _os << std::setfill(' ') 51 | << std::setw(12) << std::left << _name 52 | << std::setw(10) << std::right << std::chrono::duration_cast(tot).count() 53 | << std::setw(10) << std::right << std::chrono::duration_cast(avg).count() 54 | << std::setw(10) << std::right << std::chrono::duration_cast(min).count() 55 | << std::setw(10) << std::right << std::chrono::duration_cast(max).count() 56 | << std::endl; 57 | } 58 | }; 59 | 60 | char const* getExecStateName(ExecState _state) 61 | { 62 | switch (_state) 63 | { 64 | case ExecState::Started: return "Start"; 65 | case ExecState::CacheLoad: return "CacheLoad"; 66 | case ExecState::CacheWrite: return "CacheWrite"; 67 | case ExecState::Compilation: return "Compilation"; 68 | case ExecState::Optimization: return "Optimization"; 69 | case ExecState::CodeGen: return "CodeGen"; 70 | case ExecState::Execution: return "Execution"; 71 | case ExecState::Return: return "Return"; 72 | case ExecState::Finished: return "Finish"; 73 | } 74 | return nullptr; 75 | } 76 | } 77 | 78 | StatsCollector::~StatsCollector() 79 | { 80 | if (stats.empty()) 81 | return; 82 | 83 | std::cout << " [us] total avg min max\n"; 84 | for (int i = 0; i < (int)ExecState::Finished; ++i) 85 | { 86 | StatsAgg agg; 87 | for (auto&& s : stats) 88 | agg.update(s->time[i]); 89 | 90 | agg.output(getExecStateName(ExecState(i)), std::cout); 91 | } 92 | } 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /libevmjit/ExecStats.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace dev 9 | { 10 | namespace evmjit 11 | { 12 | 13 | enum class ExecState 14 | { 15 | Started, 16 | CacheLoad, 17 | CacheWrite, 18 | Compilation, 19 | Optimization, 20 | CodeGen, 21 | Execution, 22 | Return, 23 | Finished 24 | }; 25 | 26 | class JITListener 27 | { 28 | public: 29 | JITListener() = default; 30 | JITListener(JITListener const&) = delete; 31 | JITListener& operator=(JITListener) = delete; 32 | virtual ~JITListener() {} 33 | 34 | virtual void executionStarted() {} 35 | virtual void executionEnded() {} 36 | 37 | virtual void stateChanged(ExecState) {} 38 | }; 39 | 40 | class ExecStats : public JITListener 41 | { 42 | public: 43 | using clock = std::chrono::high_resolution_clock; 44 | using duration = clock::duration; 45 | using time_point = clock::time_point; 46 | 47 | std::string id; 48 | duration time[(int)ExecState::Finished] = {}; 49 | 50 | void stateChanged(ExecState _state) override; 51 | 52 | private: 53 | ExecState m_state = {}; 54 | time_point m_tp = {}; 55 | 56 | }; 57 | 58 | 59 | class StatsCollector 60 | { 61 | public: 62 | std::vector> stats; 63 | 64 | ~StatsCollector(); 65 | }; 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /libevmjit/Ext.cpp: -------------------------------------------------------------------------------- 1 | #include "Ext.h" 2 | 3 | #include "preprocessor/llvm_includes_start.h" 4 | #include 5 | #include 6 | #include "preprocessor/llvm_includes_end.h" 7 | 8 | #include "RuntimeManager.h" 9 | #include "Memory.h" 10 | #include "Type.h" 11 | #include "Endianness.h" 12 | 13 | namespace dev 14 | { 15 | namespace eth 16 | { 17 | namespace jit 18 | { 19 | 20 | Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan) : 21 | RuntimeHelper(_runtimeManager), 22 | m_memoryMan(_memoryMan) 23 | { 24 | m_funcs = decltype(m_funcs)(); 25 | m_argAllocas = decltype(m_argAllocas)(); 26 | m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); 27 | } 28 | 29 | namespace 30 | { 31 | 32 | using FuncDesc = std::tuple; 33 | 34 | llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list const& _argsTypes) 35 | { 36 | return llvm::FunctionType::get(_returnType, llvm::ArrayRef{_argsTypes.begin(), _argsTypes.size()}, false); 37 | } 38 | 39 | std::array::value> const& getEnvFuncDescs() 40 | { 41 | static std::array::value> descs{{ 42 | FuncDesc{"env_sload", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, 43 | FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, 44 | FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})}, 45 | FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::WordPtr, Type::EnvPtr, Type::WordPtr})}, 46 | FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})}, 47 | FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::GasPtr, Type::Gas, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size})}, 48 | FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})}, 49 | FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, 50 | FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, 51 | }}; 52 | 53 | return descs; 54 | } 55 | 56 | llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module) 57 | { 58 | auto&& desc = getEnvFuncDescs()[static_cast(_id)]; 59 | return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module); 60 | } 61 | 62 | llvm::Function* getAccountExistsFunc(llvm::Module* _module) 63 | { 64 | static const auto funcName = "evm.exists"; 65 | auto func = _module->getFunction(funcName); 66 | if (!func) 67 | { 68 | // TODO: Mark the function as pure to eliminate multiple calls. 69 | auto i32 = llvm::IntegerType::getInt32Ty(_module->getContext()); 70 | auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); 71 | auto fty = llvm::FunctionType::get( 72 | i32, {Type::EnvPtr, addrTy->getPointerTo()}, false); 73 | func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); 74 | } 75 | return func; 76 | } 77 | 78 | llvm::Function* getGetStorageFunc(llvm::Module* _module) 79 | { 80 | static const auto funcName = "evm.sload"; 81 | auto func = _module->getFunction(funcName); 82 | if (!func) 83 | { 84 | // TODO: Mark the function as pure to eliminate multiple calls. 85 | auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); 86 | auto fty = llvm::FunctionType::get( 87 | Type::Void, {Type::WordPtr, Type::EnvPtr, addrTy->getPointerTo(), Type::WordPtr}, false); 88 | func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); 89 | func->addAttribute(1, llvm::Attribute::NoAlias); 90 | func->addAttribute(1, llvm::Attribute::NoCapture); 91 | func->addAttribute(3, llvm::Attribute::ReadOnly); 92 | func->addAttribute(3, llvm::Attribute::NoAlias); 93 | func->addAttribute(3, llvm::Attribute::NoCapture); 94 | func->addAttribute(4, llvm::Attribute::ReadOnly); 95 | func->addAttribute(4, llvm::Attribute::NoAlias); 96 | func->addAttribute(4, llvm::Attribute::NoCapture); 97 | } 98 | return func; 99 | } 100 | 101 | llvm::Function* getSetStorageFunc(llvm::Module* _module) 102 | { 103 | static const auto funcName = "evm.sstore"; 104 | auto func = _module->getFunction(funcName); 105 | if (!func) 106 | { 107 | auto addrPtrTy = llvm::Type::getIntNPtrTy(_module->getContext(), 160); 108 | auto fty = llvm::FunctionType::get(Type::Void, {Type::EnvPtr, addrPtrTy, Type::WordPtr, Type::WordPtr}, false); 109 | func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); 110 | func->addAttribute(2, llvm::Attribute::ReadOnly); 111 | func->addAttribute(2, llvm::Attribute::NoAlias); 112 | func->addAttribute(2, llvm::Attribute::NoCapture); 113 | func->addAttribute(3, llvm::Attribute::ReadOnly); 114 | func->addAttribute(3, llvm::Attribute::NoAlias); 115 | func->addAttribute(3, llvm::Attribute::NoCapture); 116 | } 117 | return func; 118 | } 119 | 120 | llvm::Function* getGetBalanceFunc(llvm::Module* _module) 121 | { 122 | static const auto funcName = "evm.balance"; 123 | auto func = _module->getFunction(funcName); 124 | if (!func) 125 | { 126 | auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); 127 | auto fty = llvm::FunctionType::get( 128 | Type::Void, {Type::WordPtr, Type::EnvPtr, addrTy->getPointerTo()}, false); 129 | func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); 130 | func->addAttribute(1, llvm::Attribute::NoAlias); 131 | func->addAttribute(1, llvm::Attribute::NoCapture); 132 | func->addAttribute(3, llvm::Attribute::ReadOnly); 133 | func->addAttribute(3, llvm::Attribute::NoAlias); 134 | func->addAttribute(3, llvm::Attribute::NoCapture); 135 | } 136 | return func; 137 | } 138 | 139 | llvm::Function* getGetCodeSizeFunc(llvm::Module* _module) 140 | { 141 | static const auto funcName = "evm.codesize"; 142 | auto func = _module->getFunction(funcName); 143 | if (!func) 144 | { 145 | auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); 146 | auto fty = llvm::FunctionType::get( 147 | Type::Size, {Type::EnvPtr, addrTy->getPointerTo()}, false); 148 | func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); 149 | func->addAttribute(2, llvm::Attribute::ReadOnly); 150 | func->addAttribute(2, llvm::Attribute::NoAlias); 151 | func->addAttribute(2, llvm::Attribute::NoCapture); 152 | } 153 | return func; 154 | } 155 | 156 | llvm::Function* getGetCodeFunc(llvm::Module* _module) 157 | { 158 | static const auto funcName = "evm.code"; 159 | auto func = _module->getFunction(funcName); 160 | if (!func) 161 | { 162 | auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); 163 | auto fty = llvm::FunctionType::get( 164 | Type::Size, {Type::BytePtr->getPointerTo(), Type::EnvPtr, addrTy->getPointerTo()}, false); 165 | func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); 166 | func->addAttribute(1, llvm::Attribute::NoAlias); 167 | func->addAttribute(1, llvm::Attribute::NoCapture); 168 | func->addAttribute(3, llvm::Attribute::ReadOnly); 169 | func->addAttribute(3, llvm::Attribute::NoAlias); 170 | func->addAttribute(3, llvm::Attribute::NoCapture); 171 | } 172 | return func; 173 | } 174 | 175 | llvm::Function* getSelfdestructFunc(llvm::Module* _module) 176 | { 177 | static const auto funcName = "evm.selfdestruct"; 178 | auto func = _module->getFunction(funcName); 179 | if (!func) 180 | { 181 | auto addrPtrTy = llvm::Type::getIntNPtrTy(_module->getContext(), 160); 182 | auto fty = llvm::FunctionType::get(Type::Void, {Type::EnvPtr, addrPtrTy, addrPtrTy}, false); 183 | func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); 184 | func->addAttribute(2, llvm::Attribute::ReadOnly); 185 | func->addAttribute(2, llvm::Attribute::NoAlias); 186 | func->addAttribute(2, llvm::Attribute::NoCapture); 187 | func->addAttribute(3, llvm::Attribute::ReadOnly); 188 | func->addAttribute(3, llvm::Attribute::NoAlias); 189 | func->addAttribute(3, llvm::Attribute::NoCapture); 190 | } 191 | return func; 192 | } 193 | 194 | llvm::Function* getLogFunc(llvm::Module* _module) 195 | { 196 | static const auto funcName = "evm.log"; 197 | auto func = _module->getFunction(funcName); 198 | if (!func) 199 | { 200 | auto addrPtrTy = llvm::Type::getIntNPtrTy(_module->getContext(), 160); 201 | auto fty = llvm::FunctionType::get(Type::Void, {Type::EnvPtr, addrPtrTy, Type::BytePtr, Type::Size, Type::WordPtr, Type::Size}, false); 202 | func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); 203 | func->addAttribute(3, llvm::Attribute::ReadOnly); 204 | func->addAttribute(3, llvm::Attribute::NoAlias); 205 | func->addAttribute(3, llvm::Attribute::NoCapture); 206 | func->addAttribute(5, llvm::Attribute::ReadOnly); 207 | func->addAttribute(5, llvm::Attribute::NoAlias); 208 | func->addAttribute(5, llvm::Attribute::NoCapture); 209 | } 210 | return func; 211 | } 212 | 213 | llvm::Function* getCallFunc(llvm::Module* _module) 214 | { 215 | static const auto funcName = "call"; 216 | auto func = _module->getFunction(funcName); 217 | if (!func) 218 | { 219 | auto i32 = llvm::IntegerType::getInt32Ty(_module->getContext()); 220 | auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); 221 | auto addrPtrTy = addrTy->getPointerTo(); 222 | auto fty = llvm::FunctionType::get( 223 | Type::Gas, 224 | {Type::EnvPtr, i32, Type::Gas, addrPtrTy, Type::WordPtr, 225 | Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, 226 | Type::BytePtr->getPointerTo(), Type::Size->getPointerTo()}, 227 | false); 228 | func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, "evm.call", _module); 229 | func->addAttribute(4, llvm::Attribute::ReadOnly); 230 | func->addAttribute(4, llvm::Attribute::NoAlias); 231 | func->addAttribute(4, llvm::Attribute::NoCapture); 232 | func->addAttribute(5, llvm::Attribute::ReadOnly); 233 | func->addAttribute(5, llvm::Attribute::NoAlias); 234 | func->addAttribute(5, llvm::Attribute::NoCapture); 235 | func->addAttribute(6, llvm::Attribute::ReadOnly); 236 | func->addAttribute(6, llvm::Attribute::NoCapture); 237 | func->addAttribute(8, llvm::Attribute::NoCapture); 238 | auto callFunc = func; 239 | 240 | // Create a call wrapper to handle additional checks. 241 | fty = llvm::FunctionType::get( 242 | Type::Gas, 243 | {Type::EnvPtr, i32, Type::Gas, addrPtrTy, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, 244 | Type::BytePtr->getPointerTo(), Type::Size->getPointerTo(), addrTy, Type::Size}, 245 | false 246 | ); 247 | func = llvm::Function::Create(fty, llvm::Function::PrivateLinkage, funcName, _module); 248 | func->addAttribute(4, llvm::Attribute::ReadOnly); 249 | func->addAttribute(4, llvm::Attribute::NoAlias); 250 | func->addAttribute(4, llvm::Attribute::NoCapture); 251 | func->addAttribute(5, llvm::Attribute::ReadOnly); 252 | func->addAttribute(5, llvm::Attribute::NoAlias); 253 | func->addAttribute(5, llvm::Attribute::NoCapture); 254 | func->addAttribute(6, llvm::Attribute::ReadOnly); 255 | func->addAttribute(6, llvm::Attribute::NoCapture); 256 | func->addAttribute(8, llvm::Attribute::NoCapture); 257 | 258 | auto iter = func->arg_begin(); 259 | auto& env = *iter; 260 | std::advance(iter, 1); 261 | auto& callKind = *iter; 262 | std::advance(iter, 1); 263 | auto& gas = *iter; 264 | std::advance(iter, 2); 265 | auto& valuePtr = *iter; 266 | std::advance(iter, 7); 267 | auto& addr = *iter; 268 | std::advance(iter, 1); 269 | auto& depth = *iter; 270 | 271 | auto& ctx = _module->getContext(); 272 | llvm::IRBuilder<> builder(ctx); 273 | auto entryBB = llvm::BasicBlock::Create(ctx, "Entry", func); 274 | auto checkTransferBB = llvm::BasicBlock::Create(ctx, "CheckTransfer", func); 275 | auto checkBalanceBB = llvm::BasicBlock::Create(ctx, "CheckBalance", func); 276 | auto callBB = llvm::BasicBlock::Create(ctx, "Call", func); 277 | auto failBB = llvm::BasicBlock::Create(ctx, "Fail", func); 278 | 279 | builder.SetInsertPoint(entryBB); 280 | auto v = builder.CreateAlloca(Type::Word); 281 | auto addrAlloca = builder.CreateBitCast(builder.CreateAlloca(Type::Word), addrPtrTy); 282 | auto getBalanceFn = getGetBalanceFunc(_module); 283 | auto depthOk = builder.CreateICmpSLT(&depth, builder.getInt64(1024)); 284 | builder.CreateCondBr(depthOk, checkTransferBB, failBB); 285 | 286 | builder.SetInsertPoint(checkTransferBB); 287 | auto notDelegateCall = builder.CreateICmpNE(&callKind, builder.getInt32(EVMC_DELEGATECALL)); 288 | llvm::Value* value = builder.CreateLoad(&valuePtr); 289 | auto valueNonZero = builder.CreateICmpNE(value, Constant::get(0)); 290 | auto transfer = builder.CreateAnd(notDelegateCall, valueNonZero); 291 | builder.CreateCondBr(transfer, checkBalanceBB, callBB); 292 | 293 | builder.SetInsertPoint(checkBalanceBB); 294 | builder.CreateStore(&addr, addrAlloca); 295 | builder.CreateCall(getBalanceFn, {v, &env, addrAlloca}); 296 | llvm::Value* balance = builder.CreateLoad(v); 297 | balance = Endianness::toNative(builder, balance); 298 | value = Endianness::toNative(builder, value); 299 | auto balanceOk = builder.CreateICmpUGE(balance, value); 300 | builder.CreateCondBr(balanceOk, callBB, failBB); 301 | 302 | builder.SetInsertPoint(callBB); 303 | // Pass the first 11 args to the external call. 304 | llvm::Value* args[11]; 305 | auto it = func->arg_begin(); 306 | for (auto outIt = std::begin(args); outIt != std::end(args); ++it, ++outIt) 307 | *outIt = &*it; 308 | auto ret = builder.CreateCall(callFunc, args); 309 | builder.CreateRet(ret); 310 | 311 | builder.SetInsertPoint(failBB); 312 | auto failRet = builder.CreateOr(&gas, builder.getInt64(EVM_CALL_FAILURE)); 313 | builder.CreateRet(failRet); 314 | } 315 | return func; 316 | } 317 | 318 | llvm::Function* getBlockHashFunc(llvm::Module* _module) 319 | { 320 | static const auto funcName = "evm.blockhash"; 321 | auto func = _module->getFunction(funcName); 322 | if (!func) 323 | { 324 | // TODO: Mark the function as pure to eliminate multiple calls. 325 | auto i64 = llvm::IntegerType::getInt64Ty(_module->getContext()); 326 | auto fty = llvm::FunctionType::get(Type::Void, {Type::WordPtr, Type::EnvPtr, i64}, false); 327 | func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); 328 | func->addAttribute(1, llvm::Attribute::NoAlias); 329 | func->addAttribute(1, llvm::Attribute::NoCapture); 330 | } 331 | return func; 332 | } 333 | 334 | } 335 | 336 | 337 | 338 | llvm::Value* Ext::getArgAlloca() 339 | { 340 | auto& a = m_argAllocas[m_argCounter]; 341 | if (!a) 342 | { 343 | InsertPointGuard g{m_builder}; 344 | auto allocaIt = getMainFunction()->front().begin(); 345 | auto allocaPtr = &(*allocaIt); 346 | std::advance(allocaIt, m_argCounter); // Skip already created allocas 347 | m_builder.SetInsertPoint(allocaPtr); 348 | a = m_builder.CreateAlloca(Type::Word, nullptr, {"a.", std::to_string(m_argCounter)}); 349 | } 350 | ++m_argCounter; 351 | return a; 352 | } 353 | 354 | llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list const& _args) 355 | { 356 | auto& func = m_funcs[static_cast(_funcId)]; 357 | if (!func) 358 | func = createFunc(_funcId, getModule()); 359 | 360 | m_argCounter = 0; 361 | return m_builder.CreateCall(func, {_args.begin(), _args.size()}); 362 | } 363 | 364 | llvm::Value* Ext::createCABICall(llvm::Function* _func, std::initializer_list const& _args) 365 | { 366 | auto args = llvm::SmallVector{_args}; 367 | for (auto&& farg: _func->args()) 368 | { 369 | if (farg.hasByValAttr() || farg.getType()->isPointerTy()) 370 | { 371 | auto& arg = args[farg.getArgNo()]; 372 | // TODO: Remove defensive check and always use it this way. 373 | if (!arg->getType()->isPointerTy()) 374 | { 375 | auto mem = getArgAlloca(); 376 | // TODO: The bitcast may be redundant 377 | mem = m_builder.CreateBitCast(mem, arg->getType()->getPointerTo()); 378 | m_builder.CreateStore(arg, mem); 379 | arg = mem; 380 | } 381 | } 382 | } 383 | 384 | m_argCounter = 0; 385 | return m_builder.CreateCall(_func, args); 386 | } 387 | 388 | llvm::Value* Ext::sload(llvm::Value* _index) 389 | { 390 | auto index = Endianness::toBE(m_builder, _index); 391 | auto addrTy = m_builder.getIntNTy(160); 392 | auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); 393 | auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); 394 | m_builder.CreateStore(myAddr, pAddr); 395 | auto func = getGetStorageFunc(getModule()); 396 | auto pValue = getArgAlloca(); 397 | createCABICall(func, {pValue, getRuntimeManager().getEnvPtr(), pAddr, index}); 398 | return Endianness::toNative(m_builder, m_builder.CreateLoad(pValue)); 399 | } 400 | 401 | void Ext::sstore(llvm::Value* _index, llvm::Value* _value) 402 | { 403 | auto addrTy = m_builder.getIntNTy(160); 404 | auto index = Endianness::toBE(m_builder, _index); 405 | auto value = Endianness::toBE(m_builder, _value); 406 | auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); 407 | auto func = getSetStorageFunc(getModule()); 408 | createCABICall(func, {getRuntimeManager().getEnvPtr(), myAddr, index, value}); 409 | } 410 | 411 | void Ext::selfdestruct(llvm::Value* _beneficiary) 412 | { 413 | auto addrTy = m_builder.getIntNTy(160); 414 | auto func = getSelfdestructFunc(getModule()); 415 | auto b = Endianness::toBE(m_builder, m_builder.CreateTrunc(_beneficiary, addrTy)); 416 | auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); 417 | createCABICall(func, {getRuntimeManager().getEnvPtr(), myAddr, b}); 418 | } 419 | 420 | llvm::Value* Ext::calldataload(llvm::Value* _idx) 421 | { 422 | auto ret = getArgAlloca(); 423 | auto result = m_builder.CreateBitCast(ret, Type::BytePtr); 424 | 425 | auto callDataSize = getRuntimeManager().getCallDataSize(); 426 | auto callDataSize64 = m_builder.CreateTrunc(callDataSize, Type::Size); 427 | auto idxValid = m_builder.CreateICmpULT(_idx, callDataSize); 428 | auto idx = m_builder.CreateTrunc(m_builder.CreateSelect(idxValid, _idx, callDataSize), Type::Size, "idx"); 429 | 430 | auto end = m_builder.CreateNUWAdd(idx, m_builder.getInt64(32)); 431 | end = m_builder.CreateSelect(m_builder.CreateICmpULE(end, callDataSize64), end, callDataSize64); 432 | auto copySize = m_builder.CreateNUWSub(end, idx); 433 | auto padSize = m_builder.CreateNUWSub(m_builder.getInt64(32), copySize); 434 | auto dataBegin = m_builder.CreateGEP(Type::Byte, getRuntimeManager().getCallData(), idx); 435 | m_builder.CreateMemCpy(result, dataBegin, copySize, 1); 436 | auto pad = m_builder.CreateGEP(Type::Byte, result, copySize); 437 | m_builder.CreateMemSet(pad, m_builder.getInt8(0), padSize, 1); 438 | 439 | m_argCounter = 0; // Release args allocas. TODO: This is a bad design 440 | return Endianness::toNative(m_builder, m_builder.CreateLoad(ret)); 441 | } 442 | 443 | llvm::Value* Ext::balance(llvm::Value* _address) 444 | { 445 | auto func = getGetBalanceFunc(getModule()); 446 | auto addrTy = m_builder.getIntNTy(160); 447 | auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); 448 | auto pResult = getArgAlloca(); 449 | auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); 450 | m_builder.CreateStore(address, pAddr); 451 | createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), pAddr}); 452 | return Endianness::toNative(m_builder, m_builder.CreateLoad(pResult)); 453 | } 454 | 455 | llvm::Value* Ext::exists(llvm::Value* _address) 456 | { 457 | auto func = getAccountExistsFunc(getModule()); 458 | auto addrTy = m_builder.getIntNTy(160); 459 | auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); 460 | auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); 461 | m_builder.CreateStore(address, pAddr); 462 | auto r = createCABICall(func, {getRuntimeManager().getEnvPtr(), pAddr}); 463 | return m_builder.CreateTrunc(r, m_builder.getInt1Ty()); 464 | } 465 | 466 | llvm::Value* Ext::blockHash(llvm::Value* _number) 467 | { 468 | auto func = getBlockHashFunc(getModule()); 469 | auto number = m_builder.CreateTrunc(_number, m_builder.getInt64Ty()); 470 | auto pResult = getArgAlloca(); 471 | createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), number}); 472 | return Endianness::toNative(m_builder, m_builder.CreateLoad(pResult)); 473 | } 474 | 475 | llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) 476 | { 477 | auto begin = m_memoryMan.getBytePtr(_inOff); 478 | auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size"); 479 | auto ret = getArgAlloca(); 480 | createCall(EnvFunc::sha3, {begin, size, ret}); 481 | llvm::Value* hash = m_builder.CreateLoad(ret); 482 | return Endianness::toNative(m_builder, hash); 483 | } 484 | 485 | MemoryRef Ext::extcode(llvm::Value* _address) 486 | { 487 | auto func = getGetCodeFunc(getModule()); 488 | auto addrTy = m_builder.getIntNTy(160); 489 | auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); 490 | auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); 491 | m_builder.CreateStore(address, pAddr); 492 | auto a = getArgAlloca(); 493 | auto codePtrPtr = m_builder.CreateBitCast(a, Type::BytePtr->getPointerTo()); 494 | auto size = createCABICall(func, {codePtrPtr, getRuntimeManager().getEnvPtr(), pAddr}); 495 | auto code = m_builder.CreateLoad(codePtrPtr, "code"); 496 | auto size256 = m_builder.CreateZExt(size, Type::Word); 497 | return {code, size256}; 498 | } 499 | 500 | llvm::Value* Ext::extcodesize(llvm::Value* _address) 501 | { 502 | auto func = getGetCodeSizeFunc(getModule()); 503 | auto addrTy = m_builder.getIntNTy(160); 504 | auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); 505 | auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); 506 | m_builder.CreateStore(address, pAddr); 507 | auto size = createCABICall(func, {getRuntimeManager().getEnvPtr(), pAddr}); 508 | return m_builder.CreateZExt(size, Type::Word); 509 | } 510 | 511 | void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, llvm::ArrayRef _topics) 512 | { 513 | if (!m_topics) 514 | { 515 | InsertPointGuard g{m_builder}; 516 | auto& entryBB = getMainFunction()->front(); 517 | m_builder.SetInsertPoint(&entryBB, entryBB.begin()); 518 | m_topics = m_builder.CreateAlloca(Type::Word, m_builder.getInt32(4), "topics"); 519 | } 520 | 521 | auto dataPtr = m_memoryMan.getBytePtr(_memIdx); 522 | auto dataSize = m_builder.CreateTrunc(_numBytes, Type::Size, "data.size"); 523 | 524 | for (size_t i = 0; i < _topics.size(); ++i) 525 | { 526 | auto t = Endianness::toBE(m_builder, _topics[i]); 527 | auto p = m_builder.CreateConstGEP1_32(m_topics, static_cast(i)); 528 | m_builder.CreateStore(t, p); 529 | } 530 | auto numTopics = m_builder.getInt64(_topics.size()); 531 | 532 | auto addrTy = m_builder.getIntNTy(160); 533 | auto func = getLogFunc(getModule()); 534 | 535 | auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); 536 | createCABICall(func, { 537 | getRuntimeManager().getEnvPtr(), myAddr, dataPtr, dataSize, m_topics, numTopics 538 | }); 539 | } 540 | 541 | llvm::Value* Ext::call(int _kind, 542 | llvm::Value* _gas, 543 | llvm::Value* _addr, 544 | llvm::Value* _value, 545 | llvm::Value* _inOff, 546 | llvm::Value* _inSize, 547 | llvm::Value* _outOff, 548 | llvm::Value* _outSize) 549 | { 550 | auto gas = m_builder.CreateTrunc(_gas, Type::Size); 551 | auto addrTy = m_builder.getIntNTy(160); 552 | auto addr = m_builder.CreateTrunc(_addr, addrTy); 553 | addr = Endianness::toBE(m_builder, addr); 554 | auto inData = m_memoryMan.getBytePtr(_inOff); 555 | auto inSize = m_builder.CreateTrunc(_inSize, Type::Size); 556 | auto outData = m_memoryMan.getBytePtr(_outOff); 557 | auto outSize = m_builder.CreateTrunc(_outSize, Type::Size); 558 | 559 | auto value = getArgAlloca(); 560 | m_builder.CreateStore(Endianness::toBE(m_builder, _value), value); 561 | 562 | auto func = getCallFunc(getModule()); 563 | auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); 564 | getRuntimeManager().resetReturnBuf(); 565 | return createCABICall( 566 | func, 567 | {getRuntimeManager().getEnvPtr(), m_builder.getInt32(_kind), gas, 568 | addr, value, inData, inSize, outData, outSize, 569 | getRuntimeManager().getReturnBufDataPtr(), getRuntimeManager().getReturnBufSizePtr(), 570 | myAddr, getRuntimeManager().getDepth() 571 | }); 572 | } 573 | 574 | std::tuple Ext::create(llvm::Value* _gas, 575 | llvm::Value* _endowment, 576 | llvm::Value* _initOff, 577 | llvm::Value* _initSize) 578 | { 579 | auto addrTy = m_builder.getIntNTy(160); 580 | auto value = getArgAlloca(); 581 | m_builder.CreateStore(Endianness::toBE(m_builder, _endowment), value); 582 | auto inData = m_memoryMan.getBytePtr(_initOff); 583 | auto inSize = m_builder.CreateTrunc(_initSize, Type::Size); 584 | auto pAddr = 585 | m_builder.CreateBitCast(getArgAlloca(), m_builder.getInt8PtrTy()); 586 | 587 | auto func = getCallFunc(getModule()); 588 | auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); 589 | getRuntimeManager().resetReturnBuf(); 590 | auto ret = createCABICall( 591 | func, {getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVMC_CREATE), 592 | _gas, llvm::UndefValue::get(addrTy), value, inData, inSize, pAddr, 593 | m_builder.getInt64(20), 594 | getRuntimeManager().getReturnBufDataPtr(), getRuntimeManager().getReturnBufSizePtr(), 595 | myAddr, getRuntimeManager().getDepth() 596 | }); 597 | 598 | pAddr = m_builder.CreateBitCast(pAddr, addrTy->getPointerTo()); 599 | return std::tuple{ret, pAddr}; 600 | } 601 | } 602 | } 603 | } 604 | -------------------------------------------------------------------------------- /libevmjit/Ext.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "JIT.h" 6 | #include "CompilerHelper.h" 7 | 8 | namespace dev 9 | { 10 | namespace eth 11 | { 12 | namespace jit 13 | { 14 | 15 | /// The flag indicating call failure in evmc_call_fn() -- highest bit set. 16 | constexpr int64_t EVM_CALL_FAILURE = 0x8000000000000000; 17 | 18 | /// The hackish constant indicating EVM_CALL + EVM_STATIC flag. 19 | constexpr int EVM_STATICCALL = EVMC_CREATE + 1; 20 | 21 | class Memory; 22 | 23 | struct MemoryRef 24 | { 25 | llvm::Value* ptr; 26 | llvm::Value* size; 27 | }; 28 | 29 | template 30 | struct sizeOf 31 | { 32 | static const size_t value = static_cast(_EnumT::_size); 33 | }; 34 | 35 | enum class EnvFunc 36 | { 37 | sload, 38 | sstore, 39 | sha3, 40 | balance, 41 | create, 42 | call, 43 | log, 44 | blockhash, 45 | extcode, 46 | 47 | _size 48 | }; 49 | 50 | class Ext : public RuntimeHelper 51 | { 52 | public: 53 | Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan); 54 | 55 | llvm::Value* sload(llvm::Value* _index); 56 | void sstore(llvm::Value* _index, llvm::Value* _value); 57 | 58 | llvm::Value* balance(llvm::Value* _address); 59 | llvm::Value* exists(llvm::Value* _address); 60 | llvm::Value* calldataload(llvm::Value* _index); 61 | std::tuple create(llvm::Value* _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); 62 | llvm::Value* blockHash(llvm::Value* _number); 63 | 64 | llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); 65 | MemoryRef extcode(llvm::Value* _addr); 66 | llvm::Value* extcodesize(llvm::Value* _addr); 67 | 68 | void log(llvm::Value* _memIdx, llvm::Value* _numBytes, llvm::ArrayRef _topics); 69 | void selfdestruct(llvm::Value* _beneficiary); 70 | 71 | llvm::Value* call(int _kind, 72 | llvm::Value* _gas, 73 | llvm::Value* _addr, 74 | llvm::Value* _value, 75 | llvm::Value* _inOff, 76 | llvm::Value* _inSize, 77 | llvm::Value* _outOff, 78 | llvm::Value* _outSize); 79 | 80 | private: 81 | Memory& m_memoryMan; 82 | 83 | llvm::Value* m_size; 84 | 85 | std::array::value> m_funcs; 86 | std::array m_argAllocas; 87 | size_t m_argCounter = 0; 88 | 89 | /// Memory for array of up to 4 log topics 90 | /// TODO: Merge this memory with args allocas. 91 | llvm::Value* m_topics = nullptr; 92 | 93 | llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list const& _args); 94 | llvm::Value* getArgAlloca(); 95 | 96 | llvm::Value* createCABICall(llvm::Function* _func, 97 | std::initializer_list const& _args); 98 | }; 99 | 100 | 101 | } 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /libevmjit/GasMeter.cpp: -------------------------------------------------------------------------------- 1 | #include "GasMeter.h" 2 | 3 | #include "preprocessor/llvm_includes_start.h" 4 | #include 5 | #include "preprocessor/llvm_includes_end.h" 6 | 7 | #include "JIT.h" 8 | #include "Ext.h" 9 | #include "RuntimeManager.h" 10 | 11 | namespace dev 12 | { 13 | namespace eth 14 | { 15 | namespace jit 16 | { 17 | 18 | GasMeter::GasMeter(IRBuilder& _builder, RuntimeManager& _runtimeManager, evmc_revision rev): 19 | CompilerHelper(_builder), 20 | m_runtimeManager(_runtimeManager), 21 | m_rev(rev) 22 | { 23 | llvm::Type* gasCheckArgs[] = {Type::Gas->getPointerTo(), Type::Gas, Type::BytePtr}; 24 | m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", getModule()); 25 | m_gasCheckFunc->setDoesNotThrow(); 26 | m_gasCheckFunc->addAttribute(1, llvm::Attribute::NoCapture); 27 | 28 | auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc); 29 | auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc); 30 | auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc); 31 | 32 | auto iter = m_gasCheckFunc->arg_begin(); 33 | llvm::Argument* gasPtr = &(*iter++); 34 | gasPtr->setName("gasPtr"); 35 | llvm::Argument* cost = &(*iter++); 36 | cost->setName("cost"); 37 | llvm::Argument* jmpBuf = &(*iter); 38 | jmpBuf->setName("jmpBuf"); 39 | 40 | InsertPointGuard guard(m_builder); 41 | m_builder.SetInsertPoint(checkBB); 42 | auto gas = m_builder.CreateLoad(gasPtr, "gas"); 43 | auto gasUpdated = m_builder.CreateNSWSub(gas, cost, "gasUpdated"); 44 | auto gasOk = m_builder.CreateICmpSGE(gasUpdated, m_builder.getInt64(0), "gasOk"); // gas >= 0, with gas == 0 we can still do 0 cost instructions 45 | m_builder.CreateCondBr(gasOk, updateBB, outOfGasBB, Type::expectTrue); 46 | 47 | m_builder.SetInsertPoint(updateBB); 48 | m_builder.CreateStore(gasUpdated, gasPtr); 49 | m_builder.CreateRetVoid(); 50 | 51 | m_builder.SetInsertPoint(outOfGasBB); 52 | m_runtimeManager.abort(jmpBuf); 53 | m_builder.CreateUnreachable(); 54 | } 55 | 56 | void GasMeter::count(Instruction _inst) 57 | { 58 | if (!m_checkCall) 59 | { 60 | // Create gas check call with mocked block cost at begining of current cost-block 61 | m_checkCall = m_builder.CreateCall(m_gasCheckFunc, {m_runtimeManager.getGasPtr(), llvm::UndefValue::get(Type::Gas), m_runtimeManager.getJmpBuf()}); 62 | } 63 | 64 | m_blockCost += getStepCost(_inst); 65 | } 66 | 67 | void GasMeter::count(llvm::Value* _cost, llvm::Value* _jmpBuf, llvm::Value* _gasPtr) 68 | { 69 | if (_cost->getType() == Type::Word) 70 | { 71 | auto gasMax256 = m_builder.CreateZExt(Constant::gasMax, Type::Word); 72 | auto tooHigh = m_builder.CreateICmpUGT(_cost, gasMax256, "costTooHigh"); 73 | auto cost64 = m_builder.CreateTrunc(_cost, Type::Gas); 74 | _cost = m_builder.CreateSelect(tooHigh, Constant::gasMax, cost64, "cost"); 75 | } 76 | 77 | assert(_cost->getType() == Type::Gas); 78 | m_builder.CreateCall(m_gasCheckFunc, {_gasPtr ? _gasPtr : m_runtimeManager.getGasPtr(), _cost, _jmpBuf ? _jmpBuf : m_runtimeManager.getJmpBuf()}); 79 | } 80 | 81 | void GasMeter::countExp(llvm::Value* _exponent) 82 | { 83 | // Additional cost is 1 per significant byte of exponent 84 | // lz - leading zeros 85 | // cost = ((256 - lz) + 7) / 8 86 | 87 | // OPT: Can gas update be done in exp algorithm? 88 | auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); 89 | auto lz256 = m_builder.CreateCall(ctlz, {_exponent, m_builder.getInt1(false)}); 90 | auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz"); 91 | auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits"); 92 | auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8)); 93 | auto exponentByteCost = m_rev >= EVMC_SPURIOUS_DRAGON ? 50 : JITSchedule::expByteGas::value; 94 | count(m_builder.CreateNUWMul(sigBytes, m_builder.getInt64(exponentByteCost))); 95 | } 96 | 97 | void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue) 98 | { 99 | auto oldValue = _ext.sload(_index); 100 | auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero"); 101 | auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero"); 102 | auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert"); 103 | assert(JITSchedule::sstoreResetGas::value == JITSchedule::sstoreClearGas::value && "Update SSTORE gas cost"); 104 | auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(JITSchedule::sstoreSetGas::value), m_builder.getInt64(JITSchedule::sstoreResetGas::value), "cost"); 105 | count(cost); 106 | } 107 | 108 | void GasMeter::countLogData(llvm::Value* _dataLength) 109 | { 110 | assert(m_checkCall); 111 | assert(m_blockCost > 0); // LOGn instruction is already counted 112 | assert(JITSchedule::logDataGas::value != 1 && "Log data gas cost has changed. Update GasMeter."); 113 | count(m_builder.CreateNUWMul(_dataLength, Constant::get(JITSchedule::logDataGas::value))); // TODO: Use i64 114 | } 115 | 116 | void GasMeter::countSha3Data(llvm::Value* _dataLength) 117 | { 118 | assert(m_checkCall); 119 | assert(m_blockCost > 0); // SHA3 instruction is already counted 120 | 121 | // TODO: This round ups to 32 happens in many places 122 | assert(JITSchedule::sha3WordGas::value != 1 && "SHA3 data cost has changed. Update GasMeter"); 123 | auto dataLength64 = m_builder.CreateTrunc(_dataLength, Type::Gas); 124 | auto words64 = m_builder.CreateUDiv(m_builder.CreateNUWAdd(dataLength64, m_builder.getInt64(31)), m_builder.getInt64(32)); 125 | auto cost64 = m_builder.CreateNUWMul(m_builder.getInt64(JITSchedule::sha3WordGas::value), words64); 126 | count(cost64); 127 | } 128 | 129 | void GasMeter::giveBack(llvm::Value* _gas) 130 | { 131 | assert(_gas->getType() == Type::Gas); 132 | m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas)); 133 | } 134 | 135 | void GasMeter::commitCostBlock() 136 | { 137 | // If any uncommited block 138 | if (m_checkCall) 139 | { 140 | if (m_blockCost == 0) // Do not check 0 141 | { 142 | m_checkCall->eraseFromParent(); // Remove the gas check call 143 | m_checkCall = nullptr; 144 | return; 145 | } 146 | 147 | m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas check call 148 | m_checkCall = nullptr; // End cost-block 149 | m_blockCost = 0; 150 | } 151 | assert(m_blockCost == 0); 152 | } 153 | 154 | void GasMeter::countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr) 155 | { 156 | assert(JITSchedule::memoryGas::value != 1 && "Memory gas cost has changed. Update GasMeter."); 157 | count(_additionalMemoryInWords, _jmpBuf, _gasPtr); 158 | } 159 | 160 | void GasMeter::countCopy(llvm::Value* _copyWords) 161 | { 162 | assert(JITSchedule::copyGas::value != 1 && "Copy gas cost has changed. Update GasMeter."); 163 | count(m_builder.CreateNUWMul(_copyWords, m_builder.getInt64(JITSchedule::copyGas::value))); 164 | } 165 | 166 | int64_t GasMeter::getStepCost(Instruction inst) const 167 | { 168 | switch (inst) 169 | { 170 | // Tier 0 171 | case Instruction::STOP: 172 | case Instruction::RETURN: 173 | case Instruction::REVERT: 174 | case Instruction::SSTORE: // Handle cost of SSTORE separately in GasMeter::countSStore() 175 | return JITSchedule::stepGas0::value; 176 | 177 | // Tier 1 178 | case Instruction::ADDRESS: 179 | case Instruction::ORIGIN: 180 | case Instruction::CALLER: 181 | case Instruction::CALLVALUE: 182 | case Instruction::CALLDATASIZE: 183 | case Instruction::RETURNDATASIZE: 184 | case Instruction::CODESIZE: 185 | case Instruction::GASPRICE: 186 | case Instruction::COINBASE: 187 | case Instruction::TIMESTAMP: 188 | case Instruction::NUMBER: 189 | case Instruction::DIFFICULTY: 190 | case Instruction::GASLIMIT: 191 | case Instruction::POP: 192 | case Instruction::PC: 193 | case Instruction::MSIZE: 194 | case Instruction::GAS: 195 | return JITSchedule::stepGas1::value; 196 | 197 | // Tier 2 198 | case Instruction::ADD: 199 | case Instruction::SUB: 200 | case Instruction::LT: 201 | case Instruction::GT: 202 | case Instruction::SLT: 203 | case Instruction::SGT: 204 | case Instruction::EQ: 205 | case Instruction::ISZERO: 206 | case Instruction::AND: 207 | case Instruction::OR: 208 | case Instruction::XOR: 209 | case Instruction::NOT: 210 | case Instruction::BYTE: 211 | case Instruction::CALLDATALOAD: 212 | case Instruction::CALLDATACOPY: 213 | case Instruction::RETURNDATACOPY: 214 | case Instruction::CODECOPY: 215 | case Instruction::MLOAD: 216 | case Instruction::MSTORE: 217 | case Instruction::MSTORE8: 218 | case Instruction::ANY_PUSH: 219 | case Instruction::ANY_DUP: 220 | case Instruction::ANY_SWAP: 221 | return JITSchedule::stepGas2::value; 222 | 223 | // Tier 3 224 | case Instruction::MUL: 225 | case Instruction::DIV: 226 | case Instruction::SDIV: 227 | case Instruction::MOD: 228 | case Instruction::SMOD: 229 | case Instruction::SIGNEXTEND: 230 | return JITSchedule::stepGas3::value; 231 | 232 | // Tier 4 233 | case Instruction::ADDMOD: 234 | case Instruction::MULMOD: 235 | case Instruction::JUMP: 236 | return JITSchedule::stepGas4::value; 237 | 238 | // Tier 5 239 | case Instruction::EXP: 240 | case Instruction::JUMPI: 241 | return JITSchedule::stepGas5::value; 242 | 243 | // Tier 6 244 | case Instruction::BALANCE: 245 | return m_rev >= EVMC_TANGERINE_WHISTLE ? 400 : JITSchedule::stepGas6::value; 246 | 247 | case Instruction::EXTCODESIZE: 248 | case Instruction::EXTCODECOPY: 249 | return m_rev >= EVMC_TANGERINE_WHISTLE ? 700 : JITSchedule::stepGas6::value; 250 | 251 | case Instruction::BLOCKHASH: 252 | return m_rev >= EVMC_CONSTANTINOPLE ? 800 : JITSchedule::stepGas6::value; 253 | 254 | case Instruction::SHA3: 255 | return JITSchedule::sha3Gas::value; 256 | 257 | case Instruction::SLOAD: 258 | return m_rev >= EVMC_TANGERINE_WHISTLE ? 200 : JITSchedule::sloadGas::value; 259 | 260 | case Instruction::JUMPDEST: 261 | return JITSchedule::jumpdestGas::value; 262 | 263 | case Instruction::LOG0: 264 | case Instruction::LOG1: 265 | case Instruction::LOG2: 266 | case Instruction::LOG3: 267 | case Instruction::LOG4: 268 | { 269 | auto numTopics = static_cast(inst) - static_cast(Instruction::LOG0); 270 | return JITSchedule::logGas::value + numTopics * JITSchedule::logTopicGas::value; 271 | } 272 | 273 | case Instruction::CALL: 274 | case Instruction::CALLCODE: 275 | case Instruction::DELEGATECALL: 276 | case Instruction::STATICCALL: 277 | return m_rev >= EVMC_TANGERINE_WHISTLE ? 700 : JITSchedule::callGas::value; 278 | 279 | case Instruction::CREATE: 280 | return JITSchedule::createGas::value; 281 | 282 | case Instruction::SUICIDE: 283 | return m_rev >= EVMC_TANGERINE_WHISTLE ? 5000 : JITSchedule::stepGas0::value; 284 | 285 | default: 286 | // For invalid instruction just return 0. 287 | return 0; 288 | } 289 | } 290 | 291 | } 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /libevmjit/GasMeter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "CompilerHelper.h" 5 | #include "Instruction.h" 6 | 7 | namespace dev 8 | { 9 | namespace eth 10 | { 11 | namespace jit 12 | { 13 | class RuntimeManager; 14 | using namespace evmjit; 15 | 16 | class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper 17 | { 18 | public: 19 | GasMeter(IRBuilder& _builder, RuntimeManager& _runtimeManager, evmc_revision rev); 20 | 21 | /// Count step cost of instruction 22 | void count(Instruction _inst); 23 | 24 | /// Count additional cost 25 | void count(llvm::Value* _cost, llvm::Value* _jmpBuf = nullptr, llvm::Value* _gasPtr = nullptr); 26 | 27 | /// Calculate & count gas cost for SSTORE instruction 28 | void countSStore(class Ext& _ext, llvm::Value* _index, llvm::Value* _newValue); 29 | 30 | /// Calculate & count additional gas cost for EXP instruction 31 | void countExp(llvm::Value* _exponent); 32 | 33 | /// Count gas cost of LOG data 34 | void countLogData(llvm::Value* _dataLength); 35 | 36 | /// Count gas cost of SHA3 data 37 | void countSha3Data(llvm::Value* _dataLength); 38 | 39 | /// Finalize cost-block by checking gas needed for the block before the block 40 | void commitCostBlock(); 41 | 42 | /// Give back an amount of gas not used by a call 43 | void giveBack(llvm::Value* _gas); 44 | 45 | /// Generate code that checks the cost of additional memory used by program 46 | void countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr); 47 | 48 | /// Count addional gas cost for memory copy 49 | void countCopy(llvm::Value* _copyWords); 50 | 51 | private: 52 | int64_t getStepCost(Instruction inst) const; 53 | 54 | /// Cumulative gas cost of a block of instructions 55 | /// @TODO Handle overflow 56 | int64_t m_blockCost = 0; 57 | 58 | llvm::CallInst* m_checkCall = nullptr; 59 | llvm::Function* m_gasCheckFunc = nullptr; 60 | 61 | RuntimeManager& m_runtimeManager; 62 | 63 | /// EVM revision. 64 | evmc_revision m_rev; 65 | }; 66 | 67 | } 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /libevmjit/Instruction.cpp: -------------------------------------------------------------------------------- 1 | #include "Instruction.h" 2 | 3 | #include "preprocessor/llvm_includes_start.h" 4 | #include 5 | #include "preprocessor/llvm_includes_end.h" 6 | 7 | namespace dev 8 | { 9 | namespace evmjit 10 | { 11 | 12 | llvm::APInt readPushData(code_iterator& _curr, code_iterator _end) 13 | { 14 | auto pushInst = *_curr; 15 | assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); 16 | auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; 17 | llvm::APInt value(256, 0); 18 | ++_curr; // Point the data 19 | for (decltype(numBytes) i = 0; i < numBytes; ++i) 20 | { 21 | byte b = (_curr != _end) ? *_curr++ : 0; 22 | value <<= 8; 23 | value |= b; 24 | } 25 | --_curr; // Point the last real byte read 26 | return value; 27 | } 28 | 29 | void skipPushData(code_iterator& _curr, code_iterator _end) 30 | { 31 | auto pushInst = *_curr; 32 | assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); 33 | auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; 34 | --_end; 35 | for (decltype(numBytes) i = 0; i < numBytes && _curr < _end; ++i, ++_curr) {} 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /libevmjit/Instruction.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.h" 4 | 5 | namespace llvm 6 | { 7 | class APInt; 8 | } 9 | 10 | namespace dev 11 | { 12 | namespace evmjit 13 | { 14 | 15 | /// Virtual machine bytecode instruction. 16 | enum class Instruction: uint8_t 17 | { 18 | STOP = 0x00, ///< halts execution 19 | ADD, ///< addition operation 20 | MUL, ///< mulitplication operation 21 | SUB, ///< subtraction operation 22 | DIV, ///< integer division operation 23 | SDIV, ///< signed integer division operation 24 | MOD, ///< modulo remainder operation 25 | SMOD, ///< signed modulo remainder operation 26 | ADDMOD, ///< unsigned modular addition 27 | MULMOD, ///< unsigned modular multiplication 28 | EXP, ///< exponential operation 29 | SIGNEXTEND, ///< extend length of signed integer 30 | 31 | LT = 0x10, ///< less-than comparision 32 | GT, ///< greater-than comparision 33 | SLT, ///< signed less-than comparision 34 | SGT, ///< signed greater-than comparision 35 | EQ, ///< equality comparision 36 | ISZERO, ///< simple not operator 37 | AND, ///< bitwise AND operation 38 | OR, ///< bitwise OR operation 39 | XOR, ///< bitwise XOR operation 40 | NOT, ///< bitwise NOT opertation 41 | BYTE, ///< retrieve single byte from word 42 | 43 | SHA3 = 0x20, ///< compute SHA3-256 hash 44 | 45 | ADDRESS = 0x30, ///< get address of currently executing account 46 | BALANCE, ///< get balance of the given account 47 | ORIGIN, ///< get execution origination address 48 | CALLER, ///< get caller address 49 | CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution 50 | CALLDATALOAD, ///< get input data of current environment 51 | CALLDATASIZE, ///< get size of input data in current environment 52 | CALLDATACOPY, ///< copy input data in current environment to memory 53 | CODESIZE, ///< get size of code running in current environment 54 | CODECOPY, ///< copy code running in current environment to memory 55 | GASPRICE, ///< get price of gas in current environment 56 | EXTCODESIZE, ///< get external code size (from another contract) 57 | EXTCODECOPY, ///< copy external code (from another contract) 58 | RETURNDATASIZE = 0x3d, 59 | RETURNDATACOPY = 0x3e, 60 | 61 | BLOCKHASH = 0x40, ///< get hash of most recent complete block 62 | COINBASE, ///< get the block's coinbase address 63 | TIMESTAMP, ///< get the block's timestamp 64 | NUMBER, ///< get the block's number 65 | DIFFICULTY, ///< get the block's difficulty 66 | GASLIMIT, ///< get the block's gas limit 67 | 68 | POP = 0x50, ///< remove item from stack 69 | MLOAD, ///< load word from memory 70 | MSTORE, ///< save word to memory 71 | MSTORE8, ///< save byte to memory 72 | SLOAD, ///< load word from storage 73 | SSTORE, ///< save word to storage 74 | JUMP, ///< alter the program counter 75 | JUMPI, ///< conditionally alter the program counter 76 | PC, ///< get the program counter 77 | MSIZE, ///< get the size of active memory 78 | GAS, ///< get the amount of available gas 79 | JUMPDEST, ///< set a potential jump destination 80 | 81 | PUSH1 = 0x60, ///< place 1 byte item on stack 82 | PUSH2, ///< place 2 byte item on stack 83 | PUSH3, ///< place 3 byte item on stack 84 | PUSH4, ///< place 4 byte item on stack 85 | PUSH5, ///< place 5 byte item on stack 86 | PUSH6, ///< place 6 byte item on stack 87 | PUSH7, ///< place 7 byte item on stack 88 | PUSH8, ///< place 8 byte item on stack 89 | PUSH9, ///< place 9 byte item on stack 90 | PUSH10, ///< place 10 byte item on stack 91 | PUSH11, ///< place 11 byte item on stack 92 | PUSH12, ///< place 12 byte item on stack 93 | PUSH13, ///< place 13 byte item on stack 94 | PUSH14, ///< place 14 byte item on stack 95 | PUSH15, ///< place 15 byte item on stack 96 | PUSH16, ///< place 16 byte item on stack 97 | PUSH17, ///< place 17 byte item on stack 98 | PUSH18, ///< place 18 byte item on stack 99 | PUSH19, ///< place 19 byte item on stack 100 | PUSH20, ///< place 20 byte item on stack 101 | PUSH21, ///< place 21 byte item on stack 102 | PUSH22, ///< place 22 byte item on stack 103 | PUSH23, ///< place 23 byte item on stack 104 | PUSH24, ///< place 24 byte item on stack 105 | PUSH25, ///< place 25 byte item on stack 106 | PUSH26, ///< place 26 byte item on stack 107 | PUSH27, ///< place 27 byte item on stack 108 | PUSH28, ///< place 28 byte item on stack 109 | PUSH29, ///< place 29 byte item on stack 110 | PUSH30, ///< place 30 byte item on stack 111 | PUSH31, ///< place 31 byte item on stack 112 | PUSH32, ///< place 32 byte item on stack 113 | 114 | DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack 115 | DUP2, ///< copies the second highest item in the stack to the top of the stack 116 | DUP3, ///< copies the third highest item in the stack to the top of the stack 117 | DUP4, ///< copies the 4th highest item in the stack to the top of the stack 118 | DUP5, ///< copies the 5th highest item in the stack to the top of the stack 119 | DUP6, ///< copies the 6th highest item in the stack to the top of the stack 120 | DUP7, ///< copies the 7th highest item in the stack to the top of the stack 121 | DUP8, ///< copies the 8th highest item in the stack to the top of the stack 122 | DUP9, ///< copies the 9th highest item in the stack to the top of the stack 123 | DUP10, ///< copies the 10th highest item in the stack to the top of the stack 124 | DUP11, ///< copies the 11th highest item in the stack to the top of the stack 125 | DUP12, ///< copies the 12th highest item in the stack to the top of the stack 126 | DUP13, ///< copies the 13th highest item in the stack to the top of the stack 127 | DUP14, ///< copies the 14th highest item in the stack to the top of the stack 128 | DUP15, ///< copies the 15th highest item in the stack to the top of the stack 129 | DUP16, ///< copies the 16th highest item in the stack to the top of the stack 130 | 131 | SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack 132 | SWAP2, ///< swaps the highest and third highest value on the stack 133 | SWAP3, ///< swaps the highest and 4th highest value on the stack 134 | SWAP4, ///< swaps the highest and 5th highest value on the stack 135 | SWAP5, ///< swaps the highest and 6th highest value on the stack 136 | SWAP6, ///< swaps the highest and 7th highest value on the stack 137 | SWAP7, ///< swaps the highest and 8th highest value on the stack 138 | SWAP8, ///< swaps the highest and 9th highest value on the stack 139 | SWAP9, ///< swaps the highest and 10th highest value on the stack 140 | SWAP10, ///< swaps the highest and 11th highest value on the stack 141 | SWAP11, ///< swaps the highest and 12th highest value on the stack 142 | SWAP12, ///< swaps the highest and 13th highest value on the stack 143 | SWAP13, ///< swaps the highest and 14th highest value on the stack 144 | SWAP14, ///< swaps the highest and 15th highest value on the stack 145 | SWAP15, ///< swaps the highest and 16th highest value on the stack 146 | SWAP16, ///< swaps the highest and 17th highest value on the stack 147 | 148 | LOG0 = 0xa0, ///< Makes a log entry; no topics. 149 | LOG1, ///< Makes a log entry; 1 topic. 150 | LOG2, ///< Makes a log entry; 2 topics. 151 | LOG3, ///< Makes a log entry; 3 topics. 152 | LOG4, ///< Makes a log entry; 4 topics. 153 | 154 | CREATE = 0xf0, ///< create a new account with associated code 155 | CALL, ///< message-call into an account 156 | CALLCODE, ///< message-call with another account's code only 157 | RETURN, ///< halt execution returning output data 158 | DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender (only from homestead on) 159 | 160 | STATICCALL = 0xfa, ///< Like CALL but does not allow state modification. 161 | 162 | REVERT = 0xfd, ///< stop execution and revert state changes, without consuming all provided gas 163 | SUICIDE = 0xff ///< halt execution and register account for later deletion 164 | }; 165 | 166 | /// Reads PUSH data from pointed fragment of bytecode and constructs number out of it 167 | /// Reading out of bytecode means reading 0 168 | /// @param _curr is updated and points the last real byte read 169 | llvm::APInt readPushData(code_iterator& _curr, code_iterator _end); 170 | 171 | /// Skips PUSH data in pointed fragment of bytecode. 172 | /// @param _curr is updated and points the last real byte skipped 173 | void skipPushData(code_iterator& _curr, code_iterator _end); 174 | 175 | #define ANY_PUSH PUSH1: \ 176 | case Instruction::PUSH2: \ 177 | case Instruction::PUSH3: \ 178 | case Instruction::PUSH4: \ 179 | case Instruction::PUSH5: \ 180 | case Instruction::PUSH6: \ 181 | case Instruction::PUSH7: \ 182 | case Instruction::PUSH8: \ 183 | case Instruction::PUSH9: \ 184 | case Instruction::PUSH10: \ 185 | case Instruction::PUSH11: \ 186 | case Instruction::PUSH12: \ 187 | case Instruction::PUSH13: \ 188 | case Instruction::PUSH14: \ 189 | case Instruction::PUSH15: \ 190 | case Instruction::PUSH16: \ 191 | case Instruction::PUSH17: \ 192 | case Instruction::PUSH18: \ 193 | case Instruction::PUSH19: \ 194 | case Instruction::PUSH20: \ 195 | case Instruction::PUSH21: \ 196 | case Instruction::PUSH22: \ 197 | case Instruction::PUSH23: \ 198 | case Instruction::PUSH24: \ 199 | case Instruction::PUSH25: \ 200 | case Instruction::PUSH26: \ 201 | case Instruction::PUSH27: \ 202 | case Instruction::PUSH28: \ 203 | case Instruction::PUSH29: \ 204 | case Instruction::PUSH30: \ 205 | case Instruction::PUSH31: \ 206 | case Instruction::PUSH32 207 | 208 | #define ANY_DUP DUP1: \ 209 | case Instruction::DUP2: \ 210 | case Instruction::DUP3: \ 211 | case Instruction::DUP4: \ 212 | case Instruction::DUP5: \ 213 | case Instruction::DUP6: \ 214 | case Instruction::DUP7: \ 215 | case Instruction::DUP8: \ 216 | case Instruction::DUP9: \ 217 | case Instruction::DUP10: \ 218 | case Instruction::DUP11: \ 219 | case Instruction::DUP12: \ 220 | case Instruction::DUP13: \ 221 | case Instruction::DUP14: \ 222 | case Instruction::DUP15: \ 223 | case Instruction::DUP16 224 | 225 | #define ANY_SWAP SWAP1: \ 226 | case Instruction::SWAP2: \ 227 | case Instruction::SWAP3: \ 228 | case Instruction::SWAP4: \ 229 | case Instruction::SWAP5: \ 230 | case Instruction::SWAP6: \ 231 | case Instruction::SWAP7: \ 232 | case Instruction::SWAP8: \ 233 | case Instruction::SWAP9: \ 234 | case Instruction::SWAP10: \ 235 | case Instruction::SWAP11: \ 236 | case Instruction::SWAP12: \ 237 | case Instruction::SWAP13: \ 238 | case Instruction::SWAP14: \ 239 | case Instruction::SWAP15: \ 240 | case Instruction::SWAP16 241 | 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /libevmjit/JIT.cpp: -------------------------------------------------------------------------------- 1 | #include "JIT.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "preprocessor/llvm_includes_start.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "preprocessor/llvm_includes_end.h" 16 | 17 | #include "Ext.h" 18 | #include "Compiler.h" 19 | #include "Optimizer.h" 20 | #include "Cache.h" 21 | #include "ExecStats.h" 22 | #include "Utils.h" 23 | #include "BuildInfo.gen.h" 24 | 25 | 26 | // FIXME: Move these checks to evmc tests. 27 | static_assert(sizeof(evmc_uint256be) == 32, "evmc_uint256be is too big"); 28 | static_assert(sizeof(evmc_address) == 20, "evmc_address is too big"); 29 | static_assert(sizeof(evmc_result) == 64, "evmc_result does not fit cache line"); 30 | static_assert(sizeof(evmc_message) <= 18*8, "evmc_message not optimally packed"); 31 | static_assert(offsetof(evmc_message, code_hash) % 8 == 0, "evmc_message.code_hash not aligned"); 32 | 33 | // Check enums match int size. 34 | // On GCC/clang the underlying type should be unsigned int, on MSVC int 35 | static_assert(sizeof(evmc_call_kind) == sizeof(int), "Enum `evmc_call_kind` is not the size of int"); 36 | static_assert(sizeof(evmc_revision) == sizeof(int), "Enum `evmc_revision` is not the size of int"); 37 | 38 | constexpr size_t optionalDataSize = sizeof(evmc_result) - offsetof(evmc_result, create_address); 39 | static_assert(optionalDataSize == sizeof(evmc_result_optional_data), ""); 40 | 41 | 42 | namespace dev 43 | { 44 | namespace evmjit 45 | { 46 | using namespace eth::jit; 47 | 48 | namespace 49 | { 50 | using ExecFunc = ReturnCode(*)(ExecutionContext*); 51 | 52 | struct CodeMapEntry 53 | { 54 | ExecFunc func = nullptr; 55 | size_t hits = 0; 56 | 57 | CodeMapEntry() = default; 58 | explicit CodeMapEntry(ExecFunc func) : func(func) {} 59 | }; 60 | 61 | char toChar(evmc_revision rev) 62 | { 63 | switch (rev) 64 | { 65 | case EVMC_FRONTIER: return 'F'; 66 | case EVMC_HOMESTEAD: return 'H'; 67 | case EVMC_TANGERINE_WHISTLE: return 'T'; 68 | case EVMC_SPURIOUS_DRAGON: return 'S'; 69 | case EVMC_BYZANTIUM: return 'B'; 70 | case EVMC_CONSTANTINOPLE: return 'C'; 71 | } 72 | LLVM_BUILTIN_UNREACHABLE; 73 | } 74 | 75 | /// Combine code hash and EVM revision into a printable code identifier. 76 | std::string makeCodeId(evmc_uint256be codeHash, evmc_revision rev, uint32_t flags) 77 | { 78 | static const auto hexChars = "0123456789abcdef"; 79 | std::string str; 80 | str.reserve(sizeof(codeHash) * 2 + 1); 81 | for (auto b: codeHash.bytes) 82 | { 83 | str.push_back(hexChars[b >> 4]); 84 | str.push_back(hexChars[b & 0xf]); 85 | } 86 | str.push_back(toChar(rev)); 87 | if (flags & EVMC_STATIC) 88 | str.push_back('S'); 89 | return str; 90 | } 91 | 92 | void printVersion() 93 | { 94 | std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n" 95 | << " EVMJIT version " << EVMJIT_VERSION << "\n" 96 | #ifdef NDEBUG 97 | << " Optimized build, " 98 | #else 99 | << " DEBUG build, " 100 | #endif 101 | << __DATE__ << " (" << __TIME__ << ")\n" 102 | << std::endl; 103 | } 104 | 105 | namespace cl = llvm::cl; 106 | cl::opt g_optimize{"O", cl::desc{"Optimize"}}; 107 | cl::opt g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"}, 108 | cl::values( 109 | clEnumValN(CacheMode::off, "0", "Disabled"), 110 | clEnumValN(CacheMode::on, "1", "Enabled"), 111 | clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."), 112 | clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."), 113 | clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."), 114 | clEnumValN(CacheMode::preload, "p", "Preload all cached objects."))}; 115 | cl::opt g_stats{"st", cl::desc{"Statistics"}}; 116 | cl::opt g_dump{"dump", cl::desc{"Dump LLVM IR module"}}; 117 | 118 | void parseOptions() 119 | { 120 | static llvm::llvm_shutdown_obj shutdownObj{}; 121 | cl::AddExtraVersionPrinter(printVersion); 122 | cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); 123 | } 124 | 125 | class SymbolResolver; 126 | 127 | class JITImpl: public evmc_instance 128 | { 129 | std::unique_ptr m_engine; 130 | SymbolResolver const* m_memoryMgr = nullptr; 131 | mutable std::mutex x_codeMap; 132 | std::unordered_map m_codeMap; 133 | 134 | static llvm::LLVMContext& getLLVMContext() 135 | { 136 | // TODO: This probably should be thread_local, but for now that causes 137 | // a crash when MCJIT is destroyed. 138 | static llvm::LLVMContext llvmContext; 139 | return llvmContext; 140 | } 141 | 142 | void createEngine(); 143 | 144 | public: 145 | static JITImpl& instance() 146 | { 147 | // We need to keep this a singleton. 148 | // so we only call changeVersion on it. 149 | static JITImpl s_instance; 150 | return s_instance; 151 | } 152 | 153 | JITImpl(); 154 | 155 | void checkMemorySize(); 156 | 157 | llvm::ExecutionEngine& engine() { return *m_engine; } 158 | 159 | CodeMapEntry getExecFunc(std::string const& _codeIdentifier); 160 | void mapExecFunc(std::string const& _codeIdentifier, ExecFunc _funcAddr); 161 | 162 | ExecFunc compile(evmc_revision _rev, bool _staticCall, byte const* _code, uint64_t _codeSize, std::string const& _codeIdentifier); 163 | 164 | evmc_context_fn_table const* host = nullptr; 165 | 166 | evmc_message const* currentMsg = nullptr; 167 | std::vector returnBuffer; 168 | 169 | std::vector codeBuffer; 170 | 171 | size_t hitThreshold = 0; 172 | }; 173 | 174 | int64_t call(evmc_context* _ctx, int _kind, int64_t _gas, evmc_address const* _address, 175 | evmc_uint256be const* _value, uint8_t const* _inputData, size_t _inputSize, uint8_t* _outputData, 176 | size_t _outputSize, uint8_t const** o_bufData, size_t* o_bufSize) noexcept 177 | { 178 | // FIXME: Handle unexpected exceptions. 179 | auto& jit = JITImpl::instance(); 180 | 181 | evmc_message msg; 182 | msg.destination = *_address; 183 | msg.sender = _kind != EVMC_DELEGATECALL ? jit.currentMsg->destination : jit.currentMsg->sender; 184 | msg.value = _kind != EVMC_DELEGATECALL ? *_value : jit.currentMsg->value; 185 | msg.input_data = _inputData; 186 | msg.input_size = _inputSize; 187 | msg.gas = _gas; 188 | msg.depth = jit.currentMsg->depth + 1; 189 | msg.flags = jit.currentMsg->flags; 190 | if (_kind == EVM_STATICCALL) 191 | { 192 | msg.kind = EVMC_CALL; 193 | msg.flags |= EVMC_STATIC; 194 | } 195 | else 196 | msg.kind = static_cast(_kind); 197 | 198 | // FIXME: Handle code hash. 199 | evmc_result result; 200 | jit.host->call(&result, _ctx, &msg); 201 | // FIXME: Clarify when gas_left is valid. 202 | int64_t r = result.gas_left; 203 | 204 | // Handle output. It can contain data from RETURN or REVERT opcodes. 205 | auto size = std::min(_outputSize, result.output_size); 206 | std::copy_n(result.output_data, size, _outputData); 207 | 208 | // Update RETURNDATA buffer. 209 | // The buffer is already cleared. 210 | jit.returnBuffer = {result.output_data, result.output_data + result.output_size}; 211 | *o_bufData = jit.returnBuffer.data(); 212 | *o_bufSize = jit.returnBuffer.size(); 213 | 214 | if (_kind == EVMC_CREATE && result.status_code == EVMC_SUCCESS) 215 | std::copy_n(result.create_address.bytes, sizeof(result.create_address), _outputData); 216 | 217 | if (result.status_code != EVMC_SUCCESS) 218 | r |= EVM_CALL_FAILURE; 219 | 220 | if (result.release) 221 | result.release(&result); 222 | return r; 223 | } 224 | 225 | 226 | /// A wrapper for new EVM-C copycode callback function. 227 | size_t getCode(uint8_t** o_pCode, evmc_context* _ctx, evmc_address const* _address) noexcept 228 | { 229 | auto& jit = JITImpl::instance(); 230 | size_t codeSize = jit.host->get_code_size(_ctx, _address); 231 | jit.codeBuffer.resize(codeSize); // Allocate needed memory to store the full code. 232 | 233 | // Copy the code to JIT's buffer and send the buffer reference back to LLVM. 234 | size_t size = 235 | jit.host->copy_code(_ctx, _address, 0, jit.codeBuffer.data(), jit.codeBuffer.size()); 236 | *o_pCode = jit.codeBuffer.data(); 237 | return size; 238 | } 239 | 240 | class SymbolResolver : public llvm::SectionMemoryManager 241 | { 242 | llvm::JITSymbol findSymbol(std::string const& _name) override 243 | { 244 | auto& jit = JITImpl::instance(); 245 | 246 | // Handle symbols' global prefix. 247 | // If in current DataLayout global symbols are prefixed, drop the 248 | // prefix from the name for local search. 249 | char prefix = jit.engine().getDataLayout().getGlobalPrefix(); 250 | llvm::StringRef unprefixedName = (prefix != '\0' && _name[0] == prefix) 251 | ? llvm::StringRef{_name}.drop_front() : llvm::StringRef{_name}; 252 | 253 | auto addr = 254 | llvm::StringSwitch(unprefixedName) 255 | .Case("env_sha3", reinterpret_cast(&keccak)) 256 | .Case("evm.exists", reinterpret_cast(jit.host->account_exists)) 257 | .Case("evm.sload", reinterpret_cast(jit.host->get_storage)) 258 | .Case("evm.sstore", reinterpret_cast(jit.host->set_storage)) 259 | .Case("evm.balance", reinterpret_cast(jit.host->get_balance)) 260 | .Case("evm.codesize", reinterpret_cast(jit.host->get_code_size)) 261 | .Case("evm.code", reinterpret_cast(getCode)) 262 | .Case("evm.selfdestruct", reinterpret_cast(jit.host->selfdestruct)) 263 | .Case("evm.call", reinterpret_cast(call)) 264 | .Case("evm.get_tx_context", reinterpret_cast(jit.host->get_tx_context)) 265 | .Case("evm.blockhash", reinterpret_cast(jit.host->get_block_hash)) 266 | .Case("evm.log", reinterpret_cast(jit.host->emit_log)) 267 | .Default(0); 268 | if (addr) 269 | return {addr, llvm::JITSymbolFlags::Exported}; 270 | 271 | // Fallback to default implementation that would search for the symbol 272 | // in the current process. Use the original prefixed symbol name. 273 | // TODO: In the future we should control the whole set of requested 274 | // symbols (like memcpy, memset, etc) to improve performance. 275 | return llvm::SectionMemoryManager::findSymbol(_name); 276 | } 277 | 278 | void reportMemorySize(size_t _addedSize) 279 | { 280 | m_totalMemorySize += _addedSize; 281 | 282 | if (!g_stats) 283 | return; 284 | 285 | if (m_totalMemorySize >= m_printMemoryLimit) 286 | { 287 | constexpr size_t printMemoryStep = 10 * 1024 * 1024; 288 | auto value = double(m_totalMemorySize) / printMemoryStep; 289 | std::cerr << "EVMJIT total memory size: " << (10 * value) << " MB\n"; 290 | m_printMemoryLimit += printMemoryStep; 291 | } 292 | } 293 | 294 | uint8_t* allocateCodeSection(uintptr_t _size, unsigned _a, unsigned _id, 295 | llvm::StringRef _name) override 296 | { 297 | reportMemorySize(_size); 298 | return llvm::SectionMemoryManager::allocateCodeSection(_size, _a, _id, _name); 299 | } 300 | 301 | uint8_t* allocateDataSection(uintptr_t _size, unsigned _a, unsigned _id, 302 | llvm::StringRef _name, bool _ro) override 303 | { 304 | reportMemorySize(_size); 305 | return llvm::SectionMemoryManager::allocateDataSection(_size, _a, _id, _name, _ro); 306 | } 307 | 308 | size_t m_totalMemorySize = 0; 309 | size_t m_printMemoryLimit = 1024 * 1024; 310 | 311 | public: 312 | size_t totalMemorySize() const { return m_totalMemorySize; } 313 | }; 314 | 315 | 316 | CodeMapEntry JITImpl::getExecFunc(std::string const& _codeIdentifier) 317 | { 318 | std::lock_guard lock{x_codeMap}; 319 | auto& entry = m_codeMap[_codeIdentifier]; 320 | ++entry.hits; 321 | return entry; 322 | } 323 | 324 | void JITImpl::mapExecFunc(std::string const& _codeIdentifier, ExecFunc _funcAddr) 325 | { 326 | std::lock_guard lock{x_codeMap}; 327 | m_codeMap[_codeIdentifier].func = _funcAddr; 328 | } 329 | 330 | ExecFunc JITImpl::compile(evmc_revision _rev, bool _staticCall, byte const* _code, uint64_t _codeSize, 331 | std::string const& _codeIdentifier) 332 | { 333 | auto module = Cache::getObject(_codeIdentifier, getLLVMContext()); 334 | if (!module) 335 | { 336 | // TODO: Listener support must be redesigned. These should be a feature of JITImpl 337 | //listener->stateChanged(ExecState::Compilation); 338 | assert(_code || !_codeSize); 339 | //TODO: Can the Compiler be stateless? 340 | module = Compiler({}, _rev, _staticCall, getLLVMContext()).compile(_code, _code + _codeSize, _codeIdentifier); 341 | 342 | if (g_optimize) 343 | { 344 | //listener->stateChanged(ExecState::Optimization); 345 | optimize(*module); 346 | } 347 | 348 | prepare(*module); 349 | } 350 | 351 | if (g_dump) 352 | { 353 | llvm::raw_os_ostream cerr{std::cerr}; 354 | module->print(cerr, nullptr); 355 | } 356 | 357 | 358 | m_engine->addModule(std::move(module)); 359 | //listener->stateChanged(ExecState::CodeGen); 360 | return (ExecFunc)m_engine->getFunctionAddress(_codeIdentifier); 361 | } 362 | 363 | } // anonymous namespace 364 | 365 | 366 | ExecutionContext::~ExecutionContext() noexcept 367 | { 368 | if (m_memData) 369 | std::free(m_memData); 370 | } 371 | 372 | bytes_ref ExecutionContext::getReturnData() const 373 | { 374 | auto data = m_data->callData; 375 | auto size = static_cast(m_data->callDataSize); 376 | 377 | if (data < m_memData || data >= m_memData + m_memSize || size == 0) 378 | { 379 | assert(size == 0); // data can be an invalid pointer only if size is 0 380 | m_data->callData = nullptr; 381 | return {}; 382 | } 383 | 384 | return bytes_ref{data, size}; 385 | } 386 | 387 | extern "C" 388 | { 389 | 390 | EXPORT evmc_instance* evmjit_create() 391 | { 392 | // Let's always return the same instance. It's a bit of faking, but actually 393 | // this might be a compliant implementation. 394 | return &JITImpl::instance(); 395 | } 396 | 397 | static void destroy(evmc_instance* instance) 398 | { 399 | (void)instance; 400 | assert(instance == static_cast(&JITImpl::instance())); 401 | } 402 | 403 | static evmc_result execute(evmc_instance* instance, evmc_context* context, evmc_revision rev, 404 | evmc_message const* msg, uint8_t const* code, size_t code_size) 405 | { 406 | auto& jit = *reinterpret_cast(instance); 407 | 408 | if (msg->depth == 0) 409 | jit.checkMemorySize(); 410 | 411 | if (!jit.host) 412 | jit.host = context->fn_table; 413 | assert(jit.host == context->fn_table); // Require the fn_table not to change. 414 | 415 | // TODO: Temporary keep track of the current message. 416 | evmc_message const* prevMsg = jit.currentMsg; 417 | jit.currentMsg = msg; 418 | 419 | RuntimeData rt; 420 | rt.code = code; 421 | rt.codeSize = code_size; 422 | rt.gas = msg->gas; 423 | rt.callData = msg->input_data; 424 | rt.callDataSize = msg->input_size; 425 | std::memcpy(&rt.apparentValue, &msg->value, sizeof(msg->value)); 426 | std::memset(&rt.address, 0, 12); 427 | std::memcpy(&rt.address[12], &msg->destination, sizeof(msg->destination)); 428 | std::memset(&rt.caller, 0, 12); 429 | std::memcpy(&rt.caller[12], &msg->sender, sizeof(msg->sender)); 430 | rt.depth = msg->depth; 431 | 432 | ExecutionContext ctx{rt, context}; 433 | 434 | evmc_result result; 435 | result.status_code = EVMC_SUCCESS; 436 | result.gas_left = 0; 437 | result.output_data = nullptr; 438 | result.output_size = 0; 439 | result.release = nullptr; 440 | 441 | auto codeIdentifier = makeCodeId(msg->code_hash, rev, msg->flags); 442 | auto codeEntry = jit.getExecFunc(codeIdentifier); 443 | auto func = codeEntry.func; 444 | if (!func) 445 | { 446 | //FIXME: We have a race condition here! 447 | 448 | if (codeEntry.hits <= jit.hitThreshold) 449 | { 450 | result.status_code = EVMC_REJECTED; 451 | return result; 452 | } 453 | 454 | if (g_stats) 455 | std::cerr << "EVMJIT Compile " << codeIdentifier << " (" << codeEntry.hits << ")\n"; 456 | 457 | const bool staticCall = (msg->flags & EVMC_STATIC) != 0; 458 | func = jit.compile(rev, staticCall, ctx.code(), ctx.codeSize(), codeIdentifier); 459 | if (!func) 460 | { 461 | result.status_code = EVMC_INTERNAL_ERROR; 462 | return result; 463 | } 464 | jit.mapExecFunc(codeIdentifier, func); 465 | } 466 | 467 | auto returnCode = func(&ctx); 468 | 469 | if (returnCode == ReturnCode::Revert) 470 | { 471 | result.status_code = EVMC_REVERT; 472 | result.gas_left = rt.gas; 473 | } 474 | else if (returnCode == ReturnCode::OutOfGas) 475 | { 476 | // EVMJIT does not provide information what exactly type of failure 477 | // it was, so use generic EVM_FAILURE. 478 | result.status_code = EVMC_FAILURE; 479 | } 480 | else 481 | { 482 | // In case of success return the amount of gas left. 483 | result.gas_left = rt.gas; 484 | } 485 | 486 | if (returnCode == ReturnCode::Return || returnCode == ReturnCode::Revert) 487 | { 488 | auto out = ctx.getReturnData(); 489 | result.output_data = std::get<0>(out); 490 | result.output_size = std::get<1>(out); 491 | } 492 | 493 | // Take care of the internal memory. 494 | if (ctx.m_memData) 495 | { 496 | // Use result's reserved data to store the memory pointer. 497 | 498 | evmc_get_optional_data(&result)->pointer = ctx.m_memData; 499 | 500 | // Set pointer to the destructor that will release the memory. 501 | result.release = [](evmc_result const* r) 502 | { 503 | std::free(evmc_get_const_optional_data(r)->pointer); 504 | }; 505 | ctx.m_memData = nullptr; 506 | } 507 | 508 | jit.currentMsg = prevMsg; 509 | return result; 510 | } 511 | 512 | static int setOption(evmc_instance* instance, const char* name, const char* value) noexcept 513 | { 514 | try 515 | { 516 | if (name == std::string{"hits-threshold"}) 517 | { 518 | auto& jit = static_cast(*instance); 519 | jit.hitThreshold = std::stoul(value); 520 | return 1; 521 | } 522 | return 0; 523 | } 524 | catch (...) 525 | { 526 | return 0; 527 | } 528 | } 529 | 530 | } // extern "C" 531 | 532 | void JITImpl::createEngine() 533 | { 534 | auto module = llvm::make_unique("", getLLVMContext()); 535 | 536 | // FIXME: LLVM 3.7: test on Windows 537 | auto triple = llvm::Triple(llvm::sys::getProcessTriple()); 538 | if (triple.getOS() == llvm::Triple::OSType::Win32) 539 | triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format 540 | module->setTargetTriple(triple.str()); 541 | 542 | llvm::EngineBuilder builder(std::move(module)); 543 | builder.setEngineKind(llvm::EngineKind::JIT); 544 | auto memoryMgr = llvm::make_unique(); 545 | m_memoryMgr = memoryMgr.get(); 546 | builder.setMCJITMemoryManager(std::move(memoryMgr)); 547 | builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); 548 | #ifndef NDEBUG 549 | builder.setVerifyModules(true); 550 | #endif 551 | 552 | m_engine.reset(builder.create()); 553 | 554 | // TODO: Update cache listener 555 | m_engine->setObjectCache(Cache::init(g_cache, nullptr)); 556 | 557 | // FIXME: Disabled during API changes 558 | //if (preloadCache) 559 | // Cache::preload(*m_engine, funcCache); 560 | } 561 | 562 | JITImpl::JITImpl() 563 | : evmc_instance({ 564 | EVMC_ABI_VERSION, 565 | "evmjit", 566 | EVMJIT_VERSION, 567 | evmjit::destroy, 568 | evmjit::execute, 569 | evmjit::setOption, 570 | }) 571 | { 572 | parseOptions(); 573 | 574 | bool preloadCache = g_cache == CacheMode::preload; 575 | if (preloadCache) 576 | g_cache = CacheMode::on; 577 | 578 | llvm::InitializeNativeTarget(); 579 | llvm::InitializeNativeTargetAsmPrinter(); 580 | 581 | createEngine(); 582 | } 583 | 584 | void JITImpl::checkMemorySize() 585 | { 586 | constexpr size_t memoryLimit = 1000 * 1024 * 1024; 587 | 588 | if (m_memoryMgr->totalMemorySize() > memoryLimit) 589 | { 590 | if (g_stats) 591 | std::cerr << "EVMJIT reset!\n"; 592 | 593 | std::lock_guard lock{x_codeMap}; 594 | m_codeMap.clear(); 595 | m_engine.reset(); 596 | createEngine(); 597 | } 598 | } 599 | 600 | } 601 | } 602 | -------------------------------------------------------------------------------- /libevmjit/JIT.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace dev 11 | { 12 | namespace evmjit 13 | { 14 | 15 | using byte = uint8_t; 16 | using bytes_ref = std::tuple; 17 | 18 | /// Representation of 256-bit value binary compatible with LLVM i256 19 | struct i256 20 | { 21 | uint64_t words[4]; 22 | 23 | i256() = default; 24 | }; 25 | 26 | // TODO: Merge with ExecutionContext 27 | struct RuntimeData 28 | { 29 | enum Index 30 | { 31 | Gas, 32 | GasPrice, 33 | CallData, 34 | CallDataSize, 35 | Value, // Value of msg.value - different during DELEGATECALL. 36 | Code, 37 | CodeSize, 38 | Address, 39 | Sender, 40 | Depth, 41 | 42 | ReturnData = CallData, ///< Return data pointer (set only in case of RETURN) 43 | ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN) 44 | }; 45 | 46 | static size_t const numElements = Depth + 1; 47 | 48 | int64_t gas = 0; 49 | int64_t gasPrice = 0; 50 | byte const* callData = nullptr; 51 | uint64_t callDataSize = 0; 52 | i256 apparentValue; 53 | byte const* code = nullptr; 54 | uint64_t codeSize = 0; 55 | byte address[32]; 56 | byte caller[32]; 57 | int64_t depth; 58 | }; 59 | 60 | struct JITSchedule 61 | { 62 | // TODO: Move to constexpr once all our target compilers support it. 63 | typedef std::integral_constant stackLimit; 64 | typedef std::integral_constant stepGas0; 65 | typedef std::integral_constant stepGas1; 66 | typedef std::integral_constant stepGas2; 67 | typedef std::integral_constant stepGas3; 68 | typedef std::integral_constant stepGas4; 69 | typedef std::integral_constant stepGas5; 70 | typedef std::integral_constant stepGas6; 71 | typedef std::integral_constant stepGas7; 72 | typedef std::integral_constant expByteGas; 73 | typedef std::integral_constant sha3Gas; 74 | typedef std::integral_constant sha3WordGas; 75 | typedef std::integral_constant sloadGas; 76 | typedef std::integral_constant sstoreSetGas; 77 | typedef std::integral_constant sstoreResetGas; 78 | typedef std::integral_constant sstoreClearGas; 79 | typedef std::integral_constant jumpdestGas; 80 | typedef std::integral_constant logGas; 81 | typedef std::integral_constant logDataGas; 82 | typedef std::integral_constant logTopicGas; 83 | typedef std::integral_constant createGas; 84 | typedef std::integral_constant callGas; 85 | typedef std::integral_constant memoryGas; 86 | typedef std::integral_constant copyGas; 87 | typedef std::integral_constant valueTransferGas; 88 | typedef std::integral_constant callStipend; 89 | typedef std::integral_constant callNewAccount; 90 | }; 91 | 92 | enum class ReturnCode 93 | { 94 | // Success codes 95 | Stop = 0, 96 | Return = 1, 97 | Revert = 2, 98 | 99 | // Standard error codes 100 | OutOfGas = -1, 101 | 102 | // Internal error codes 103 | LLVMError = -101, 104 | 105 | UnexpectedException = -111, 106 | }; 107 | 108 | class ExecutionContext 109 | { 110 | public: 111 | ExecutionContext() = default; 112 | ExecutionContext(RuntimeData& _data, evmc_context* _ctx) { init(_data, _ctx); } 113 | ExecutionContext(ExecutionContext const&) = delete; 114 | ExecutionContext& operator=(ExecutionContext const&) = delete; 115 | ~ExecutionContext() noexcept; 116 | 117 | void init(RuntimeData& _data, evmc_context* _ctx) { m_data = &_data; m_ctx = _ctx; } 118 | 119 | byte const* code() const { return m_data->code; } 120 | uint64_t codeSize() const { return m_data->codeSize; } 121 | 122 | bytes_ref getReturnData() const; 123 | 124 | public: 125 | RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract. 126 | evmc_context* m_ctx = nullptr; ///< Pointer to Host execution context. Expected by compiled contract. 127 | byte* m_memData = nullptr; 128 | uint64_t m_memSize = 0; 129 | uint64_t m_memCap = 0; 130 | 131 | public: 132 | /// Reference to returned data (RETURN opcode used) 133 | bytes_ref returnData; 134 | }; 135 | 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /libevmjit/Memory.cpp: -------------------------------------------------------------------------------- 1 | #include "Memory.h" 2 | 3 | #include "preprocessor/llvm_includes_start.h" 4 | #include 5 | #include "preprocessor/llvm_includes_end.h" 6 | 7 | #include "Type.h" 8 | #include "GasMeter.h" 9 | #include "Endianness.h" 10 | #include "RuntimeManager.h" 11 | 12 | namespace dev 13 | { 14 | namespace eth 15 | { 16 | namespace jit 17 | { 18 | 19 | Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): 20 | RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed 21 | m_memory{m_builder, _runtimeManager.getMem()}, 22 | m_gasMeter(_gasMeter) 23 | {} 24 | 25 | llvm::Function* Memory::getRequireFunc() 26 | { 27 | auto& func = m_require; 28 | if (!func) 29 | { 30 | llvm::Type* argTypes[] = {Array::getType()->getPointerTo(), Type::Word, Type::Word, Type::BytePtr, Type::GasPtr}; 31 | func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); 32 | func->setDoesNotThrow(); 33 | 34 | auto iter = func->arg_begin(); 35 | llvm::Argument* mem = &(*iter++); 36 | mem->setName("mem"); 37 | llvm::Argument* blkOffset = &(*iter++); 38 | blkOffset->setName("blkOffset"); 39 | llvm::Argument* blkSize = &(*iter++); 40 | blkSize->setName("blkSize"); 41 | llvm::Argument* jmpBuf = &(*iter++); 42 | jmpBuf->setName("jmpBuf"); 43 | llvm::Argument* gas = &(*iter); 44 | gas->setName("gas"); 45 | 46 | auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); 47 | auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); 48 | auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); 49 | auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); 50 | 51 | InsertPointGuard guard(m_builder); // Restores insert point at function exit 52 | 53 | // BB "Pre": Ignore checks with size 0 54 | m_builder.SetInsertPoint(preBB); 55 | m_builder.CreateCondBr(m_builder.CreateICmpNE(blkSize, Constant::get(0)), checkBB, returnBB, Type::expectTrue); 56 | 57 | // BB "Check" 58 | m_builder.SetInsertPoint(checkBB); 59 | static const auto c_inputMax = uint64_t(1) << 33; // max value of blkSize and blkOffset that will not result in integer overflow in calculations below 60 | auto blkOffsetOk = m_builder.CreateICmpULE(blkOffset, Constant::get(c_inputMax), "blkOffsetOk"); 61 | auto blkO = m_builder.CreateSelect(blkOffsetOk, m_builder.CreateTrunc(blkOffset, Type::Size), m_builder.getInt64(c_inputMax), "bklO"); 62 | auto blkSizeOk = m_builder.CreateICmpULE(blkSize, Constant::get(c_inputMax), "blkSizeOk"); 63 | auto blkS = m_builder.CreateSelect(blkSizeOk, m_builder.CreateTrunc(blkSize, Type::Size), m_builder.getInt64(c_inputMax), "bklS"); 64 | 65 | auto sizeReq0 = m_builder.CreateNUWAdd(blkO, blkS, "sizeReq0"); 66 | auto sizeReq = m_builder.CreateAnd(m_builder.CreateNUWAdd(sizeReq0, m_builder.getInt64(31)), uint64_t(-1) << 5, "sizeReq"); // s' = ((s0 + 31) / 32) * 32 67 | auto sizeCur = m_memory.size(mem); 68 | auto sizeOk = m_builder.CreateICmpULE(sizeReq, sizeCur, "sizeOk"); 69 | 70 | m_builder.CreateCondBr(sizeOk, returnBB, resizeBB, Type::expectTrue); 71 | 72 | // BB "Resize" 73 | m_builder.SetInsertPoint(resizeBB); 74 | // Check gas first 75 | auto w1 = m_builder.CreateLShr(sizeReq, 5); 76 | auto w1s = m_builder.CreateNUWMul(w1, w1); 77 | auto c1 = m_builder.CreateAdd(m_builder.CreateNUWMul(w1, m_builder.getInt64(3)), m_builder.CreateLShr(w1s, 9)); 78 | auto w0 = m_builder.CreateLShr(sizeCur, 5); 79 | auto w0s = m_builder.CreateNUWMul(w0, w0); 80 | auto c0 = m_builder.CreateAdd(m_builder.CreateNUWMul(w0, m_builder.getInt64(3)), m_builder.CreateLShr(w0s, 9)); 81 | auto cc = m_builder.CreateNUWSub(c1, c0); 82 | auto costOk = m_builder.CreateAnd(blkOffsetOk, blkSizeOk, "costOk"); 83 | auto c = m_builder.CreateSelect(costOk, cc, m_builder.getInt64(std::numeric_limits::max()), "c"); 84 | m_gasMeter.count(c, jmpBuf, gas); 85 | // Resize 86 | m_memory.extend(mem, sizeReq); 87 | m_builder.CreateBr(returnBB); 88 | 89 | // BB "Return" 90 | m_builder.SetInsertPoint(returnBB); 91 | m_builder.CreateRetVoid(); 92 | } 93 | return func; 94 | } 95 | 96 | llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType) 97 | { 98 | auto isWord = _valueType == Type::Word; 99 | 100 | llvm::Type* storeArgs[] = {Array::getType()->getPointerTo(), Type::Word, _valueType}; 101 | llvm::Type* loadArgs[] = {Array::getType()->getPointerTo(), Type::Word}; 102 | auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; 103 | auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false); 104 | auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); 105 | 106 | InsertPointGuard guard(m_builder); // Restores insert point at function exit 107 | 108 | m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); 109 | 110 | auto iter = func->arg_begin(); 111 | llvm::Argument* mem = &(*iter++); 112 | mem->setName("mem"); 113 | llvm::Argument* index = &(*iter++); 114 | index->setName("index"); 115 | 116 | if (_isStore) 117 | { 118 | llvm::Argument* valueArg = &(*iter); 119 | valueArg->setName("value"); 120 | auto value = isWord ? Endianness::toBE(m_builder, valueArg) : valueArg; 121 | auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size)); 122 | auto valuePtr = m_builder.CreateBitCast(memPtr, _valueType->getPointerTo(), "valuePtr"); 123 | m_builder.CreateStore(value, valuePtr); 124 | m_builder.CreateRetVoid(); 125 | } 126 | else 127 | { 128 | auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size)); 129 | llvm::Value* ret = m_builder.CreateLoad(memPtr); 130 | ret = Endianness::toNative(m_builder, ret); 131 | m_builder.CreateRet(ret); 132 | } 133 | 134 | return func; 135 | } 136 | 137 | llvm::Function* Memory::getLoadWordFunc() 138 | { 139 | auto& func = m_loadWord; 140 | if (!func) 141 | func = createFunc(false, Type::Word); 142 | return func; 143 | } 144 | 145 | llvm::Function* Memory::getStoreWordFunc() 146 | { 147 | auto& func = m_storeWord; 148 | if (!func) 149 | func = createFunc(true, Type::Word); 150 | return func; 151 | } 152 | 153 | llvm::Function* Memory::getStoreByteFunc() 154 | { 155 | auto& func = m_storeByte; 156 | if (!func) 157 | func = createFunc(true, Type::Byte); 158 | return func; 159 | } 160 | 161 | 162 | llvm::Value* Memory::loadWord(llvm::Value* _addr) 163 | { 164 | require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8)); 165 | return m_builder.CreateCall(getLoadWordFunc(), {getRuntimeManager().getMem(), _addr}); 166 | } 167 | 168 | void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) 169 | { 170 | require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8)); 171 | m_builder.CreateCall(getStoreWordFunc(), {getRuntimeManager().getMem(), _addr, _word}); 172 | } 173 | 174 | void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) 175 | { 176 | require(_addr, Constant::get(Type::Byte->getPrimitiveSizeInBits() / 8)); 177 | auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); 178 | m_builder.CreateCall(getStoreByteFunc(), {getRuntimeManager().getMem(), _addr, byte}); 179 | } 180 | 181 | llvm::Value* Memory::getData() 182 | { 183 | auto memPtr = m_builder.CreateBitCast(getRuntimeManager().getMem(), Type::BytePtr->getPointerTo()); 184 | auto data = m_builder.CreateLoad(memPtr, "data"); 185 | assert(data->getType() == Type::BytePtr); 186 | return data; 187 | } 188 | 189 | llvm::Value* Memory::getSize() 190 | { 191 | return m_builder.CreateZExt(m_memory.size(), Type::Word, "msize"); 192 | } 193 | 194 | llvm::Value* Memory::getBytePtr(llvm::Value* _index) 195 | { 196 | return m_builder.CreateGEP(getData(), _index, "ptr"); 197 | } 198 | 199 | void Memory::require(llvm::Value* _offset, llvm::Value* _size) 200 | { 201 | if (auto constant = llvm::dyn_cast(_size)) 202 | { 203 | if (!constant->getValue()) 204 | return; 205 | } 206 | m_builder.CreateCall(getRequireFunc(), {getRuntimeManager().getMem(), _offset, _size, getRuntimeManager().getJmpBuf(), getRuntimeManager().getGasPtr()}); 207 | } 208 | 209 | void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, 210 | llvm::Value* _destMemIdx, llvm::Value* _reqBytes) 211 | { 212 | require(_destMemIdx, _reqBytes); 213 | 214 | // Additional copy cost 215 | // TODO: This round ups to 32 happens in many places 216 | auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Gas); 217 | auto copyWords = m_builder.CreateUDiv(m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32)); 218 | m_gasMeter.countCopy(copyWords); 219 | 220 | // Algorithm: 221 | // isOutsideData = idx256 >= size256 222 | // idx64 = trunc idx256 223 | // size64 = trunc size256 224 | // dataLeftSize = size64 - idx64 // safe if not isOutsideData 225 | // reqBytes64 = trunc _reqBytes // require() handles large values 226 | // bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min 227 | // bytesToCopy = select(isOutsideData, 0, bytesToCopy0) 228 | 229 | auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); 230 | auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size); 231 | auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size); 232 | auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); 233 | auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize); 234 | auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes); 235 | auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner, "bytesToCopy"); 236 | auto bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero"); 237 | 238 | auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); 239 | auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); 240 | auto padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx"); 241 | auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx); 242 | auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx); 243 | m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); 244 | m_builder.CreateMemSet(pad, m_builder.getInt8(0), bytesToZero, 0); 245 | } 246 | 247 | void Memory::copyBytesNoPadding(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, 248 | llvm::Value* _destMemIdx, llvm::Value* _reqBytes) 249 | { 250 | require(_destMemIdx, _reqBytes); 251 | 252 | // Additional copy cost 253 | // TODO: This round ups to 32 happens in many places 254 | auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Size); 255 | auto copyWords = m_builder.CreateUDiv(m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32)); 256 | 257 | auto reqSize = m_builder.CreateAdd(_srcIdx, _reqBytes); 258 | auto overflow = m_builder.CreateICmpULT(reqSize, _reqBytes); 259 | auto outOfRange = m_builder.CreateICmpUGT(reqSize, m_builder.CreateZExt(_srcSize, Type::Word)); 260 | auto bufferOverrun = m_builder.CreateOr(overflow, outOfRange); 261 | auto penalty = m_builder.getInt64(std::numeric_limits::max()); 262 | auto cost = m_builder.CreateSelect(bufferOverrun, penalty, copyWords); 263 | m_gasMeter.countCopy(cost); 264 | 265 | auto srcIdx = m_builder.CreateTrunc(_srcIdx, Type::Size); 266 | 267 | auto src = m_builder.CreateGEP(_srcPtr, srcIdx, "src"); 268 | auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); 269 | auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx); 270 | m_builder.CreateMemCpy(dst, src, reqBytes, 0); 271 | } 272 | 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /libevmjit/Memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Array.h" 4 | 5 | namespace dev 6 | { 7 | namespace eth 8 | { 9 | namespace jit 10 | { 11 | class GasMeter; 12 | 13 | class Memory : public RuntimeHelper 14 | { 15 | public: 16 | Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter); 17 | 18 | llvm::Value* loadWord(llvm::Value* _addr); 19 | void storeWord(llvm::Value* _addr, llvm::Value* _word); 20 | void storeByte(llvm::Value* _addr, llvm::Value* _byte); 21 | llvm::Value* getData(); 22 | llvm::Value* getSize(); 23 | llvm::Value* getBytePtr(llvm::Value* _index); 24 | void copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIndex, 25 | llvm::Value* _destMemIdx, llvm::Value* _byteCount); 26 | void copyBytesNoPadding(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIndex, 27 | llvm::Value* _destMemIdx, llvm::Value* _byteCount); 28 | 29 | /// Requires the amount of memory to for data defined by offset and size. And counts gas fee for that memory. 30 | void require(llvm::Value* _offset, llvm::Value* _size); 31 | 32 | private: 33 | Array m_memory; 34 | 35 | GasMeter& m_gasMeter; 36 | 37 | llvm::Function* createFunc(bool _isStore, llvm::Type* _type); 38 | 39 | llvm::Function* getRequireFunc(); 40 | llvm::Function* getLoadWordFunc(); 41 | llvm::Function* getStoreWordFunc(); 42 | llvm::Function* getStoreByteFunc(); 43 | 44 | llvm::Function* m_require = nullptr; 45 | llvm::Function* m_loadWord = nullptr; 46 | llvm::Function* m_storeWord = nullptr; 47 | llvm::Function* m_storeByte = nullptr; 48 | }; 49 | 50 | } 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /libevmjit/Optimizer.cpp: -------------------------------------------------------------------------------- 1 | #include "Optimizer.h" 2 | 3 | #include "preprocessor/llvm_includes_start.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "preprocessor/llvm_includes_end.h" 12 | 13 | #include "Arith256.h" 14 | #include "Type.h" 15 | 16 | namespace dev 17 | { 18 | namespace eth 19 | { 20 | namespace jit 21 | { 22 | 23 | namespace 24 | { 25 | 26 | class LongJmpEliminationPass: public llvm::FunctionPass 27 | { 28 | static char ID; 29 | 30 | public: 31 | LongJmpEliminationPass(): 32 | llvm::FunctionPass(ID) 33 | {} 34 | 35 | virtual bool runOnFunction(llvm::Function& _func) override; 36 | }; 37 | 38 | char LongJmpEliminationPass::ID = 0; 39 | 40 | bool LongJmpEliminationPass::runOnFunction(llvm::Function& _func) 41 | { 42 | auto iter = _func.getParent()->begin(); 43 | if (&_func != &(*iter)) 44 | return false; 45 | 46 | auto& mainFunc = _func; 47 | auto& ctx = _func.getContext(); 48 | auto abortCode = llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), -1); 49 | 50 | auto& exitBB = mainFunc.back(); 51 | assert(exitBB.getName() == "Exit"); 52 | auto retPhi = llvm::cast(&exitBB.front()); 53 | 54 | auto modified = false; 55 | for (auto bbIt = mainFunc.begin(); bbIt != mainFunc.end(); ++bbIt) 56 | { 57 | if (auto term = llvm::dyn_cast(bbIt->getTerminator())) 58 | { 59 | auto longjmp = term->getPrevNode(); 60 | assert(llvm::isa(longjmp)); 61 | auto bbPtr = &(*bbIt); 62 | retPhi->addIncoming(abortCode, bbPtr); 63 | llvm::ReplaceInstWithInst(term, llvm::BranchInst::Create(&exitBB)); 64 | longjmp->eraseFromParent(); 65 | modified = true; 66 | } 67 | } 68 | 69 | return modified; 70 | } 71 | 72 | } 73 | 74 | bool optimize(llvm::Module& _module) 75 | { 76 | auto pm = llvm::legacy::PassManager{}; 77 | pm.add(llvm::createFunctionInliningPass(2, 2, false)); 78 | pm.add(new LongJmpEliminationPass{}); // TODO: Takes a lot of time with little effect 79 | pm.add(llvm::createCFGSimplificationPass()); 80 | pm.add(llvm::createInstructionCombiningPass()); 81 | pm.add(llvm::createAggressiveDCEPass()); 82 | pm.add(llvm::createLowerSwitchPass()); 83 | return pm.run(_module); 84 | } 85 | 86 | namespace 87 | { 88 | 89 | class LowerEVMPass: public llvm::BasicBlockPass 90 | { 91 | static char ID; 92 | 93 | public: 94 | LowerEVMPass(): 95 | llvm::BasicBlockPass(ID) 96 | {} 97 | 98 | virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override; 99 | 100 | using llvm::BasicBlockPass::doFinalization; 101 | virtual bool doFinalization(llvm::Module& _module) override; 102 | }; 103 | 104 | char LowerEVMPass::ID = 0; 105 | 106 | bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb) 107 | { 108 | auto modified = false; 109 | auto module = _bb.getParent()->getParent(); 110 | auto i512Ty = llvm::IntegerType::get(_bb.getContext(), 512); 111 | for (auto it = _bb.begin(); it != _bb.end(); ++it) 112 | { 113 | auto& inst = *it; 114 | llvm::Function* func = nullptr; 115 | if (inst.getType() == Type::Word) 116 | { 117 | switch (inst.getOpcode()) 118 | { 119 | case llvm::Instruction::UDiv: 120 | func = Arith256::getUDiv256Func(*module); 121 | break; 122 | 123 | case llvm::Instruction::URem: 124 | func = Arith256::getURem256Func(*module); 125 | break; 126 | 127 | case llvm::Instruction::SDiv: 128 | func = Arith256::getSDiv256Func(*module); 129 | break; 130 | 131 | case llvm::Instruction::SRem: 132 | func = Arith256::getSRem256Func(*module); 133 | break; 134 | } 135 | } 136 | else if (inst.getType() == i512Ty) 137 | { 138 | switch (inst.getOpcode()) 139 | { 140 | case llvm::Instruction::URem: 141 | func = Arith256::getURem512Func(*module); 142 | break; 143 | } 144 | } 145 | 146 | if (func) 147 | { 148 | auto call = llvm::CallInst::Create(func, {inst.getOperand(0), inst.getOperand(1)}); 149 | llvm::ReplaceInstWithInst(_bb.getInstList(), it, call); 150 | modified = true; 151 | } 152 | } 153 | return modified; 154 | } 155 | 156 | bool LowerEVMPass::doFinalization(llvm::Module&) 157 | { 158 | return false; 159 | } 160 | 161 | } 162 | 163 | bool prepare(llvm::Module& _module) 164 | { 165 | auto pm = llvm::legacy::PassManager{}; 166 | pm.add(llvm::createCFGSimplificationPass()); 167 | pm.add(llvm::createDeadCodeEliminationPass()); 168 | pm.add(new LowerEVMPass{}); 169 | return pm.run(_module); 170 | } 171 | 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /libevmjit/Optimizer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace llvm 4 | { 5 | class Module; 6 | } 7 | 8 | namespace dev 9 | { 10 | namespace eth 11 | { 12 | namespace jit 13 | { 14 | 15 | bool optimize(llvm::Module& _module); 16 | 17 | bool prepare(llvm::Module& _module); 18 | 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libevmjit/RuntimeManager.cpp: -------------------------------------------------------------------------------- 1 | #include "RuntimeManager.h" 2 | 3 | #include "preprocessor/llvm_includes_start.h" 4 | #include 5 | #include 6 | #include "preprocessor/llvm_includes_end.h" 7 | 8 | #include "Array.h" 9 | #include "Utils.h" 10 | 11 | namespace dev 12 | { 13 | namespace eth 14 | { 15 | namespace jit 16 | { 17 | 18 | llvm::StructType* RuntimeManager::getRuntimeDataType() 19 | { 20 | static llvm::StructType* type = nullptr; 21 | if (!type) 22 | { 23 | llvm::Type* elems[] = 24 | { 25 | Type::Size, // gas 26 | Type::Size, // gasPrice 27 | Type::BytePtr, // callData 28 | Type::Size, // callDataSize 29 | Type::Word, // apparentValue 30 | Type::BytePtr, // code 31 | Type::Size, // codeSize 32 | Type::Word, // adddress 33 | Type::Word, // caller 34 | Type::Size, // depth 35 | }; 36 | type = llvm::StructType::create(elems, "RuntimeData"); 37 | } 38 | return type; 39 | } 40 | 41 | llvm::StructType* RuntimeManager::getRuntimeType() 42 | { 43 | static llvm::StructType* type = nullptr; 44 | if (!type) 45 | { 46 | llvm::Type* elems[] = 47 | { 48 | Type::RuntimeDataPtr, // data 49 | Type::EnvPtr, // Env* 50 | Array::getType() // memory 51 | }; 52 | type = llvm::StructType::create(elems, "Runtime"); 53 | } 54 | return type; 55 | } 56 | 57 | llvm::StructType* RuntimeManager::getTxContextType() 58 | { 59 | auto name = "evm.txctx"; 60 | auto type = getModule()->getTypeByName(name); 61 | if (type) 62 | return type; 63 | 64 | auto& ctx = getModule()->getContext(); 65 | auto i256 = llvm::IntegerType::get(ctx, 256); 66 | auto h160 = llvm::ArrayType::get(llvm::IntegerType::get(ctx, 8), 20); 67 | auto i64 = llvm::IntegerType::get(ctx, 64); 68 | return llvm::StructType::create({i256, h160, h160, i64, i64, i64, i256}, name); 69 | } 70 | 71 | llvm::Value* RuntimeManager::getTxContextItem(unsigned _index) 72 | { 73 | auto call = m_builder.CreateCall(m_loadTxCtxFn, {m_txCtxLoaded, m_txCtx, m_envPtr}); 74 | call->setCallingConv(llvm::CallingConv::Fast); 75 | auto ptr = m_builder.CreateStructGEP(getTxContextType(), m_txCtx, _index); 76 | if (_index == 1 || _index == 2) 77 | { 78 | // In struct addresses are represented as char[20] to fix alignment 79 | // issues (i160 has alignment of 8). Here we convert them back to i160. 80 | ptr = m_builder.CreateBitCast(ptr, m_builder.getIntNTy(160)->getPointerTo()); 81 | } 82 | return m_builder.CreateLoad(ptr); 83 | } 84 | 85 | namespace 86 | { 87 | llvm::Twine getName(RuntimeData::Index _index) 88 | { 89 | switch (_index) 90 | { 91 | default: return ""; 92 | case RuntimeData::Gas: return "msg.gas"; 93 | case RuntimeData::GasPrice: return "tx.gasprice"; 94 | case RuntimeData::CallData: return "msg.data.ptr"; 95 | case RuntimeData::CallDataSize: return "msg.data.size"; 96 | case RuntimeData::Value: return "msg.value"; 97 | case RuntimeData::Code: return "code.ptr"; 98 | case RuntimeData::CodeSize: return "code.size"; 99 | case RuntimeData::Address: return "msg.address"; 100 | case RuntimeData::Sender: return "msg.sender"; 101 | case RuntimeData::Depth: return "msg.depth"; 102 | } 103 | } 104 | } 105 | 106 | RuntimeManager::RuntimeManager(IRBuilder& _builder, code_iterator _codeBegin, code_iterator _codeEnd): 107 | CompilerHelper(_builder), 108 | m_codeBegin(_codeBegin), 109 | m_codeEnd(_codeEnd) 110 | { 111 | m_txCtxLoaded = m_builder.CreateAlloca(m_builder.getInt1Ty(), nullptr, "txctx.loaded"); 112 | m_builder.CreateStore(m_builder.getInt1(false), m_txCtxLoaded); 113 | m_txCtx = m_builder.CreateAlloca(getTxContextType(), nullptr, "txctx"); 114 | 115 | auto getTxCtxFnTy = llvm::FunctionType::get(Type::Void, {m_txCtx->getType(), Type::EnvPtr}, false); 116 | auto getTxCtxFn = llvm::Function::Create(getTxCtxFnTy, llvm::Function::ExternalLinkage, "evm.get_tx_context", getModule()); 117 | auto loadTxCtxFnTy = llvm::FunctionType::get(Type::Void, {m_txCtxLoaded->getType(), m_txCtx->getType(), Type::EnvPtr}, false); 118 | m_loadTxCtxFn = llvm::Function::Create(loadTxCtxFnTy, llvm::Function::PrivateLinkage, "loadTxCtx", getModule()); 119 | m_loadTxCtxFn->setCallingConv(llvm::CallingConv::Fast); 120 | auto checkBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Check", m_loadTxCtxFn); 121 | auto loadBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Load", m_loadTxCtxFn); 122 | auto exitBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Exit", m_loadTxCtxFn); 123 | 124 | auto iter = m_loadTxCtxFn->arg_begin(); 125 | llvm::Argument* flag = &(*iter++); 126 | flag->setName("flag"); 127 | llvm::Argument* txCtx = &(*iter++); 128 | txCtx->setName("txctx"); 129 | llvm::Argument* env = &(*iter); 130 | env->setName("env"); 131 | 132 | auto b = IRBuilder{checkBB}; 133 | auto f = b.CreateLoad(flag); 134 | b.CreateCondBr(f, exitBB, loadBB); 135 | b.SetInsertPoint(loadBB); 136 | b.CreateStore(b.getInt1(true), flag); 137 | b.CreateCall(getTxCtxFn, {txCtx, env}); 138 | b.CreateBr(exitBB); 139 | b.SetInsertPoint(exitBB); 140 | b.CreateRetVoid(); 141 | 142 | // Unpack data 143 | auto rtPtr = getRuntimePtr(); 144 | m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr"); 145 | assert(m_dataPtr->getType() == Type::RuntimeDataPtr); 146 | m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem"); 147 | assert(m_memPtr->getType() == Array::getType()->getPointerTo()); 148 | m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env"); 149 | assert(m_envPtr->getType() == Type::EnvPtr); 150 | 151 | auto mallocFunc = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, {Type::Size}, false), llvm::Function::ExternalLinkage, "malloc", getModule()); 152 | mallocFunc->setDoesNotThrow(); 153 | mallocFunc->addAttribute(0, llvm::Attribute::NoAlias); 154 | 155 | m_stackBase = m_builder.CreateCall(mallocFunc, m_builder.getInt64(Type::Word->getPrimitiveSizeInBits() / 8 * stackSizeLimit), "stack.base"); // TODO: Use Type::SizeT type 156 | m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stack.size"); 157 | m_builder.CreateStore(m_builder.getInt64(0), m_stackSize); 158 | 159 | auto data = m_builder.CreateLoad(m_dataPtr, "data"); 160 | for (unsigned i = 0; i < m_dataElts.size(); ++i) 161 | m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i))); 162 | 163 | m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr"); 164 | m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr); 165 | 166 | m_returnBufDataPtr = m_builder.CreateAlloca(Type::BytePtr, nullptr, "returndata.ptr"); 167 | m_returnBufSizePtr = m_builder.CreateAlloca(Type::Size, nullptr, "returndatasize.ptr"); 168 | resetReturnBuf(); 169 | 170 | m_exitBB = llvm::BasicBlock::Create(m_builder.getContext(), "Exit", getMainFunction()); 171 | InsertPointGuard guard{m_builder}; 172 | m_builder.SetInsertPoint(m_exitBB); 173 | auto retPhi = m_builder.CreatePHI(Type::MainReturn, 16, "ret"); 174 | auto freeFunc = getModule()->getFunction("free"); 175 | if (!freeFunc) 176 | { 177 | freeFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::WordPtr, false), llvm::Function::ExternalLinkage, "free", getModule()); 178 | freeFunc->setDoesNotThrow(); 179 | freeFunc->addAttribute(1, llvm::Attribute::NoCapture); 180 | } 181 | m_builder.CreateCall(freeFunc, {m_stackBase}); 182 | auto extGasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), RuntimeData::Index::Gas, "msg.gas.ptr"); 183 | m_builder.CreateStore(getGas(), extGasPtr); 184 | m_builder.CreateRet(retPhi); 185 | } 186 | 187 | llvm::Value* RuntimeManager::getRuntimePtr() 188 | { 189 | // Expect first argument of a function to be a pointer to Runtime 190 | auto func = m_builder.GetInsertBlock()->getParent(); 191 | auto rtPtr = func->args().begin(); 192 | assert(rtPtr->getType() == Type::RuntimePtr); 193 | return rtPtr; 194 | } 195 | 196 | llvm::Value* RuntimeManager::getDataPtr() 197 | { 198 | if (getMainFunction()) 199 | return m_dataPtr; 200 | 201 | auto rtPtr = getRuntimePtr(); 202 | auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data"); 203 | assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo()); 204 | return dataPtr; 205 | } 206 | 207 | llvm::Value* RuntimeManager::getEnvPtr() 208 | { 209 | assert(getMainFunction()); // Available only in main function 210 | return m_envPtr; 211 | } 212 | 213 | llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) 214 | { 215 | auto ptr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), _index); 216 | assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType()); 217 | return ptr; 218 | } 219 | 220 | llvm::Value* RuntimeManager::getAddress() 221 | { 222 | return m_dataElts[RuntimeData::Address]; 223 | } 224 | 225 | llvm::Value* RuntimeManager::getSender() 226 | { 227 | return m_dataElts[RuntimeData::Sender]; 228 | } 229 | 230 | llvm::Value* RuntimeManager::getValue() 231 | { 232 | return m_dataElts[RuntimeData::Value]; 233 | } 234 | 235 | llvm::Value* RuntimeManager::getDepth() 236 | { 237 | return m_dataElts[RuntimeData::Depth]; 238 | } 239 | 240 | void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value) 241 | { 242 | auto ptr = getPtr(_index); 243 | assert(ptr->getType() == _value->getType()->getPointerTo()); 244 | m_builder.CreateStore(_value, ptr); 245 | } 246 | 247 | void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size) 248 | { 249 | auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo()); 250 | auto mem = m_builder.CreateLoad(memPtr, "memory"); 251 | auto returnDataPtr = m_builder.CreateGEP(mem, _offset); 252 | set(RuntimeData::ReturnData, returnDataPtr); 253 | 254 | auto size64 = m_builder.CreateTrunc(_size, Type::Size); 255 | set(RuntimeData::ReturnDataSize, size64); 256 | } 257 | 258 | void RuntimeManager::exit(ReturnCode _returnCode) 259 | { 260 | m_builder.CreateBr(m_exitBB); 261 | auto retPhi = llvm::cast(&m_exitBB->front()); 262 | retPhi->addIncoming(Constant::get(_returnCode), m_builder.GetInsertBlock()); 263 | } 264 | 265 | void RuntimeManager::abort(llvm::Value* _jmpBuf) 266 | { 267 | auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); 268 | m_builder.CreateCall(longjmp, {_jmpBuf}); 269 | } 270 | 271 | void RuntimeManager::resetReturnBuf() 272 | { 273 | m_builder.CreateStore(m_builder.getInt64(0), m_returnBufSizePtr); 274 | } 275 | 276 | llvm::Value* RuntimeManager::getCallData() 277 | { 278 | return m_dataElts[RuntimeData::CallData]; 279 | } 280 | 281 | llvm::Value* RuntimeManager::getCode() 282 | { 283 | // OPT Check what is faster 284 | //return get(RuntimeData::Code); 285 | if (!m_codePtr) 286 | m_codePtr = m_builder.CreateGlobalStringPtr({reinterpret_cast(m_codeBegin), static_cast(m_codeEnd - m_codeBegin)}, "code"); 287 | return m_codePtr; 288 | } 289 | 290 | llvm::Value* RuntimeManager::getCodeSize() 291 | { 292 | return Constant::get(m_codeEnd - m_codeBegin); 293 | } 294 | 295 | llvm::Value* RuntimeManager::getCallDataSize() 296 | { 297 | auto value = m_dataElts[RuntimeData::CallDataSize]; 298 | assert(value->getType() == Type::Size); 299 | return m_builder.CreateZExt(value, Type::Word); 300 | } 301 | 302 | llvm::Value* RuntimeManager::getGas() 303 | { 304 | return m_builder.CreateLoad(getGasPtr(), "gas"); 305 | } 306 | 307 | llvm::Value* RuntimeManager::getGasPtr() 308 | { 309 | assert(getMainFunction()); 310 | return m_gasPtr; 311 | } 312 | 313 | llvm::Value* RuntimeManager::getMem() 314 | { 315 | assert(getMainFunction()); 316 | return m_memPtr; 317 | } 318 | 319 | void RuntimeManager::setGas(llvm::Value* _gas) 320 | { 321 | assert(_gas->getType() == Type::Gas); 322 | m_builder.CreateStore(_gas, getGasPtr()); 323 | } 324 | 325 | } 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /libevmjit/RuntimeManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "CompilerHelper.h" 6 | #include "Type.h" 7 | #include "Instruction.h" 8 | 9 | namespace dev 10 | { 11 | namespace eth 12 | { 13 | namespace jit 14 | { 15 | using namespace evmjit; 16 | 17 | class RuntimeManager: public CompilerHelper 18 | { 19 | public: 20 | RuntimeManager(IRBuilder& _builder, code_iterator _codeBegin, code_iterator _codeEnd); 21 | 22 | llvm::Value* getRuntimePtr(); 23 | llvm::Value* getDataPtr(); 24 | llvm::Value* getEnvPtr(); 25 | 26 | llvm::Value* getAddress(); 27 | llvm::Value* getSender(); 28 | llvm::Value* getValue(); 29 | llvm::Value* getGas(); 30 | llvm::Value* getGasPtr(); 31 | llvm::Value* getCallData(); 32 | llvm::Value* getCode(); 33 | llvm::Value* getCodeSize(); 34 | llvm::Value* getCallDataSize(); 35 | llvm::Value* getDepth(); 36 | llvm::Value* getJmpBuf() { return m_jmpBuf; } 37 | void setGas(llvm::Value* _gas); 38 | 39 | llvm::Value* getMem(); 40 | 41 | void registerReturnData(llvm::Value* _index, llvm::Value* _size); // TODO: Move to Memory. 42 | 43 | void exit(ReturnCode _returnCode); 44 | 45 | void abort(llvm::Value* _jmpBuf); 46 | 47 | llvm::Value* getStackBase() const { return m_stackBase; } 48 | llvm::Value* getStackSize() const { return m_stackSize; } 49 | 50 | llvm::Value* getReturnBufDataPtr() const { return m_returnBufDataPtr; } 51 | llvm::Value* getReturnBufSizePtr() const { return m_returnBufSizePtr; } 52 | 53 | /// Reset RETURNDATA buffer (before calls). 54 | /// This effectively only sets the buffer size to 0. 55 | void resetReturnBuf(); 56 | 57 | void setJmpBuf(llvm::Value* _jmpBuf) { m_jmpBuf = _jmpBuf; } 58 | void setExitBB(llvm::BasicBlock* _bb) { m_exitBB = _bb; } 59 | 60 | static llvm::StructType* getRuntimeType(); 61 | static llvm::StructType* getRuntimeDataType(); 62 | llvm::StructType* getTxContextType(); 63 | 64 | llvm::Value* getTxContextItem(unsigned _index); 65 | 66 | //TODO Move to schedule 67 | static const size_t stackSizeLimit = 1024; 68 | 69 | private: 70 | llvm::Value* getPtr(RuntimeData::Index _index); 71 | void set(RuntimeData::Index _index, llvm::Value* _value); 72 | 73 | llvm::Value* m_jmpBuf = nullptr; 74 | llvm::Value* m_dataPtr = nullptr; 75 | llvm::Value* m_gasPtr = nullptr; 76 | llvm::Value* m_memPtr = nullptr; 77 | llvm::Value* m_envPtr = nullptr; 78 | llvm::Value* m_returnBufDataPtr = nullptr; 79 | llvm::Value* m_returnBufSizePtr = nullptr; 80 | 81 | llvm::Value* m_txCtxLoaded = nullptr; 82 | llvm::Value* m_txCtx = nullptr; 83 | llvm::Function* m_loadTxCtxFn = nullptr; 84 | 85 | std::array m_dataElts; 86 | 87 | llvm::Value* m_stackBase = nullptr; 88 | llvm::Value* m_stackSize = nullptr; 89 | 90 | llvm::BasicBlock* m_exitBB = nullptr; 91 | 92 | code_iterator m_codeBegin = {}; 93 | code_iterator m_codeEnd = {}; 94 | llvm::Value* m_codePtr = nullptr; 95 | }; 96 | 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /libevmjit/Type.cpp: -------------------------------------------------------------------------------- 1 | #include "Type.h" 2 | 3 | #include 4 | 5 | #include "RuntimeManager.h" 6 | 7 | namespace dev 8 | { 9 | namespace eth 10 | { 11 | namespace jit 12 | { 13 | 14 | llvm::IntegerType* Type::Word; 15 | llvm::PointerType* Type::WordPtr; 16 | llvm::IntegerType* Type::Bool; 17 | llvm::IntegerType* Type::Size; 18 | llvm::IntegerType* Type::Gas; 19 | llvm::PointerType* Type::GasPtr; 20 | llvm::IntegerType* Type::Byte; 21 | llvm::PointerType* Type::BytePtr; 22 | llvm::Type* Type::Void; 23 | llvm::IntegerType* Type::MainReturn; 24 | llvm::PointerType* Type::EnvPtr; 25 | llvm::PointerType* Type::RuntimeDataPtr; 26 | llvm::PointerType* Type::RuntimePtr; 27 | llvm::ConstantInt* Constant::gasMax; 28 | llvm::MDNode* Type::expectTrue; 29 | 30 | void Type::init(llvm::LLVMContext& _context) 31 | { 32 | if (!Word) // Do init only once 33 | { 34 | Word = llvm::Type::getIntNTy(_context, 256); 35 | WordPtr = Word->getPointerTo(); 36 | Bool = llvm::Type::getInt1Ty(_context); 37 | Size = llvm::Type::getInt64Ty(_context); 38 | Gas = Size; 39 | GasPtr = Gas->getPointerTo(); 40 | Byte = llvm::Type::getInt8Ty(_context); 41 | BytePtr = Byte->getPointerTo(); 42 | Void = llvm::Type::getVoidTy(_context); 43 | MainReturn = llvm::Type::getInt32Ty(_context); 44 | 45 | EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo(); 46 | RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo(); 47 | RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo(); 48 | 49 | Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits::max()); 50 | 51 | expectTrue = llvm::MDBuilder{_context}.createBranchWeights(1, 0); 52 | } 53 | } 54 | 55 | llvm::ConstantInt* Constant::get(int64_t _n) 56 | { 57 | return llvm::ConstantInt::getSigned(Type::Word, _n); 58 | } 59 | 60 | llvm::ConstantInt* Constant::get(llvm::APInt const& _n) 61 | { 62 | return llvm::ConstantInt::get(Type::Word->getContext(), _n); 63 | } 64 | 65 | llvm::ConstantInt* Constant::get(ReturnCode _returnCode) 66 | { 67 | return llvm::ConstantInt::get(Type::MainReturn, static_cast(_returnCode)); 68 | } 69 | 70 | } 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /libevmjit/Type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "preprocessor/llvm_includes_start.h" 4 | #include 5 | #include 6 | #include 7 | #include "preprocessor/llvm_includes_end.h" 8 | 9 | #include "JIT.h" 10 | 11 | namespace dev 12 | { 13 | namespace eth 14 | { 15 | namespace jit 16 | { 17 | using namespace evmjit; 18 | 19 | struct Type 20 | { 21 | static llvm::IntegerType* Word; 22 | static llvm::PointerType* WordPtr; 23 | 24 | static llvm::IntegerType* Bool; 25 | static llvm::IntegerType* Size; 26 | static llvm::IntegerType* Gas; 27 | static llvm::PointerType* GasPtr; 28 | 29 | static llvm::IntegerType* Byte; 30 | static llvm::PointerType* BytePtr; 31 | 32 | static llvm::Type* Void; 33 | 34 | /// Main function return type 35 | static llvm::IntegerType* MainReturn; 36 | 37 | static llvm::PointerType* EnvPtr; 38 | static llvm::PointerType* RuntimeDataPtr; 39 | static llvm::PointerType* RuntimePtr; 40 | 41 | // TODO: Redesign static LLVM objects 42 | static llvm::MDNode* expectTrue; 43 | 44 | static void init(llvm::LLVMContext& _context); 45 | }; 46 | 47 | struct Constant 48 | { 49 | static llvm::ConstantInt* gasMax; 50 | 51 | /// Returns word-size constant 52 | static llvm::ConstantInt* get(int64_t _n); 53 | static llvm::ConstantInt* get(llvm::APInt const& _n); 54 | 55 | static llvm::ConstantInt* get(ReturnCode _returnCode); 56 | }; 57 | 58 | } 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /libevmjit/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include "Utils.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "BuildInfo.gen.h" 8 | 9 | namespace dev 10 | { 11 | namespace evmjit 12 | { 13 | 14 | #if !defined(NDEBUG) // Debug 15 | 16 | std::ostream& getLogStream(char const* _channel) 17 | { 18 | static std::ostream nullStream{nullptr}; 19 | #if LLVM_DEBUG 20 | return (llvm::DebugFlag && llvm::isCurrentDebugType(_channel)) ? std::cerr : nullStream; 21 | #else 22 | return (void)_channel, nullStream; 23 | #endif 24 | } 25 | 26 | #endif 27 | 28 | namespace 29 | { 30 | 31 | /** libkeccak-tiny 32 | * 33 | * A single-file implementation of SHA-3 and SHAKE. 34 | * 35 | * Implementor: David Leon Gil 36 | * License: CC0, attribution kindly requested. Blame taken too, 37 | * but not liability. 38 | */ 39 | 40 | /******** The Keccak-f[1600] permutation ********/ 41 | 42 | /*** Constants. ***/ 43 | static const uint8_t rho[24] = \ 44 | { 1, 3, 6, 10, 15, 21, 45 | 28, 36, 45, 55, 2, 14, 46 | 27, 41, 56, 8, 25, 43, 47 | 62, 18, 39, 61, 20, 44}; 48 | static const uint8_t pi[24] = \ 49 | {10, 7, 11, 17, 18, 3, 50 | 5, 16, 8, 21, 24, 4, 51 | 15, 23, 19, 13, 12, 2, 52 | 20, 14, 22, 9, 6, 1}; 53 | static const uint64_t RC[24] = \ 54 | {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, 55 | 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 56 | 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, 57 | 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, 58 | 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL, 59 | 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL}; 60 | 61 | /*** Helper macros to unroll the permutation. ***/ 62 | #define rol(x, s) (((x) << s) | ((x) >> (64 - s))) 63 | #define REPEAT6(e) e e e e e e 64 | #define REPEAT24(e) REPEAT6(e e e e) 65 | #define REPEAT5(e) e e e e e 66 | #define FOR5(v, s, e) \ 67 | v = 0; \ 68 | REPEAT5(e; v = decltype(v)(v + s);) 69 | 70 | /*** Keccak-f[1600] ***/ 71 | static inline void keccakf(void* state) { 72 | uint64_t* a = (uint64_t*)state; 73 | uint64_t b[5] = {0}; 74 | uint64_t t = 0; 75 | uint8_t x, y; 76 | 77 | for (int i = 0; i < 24; i++) { 78 | // Theta 79 | FOR5(x, 1, 80 | b[x] = 0; 81 | FOR5(y, 5, 82 | b[x] ^= a[x + y]; )) 83 | FOR5(x, 1, 84 | FOR5(y, 5, 85 | a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) 86 | // Rho and pi 87 | t = a[1]; 88 | x = 0; 89 | REPEAT24(b[0] = a[pi[x]]; 90 | a[pi[x]] = rol(t, rho[x]); 91 | t = b[0]; 92 | x++; ) 93 | // Chi 94 | FOR5(y, 95 | 5, 96 | FOR5(x, 1, 97 | b[x] = a[y + x];) 98 | FOR5(x, 1, 99 | a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); )) 100 | // Iota 101 | a[0] ^= RC[i]; 102 | } 103 | } 104 | 105 | /******** The FIPS202-defined functions. ********/ 106 | 107 | /*** Some helper macros. ***/ 108 | 109 | #define _(S) do { S } while (0) 110 | #define FOR(i, ST, L, S) \ 111 | _(for (size_t i = 0; i < L; i += ST) { S; }) 112 | #define mkapply_ds(NAME, S) \ 113 | static inline void NAME(uint8_t* dst, \ 114 | const uint8_t* src, \ 115 | size_t len) { \ 116 | FOR(i, 1, len, S); \ 117 | } 118 | #define mkapply_sd(NAME, S) \ 119 | static inline void NAME(const uint8_t* src, \ 120 | uint8_t* dst, \ 121 | size_t len) { \ 122 | FOR(i, 1, len, S); \ 123 | } 124 | 125 | mkapply_ds(xorin, dst[i] ^= src[i]) // xorin 126 | mkapply_sd(setout, dst[i] = src[i]) // setout 127 | 128 | #define P keccakf 129 | #define Plen 200 130 | 131 | // Fold P*F over the full blocks of an input. 132 | #define foldP(I, L, F) \ 133 | while (L >= rate) { \ 134 | F(a, I, rate); \ 135 | P(a); \ 136 | I += rate; \ 137 | L -= rate; \ 138 | } 139 | 140 | /** The sponge-based hash construction. **/ 141 | static inline int hash(uint8_t* out, size_t outlen, 142 | const uint8_t* in, size_t inlen, 143 | size_t rate, uint8_t delim) { 144 | if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) { 145 | return -1; 146 | } 147 | uint8_t a[Plen] = {0}; 148 | // Absorb input. 149 | foldP(in, inlen, xorin); 150 | // Xor in the DS and pad frame. 151 | a[inlen] ^= delim; 152 | a[rate - 1] ^= 0x80; 153 | // Xor in the last block. 154 | xorin(a, in, inlen); 155 | // Apply P 156 | P(a); 157 | // Squeeze output. 158 | foldP(out, outlen, setout); 159 | setout(a, out, outlen); 160 | memset(a, 0, 200); 161 | return 0; 162 | } 163 | 164 | /*** Helper macros to define SHA3 and SHAKE instances. ***/ 165 | #define defkeccak(bits) \ 166 | static int keccak_##bits(uint8_t* out, size_t outlen, \ 167 | const uint8_t* in, size_t inlen) { \ 168 | if (outlen > (bits/8)) { \ 169 | return -1; \ 170 | } \ 171 | return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \ 172 | } 173 | 174 | defkeccak(256) 175 | 176 | } 177 | 178 | void keccak(uint8_t const* _data, uint64_t _size, uint8_t* o_hash) 179 | { 180 | keccak_256(o_hash, 32, _data, _size); 181 | } 182 | 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /libevmjit/Utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace dev { 7 | namespace evmjit { 8 | 9 | void keccak(uint8_t const *_data, uint64_t _size, uint8_t *o_hash); 10 | 11 | // The same as assert, but expression is always evaluated and result returned 12 | #define CHECK(expr) (assert(expr), expr) 13 | 14 | #if !defined(NDEBUG) // Debug 15 | 16 | std::ostream &getLogStream(char const *_channel); 17 | 18 | #define DLOG(CHANNEL) ::dev::evmjit::getLogStream(#CHANNEL) 19 | 20 | #else // Release 21 | 22 | struct Voider 23 | { 24 | void operator=(std::ostream const&) {} 25 | }; 26 | 27 | #define DLOG(CHANNEL) true ? (void)0 : ::dev::evmjit::Voider{} = std::cerr 28 | 29 | #endif 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /libevmjit/preprocessor/llvm_includes_end.h: -------------------------------------------------------------------------------- 1 | #if defined(_MSC_VER) 2 | #pragma warning(pop) 3 | #endif 4 | -------------------------------------------------------------------------------- /libevmjit/preprocessor/llvm_includes_start.h: -------------------------------------------------------------------------------- 1 | #if defined(_MSC_VER) 2 | #pragma warning(push) 3 | #pragma warning(disable: 4267 4244 4800 4624 4141 4291 4146) 4 | #endif 5 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -x -e 3 | 4 | mkdir -p build && cd build 5 | cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DEVMJIT_EXAMPLES=On -DEVMJIT_TESTS=On .. 6 | cmake --build . 7 | cmake --build . --target evmjit-standalone 8 | cmake --build . --target example-capi 9 | -------------------------------------------------------------------------------- /scripts/install_cmake.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # This script downloads the CMake binary and installs it in ~/.local directory 4 | # (the cmake executable will be in ~/.local/bin). 5 | # This is mostly suitable for CIs, not end users. 6 | 7 | set -e 8 | 9 | VERSION=3.7.1 10 | PREFIX=~/.local 11 | 12 | OS=$(uname -s) 13 | case $OS in 14 | Linux) SHA256=7b4b7a1d9f314f45722899c0521c261e4bfab4a6b532609e37fef391da6bade2;; 15 | Darwin) SHA256=1851d1448964893fdc5a8c05863326119f397a3790e0c84c40b83499c7960267;; 16 | esac 17 | 18 | 19 | BIN=$PREFIX/bin 20 | 21 | if test -f $BIN/cmake && ($BIN/cmake --version | grep -q "$VERSION"); then 22 | echo "CMake $VERSION already installed in $BIN" 23 | else 24 | FILE=cmake-$VERSION-$OS-x86_64.tar.gz 25 | URL=https://cmake.org/files/v3.7/$FILE 26 | ERROR=0 27 | TMPFILE=$(mktemp --tmpdir cmake-$VERSION-$OS-x86_64.XXXXXXXX.tar.gz) 28 | echo "Downloading CMake ($URL)..." 29 | wget "$URL" -O "$TMPFILE" -nv 30 | if ! (shasum -a256 "$TMPFILE" | grep -q "$SHA256"); then 31 | echo "Checksum mismatch ($TMPFILE)" 32 | exit 1 33 | fi 34 | mkdir -p "$PREFIX" 35 | tar xzf "$TMPFILE" -C "$PREFIX" --strip 1 36 | rm $TMPFILE 37 | fi 38 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Library tests: 2 | add_executable(test-evmjit-standalone test-evmjit-standalone.c) 3 | target_include_directories(test-evmjit-standalone PRIVATE ../include) 4 | if (MSVC) 5 | set(SYSTEM_LIBS "") 6 | elseif (APPLE) 7 | set(SYSTEM_LIBS stdc++ curses z pthread m) 8 | else() 9 | set(SYSTEM_LIBS stdc++ ncurses z pthread dl m) 10 | endif() 11 | target_link_libraries(test-evmjit-standalone PRIVATE ${EVMJIT_STANDALONE_LIB} evmc ${SYSTEM_LIBS}) 12 | add_dependencies(test-evmjit-standalone evmjit-standalone) 13 | 14 | 15 | add_test(evmjit-standalone test-evmjit-standalone) 16 | -------------------------------------------------------------------------------- /tests/test-evmjit-standalone.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | struct evmc_instance* instance = evmjit_create(); 7 | if (EVMC_ABI_VERSION != instance->abi_version) 8 | { 9 | printf("Error: expected ABI version %u!\n", EVMC_ABI_VERSION); 10 | return 1; 11 | } 12 | printf("EVMJIT ABI version %u\n", instance->abi_version); 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /wercker.yml: -------------------------------------------------------------------------------- 1 | box: chfast/cpp-ethereum-dev 2 | 3 | # This is the build pipeline. Pipelines are the core of wercker 4 | # Read more about pipelines on our dev center 5 | # http://devcenter.wercker.com/docs/pipelines/index.html 6 | build: 7 | # Steps make up the actions in your pipeline 8 | # Read more about steps on our dev center: 9 | # http://devcenter.wercker.com/docs/steps/index.html 10 | steps: 11 | - script: 12 | name: configure 13 | code: | 14 | mkdir build && cd build 15 | cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DLLVM_DIR=/usr/lib/llvm-3.9/lib/cmake/llvm 16 | - script: 17 | name: build 18 | code: cmake --build build 19 | --------------------------------------------------------------------------------