diff --git a/CMakeLists.txt b/CMakeLists.txt index efb936ab..0f0cfe08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.4.0) +cmake_minimum_required(VERSION 3.5) cmake_policy(SET CMP0042 OLD) # Fix MACOSX_RPATH. cmake_policy(SET CMP0048 NEW) # Allow VERSION argument in project(). @@ -6,10 +6,14 @@ if (POLICY CMP0054) cmake_policy(SET CMP0054 NEW) # No longer implicitly dereference variables. endif() -set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo) +include(cmake/cable/bootstrap.cmake) +include(CableBuildType) + +cable_set_build_type(DEFAULT RelWithDebInfo CONFIGURATION_TYPES Debug Release RelWithDebInfo) project(EVMJIT VERSION 0.9.0.2 LANGUAGES CXX C) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") message(STATUS "EVM JIT ${EVMJIT_VERSION_MAJOR}.${EVMJIT_VERSION_MINOR}.${EVMJIT_VERSION_PATCH}") diff --git a/cmake/cable/.gitignore b/cmake/cable/.gitignore new file mode 100644 index 00000000..a09c56df --- /dev/null +++ b/cmake/cable/.gitignore @@ -0,0 +1 @@ +/.idea diff --git a/cmake/cable/CableBuildInfo.cmake b/cmake/cable/CableBuildInfo.cmake new file mode 100644 index 00000000..5ce703ef --- /dev/null +++ b/cmake/cable/CableBuildInfo.cmake @@ -0,0 +1,99 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +if(cable_build_info_included) + return() +endif() +set(cable_build_info_included TRUE) + +include(GNUInstallDirs) + +set(cable_buildinfo_template_dir ${CMAKE_CURRENT_LIST_DIR}/buildinfo) + +function(cable_add_buildinfo_library) + + cmake_parse_arguments("" "" PROJECT_NAME;EXPORT "" ${ARGN}) + + if(NOT _PROJECT_NAME) + message(FATAL_ERROR "The PROJECT_NAME argument missing") + endif() + + # Come up with the target and the C function names. + set(name ${_PROJECT_NAME}-buildinfo) + set(FUNCTION_NAME ${_PROJECT_NAME}_get_buildinfo) + + set(output_dir ${CMAKE_CURRENT_BINARY_DIR}/${_PROJECT_NAME}) + set(header_file ${output_dir}/buildinfo.h) + set(source_file ${output_dir}/buildinfo.c) + + if(CMAKE_CONFIGURATION_TYPES) + set(build_type ${CMAKE_CFG_INTDIR}) + else() + set(build_type ${CMAKE_BUILD_TYPE}) + endif() + + # Find git here to allow the user to provide hints. + find_package(Git) + + # Git info target. + # + # This target is named -git and is always built. + # The executed script gitinfo.cmake check git status and updates files + # containing git information if anything has changed. + add_custom_target( + ${name}-git + COMMAND ${CMAKE_COMMAND} + -DGIT=${GIT_EXECUTABLE} + -DSOURCE_DIR=${PROJECT_SOURCE_DIR} + -DOUTPUT_DIR=${output_dir} + -P ${cable_buildinfo_template_dir}/gitinfo.cmake + BYPRODUCTS ${output_dir}/gitinfo.txt + ) + + add_custom_command( + COMMENT "Updating ${name}:" + OUTPUT ${source_file} ${output_dir}/buildinfo.json + COMMAND ${CMAKE_COMMAND} + -DOUTPUT_DIR=${output_dir} + -DPROJECT_NAME=${_PROJECT_NAME} + -DFUNCTION_NAME=${FUNCTION_NAME} + -DPROJECT_VERSION=${PROJECT_VERSION} + -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME} + -DSYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} + -DCOMPILER_ID=${CMAKE_CXX_COMPILER_ID} + -DCOMPILER_VERSION=${CMAKE_CXX_COMPILER_VERSION} + -DBUILD_TYPE=${build_type} + -P ${cable_buildinfo_template_dir}/buildinfo.cmake + DEPENDS + ${cable_buildinfo_template_dir}/buildinfo.cmake + ${cable_buildinfo_template_dir}/buildinfo.c.in + ${cable_buildinfo_template_dir}/buildinfo.json.in + ${cable_buildinfo_template_dir}/version.h.in + ${name}-git + ${output_dir}/gitinfo.txt + ) + + string(TIMESTAMP TIMESTAMP) + configure_file(${cable_buildinfo_template_dir}/buildinfo.h.in ${header_file}) + + # Add buildinfo library under given name. + # Make is static and do not build by default until some other target will actually use it. + add_library(${name} STATIC ${source_file} ${header_file}) + + target_include_directories(${name} PUBLIC $) + set_target_properties( + ${name} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${output_dir} + ARCHIVE_OUTPUT_DIRECTORY ${output_dir} + ) + + if(_EXPORT) + install(TARGETS ${name} EXPORT ${_EXPORT} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + endif() + +endfunction() diff --git a/cmake/cable/CableBuildType.cmake b/cmake/cable/CableBuildType.cmake new file mode 100644 index 00000000..80722965 --- /dev/null +++ b/cmake/cable/CableBuildType.cmake @@ -0,0 +1,44 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +if(cable_build_type_included) + return() +endif() +set(cable_build_type_included TRUE) + +macro(cable_set_build_type) + if(NOT PROJECT_IS_NESTED) + # Do this configuration only in the top project. + if(PROJECT_SOURCE_DIR) + message(FATAL_ERROR "cable_set_build_type() can be used before project()") + endif() + + cmake_parse_arguments(build_type "" DEFAULT CONFIGURATION_TYPES ${ARGN}) + + if(CMAKE_CONFIGURATION_TYPES) + if(build_type_CONFIGURATION_TYPES) + set( + CMAKE_CONFIGURATION_TYPES + ${build_type_CONFIGURATION_TYPES} + CACHE + STRING + "Available configurations for multi-configuration generators" + FORCE + ) + endif() + cable_log("Configurations: ${CMAKE_CONFIGURATION_TYPES}") + else() + if(build_type_DEFAULT AND NOT CMAKE_BUILD_TYPE) + set( + CMAKE_BUILD_TYPE + ${build_type_DEFAULT} + CACHE STRING + "Build type for single-configuration generators" + FORCE + ) + endif() + cable_log("Build type: ${CMAKE_BUILD_TYPE}") + endif() + endif() +endmacro() diff --git a/cmake/cable/CableCompilerSettings.cmake b/cmake/cable/CableCompilerSettings.cmake new file mode 100644 index 00000000..f86aa6ef --- /dev/null +++ b/cmake/cable/CableCompilerSettings.cmake @@ -0,0 +1,157 @@ +# Cable: CMake Bootstrap Library +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +include(CheckCXXCompilerFlag) + +# Adds CXX compiler flag if the flag is supported by the compiler. +# +# This is effectively a combination of CMake's check_cxx_compiler_flag() +# and add_compile_options(): +# +# if(check_cxx_compiler_flag(flag)) +# add_compile_options(flag) +# +function(cable_add_cxx_compiler_flag_if_supported FLAG) + # Remove leading - or / from the flag name. + string(REGEX REPLACE "^-|/" "" name ${FLAG}) + check_cxx_compiler_flag(${FLAG} ${name}) + if(${name}) + add_compile_options(${FLAG}) + endif() + + # If the optional argument passed, store the result there. + if(ARGV1) + set(${ARGV1} ${name} PARENT_SCOPE) + endif() +endfunction() + + +# Configures the compiler with default flags. +macro(cable_configure_compiler) + if(NOT PROJECT_IS_NESTED) + # Do this configuration only in the top project. + + cmake_parse_arguments(cable "NO_CONVERSION_WARNINGS;NO_STACK_PROTECTION;NO_PEDANTIC" "" "" ${ARGN}) + + if(cable_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "cable_configure_compiler: Unknown options: ${cable_UNPARSED_ARGUMENTS}") + endif() + + # Set helper variables recognizing C++ compilers. + if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) + set(CABLE_COMPILER_GNU TRUE) + elseif(${CMAKE_CXX_COMPILER_ID} MATCHES Clang) + # This matches both clang and AppleClang. + set(CABLE_COMPILER_CLANG TRUE) + endif() + + if(CABLE_COMPILER_GNU OR CABLE_COMPILER_CLANG) + set(CABLE_COMPILER_GNULIKE TRUE) + endif() + + if(CABLE_COMPILER_GNULIKE) + + if(NOT cable_NO_PEDANTIC) + add_compile_options(-Wpedantic) + endif() + + # Enable basing warnings set and treat them as errors. + add_compile_options(-Werror -Wall -Wextra -Wshadow) + + if(NOT cable_NO_CONVERSION_WARNINGS) + # Enable conversion warnings if not explicitly disabled. + add_compile_options(-Wconversion -Wsign-conversion) + endif() + + # Allow unknown pragmas, we don't want to wrap them with #ifdefs. + add_compile_options(-Wno-unknown-pragmas) + + # Stack protection. + check_cxx_compiler_flag(-fstack-protector fstack-protector) + if(fstack-protector) + # The compiler supports stack protection options. + if(cable_NO_STACK_PROTECTION) + # Stack protection explicitly disabled. + # Add "no" flag, because in some configuration the compiler has it enabled by default. + add_compile_options(-fno-stack-protector) + else() + # Try enabling the "strong" variant. + cable_add_cxx_compiler_flag_if_supported(-fstack-protector-strong have_stack_protector_strong_support) + if(NOT have_stack_protector_strong_support) + # Fallback to standard variant of "strong" not available. + add_compile_options(-fstack-protector) + endif() + endif() + endif() + + cable_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough) + + elseif(MSVC) + + # Get rid of default warning level. + string(REPLACE " /W3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE " /W3" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + + # Enable basing warnings set and treat them as errors. + add_compile_options(/W4 /WX) + + # Allow unknown pragmas, we don't want to wrap them with #ifdefs. + add_compile_options(/wd4068) + + endif() + + # Option for arch=native. + option(NATIVE "Build for native CPU" OFF) + if(NATIVE) + if(MSVC) + add_compile_options(-arch:AVX) + else() + add_compile_options(-mtune=native -march=native) + endif() + elseif(NOT MSVC) + # Tune for currently most common CPUs. + cable_add_cxx_compiler_flag_if_supported(-mtune=generic) + endif() + + # Sanitizers support. + set(SANITIZE OFF CACHE STRING "Build with the specified sanitizer") + if(SANITIZE) + # Set the linker flags first, they are required to properly test the compiler flag. + set(CMAKE_SHARED_LINKER_FLAGS "-fsanitize=${SANITIZE} ${CMAKE_SHARED_LINKER_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "-fsanitize=${SANITIZE} ${CMAKE_EXE_LINKER_FLAGS}") + + set(test_name have_fsanitize_${SANITIZE}) + check_cxx_compiler_flag(-fsanitize=${SANITIZE} ${test_name}) + if(NOT ${test_name}) + message(FATAL_ERROR "Unsupported sanitizer: ${SANITIZE}") + endif() + add_compile_options(-fno-omit-frame-pointer -fsanitize=${SANITIZE}) + + set(backlist_file ${PROJECT_SOURCE_DIR}/sanitizer-blacklist.txt) + if(EXISTS ${backlist_file}) + check_cxx_compiler_flag(-fsanitize-blacklist=${backlist_file} have_fsanitize-blacklist) + if(have_fsanitize-blacklist) + add_compile_options(-fsanitize-blacklist=${backlist_file}) + endif() + endif() + endif() + + # Code coverage support. + option(COVERAGE "Build with code coverage support" OFF) + if(COVERAGE) + # Set the linker flags first, they are required to properly test the compiler flag. + set(CMAKE_SHARED_LINKER_FLAGS "--coverage ${CMAKE_SHARED_LINKER_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "--coverage ${CMAKE_EXE_LINKER_FLAGS}") + + set(CMAKE_REQUIRED_LIBRARIES "--coverage ${CMAKE_REQUIRED_LIBRARIES}") + check_cxx_compiler_flag(--coverage have_coverage) + string(REPLACE "--coverage " "" CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + if(NOT have_coverage) + message(FATAL_ERROR "Coverage not supported") + endif() + add_compile_options(-g --coverage) + endif() + + endif() +endmacro() diff --git a/cmake/cable/CablePackage.cmake b/cmake/cable/CablePackage.cmake new file mode 100644 index 00000000..d3207da7 --- /dev/null +++ b/cmake/cable/CablePackage.cmake @@ -0,0 +1,28 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +if(cable_package_included) + return() +endif() +set(cable_package_included TRUE) + +# Configures CPack to build the archive package. +macro(cable_add_archive_package) + if(WIN32) + set(CPACK_GENERATOR ZIP) + set(CPACK_SOURCE_GENERATOR ZIP) + else() + set(CPACK_GENERATOR TGZ) + set(CPACK_SOURCE_GENERATOR TGZ) + endif() + string(TOLOWER ${CMAKE_SYSTEM_NAME} system_name) + string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} system_processor) + set(CPACK_PACKAGE_FILE_NAME ${PROJECT_NAME}-${PROJECT_VERSION}-${system_name}-${system_processor}) + set(CPACK_SOURCE_PACKAGE_FILE_NAME ${PROJECT_NAME}-${PROJECT_VERSION}-source) + set(CPACK_PACKAGE_CHECKSUM SHA256) + set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY FALSE) + unset(system_name) + unset(system_processor) + include(CPack) +endmacro() diff --git a/cmake/cable/CableToolchains.cmake b/cmake/cable/CableToolchains.cmake new file mode 100644 index 00000000..d5af2cb3 --- /dev/null +++ b/cmake/cable/CableToolchains.cmake @@ -0,0 +1,31 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +set(cable_toolchain_dir ${CMAKE_CURRENT_LIST_DIR}/toolchains) + +function(cable_configure_toolchain) + if(NOT PROJECT_IS_NESTED) + # Do this configuration only in the top project. + + cmake_parse_arguments("" "" "DEFAULT" "" ${ARGN}) + + set(default_toolchain default) + if(_DEFAULT) + set(default_toolchain ${_DEFAULT}) + endif() + + set(TOOLCHAIN ${default_toolchain} CACHE STRING "CMake toolchain") + + set(toolchain_file toolchains/${TOOLCHAIN}.cmake) + foreach(path ${CMAKE_MODULE_PATH}) + if(EXISTS "${path}/${toolchain_file}") + set(toolchain_file "${path}/${toolchain_file}") + break() + endif() + endforeach() + + cable_debug("Toolchain file: ${toolchain_file}") + set(CMAKE_TOOLCHAIN_FILE ${toolchain_file} CACHE FILEPATH "CMake toolchain file") + endif() +endfunction() diff --git a/cmake/cable/HunterGate.cmake b/cmake/cable/HunterGate.cmake new file mode 100644 index 00000000..e78d3e89 --- /dev/null +++ b/cmake/cable/HunterGate.cmake @@ -0,0 +1,528 @@ +# Copyright (c) 2013-2019, Ruslan Baratov +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This is a gate file to Hunter package manager. +# Include this file using `include` command and add package you need, example: +# +# cmake_minimum_required(VERSION 3.2) +# +# include("cmake/HunterGate.cmake") +# HunterGate( +# URL "https://github.com/path/to/hunter/archive.tar.gz" +# SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d" +# ) +# +# project(MyProject) +# +# hunter_add_package(Foo) +# hunter_add_package(Boo COMPONENTS Bar Baz) +# +# Projects: +# * https://github.com/hunter-packages/gate/ +# * https://github.com/ruslo/hunter + +option(HUNTER_ENABLED "Enable Hunter package manager support" ON) + +if(HUNTER_ENABLED) + if(CMAKE_VERSION VERSION_LESS "3.2") + message( + FATAL_ERROR + "At least CMake version 3.2 required for Hunter dependency management." + " Update CMake or set HUNTER_ENABLED to OFF." + ) + endif() +endif() + +include(CMakeParseArguments) # cmake_parse_arguments + +option(HUNTER_STATUS_PRINT "Print working status" ON) +option(HUNTER_STATUS_DEBUG "Print a lot info" OFF) +option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON) + +set(HUNTER_ERROR_PAGE "https://docs.hunter.sh/en/latest/reference/errors") + +function(hunter_gate_status_print) + if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG) + foreach(print_message ${ARGV}) + message(STATUS "[hunter] ${print_message}") + endforeach() + endif() +endfunction() + +function(hunter_gate_status_debug) + if(HUNTER_STATUS_DEBUG) + foreach(print_message ${ARGV}) + string(TIMESTAMP timestamp) + message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}") + endforeach() + endif() +endfunction() + +function(hunter_gate_error_page error_page) + message("------------------------------ ERROR ------------------------------") + message(" ${HUNTER_ERROR_PAGE}/${error_page}.html") + message("-------------------------------------------------------------------") + message("") + message(FATAL_ERROR "") +endfunction() + +function(hunter_gate_internal_error) + message("") + foreach(print_message ${ARGV}) + message("[hunter ** INTERNAL **] ${print_message}") + endforeach() + message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") + message("") + hunter_gate_error_page("error.internal") +endfunction() + +function(hunter_gate_fatal_error) + cmake_parse_arguments(hunter "" "ERROR_PAGE" "" "${ARGV}") + if("${hunter_ERROR_PAGE}" STREQUAL "") + hunter_gate_internal_error("Expected ERROR_PAGE") + endif() + message("") + foreach(x ${hunter_UNPARSED_ARGUMENTS}) + message("[hunter ** FATAL ERROR **] ${x}") + endforeach() + message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]") + message("") + hunter_gate_error_page("${hunter_ERROR_PAGE}") +endfunction() + +function(hunter_gate_user_error) + hunter_gate_fatal_error(${ARGV} ERROR_PAGE "error.incorrect.input.data") +endfunction() + +function(hunter_gate_self root version sha1 result) + string(COMPARE EQUAL "${root}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("root is empty") + endif() + + string(COMPARE EQUAL "${version}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("version is empty") + endif() + + string(COMPARE EQUAL "${sha1}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("sha1 is empty") + endif() + + string(SUBSTRING "${sha1}" 0 7 archive_id) + + set( + hunter_self + "${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked" + ) + + set("${result}" "${hunter_self}" PARENT_SCOPE) +endfunction() + +# Set HUNTER_GATE_ROOT cmake variable to suitable value. +function(hunter_gate_detect_root) + # Check CMake variable + string(COMPARE NOTEQUAL "${HUNTER_ROOT}" "" not_empty) + if(not_empty) + set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable") + return() + endif() + + # Check environment variable + string(COMPARE NOTEQUAL "$ENV{HUNTER_ROOT}" "" not_empty) + if(not_empty) + set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT detected by environment variable") + return() + endif() + + # Check HOME environment variable + string(COMPARE NOTEQUAL "$ENV{HOME}" "" result) + if(result) + set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable") + return() + endif() + + # Check SYSTEMDRIVE and USERPROFILE environment variable (windows only) + if(WIN32) + string(COMPARE NOTEQUAL "$ENV{SYSTEMDRIVE}" "" result) + if(result) + set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug( + "HUNTER_ROOT set using SYSTEMDRIVE environment variable" + ) + return() + endif() + + string(COMPARE NOTEQUAL "$ENV{USERPROFILE}" "" result) + if(result) + set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE) + hunter_gate_status_debug( + "HUNTER_ROOT set using USERPROFILE environment variable" + ) + return() + endif() + endif() + + hunter_gate_fatal_error( + "Can't detect HUNTER_ROOT" + ERROR_PAGE "error.detect.hunter.root" + ) +endfunction() + +function(hunter_gate_download dir) + string( + COMPARE + NOTEQUAL + "$ENV{HUNTER_DISABLE_AUTOINSTALL}" + "" + disable_autoinstall + ) + if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL) + hunter_gate_fatal_error( + "Hunter not found in '${dir}'" + "Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'" + "Settings:" + " HUNTER_ROOT: ${HUNTER_GATE_ROOT}" + " HUNTER_SHA1: ${HUNTER_GATE_SHA1}" + ERROR_PAGE "error.run.install" + ) + endif() + string(COMPARE EQUAL "${dir}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("Empty 'dir' argument") + endif() + + string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("HUNTER_GATE_SHA1 empty") + endif() + + string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad) + if(is_bad) + hunter_gate_internal_error("HUNTER_GATE_URL empty") + endif() + + set(done_location "${dir}/DONE") + set(sha1_location "${dir}/SHA1") + + set(build_dir "${dir}/Build") + set(cmakelists "${dir}/CMakeLists.txt") + + hunter_gate_status_debug("Locking directory: ${dir}") + file(LOCK "${dir}" DIRECTORY GUARD FUNCTION) + hunter_gate_status_debug("Lock done") + + if(EXISTS "${done_location}") + # while waiting for lock other instance can do all the job + hunter_gate_status_debug("File '${done_location}' found, skip install") + return() + endif() + + file(REMOVE_RECURSE "${build_dir}") + file(REMOVE_RECURSE "${cmakelists}") + + file(MAKE_DIRECTORY "${build_dir}") # check directory permissions + + # Disabling languages speeds up a little bit, reduces noise in the output + # and avoids path too long windows error + file( + WRITE + "${cmakelists}" + "cmake_minimum_required(VERSION 3.2)\n" + "project(HunterDownload LANGUAGES NONE)\n" + "include(ExternalProject)\n" + "ExternalProject_Add(\n" + " Hunter\n" + " URL\n" + " \"${HUNTER_GATE_URL}\"\n" + " URL_HASH\n" + " SHA1=${HUNTER_GATE_SHA1}\n" + " DOWNLOAD_DIR\n" + " \"${dir}\"\n" + " TLS_VERIFY\n" + " ${HUNTER_TLS_VERIFY}\n" + " SOURCE_DIR\n" + " \"${dir}/Unpacked\"\n" + " CONFIGURE_COMMAND\n" + " \"\"\n" + " BUILD_COMMAND\n" + " \"\"\n" + " INSTALL_COMMAND\n" + " \"\"\n" + ")\n" + ) + + if(HUNTER_STATUS_DEBUG) + set(logging_params "") + else() + set(logging_params OUTPUT_QUIET) + endif() + + hunter_gate_status_debug("Run generate") + + # Need to add toolchain file too. + # Otherwise on Visual Studio + MDD this will fail with error: + # "Could not find an appropriate version of the Windows 10 SDK installed on this machine" + if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") + get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE) + set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}") + else() + # 'toolchain_arg' can't be empty + set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=") + endif() + + string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make) + if(no_make) + set(make_arg "") + else() + # Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM + set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}") + endif() + + execute_process( + COMMAND + "${CMAKE_COMMAND}" + "-H${dir}" + "-B${build_dir}" + "-G${CMAKE_GENERATOR}" + "${toolchain_arg}" + ${make_arg} + WORKING_DIRECTORY "${dir}" + RESULT_VARIABLE download_result + ${logging_params} + ) + + if(NOT download_result EQUAL 0) + hunter_gate_internal_error( + "Configure project failed." + "To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}" + "In directory ${dir}" + ) + endif() + + hunter_gate_status_print( + "Initializing Hunter workspace (${HUNTER_GATE_SHA1})" + " ${HUNTER_GATE_URL}" + " -> ${dir}" + ) + execute_process( + COMMAND "${CMAKE_COMMAND}" --build "${build_dir}" + WORKING_DIRECTORY "${dir}" + RESULT_VARIABLE download_result + ${logging_params} + ) + + if(NOT download_result EQUAL 0) + hunter_gate_internal_error("Build project failed") + endif() + + file(REMOVE_RECURSE "${build_dir}") + file(REMOVE_RECURSE "${cmakelists}") + + file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}") + file(WRITE "${done_location}" "DONE") + + hunter_gate_status_debug("Finished") +endfunction() + +# Must be a macro so master file 'cmake/Hunter' can +# apply all variables easily just by 'include' command +# (otherwise PARENT_SCOPE magic needed) +macro(HunterGate) + if(HUNTER_GATE_DONE) + # variable HUNTER_GATE_DONE set explicitly for external project + # (see `hunter_download`) + set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) + endif() + + # First HunterGate command will init Hunter, others will be ignored + get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET) + + if(NOT HUNTER_ENABLED) + # Empty function to avoid error "unknown function" + function(hunter_add_package) + endfunction() + + set( + _hunter_gate_disabled_mode_dir + "${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode" + ) + if(EXISTS "${_hunter_gate_disabled_mode_dir}") + hunter_gate_status_debug( + "Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}" + ) + list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}") + endif() + elseif(_hunter_gate_done) + hunter_gate_status_debug("Secondary HunterGate (use old settings)") + hunter_gate_self( + "${HUNTER_CACHED_ROOT}" + "${HUNTER_VERSION}" + "${HUNTER_SHA1}" + _hunter_self + ) + include("${_hunter_self}/cmake/Hunter") + else() + set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}") + + string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name) + if(_have_project_name) + hunter_gate_fatal_error( + "Please set HunterGate *before* 'project' command. " + "Detected project: ${PROJECT_NAME}" + ERROR_PAGE "error.huntergate.before.project" + ) + endif() + + cmake_parse_arguments( + HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV} + ) + + string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1) + string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url) + string( + COMPARE + NOTEQUAL + "${HUNTER_GATE_UNPARSED_ARGUMENTS}" + "" + _have_unparsed + ) + string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global) + string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath) + + if(_have_unparsed) + hunter_gate_user_error( + "HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}" + ) + endif() + if(_empty_sha1) + hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory") + endif() + if(_empty_url) + hunter_gate_user_error("URL suboption of HunterGate is mandatory") + endif() + if(_have_global) + if(HUNTER_GATE_LOCAL) + hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)") + endif() + if(_have_filepath) + hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)") + endif() + endif() + if(HUNTER_GATE_LOCAL) + if(_have_global) + hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)") + endif() + if(_have_filepath) + hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)") + endif() + endif() + if(_have_filepath) + if(_have_global) + hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)") + endif() + if(HUNTER_GATE_LOCAL) + hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)") + endif() + endif() + + hunter_gate_detect_root() # set HUNTER_GATE_ROOT + + # Beautify path, fix probable problems with windows path slashes + get_filename_component( + HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE + ) + hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}") + if(NOT HUNTER_ALLOW_SPACES_IN_PATH) + string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces) + if(NOT _contain_spaces EQUAL -1) + hunter_gate_fatal_error( + "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces." + "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error" + "(Use at your own risk!)" + ERROR_PAGE "error.spaces.in.hunter.root" + ) + endif() + endif() + + string( + REGEX + MATCH + "[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*" + HUNTER_GATE_VERSION + "${HUNTER_GATE_URL}" + ) + string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty) + if(_is_empty) + set(HUNTER_GATE_VERSION "unknown") + endif() + + hunter_gate_self( + "${HUNTER_GATE_ROOT}" + "${HUNTER_GATE_VERSION}" + "${HUNTER_GATE_SHA1}" + _hunter_self + ) + + set(_master_location "${_hunter_self}/cmake/Hunter") + get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE) + set(_done_location "${_archive_id_location}/DONE") + set(_sha1_location "${_archive_id_location}/SHA1") + + # Check Hunter already downloaded by HunterGate + if(NOT EXISTS "${_done_location}") + hunter_gate_download("${_archive_id_location}") + endif() + + if(NOT EXISTS "${_done_location}") + hunter_gate_internal_error("hunter_gate_download failed") + endif() + + if(NOT EXISTS "${_sha1_location}") + hunter_gate_internal_error("${_sha1_location} not found") + endif() + file(READ "${_sha1_location}" _sha1_value) + string(COMPARE EQUAL "${_sha1_value}" "${HUNTER_GATE_SHA1}" _is_equal) + if(NOT _is_equal) + hunter_gate_internal_error( + "Short SHA1 collision:" + " ${_sha1_value} (from ${_sha1_location})" + " ${HUNTER_GATE_SHA1} (HunterGate)" + ) + endif() + if(NOT EXISTS "${_master_location}") + hunter_gate_user_error( + "Master file not found:" + " ${_master_location}" + "try to update Hunter/HunterGate" + ) + endif() + include("${_master_location}") + set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES) + endif() +endmacro() diff --git a/cmake/cable/LICENSE b/cmake/cable/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/cmake/cable/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/cmake/cable/README.md b/cmake/cable/README.md new file mode 100644 index 00000000..a54d28eb --- /dev/null +++ b/cmake/cable/README.md @@ -0,0 +1,92 @@ +# Cable + +[![readme style: standard][readme style standard badge]][standard readme] + +> Cable: CMake Bootstrap Library + +Cable is a set of CMake modules and scripts containing common patterns used +in CMake-based C++ projects. The design goal is to be pragmatic rather than +generic so the number of provided options is minimal. The Cable modules are +independent and it is easy to use them individually. + + +## Table of Contents + +- [Install](#install) +- [Usage](#usage) +- [Maintainer](#maintainer) +- [License](#license) + + +## Install + +The suggested Cable location is `cmake/cable` relative to your project root directory. + +### As git subtree + +Adding a dependency project as a [git subtree] is just a copy of the source code +done in a bit more systematic way. + +If you are not familiar with managing dependencies with git subtree read the +[Git subtree: the alternative to Git submodule][git subtree tutorial]. + +#### To install + +```sh +git remote add cable https://github.com/ethereum/cable +git subtree add --prefix cmake/cable cable master --squash +``` + +#### To update + +```sh +git subtree pull --prefix cmake/cable cable master --squash +``` + +### As git submodule + +Include the Cable library as [git submodule] in your project. + +```sh +git submodule add https://github.com/ethereum/cable cmake/cable +``` + +## Usage + +Cable contains the `bootstrap.cmake` file that initializes the library. +Start by including this file in your main `CMakeLists.txt` from the Cable +submodule/subtree or any other location. The `bootstrap.cmake` must be included +before the `project()` command. After that, you can include and use other +Cable modules. + +### Example + +```cmake +cmake_minimum_required(VERSION 3.5) + +include(cmake/cable/bootstrap.cmake) +include(CableBuildType) + +project(tothemoon) + +cable_set_build_type(DEFAULT RelWithDebInfo CONFIGURATION_TYPES Debug Release RelWithDebInfo) +``` + + +## Maintainer + +Paweł Bylica [@chfast] + +## License + +Licensed under the [Apache License, Version 2.0]. + + +[@chfast]: https://github.com/chfast +[Apache License, Version 2.0]: LICENSE +[git submodule]: https://git-scm.com/book/en/v2/Git-Tools-Submodules +[git subtree]: https://github.com/git/git/blob/master/contrib/subtree/git-subtree.txt +[git subtree tutorial]: https://www.atlassian.com/blog/git/alternatives-to-git-submodule-git-subtree +[standard readme]: https://github.com/RichardLitt/standard-readme + +[readme style standard badge]: https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square diff --git a/cmake/cable/bootstrap.cmake b/cmake/cable/bootstrap.cmake new file mode 100644 index 00000000..39ec92a4 --- /dev/null +++ b/cmake/cable/bootstrap.cmake @@ -0,0 +1,71 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +# Bootstrap the Cable - CMake Bootstrap Library by including this file. +# e.g. include(cmake/cable/bootstrap.cmake). + + +# Cable version. +# +# This is internal variable automatically updated with external tools. +# Use CABLE_VERSION variable if you need this information. +set(version 0.4.4) + +# For convenience, add the project CMake module dir to module path. +set(module_dir ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +if(EXISTS ${module_dir}) + list(APPEND CMAKE_MODULE_PATH ${module_dir}) +endif() + +# Always add this Cable instance modules to the CMake module path. +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +if(CABLE_VERSION) + # Some other instance of Cable has been initialized in the top project. + + # Mark this project as nested. + set(PROJECT_IS_NESTED TRUE) + + # Compare versions of the top project and this instances. + if(CABLE_VERSION VERSION_LESS version) + set(comment " (version older than ${version})") + elseif(CABLE_VERSION VERSION_GREATER version) + set(comment " (version newer than ${version})") + endif() + + # Log information about initialization in the top project. + file(RELATIVE_PATH subproject_dir ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) + cable_debug("${subproject_dir}: Cable ${CABLE_VERSION}${comment} already initialized in the top project") + cable_debug("Project CMake modules directory: ${module_dir}") + + unset(version) + unset(module_dir) + unset(comment) + return() +endif() + + +option(CABLE_DEBUG "Enable Cable debug logs" OFF) + +function(cable_log) + message(STATUS "[cable ] ${ARGN}") +endfunction() + +function(cable_debug) + if(CABLE_DEBUG) + message(STATUS "[cable*] ${ARGN}") + endif() +endfunction() + +# Export Cable version. +set(CABLE_VERSION ${version}) + +# Mark this project as non-nested. +set(PROJECT_IS_NESTED FALSE) + +cable_log("Cable ${CABLE_VERSION} initialized") +cable_debug("Project CMake modules directory: ${module_dir}") + +unset(version) +unset(module_dir) diff --git a/cmake/cable/buildinfo/buildinfo.c.in b/cmake/cable/buildinfo/buildinfo.c.in new file mode 100644 index 00000000..efd20405 --- /dev/null +++ b/cmake/cable/buildinfo/buildinfo.c.in @@ -0,0 +1,24 @@ +/* Cable: CMake Bootstrap Library. + * Copyright 2018 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. See the LICENSE file. + */ + +/* Generated by Cable Build Info on @TIMESTAMP@. Do not modify directly. */ + +#include "buildinfo.h" + +const struct buildinfo* @FUNCTION_NAME@() +{ + static const struct buildinfo buildinfo = { + .project_name = "@PROJECT_NAME@", + .project_version = "@PROJECT_VERSION@", + .project_name_with_version = "@PROJECT_NAME@-@PROJECT_VERSION@", + .git_commit_hash = "@GIT_COMMIT_HASH@", + .system_name = "@SYSTEM_NAME@", + .system_processor = "@SYSTEM_PROCESSOR@", + .compiler_id = "@COMPILER_ID@", + .compiler_version = "@COMPILER_VERSION@", + .build_type = "@BUILD_TYPE@", + }; + return &buildinfo; +} diff --git a/cmake/cable/buildinfo/buildinfo.cmake b/cmake/cable/buildinfo/buildinfo.cmake new file mode 100644 index 00000000..651b754b --- /dev/null +++ b/cmake/cable/buildinfo/buildinfo.cmake @@ -0,0 +1,87 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018-2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +string(TOUPPER "${PROJECT_NAME}" PROJECT_NAME_UPPERCASE) +string(TOLOWER "${SYSTEM_NAME}" SYSTEM_NAME) +string(TOLOWER "${SYSTEM_PROCESSOR}" SYSTEM_PROCESSOR) +string(TOLOWER "${COMPILER_ID}" COMPILER_ID) +string(TOLOWER "${BUILD_TYPE}" BUILD_TYPE) +string(TIMESTAMP TIMESTAMP) + +# Read the git info from a file. The gitinfo is suppose to update the file +# only if the information has changed. +file(STRINGS ${OUTPUT_DIR}/gitinfo.txt gitinfo) +list(LENGTH gitinfo gitinfo_len) +if(gitinfo_len LESS 3) + message(WARNING "Git info not available") +else() + list(GET gitinfo 0 describe) + list(GET gitinfo 1 GIT_BRANCH) + list(GET gitinfo 2 GIT_ORIGIN_URL) +endif() + +# The output of `git describe --always --long --tags --match=v*`. +string(REGEX MATCH "(v(.+)-([0-9]+)-g)?([0-9a-f]+)(-dirty)?" match "${describe}") + +if(DEFINED describe AND NOT match) + message(WARNING "Cannot parse git describe: ${describe}") +endif() + +set(GIT_LATEST_PROJECT_VERSION ${CMAKE_MATCH_2}) +set(GIT_LATEST_PROJECT_VERSION_DISTANCE ${CMAKE_MATCH_3}) +set(GIT_COMMIT_HASH ${CMAKE_MATCH_4}) +if(CMAKE_MATCH_5) + set(GIT_DIRTY TRUE) + set(dirty_msg " (dirty)") +else() + set(GIT_DIRTY FALSE) +endif() + +if(GIT_COMMIT_HASH) + string(SUBSTRING ${GIT_COMMIT_HASH} 0 8 abbrev) + set(version_commit "+commit.${abbrev}") + if(GIT_DIRTY) + set(version_commit "${version_commit}.dirty") + endif() +endif() + +if(NOT PROJECT_VERSION) + message(WARNING "PROJECT_VERSION not specified") +endif() + +if(PROJECT_VERSION STREQUAL GIT_LATEST_PROJECT_VERSION) + if(${GIT_LATEST_PROJECT_VERSION_DISTANCE} GREATER 0) + set(PROJECT_VERSION "${PROJECT_VERSION}-${GIT_LATEST_PROJECT_VERSION_DISTANCE}${version_commit}") + endif() +else() + if(GIT_LATEST_PROJECT_VERSION) + message(WARNING "Git project version mismatch: '${GIT_LATEST_PROJECT_VERSION}' vs '${PROJECT_VERSION}'") + endif() + set(PROJECT_VERSION "${PROJECT_VERSION}${version_commit}") +endif() + +if(PROJECT_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$") + set(PROJECT_VERSION_IS_PRERELEASE false) +else() + set(PROJECT_VERSION_IS_PRERELEASE true) + set(prerelease_comment " (prerelease)") +endif() + +message( + " Project Version: ${PROJECT_VERSION}${prerelease_comment}\n" + " System Name: ${SYSTEM_NAME}\n" + " System Processor: ${SYSTEM_PROCESSOR}\n" + " Compiler ID: ${COMPILER_ID}\n" + " Compiler Version: ${COMPILER_VERSION}\n" + " Build Type: ${BUILD_TYPE}\n" + " Git Info: ${GIT_LATEST_PROJECT_VERSION} ${GIT_LATEST_PROJECT_VERSION_DISTANCE} ${GIT_COMMIT_HASH}${dirty_msg}\n" + " Timestamp: ${TIMESTAMP}" +) + +configure_file(${CMAKE_CURRENT_LIST_DIR}/buildinfo.c.in ${OUTPUT_DIR}/buildinfo.c) +configure_file(${CMAKE_CURRENT_LIST_DIR}/buildinfo.json.in ${OUTPUT_DIR}/buildinfo.json) +configure_file(${CMAKE_CURRENT_LIST_DIR}/buildinfo.sh.in ${OUTPUT_DIR}/buildinfo.sh) +configure_file(${CMAKE_CURRENT_LIST_DIR}/buildinfo.ps1.in ${OUTPUT_DIR}/buildinfo.ps1) + +configure_file(${CMAKE_CURRENT_LIST_DIR}/version.h.in ${OUTPUT_DIR}/version.h) diff --git a/cmake/cable/buildinfo/buildinfo.h.in b/cmake/cable/buildinfo/buildinfo.h.in new file mode 100644 index 00000000..d91974b1 --- /dev/null +++ b/cmake/cable/buildinfo/buildinfo.h.in @@ -0,0 +1,32 @@ +/* Cable: CMake Bootstrap Library. + * Copyright 2018 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. See the LICENSE file. + */ + +/* Generated by Cable Build Info on @TIMESTAMP@. Do not modify directly. */ + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct buildinfo +{ + const char* project_name; + const char* project_version; + const char* project_name_with_version; + const char* git_commit_hash; + const char* system_name; + const char* system_processor; + const char* compiler_id; + const char* compiler_version; + const char* build_type; +}; + +const struct buildinfo* @FUNCTION_NAME@(); + +#ifdef __cplusplus +} +#endif diff --git a/cmake/cable/buildinfo/buildinfo.json.in b/cmake/cable/buildinfo/buildinfo.json.in new file mode 100644 index 00000000..6a36a738 --- /dev/null +++ b/cmake/cable/buildinfo/buildinfo.json.in @@ -0,0 +1,10 @@ +{ + "name": "@PROJECT_NAME@", + "version": "@PROJECT_VERSION@", + "is_prerelease": @PROJECT_VERSION_IS_PRERELEASE@, + "commit": "@GIT_COMMIT_HASH@", + "branch": "@GIT_BRANCH@", + "repo": "@GIT_ORIGIN_URL@", + "system_name": "@SYSTEM_NAME@", + "system_processor": "@SYSTEM_PROCESSOR@" +} diff --git a/cmake/cable/buildinfo/buildinfo.ps1.in b/cmake/cable/buildinfo/buildinfo.ps1.in new file mode 100644 index 00000000..a121ec7a --- /dev/null +++ b/cmake/cable/buildinfo/buildinfo.ps1.in @@ -0,0 +1,5 @@ +$env:project_name="@PROJECT_NAME@" +$env:project_version="@PROJECT_VERSION@" +$env:project_version_is_prerelease="@PROJECT_VERSION_IS_PRERELEASE@" +$env:system_name='@SYSTEM_NAME@' +$env:system_processor='@SYSTEM_PROCESSOR@' diff --git a/cmake/cable/buildinfo/buildinfo.sh.in b/cmake/cable/buildinfo/buildinfo.sh.in new file mode 100644 index 00000000..97cf1ae9 --- /dev/null +++ b/cmake/cable/buildinfo/buildinfo.sh.in @@ -0,0 +1,5 @@ +PROJECT_NAME='@PROJECT_NAME@' +PROJECT_VERSION='@PROJECT_VERSION@' +PROJECT_VERSION_IS_PRERELEASE='@PROJECT_VERSION_IS_PRERELEASE@' +SYSTEM_NAME='@SYSTEM_NAME@' +SYSTEM_PROCESSOR='@SYSTEM_PROCESSOR@' diff --git a/cmake/cable/buildinfo/gitinfo.cmake b/cmake/cable/buildinfo/gitinfo.cmake new file mode 100644 index 00000000..0d7bc2fc --- /dev/null +++ b/cmake/cable/buildinfo/gitinfo.cmake @@ -0,0 +1,59 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +# Execute git only if the tool is available. +if(GIT) + execute_process( + COMMAND ${GIT} describe --always --long --tags --first-parent --match=v* --abbrev=40 --dirty + WORKING_DIRECTORY ${SOURCE_DIR} + OUTPUT_VARIABLE gitinfo + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE error + ERROR_STRIP_TRAILING_WHITESPACE + ) + if(error) + message(WARNING "Git ${error}") + endif() + + execute_process( + COMMAND ${GIT} rev-parse --abbrev-ref HEAD + WORKING_DIRECTORY ${SOURCE_DIR} + OUTPUT_VARIABLE gitbranch + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE error + ERROR_STRIP_TRAILING_WHITESPACE + ) + if(error) + message(WARNING "Git ${error}") + else() + set(gitinfo "${gitinfo}\n${gitbranch}") + endif() + + execute_process( + COMMAND ${GIT} config --get remote.origin.url + WORKING_DIRECTORY ${SOURCE_DIR} + OUTPUT_VARIABLE gitorigin + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_VARIABLE error + ERROR_STRIP_TRAILING_WHITESPACE + ) + if(error) + message(WARNING "Git ${error}") + else() + set(gitinfo "${gitinfo}\n${gitorigin}\n") + endif() +endif() + +set(gitinfo_file ${OUTPUT_DIR}/gitinfo.txt) + +if(EXISTS ${gitinfo_file}) + file(READ ${gitinfo_file} prev_gitinfo) +else() + # Create empty file, because other targets expect it to exist. + file(WRITE ${gitinfo_file} "") +endif() + +if(NOT "${gitinfo}" STREQUAL "${prev_gitinfo}") + file(WRITE ${gitinfo_file} ${gitinfo}) +endif() diff --git a/cmake/cable/buildinfo/version.h.in b/cmake/cable/buildinfo/version.h.in new file mode 100644 index 00000000..c53e5364 --- /dev/null +++ b/cmake/cable/buildinfo/version.h.in @@ -0,0 +1,14 @@ +/* Cable: CMake Bootstrap Library. + * Copyright 2019 Pawel Bylica. + * Licensed under the Apache License, Version 2.0. + */ + +/* Generated by Cable Build Info on @TIMESTAMP@. Do not modify directly. */ + +#pragma once + +#define @PROJECT_NAME_UPPERCASE@_VERSION "@PROJECT_VERSION@" + +#ifdef __cplusplus +constexpr auto @PROJECT_NAME@_version = "@PROJECT_VERSION@"; +#endif diff --git a/cmake/cable/defaults/HunterCacheServers-passwords.cmake b/cmake/cable/defaults/HunterCacheServers-passwords.cmake new file mode 100644 index 00000000..30196694 --- /dev/null +++ b/cmake/cable/defaults/HunterCacheServers-passwords.cmake @@ -0,0 +1,18 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +# Hunter passwords file used by HunterCacheServers.cmake. +# Do not include directly. + +hunter_upload_password( + # REPO_OWNER + REPO = https://github.com/ethereum/hunter-cache + REPO_OWNER ethereum + REPO hunter-cache + + # USERNAME = https://github.com/hunter-cache-bot + USERNAME hunter-cache-bot + + # PASSWORD = GitHub token saved as a secure environment variable + PASSWORD "$ENV{HUNTER_CACHE_TOKEN}" +) diff --git a/cmake/cable/defaults/HunterCacheServers.cmake b/cmake/cable/defaults/HunterCacheServers.cmake new file mode 100644 index 00000000..b6f8449d --- /dev/null +++ b/cmake/cable/defaults/HunterCacheServers.cmake @@ -0,0 +1,27 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +# This module, when included, sets default values for params related to +# Hunter cache servers, including upload options. + +# Default Hunter cache servers. +set(HUNTER_CACHE_SERVERS + "https://github.com/ethereum/hunter-cache;https://github.com/ingenue/hunter-cache" + CACHE STRING "Hunter cache servers") + +# Default path to Hunter passwords file containing information how to access +# Ethereum's cache server. +set(HUNTER_PASSWORDS_PATH + ${CMAKE_CURRENT_LIST_DIR}/HunterCacheServers-passwords.cmake + CACHE STRING "Hunter passwords file") + +# In CI builds upload the binaries if the HUNTER_CACHE_TOKEN was decrypted +# (only for branches and internal PRs). +if("$ENV{CI}" AND NOT "$ENV{HUNTER_CACHE_TOKEN}" STREQUAL "") + set(run_upload YES) +else() + set(run_upload NO) +endif() +option(HUNTER_RUN_UPLOAD "Upload binaries to the Hunter cache server" ${run_upload}) +unset(run_upload) diff --git a/cmake/cable/toolchains/cxx11-32bit.cmake b/cmake/cable/toolchains/cxx11-32bit.cmake new file mode 100644 index 00000000..f3f28cf2 --- /dev/null +++ b/cmake/cable/toolchains/cxx11-32bit.cmake @@ -0,0 +1,10 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018-2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_C_FLAGS_INIT -m32) +set(CMAKE_CXX_FLAGS_INIT -m32) diff --git a/cmake/cable/toolchains/cxx11-c99.cmake b/cmake/cable/toolchains/cxx11-c99.cmake new file mode 100644 index 00000000..91a93b33 --- /dev/null +++ b/cmake/cable/toolchains/cxx11-c99.cmake @@ -0,0 +1,10 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_EXTENSIONS OFF) diff --git a/cmake/cable/toolchains/cxx11-fpic.cmake b/cmake/cable/toolchains/cxx11-fpic.cmake new file mode 100644 index 00000000..52d4f972 --- /dev/null +++ b/cmake/cable/toolchains/cxx11-fpic.cmake @@ -0,0 +1,12 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +set(CMAKE_CXX_FLAGS_INIT "-fPIC" CACHE STRING "" FORCE) +set(CMAKE_C_FLAGS_INIT "-fPIC" CACHE STRING "" FORCE) diff --git a/cmake/cable/toolchains/cxx11-pic.cmake b/cmake/cable/toolchains/cxx11-pic.cmake new file mode 100644 index 00000000..db7d9e5b --- /dev/null +++ b/cmake/cable/toolchains/cxx11-pic.cmake @@ -0,0 +1,9 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) diff --git a/cmake/cable/toolchains/cxx11.cmake b/cmake/cable/toolchains/cxx11.cmake new file mode 100644 index 00000000..cd6b82ff --- /dev/null +++ b/cmake/cable/toolchains/cxx11.cmake @@ -0,0 +1,7 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/cmake/cable/toolchains/cxx14-32bit.cmake b/cmake/cable/toolchains/cxx14-32bit.cmake new file mode 100644 index 00000000..15145ff9 --- /dev/null +++ b/cmake/cable/toolchains/cxx14-32bit.cmake @@ -0,0 +1,10 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_C_FLAGS_INIT -m32) +set(CMAKE_CXX_FLAGS_INIT -m32) diff --git a/cmake/cable/toolchains/cxx14-pic.cmake b/cmake/cable/toolchains/cxx14-pic.cmake new file mode 100644 index 00000000..507771fc --- /dev/null +++ b/cmake/cable/toolchains/cxx14-pic.cmake @@ -0,0 +1,9 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) diff --git a/cmake/cable/toolchains/cxx14.cmake b/cmake/cable/toolchains/cxx14.cmake new file mode 100644 index 00000000..646acafb --- /dev/null +++ b/cmake/cable/toolchains/cxx14.cmake @@ -0,0 +1,7 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/cmake/cable/toolchains/cxx17-32bit.cmake b/cmake/cable/toolchains/cxx17-32bit.cmake new file mode 100644 index 00000000..183aab31 --- /dev/null +++ b/cmake/cable/toolchains/cxx17-32bit.cmake @@ -0,0 +1,10 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_C_FLAGS_INIT -m32) +set(CMAKE_CXX_FLAGS_INIT -m32) diff --git a/cmake/cable/toolchains/cxx17-pic.cmake b/cmake/cable/toolchains/cxx17-pic.cmake new file mode 100644 index 00000000..edb3559e --- /dev/null +++ b/cmake/cable/toolchains/cxx17-pic.cmake @@ -0,0 +1,9 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) diff --git a/cmake/cable/toolchains/cxx17.cmake b/cmake/cable/toolchains/cxx17.cmake new file mode 100644 index 00000000..a4efcaa2 --- /dev/null +++ b/cmake/cable/toolchains/cxx17.cmake @@ -0,0 +1,7 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2019 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/cmake/cable/toolchains/default.cmake b/cmake/cable/toolchains/default.cmake new file mode 100644 index 00000000..9869b6e1 --- /dev/null +++ b/cmake/cable/toolchains/default.cmake @@ -0,0 +1,3 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. diff --git a/cmake/cable/toolchains/mips64.cmake b/cmake/cable/toolchains/mips64.cmake new file mode 100644 index 00000000..e90d748f --- /dev/null +++ b/cmake/cable/toolchains/mips64.cmake @@ -0,0 +1,24 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +set(CMAKE_SYSTEM_PROCESSOR mips64) +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_C_COMPILER mips64-linux-gnuabi64-gcc) +set(CMAKE_CXX_COMPILER mips64-linux-gnuabi64-g++) + +set(CMAKE_FIND_ROOT_PATH /usr/mips64-linux-gnuabi64) +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS Off) + +if(${CMAKE_VERSION} VERSION_LESS 3.10.0) + # Until CMake 3.10 the FindThreads uses try_run() check of -pthread flag, + # what causes CMake error in crosscompiling mode. Avoid the try_run() check + # by specifying the result up front. + set(THREADS_PTHREAD_ARG TRUE) +endif() diff --git a/cmake/cable/toolchains/powerpc64.cmake b/cmake/cable/toolchains/powerpc64.cmake new file mode 100644 index 00000000..e6c7e7fc --- /dev/null +++ b/cmake/cable/toolchains/powerpc64.cmake @@ -0,0 +1,24 @@ +# Cable: CMake Bootstrap Library. +# Copyright 2018 Pawel Bylica. +# Licensed under the Apache License, Version 2.0. See the LICENSE file. + +set(CMAKE_SYSTEM_PROCESSOR powerpc64) +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_C_COMPILER powerpc64-linux-gnu-gcc) +set(CMAKE_CXX_COMPILER powerpc64-linux-gnu-g++) + +set(CMAKE_FIND_ROOT_PATH /usr/powerpc64-linux-gnu) +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + +if(${CMAKE_VERSION} VERSION_LESS 3.10.0) + # Until CMake 3.10 the FindThreads uses try_run() check of -pthread flag, + # what causes CMake error in crosscompiling mode. Avoid the try_run() check + # by specifying the result up front. + set(THREADS_PTHREAD_ARG TRUE) +endif() diff --git a/evmc b/evmc index 6743fb92..e9d46482 160000 --- a/evmc +++ b/evmc @@ -1 +1 @@ -Subproject commit 6743fb9232c5b803597e73d2480605057348dc35 +Subproject commit e9d4648200d73c1ce63b74a33a50758db1fd48db diff --git a/include/evmjit.h b/include/evmjit.h index 4c77a8d4..282af642 100644 --- a/include/evmjit.h +++ b/include/evmjit.h @@ -10,7 +10,7 @@ #endif #else -#define EXPORT __attribute__ ((visibility ("default"))) +#define EXPORT __attribute__((visibility("default"))) #endif #if __cplusplus diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index bebb03bc..f72534e0 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -1,15 +1,15 @@ #include "Arith256.h" -#include #include +#include +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" -#include #include -#include "preprocessor/llvm_includes_end.h" +#include -#include "Type.h" #include "Endianness.h" +#include "Type.h" #include "Utils.h" namespace dev @@ -18,403 +18,418 @@ namespace eth { namespace jit { - -Arith256::Arith256(IRBuilder& _builder) : - CompilerHelper(_builder) -{} +Arith256::Arith256(IRBuilder& _builder) : CompilerHelper(_builder) {} void Arith256::debug(llvm::Value* _value, char _c, llvm::Module& _module, IRBuilder& _builder) { - static const auto funcName = "debug"; - auto func = _module.getFunction(funcName); - if (!func) - func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {Type::Word, _builder.getInt8Ty()}, false), llvm::Function::ExternalLinkage, funcName, &_module); - - _builder.CreateCall(func, {_builder.CreateZExtOrTrunc(_value, Type::Word), _builder.getInt8(_c)}); + static const auto funcName = "debug"; + auto func = _module.getFunction(funcName); + if (!func) + func = llvm::Function::Create( + llvm::FunctionType::get(Type::Void, {Type::Word, _builder.getInt8Ty()}, false), + llvm::Function::ExternalLinkage, funcName, &_module); + + _builder.CreateCall( + func, {_builder.CreateZExtOrTrunc(_value, Type::Word), _builder.getInt8(_c)}); } namespace { llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) { - // Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research - // The following algorithm also handles divisor of value 0 returning 0 for both quotient and remainder - - auto retType = llvm::VectorType::get(_type, 2); - auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); - func->setDoesNotThrow(); - func->setDoesNotAccessMemory(); - - auto zero = llvm::ConstantInt::get(_type, 0); - auto one = llvm::ConstantInt::get(_type, 1); - - auto iter = func->arg_begin(); - llvm::Argument* x = &(*iter++); - x->setName("x"); - llvm::Argument* y = &(*iter); - y->setName("y"); - - auto entryBB = llvm::BasicBlock::Create(_module.getContext(), "Entry", func); - auto mainBB = llvm::BasicBlock::Create(_module.getContext(), "Main", func); - auto loopBB = llvm::BasicBlock::Create(_module.getContext(), "Loop", func); - auto continueBB = llvm::BasicBlock::Create(_module.getContext(), "Continue", func); - auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func); - - auto builder = IRBuilder{entryBB}; - auto yLEx = builder.CreateICmpULE(y, x); - auto r0 = x; - builder.CreateCondBr(yLEx, mainBB, returnBB); - - builder.SetInsertPoint(mainBB); - auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type); - // both y and r are non-zero - auto yLz = builder.CreateCall(ctlzIntr, {y, builder.getInt1(true)}, "y.lz"); - auto rLz = builder.CreateCall(ctlzIntr, {r0, builder.getInt1(true)}, "r.lz"); - auto i0 = builder.CreateNUWSub(yLz, rLz, "i0"); - auto y0 = builder.CreateShl(y, i0); - builder.CreateBr(loopBB); - - builder.SetInsertPoint(loopBB); - auto yPhi = builder.CreatePHI(_type, 2, "y.phi"); - auto rPhi = builder.CreatePHI(_type, 2, "r.phi"); - auto iPhi = builder.CreatePHI(_type, 2, "i.phi"); - auto qPhi = builder.CreatePHI(_type, 2, "q.phi"); - auto rUpdate = builder.CreateNUWSub(rPhi, yPhi); - auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0 - auto rGEy = builder.CreateICmpUGE(rPhi, yPhi); - auto r1 = builder.CreateSelect(rGEy, rUpdate, rPhi, "r1"); - auto q1 = builder.CreateSelect(rGEy, qUpdate, qPhi, "q"); - auto iZero = builder.CreateICmpEQ(iPhi, zero); - builder.CreateCondBr(iZero, returnBB, continueBB); - - builder.SetInsertPoint(continueBB); - auto i2 = builder.CreateNUWSub(iPhi, one); - auto q2 = builder.CreateShl(q1, one); - auto y2 = builder.CreateLShr(yPhi, one); - builder.CreateBr(loopBB); - - yPhi->addIncoming(y0, mainBB); - yPhi->addIncoming(y2, continueBB); - rPhi->addIncoming(r0, mainBB); - rPhi->addIncoming(r1, continueBB); - iPhi->addIncoming(i0, mainBB); - iPhi->addIncoming(i2, continueBB); - qPhi->addIncoming(zero, mainBB); - qPhi->addIncoming(q2, continueBB); - - builder.SetInsertPoint(returnBB); - auto qRet = builder.CreatePHI(_type, 2, "q.ret"); - qRet->addIncoming(zero, entryBB); - qRet->addIncoming(q1, loopBB); - auto rRet = builder.CreatePHI(_type, 2, "r.ret"); - rRet->addIncoming(r0, entryBB); - rRet->addIncoming(r1, loopBB); - auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0"); - ret = builder.CreateInsertElement(ret, rRet, 1, "ret"); - builder.CreateRet(ret); - - return func; -} + // Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft + // Research The following algorithm also handles divisor of value 0 returning 0 for both + // quotient and remainder + + auto retType = llvm::VectorType::get(_type, 2); + auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), + llvm::Function::PrivateLinkage, _funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto zero = llvm::ConstantInt::get(_type, 0); + auto one = llvm::ConstantInt::get(_type, 1); + + auto iter = func->arg_begin(); + llvm::Argument* x = &(*iter++); + x->setName("x"); + llvm::Argument* y = &(*iter); + y->setName("y"); + + auto entryBB = llvm::BasicBlock::Create(_module.getContext(), "Entry", func); + auto mainBB = llvm::BasicBlock::Create(_module.getContext(), "Main", func); + auto loopBB = llvm::BasicBlock::Create(_module.getContext(), "Loop", func); + auto continueBB = llvm::BasicBlock::Create(_module.getContext(), "Continue", func); + auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func); + + auto builder = IRBuilder{entryBB}; + auto yLEx = builder.CreateICmpULE(y, x); + auto r0 = x; + builder.CreateCondBr(yLEx, mainBB, returnBB); + + builder.SetInsertPoint(mainBB); + auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type); + // both y and r are non-zero + auto yLz = builder.CreateCall(ctlzIntr, {y, builder.getInt1(true)}, "y.lz"); + auto rLz = builder.CreateCall(ctlzIntr, {r0, builder.getInt1(true)}, "r.lz"); + auto i0 = builder.CreateNUWSub(yLz, rLz, "i0"); + auto y0 = builder.CreateShl(y, i0); + builder.CreateBr(loopBB); + + builder.SetInsertPoint(loopBB); + auto yPhi = builder.CreatePHI(_type, 2, "y.phi"); + auto rPhi = builder.CreatePHI(_type, 2, "r.phi"); + auto iPhi = builder.CreatePHI(_type, 2, "i.phi"); + auto qPhi = builder.CreatePHI(_type, 2, "q.phi"); + auto rUpdate = builder.CreateNUWSub(rPhi, yPhi); + auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0 + auto rGEy = builder.CreateICmpUGE(rPhi, yPhi); + auto r1 = builder.CreateSelect(rGEy, rUpdate, rPhi, "r1"); + auto q1 = builder.CreateSelect(rGEy, qUpdate, qPhi, "q"); + auto iZero = builder.CreateICmpEQ(iPhi, zero); + builder.CreateCondBr(iZero, returnBB, continueBB); + + builder.SetInsertPoint(continueBB); + auto i2 = builder.CreateNUWSub(iPhi, one); + auto q2 = builder.CreateShl(q1, one); + auto y2 = builder.CreateLShr(yPhi, one); + builder.CreateBr(loopBB); + + yPhi->addIncoming(y0, mainBB); + yPhi->addIncoming(y2, continueBB); + rPhi->addIncoming(r0, mainBB); + rPhi->addIncoming(r1, continueBB); + iPhi->addIncoming(i0, mainBB); + iPhi->addIncoming(i2, continueBB); + qPhi->addIncoming(zero, mainBB); + qPhi->addIncoming(q2, continueBB); + + builder.SetInsertPoint(returnBB); + auto qRet = builder.CreatePHI(_type, 2, "q.ret"); + qRet->addIncoming(zero, entryBB); + qRet->addIncoming(q1, loopBB); + auto rRet = builder.CreatePHI(_type, 2, "r.ret"); + rRet->addIncoming(r0, entryBB); + rRet->addIncoming(r1, loopBB); + auto ret = + builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0"); + ret = builder.CreateInsertElement(ret, rRet, 1, "ret"); + builder.CreateRet(ret); + + return func; } +} // namespace llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module) { - static const auto funcName = "evm.udivrem.i256"; - if (auto func = _module.getFunction(funcName)) - return func; + static const auto funcName = "evm.udivrem.i256"; + if (auto func = _module.getFunction(funcName)) + return func; - return createUDivRemFunc(Type::Word, _module, funcName); + return createUDivRemFunc(Type::Word, _module, funcName); } llvm::Function* Arith256::getUDivRem512Func(llvm::Module& _module) { - static const auto funcName = "evm.udivrem.i512"; - if (auto func = _module.getFunction(funcName)) - return func; + static const auto funcName = "evm.udivrem.i512"; + if (auto func = _module.getFunction(funcName)) + return func; - return createUDivRemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); + return createUDivRemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); } llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module) { - static const auto funcName = "evm.udiv.i256"; - if (auto func = _module.getFunction(funcName)) - return func; - - auto udivremFunc = getUDivRem256Func(_module); - - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); - func->setDoesNotThrow(); - func->setDoesNotAccessMemory(); - - auto iter = func->arg_begin(); - llvm::Argument* x = &(*iter++); - x->setName("x"); - llvm::Argument* y = &(*iter); - y->setName("y"); - - auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); - auto builder = IRBuilder{bb}; - auto udivrem = builder.CreateCall(udivremFunc, {x, y}); - auto udiv = builder.CreateExtractElement(udivrem, uint64_t(0)); - builder.CreateRet(udiv); - - return func; + static const auto funcName = "evm.udiv.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + auto udivremFunc = getUDivRem256Func(_module); + + auto func = + llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), + llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto iter = func->arg_begin(); + llvm::Argument* x = &(*iter++); + x->setName("x"); + llvm::Argument* y = &(*iter); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); + auto builder = IRBuilder{bb}; + auto udivrem = builder.CreateCall(udivremFunc, {x, y}); + auto udiv = builder.CreateExtractElement(udivrem, uint64_t(0)); + builder.CreateRet(udiv); + + return func; } namespace { llvm::Function* createURemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) { - auto udivremFunc = _type == Type::Word ? Arith256::getUDivRem256Func(_module) : Arith256::getUDivRem512Func(_module); - - auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); - func->setDoesNotThrow(); - func->setDoesNotAccessMemory(); - - auto iter = func->arg_begin(); - llvm::Argument* x = &(*iter++); - x->setName("x"); - llvm::Argument* y = &(*iter); - y->setName("y"); - - auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); - auto builder = IRBuilder{bb}; - auto udivrem = builder.CreateCall(udivremFunc, {x, y}); - auto r = builder.CreateExtractElement(udivrem, uint64_t(1)); - builder.CreateRet(r); - - return func; -} + auto udivremFunc = _type == Type::Word ? Arith256::getUDivRem256Func(_module) : + Arith256::getUDivRem512Func(_module); + + auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), + llvm::Function::PrivateLinkage, _funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto iter = func->arg_begin(); + llvm::Argument* x = &(*iter++); + x->setName("x"); + llvm::Argument* y = &(*iter); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); + auto builder = IRBuilder{bb}; + auto udivrem = builder.CreateCall(udivremFunc, {x, y}); + auto r = builder.CreateExtractElement(udivrem, uint64_t(1)); + builder.CreateRet(r); + + return func; } +} // namespace llvm::Function* Arith256::getURem256Func(llvm::Module& _module) { - static const auto funcName = "evm.urem.i256"; - if (auto func = _module.getFunction(funcName)) - return func; - return createURemFunc(Type::Word, _module, funcName); + static const auto funcName = "evm.urem.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + return createURemFunc(Type::Word, _module, funcName); } llvm::Function* Arith256::getURem512Func(llvm::Module& _module) { - static const auto funcName = "evm.urem.i512"; - if (auto func = _module.getFunction(funcName)) - return func; - return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); + static const auto funcName = "evm.urem.i512"; + if (auto func = _module.getFunction(funcName)) + return func; + return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); } llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module) { - static const auto funcName = "evm.sdivrem.i256"; - if (auto func = _module.getFunction(funcName)) - return func; - - auto udivremFunc = getUDivRem256Func(_module); - - auto retType = llvm::VectorType::get(Type::Word, 2); - auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); - func->setDoesNotThrow(); - func->setDoesNotAccessMemory(); - - auto iter = func->arg_begin(); - llvm::Argument* x = &(*iter++); - x->setName("x"); - llvm::Argument* y = &(*iter); - y->setName("y"); - - auto bb = llvm::BasicBlock::Create(_module.getContext(), "", func); - auto builder = IRBuilder{bb}; - auto xIsNeg = builder.CreateICmpSLT(x, Constant::get(0)); - auto xNeg = builder.CreateSub(Constant::get(0), x); - auto xAbs = builder.CreateSelect(xIsNeg, xNeg, x); - - auto yIsNeg = builder.CreateICmpSLT(y, Constant::get(0)); - auto yNeg = builder.CreateSub(Constant::get(0), y); - auto yAbs = builder.CreateSelect(yIsNeg, yNeg, y); - - auto res = builder.CreateCall(udivremFunc, {xAbs, yAbs}); - auto qAbs = builder.CreateExtractElement(res, uint64_t(0)); - auto rAbs = builder.CreateExtractElement(res, 1); - - // the remainder has the same sign as dividend - auto rNeg = builder.CreateSub(Constant::get(0), rAbs); - auto r = builder.CreateSelect(xIsNeg, rNeg, rAbs); - - auto qNeg = builder.CreateSub(Constant::get(0), qAbs); - auto xyOpposite = builder.CreateXor(xIsNeg, yIsNeg); - auto q = builder.CreateSelect(xyOpposite, qNeg, qAbs); - - auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), q, uint64_t(0)); - ret = builder.CreateInsertElement(ret, r, 1); - builder.CreateRet(ret); - - return func; + static const auto funcName = "evm.sdivrem.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + auto udivremFunc = getUDivRem256Func(_module); + + auto retType = llvm::VectorType::get(Type::Word, 2); + auto func = + llvm::Function::Create(llvm::FunctionType::get(retType, {Type::Word, Type::Word}, false), + llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto iter = func->arg_begin(); + llvm::Argument* x = &(*iter++); + x->setName("x"); + llvm::Argument* y = &(*iter); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), "", func); + auto builder = IRBuilder{bb}; + auto xIsNeg = builder.CreateICmpSLT(x, Constant::get(0)); + auto xNeg = builder.CreateSub(Constant::get(0), x); + auto xAbs = builder.CreateSelect(xIsNeg, xNeg, x); + + auto yIsNeg = builder.CreateICmpSLT(y, Constant::get(0)); + auto yNeg = builder.CreateSub(Constant::get(0), y); + auto yAbs = builder.CreateSelect(yIsNeg, yNeg, y); + + auto res = builder.CreateCall(udivremFunc, {xAbs, yAbs}); + auto qAbs = builder.CreateExtractElement(res, uint64_t(0)); + auto rAbs = builder.CreateExtractElement(res, 1); + + // the remainder has the same sign as dividend + auto rNeg = builder.CreateSub(Constant::get(0), rAbs); + auto r = builder.CreateSelect(xIsNeg, rNeg, rAbs); + + auto qNeg = builder.CreateSub(Constant::get(0), qAbs); + auto xyOpposite = builder.CreateXor(xIsNeg, yIsNeg); + auto q = builder.CreateSelect(xyOpposite, qNeg, qAbs); + + auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), q, uint64_t(0)); + ret = builder.CreateInsertElement(ret, r, 1); + builder.CreateRet(ret); + + return func; } llvm::Function* Arith256::getSDiv256Func(llvm::Module& _module) { - static const auto funcName = "evm.sdiv.i256"; - if (auto func = _module.getFunction(funcName)) - return func; - - auto sdivremFunc = getSDivRem256Func(_module); - - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); - func->setDoesNotThrow(); - func->setDoesNotAccessMemory(); - - auto iter = func->arg_begin(); - llvm::Argument* x = &(*iter++); - x->setName("x"); - llvm::Argument* y = &(*iter); - y->setName("y"); - - auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); - auto builder = IRBuilder{bb}; - auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); - auto q = builder.CreateExtractElement(sdivrem, uint64_t(0)); - builder.CreateRet(q); - - return func; + static const auto funcName = "evm.sdiv.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + auto sdivremFunc = getSDivRem256Func(_module); + + auto func = + llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), + llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto iter = func->arg_begin(); + llvm::Argument* x = &(*iter++); + x->setName("x"); + llvm::Argument* y = &(*iter); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); + auto builder = IRBuilder{bb}; + auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); + auto q = builder.CreateExtractElement(sdivrem, uint64_t(0)); + builder.CreateRet(q); + + return func; } llvm::Function* Arith256::getSRem256Func(llvm::Module& _module) { - static const auto funcName = "evm.srem.i256"; - if (auto func = _module.getFunction(funcName)) - return func; - - auto sdivremFunc = getSDivRem256Func(_module); - - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); - func->setDoesNotThrow(); - func->setDoesNotAccessMemory(); - - auto iter = func->arg_begin(); - llvm::Argument* x = &(*iter++); - x->setName("x"); - llvm::Argument* y = &(*iter); - y->setName("y"); - - auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); - auto builder = IRBuilder{bb}; - auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); - auto r = builder.CreateExtractElement(sdivrem, uint64_t(1)); - builder.CreateRet(r); - - return func; + static const auto funcName = "evm.srem.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + auto sdivremFunc = getSDivRem256Func(_module); + + auto func = + llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), + llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto iter = func->arg_begin(); + llvm::Argument* x = &(*iter++); + x->setName("x"); + llvm::Argument* y = &(*iter); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); + auto builder = IRBuilder{bb}; + auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); + auto r = builder.CreateExtractElement(sdivrem, uint64_t(1)); + builder.CreateRet(r); + + return func; } llvm::Function* Arith256::getExpFunc() { - if (!m_exp) - { - llvm::Type* argTypes[] = {Type::Word, Type::Word}; - m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "exp", getModule()); - m_exp->setDoesNotThrow(); - m_exp->setDoesNotAccessMemory(); - - auto iter = m_exp->arg_begin(); - llvm::Argument* base = &(*iter++); - base->setName("base"); - llvm::Argument* exponent = &(*iter); - exponent->setName("exponent"); - - InsertPointGuard guard{m_builder}; - - // while (e != 0) { - // if (e % 2 == 1) - // r *= b; - // b *= b; - // e /= 2; - // } - - auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", m_exp); - auto headerBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopHeader", m_exp); - auto bodyBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopBody", m_exp); - auto updateBB = llvm::BasicBlock::Create(m_builder.getContext(), "ResultUpdate", m_exp); - auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", m_exp); - auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp); - - m_builder.SetInsertPoint(entryBB); - m_builder.CreateBr(headerBB); - - m_builder.SetInsertPoint(headerBB); - auto r = m_builder.CreatePHI(Type::Word, 2, "r"); - auto b = m_builder.CreatePHI(Type::Word, 2, "b"); - auto e = m_builder.CreatePHI(Type::Word, 2, "e"); - auto eNonZero = m_builder.CreateICmpNE(e, Constant::get(0), "e.nonzero"); - m_builder.CreateCondBr(eNonZero, bodyBB, returnBB); - - m_builder.SetInsertPoint(bodyBB); - auto eOdd = m_builder.CreateICmpNE(m_builder.CreateAnd(e, Constant::get(1)), Constant::get(0), "e.isodd"); - m_builder.CreateCondBr(eOdd, updateBB, continueBB); - - m_builder.SetInsertPoint(updateBB); - auto r0 = m_builder.CreateMul(r, b); - m_builder.CreateBr(continueBB); - - m_builder.SetInsertPoint(continueBB); - auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); - r1->addIncoming(r, bodyBB); - r1->addIncoming(r0, updateBB); - auto b1 = m_builder.CreateMul(b, b); - auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1"); - m_builder.CreateBr(headerBB); - - r->addIncoming(Constant::get(1), entryBB); - r->addIncoming(r1, continueBB); - b->addIncoming(base, entryBB); - b->addIncoming(b1, continueBB); - e->addIncoming(exponent, entryBB); - e->addIncoming(e1, continueBB); - - m_builder.SetInsertPoint(returnBB); - m_builder.CreateRet(r); - } - return m_exp; + if (!m_exp) + { + llvm::Type* argTypes[] = {Type::Word, Type::Word}; + m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), + llvm::Function::PrivateLinkage, "exp", getModule()); + m_exp->setDoesNotThrow(); + m_exp->setDoesNotAccessMemory(); + + auto iter = m_exp->arg_begin(); + llvm::Argument* base = &(*iter++); + base->setName("base"); + llvm::Argument* exponent = &(*iter); + exponent->setName("exponent"); + + InsertPointGuard guard{m_builder}; + + // while (e != 0) { + // if (e % 2 == 1) + // r *= b; + // b *= b; + // e /= 2; + // } + + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", m_exp); + auto headerBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopHeader", m_exp); + auto bodyBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopBody", m_exp); + auto updateBB = llvm::BasicBlock::Create(m_builder.getContext(), "ResultUpdate", m_exp); + auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", m_exp); + auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp); + + m_builder.SetInsertPoint(entryBB); + m_builder.CreateBr(headerBB); + + m_builder.SetInsertPoint(headerBB); + auto r = m_builder.CreatePHI(Type::Word, 2, "r"); + auto b = m_builder.CreatePHI(Type::Word, 2, "b"); + auto e = m_builder.CreatePHI(Type::Word, 2, "e"); + auto eNonZero = m_builder.CreateICmpNE(e, Constant::get(0), "e.nonzero"); + m_builder.CreateCondBr(eNonZero, bodyBB, returnBB); + + m_builder.SetInsertPoint(bodyBB); + auto eOdd = m_builder.CreateICmpNE( + m_builder.CreateAnd(e, Constant::get(1)), Constant::get(0), "e.isodd"); + m_builder.CreateCondBr(eOdd, updateBB, continueBB); + + m_builder.SetInsertPoint(updateBB); + auto r0 = m_builder.CreateMul(r, b); + m_builder.CreateBr(continueBB); + + m_builder.SetInsertPoint(continueBB); + auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); + r1->addIncoming(r, bodyBB); + r1->addIncoming(r0, updateBB); + auto b1 = m_builder.CreateMul(b, b); + auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1"); + m_builder.CreateBr(headerBB); + + r->addIncoming(Constant::get(1), entryBB); + r->addIncoming(r1, continueBB); + b->addIncoming(base, entryBB); + b->addIncoming(b1, continueBB); + e->addIncoming(exponent, entryBB); + e->addIncoming(e1, continueBB); + + m_builder.SetInsertPoint(returnBB); + m_builder.CreateRet(r); + } + return m_exp; } llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) { - // while (e != 0) { - // if (e % 2 == 1) - // r *= b; - // b *= b; - // e /= 2; - // } - - if (auto c1 = llvm::dyn_cast(_arg1)) - { - if (auto c2 = llvm::dyn_cast(_arg2)) - { - auto b = c1->getValue(); - auto e = c2->getValue(); - auto r = llvm::APInt{256, 1}; - while (e != 0) - { - if (e[0]) - r *= b; - b *= b; - e = e.lshr(1); - } - return Constant::get(r); - } - } - - return m_builder.CreateCall(getExpFunc(), {_arg1, _arg2}); + // while (e != 0) { + // if (e % 2 == 1) + // r *= b; + // b *= b; + // e /= 2; + // } + + if (auto c1 = llvm::dyn_cast(_arg1)) + { + if (auto c2 = llvm::dyn_cast(_arg2)) + { + auto b = c1->getValue(); + auto e = c2->getValue(); + auto r = llvm::APInt{256, 1}; + while (e != 0) + { + if (e[0]) + r *= b; + b *= b; + e = e.lshr(1); + } + return Constant::get(r); + } + } + + return m_builder.CreateCall(getExpFunc(), {_arg1, _arg2}); } -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev -extern "C" +extern "C" { +EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z) { - EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z) - { - DLOG(JIT) << "DEBUG " << std::dec << z << ": " //<< d << c << b << a - << " [" << std::hex << std::setfill('0') << std::setw(16) << d << std::setw(16) << c << std::setw(16) << b << std::setw(16) << a << "]\n"; - } + DLOG(JIT) << "DEBUG " << std::dec << z << ": " //<< d << c << b << a + << " [" << std::hex << std::setfill('0') << std::setw(16) << d << std::setw(16) << c + << std::setw(16) << b << std::setw(16) << a << "]\n"; +} } diff --git a/libevmjit/Arith256.h b/libevmjit/Arith256.h index 696705be..7cf5adff 100644 --- a/libevmjit/Arith256.h +++ b/libevmjit/Arith256.h @@ -8,32 +8,31 @@ namespace eth { namespace jit { - class Arith256 : public CompilerHelper { public: - Arith256(IRBuilder& _builder); + Arith256(IRBuilder& _builder); - llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); + llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); - static void debug(llvm::Value *_value, char _c, llvm::Module &_module, IRBuilder &_builder); + static void debug(llvm::Value* _value, char _c, llvm::Module& _module, IRBuilder& _builder); - static llvm::Function* getUDiv256Func(llvm::Module& _module); - static llvm::Function* getURem256Func(llvm::Module& _module); - static llvm::Function* getURem512Func(llvm::Module& _module); - static llvm::Function* getUDivRem256Func(llvm::Module& _module); - static llvm::Function* getSDiv256Func(llvm::Module& _module); - static llvm::Function* getSRem256Func(llvm::Module& _module); - static llvm::Function* getSDivRem256Func(llvm::Module& _module); - static llvm::Function* getUDivRem512Func(llvm::Module& _module); + static llvm::Function* getUDiv256Func(llvm::Module& _module); + static llvm::Function* getURem256Func(llvm::Module& _module); + static llvm::Function* getURem512Func(llvm::Module& _module); + static llvm::Function* getUDivRem256Func(llvm::Module& _module); + static llvm::Function* getSDiv256Func(llvm::Module& _module); + static llvm::Function* getSRem256Func(llvm::Module& _module); + static llvm::Function* getSDivRem256Func(llvm::Module& _module); + static llvm::Function* getUDivRem512Func(llvm::Module& _module); private: - llvm::Function* getExpFunc(); + llvm::Function* getExpFunc(); - llvm::Function* m_exp = nullptr; + llvm::Function* m_exp = nullptr; }; -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Array.cpp b/libevmjit/Array.cpp index 9e02045d..0555929a 100644 --- a/libevmjit/Array.cpp +++ b/libevmjit/Array.cpp @@ -1,9 +1,9 @@ #include "Array.h" +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" -#include #include -#include "preprocessor/llvm_includes_end.h" +#include #include "RuntimeManager.h" #include "Utils.h" @@ -14,249 +14,259 @@ namespace eth { namespace jit { - static const auto c_reallocStep = 1; -llvm::Value* LazyFunction::call(IRBuilder& _builder, std::initializer_list const& _args, llvm::Twine const& _name) +llvm::Value* LazyFunction::call( + IRBuilder& _builder, std::initializer_list const& _args, llvm::Twine const& _name) { - if (!m_func) - m_func = m_creator(); + if (!m_func) + m_func = m_creator(); - return _builder.CreateCall(m_func, {_args.begin(), _args.size()}, _name); + return _builder.CreateCall(m_func, {_args.begin(), _args.size()}, _name); } llvm::Function* Array::createArrayPushFunc() { - llvm::Type* argTypes[] = {m_array->getType(), Type::Word}; - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.push", getModule()); - func->setDoesNotThrow(); - func->addAttribute(1, llvm::Attribute::NoCapture); - - auto iter = func->arg_begin(); - llvm::Argument* arrayPtr = &(*iter++); - arrayPtr->setName("arrayPtr"); - llvm::Argument* value = &(*iter); - value->setName("value"); - - InsertPointGuard guard{m_builder}; - auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func); - auto reallocBB = llvm::BasicBlock::Create(m_builder.getContext(), "Realloc", func); - auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func); - - m_builder.SetInsertPoint(entryBB); - auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); - auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); - auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); - auto data = m_builder.CreateLoad(dataPtr, "data"); - auto size = m_builder.CreateLoad(sizePtr, "size"); - auto cap = m_builder.CreateLoad(capPtr, "cap"); - auto reallocReq = m_builder.CreateICmpEQ(cap, size, "reallocReq"); - m_builder.CreateCondBr(reallocReq, reallocBB, pushBB); - - m_builder.SetInsertPoint(reallocBB); - auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap"); - auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32 - auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes"); - auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes"); - auto newData = m_builder.CreateBitCast(newBytes, Type::WordPtr, "newData"); - m_builder.CreateStore(newData, dataPtr); - m_builder.CreateStore(newCap, capPtr); - m_builder.CreateBr(pushBB); - - m_builder.SetInsertPoint(pushBB); - auto dataPhi = m_builder.CreatePHI(Type::WordPtr, 2, "dataPhi"); - dataPhi->addIncoming(data, entryBB); - dataPhi->addIncoming(newData, reallocBB); - auto newElemPtr = m_builder.CreateGEP(dataPhi, size, "newElemPtr"); - m_builder.CreateStore(value, newElemPtr); - auto newSize = m_builder.CreateNUWAdd(size, m_builder.getInt64(1), "newSize"); - m_builder.CreateStore(newSize, sizePtr); - m_builder.CreateRetVoid(); - - return func; + llvm::Type* argTypes[] = {m_array->getType(), Type::Word}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), + llvm::Function::PrivateLinkage, "array.push", getModule()); + func->setDoesNotThrow(); + func->addAttribute(1, llvm::Attribute::NoCapture); + + auto iter = func->arg_begin(); + llvm::Argument* arrayPtr = &(*iter++); + arrayPtr->setName("arrayPtr"); + llvm::Argument* value = &(*iter); + value->setName("value"); + + InsertPointGuard guard{m_builder}; + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func); + auto reallocBB = llvm::BasicBlock::Create(m_builder.getContext(), "Realloc", func); + auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func); + + m_builder.SetInsertPoint(entryBB); + auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); + auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); + auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); + auto data = m_builder.CreateLoad(dataPtr, "data"); + auto size = m_builder.CreateLoad(sizePtr, "size"); + auto cap = m_builder.CreateLoad(capPtr, "cap"); + auto reallocReq = m_builder.CreateICmpEQ(cap, size, "reallocReq"); + m_builder.CreateCondBr(reallocReq, reallocBB, pushBB); + + m_builder.SetInsertPoint(reallocBB); + auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap"); + auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32 + auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes"); + auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes"); + auto newData = m_builder.CreateBitCast(newBytes, Type::WordPtr, "newData"); + m_builder.CreateStore(newData, dataPtr); + m_builder.CreateStore(newCap, capPtr); + m_builder.CreateBr(pushBB); + + m_builder.SetInsertPoint(pushBB); + auto dataPhi = m_builder.CreatePHI(Type::WordPtr, 2, "dataPhi"); + dataPhi->addIncoming(data, entryBB); + dataPhi->addIncoming(newData, reallocBB); + auto newElemPtr = m_builder.CreateGEP(dataPhi, size, "newElemPtr"); + m_builder.CreateStore(value, newElemPtr); + auto newSize = m_builder.CreateNUWAdd(size, m_builder.getInt64(1), "newSize"); + m_builder.CreateStore(newSize, sizePtr); + m_builder.CreateRetVoid(); + + return func; } llvm::Function* Array::createArraySetFunc() { - llvm::Type* argTypes[] = {m_array->getType(), Type::Size, Type::Word}; - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.set", getModule()); - func->setDoesNotThrow(); - func->addAttribute(1, llvm::Attribute::NoCapture); - - auto iter = func->arg_begin(); - llvm::Argument* arrayPtr = &(*iter++); - arrayPtr->setName("arrayPtr"); - llvm::Argument* index = &(*iter++); - index->setName("index"); - llvm::Argument* value = &(*iter); - value->setName("value"); - - InsertPointGuard guard{m_builder}; - m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); - auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); - auto data = m_builder.CreateLoad(dataPtr, "data"); - auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); - m_builder.CreateStore(value, valuePtr); - m_builder.CreateRetVoid(); - return func; + llvm::Type* argTypes[] = {m_array->getType(), Type::Size, Type::Word}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), + llvm::Function::PrivateLinkage, "array.set", getModule()); + func->setDoesNotThrow(); + func->addAttribute(1, llvm::Attribute::NoCapture); + + auto iter = func->arg_begin(); + llvm::Argument* arrayPtr = &(*iter++); + arrayPtr->setName("arrayPtr"); + llvm::Argument* index = &(*iter++); + index->setName("index"); + llvm::Argument* value = &(*iter); + value->setName("value"); + + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); + auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); + auto data = m_builder.CreateLoad(dataPtr, "data"); + auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); + m_builder.CreateStore(value, valuePtr); + m_builder.CreateRetVoid(); + return func; } llvm::Function* Array::createArrayGetFunc() { - llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "array.get", getModule()); - func->setDoesNotThrow(); - func->addAttribute(1, llvm::Attribute::NoCapture); - - auto iter = func->arg_begin(); - llvm::Argument* arrayPtr = &(*iter++); - arrayPtr->setName("arrayPtr"); - llvm::Argument* index = &(*iter++); - index->setName("index"); - - InsertPointGuard guard{m_builder}; - m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); - auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); - auto data = m_builder.CreateLoad(dataPtr, "data"); - auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); - auto value = m_builder.CreateLoad(valuePtr, "value"); - m_builder.CreateRet(value); - return func; + llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), + llvm::Function::PrivateLinkage, "array.get", getModule()); + func->setDoesNotThrow(); + func->addAttribute(1, llvm::Attribute::NoCapture); + + auto iter = func->arg_begin(); + llvm::Argument* arrayPtr = &(*iter++); + arrayPtr->setName("arrayPtr"); + llvm::Argument* index = &(*iter++); + index->setName("index"); + + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); + auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); + auto data = m_builder.CreateLoad(dataPtr, "data"); + auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); + auto value = m_builder.CreateLoad(valuePtr, "value"); + m_builder.CreateRet(value); + return func; } llvm::Function* Array::createGetPtrFunc() { - llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), llvm::Function::PrivateLinkage, "array.getPtr", getModule()); - func->setDoesNotThrow(); - func->addAttribute(1, llvm::Attribute::NoCapture); - - auto iter = func->arg_begin(); - llvm::Argument* arrayPtr = &(*iter++); - arrayPtr->setName("arrayPtr"); - llvm::Argument* index = &(*iter++); - index->setName("index"); - - InsertPointGuard guard{m_builder}; - m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); - auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr"); - auto data = m_builder.CreateLoad(dataPtr, "data"); - auto bytePtr = m_builder.CreateGEP(data, index, "bytePtr"); - auto wordPtr = m_builder.CreateBitCast(bytePtr, Type::WordPtr, "wordPtr"); - m_builder.CreateRet(wordPtr); - return func; + llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), + llvm::Function::PrivateLinkage, "array.getPtr", getModule()); + func->setDoesNotThrow(); + func->addAttribute(1, llvm::Attribute::NoCapture); + + auto iter = func->arg_begin(); + llvm::Argument* arrayPtr = &(*iter++); + arrayPtr->setName("arrayPtr"); + llvm::Argument* index = &(*iter++); + index->setName("index"); + + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); + auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr"); + auto data = m_builder.CreateLoad(dataPtr, "data"); + auto bytePtr = m_builder.CreateGEP(data, index, "bytePtr"); + auto wordPtr = m_builder.CreateBitCast(bytePtr, Type::WordPtr, "wordPtr"); + m_builder.CreateRet(wordPtr); + return func; } llvm::Function* Array::createFreeFunc() { - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, m_array->getType(), false), llvm::Function::PrivateLinkage, "array.free", getModule()); - func->setDoesNotThrow(); - func->addAttribute(1, llvm::Attribute::NoCapture); - - auto freeFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::BytePtr, false), llvm::Function::ExternalLinkage, "free", getModule()); - freeFunc->setDoesNotThrow(); - freeFunc->addAttribute(1, llvm::Attribute::NoCapture); - - llvm::Argument* arrayPtr{func->arg_begin()}; - arrayPtr->setName("arrayPtr"); - - InsertPointGuard guard{m_builder}; - m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); - auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); - auto data = m_builder.CreateLoad(dataPtr, "data"); - auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem"); - m_builder.CreateCall(freeFunc, mem); - m_builder.CreateRetVoid(); - return func; + auto func = + llvm::Function::Create(llvm::FunctionType::get(Type::Void, m_array->getType(), false), + llvm::Function::PrivateLinkage, "array.free", getModule()); + func->setDoesNotThrow(); + func->addAttribute(1, llvm::Attribute::NoCapture); + + auto freeFunc = + llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::BytePtr, false), + llvm::Function::ExternalLinkage, "free", getModule()); + freeFunc->setDoesNotThrow(); + freeFunc->addAttribute(1, llvm::Attribute::NoCapture); + + llvm::Argument* arrayPtr{func->arg_begin()}; + arrayPtr->setName("arrayPtr"); + + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); + auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); + auto data = m_builder.CreateLoad(dataPtr, "data"); + auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem"); + m_builder.CreateCall(freeFunc, mem); + m_builder.CreateRetVoid(); + return func; } llvm::Function* Array::getReallocFunc() { - if (auto func = getModule()->getFunction("realloc")) - return func; - - llvm::Type* reallocArgTypes[] = {Type::BytePtr, Type::Size}; - auto reallocFunc = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, reallocArgTypes, false), llvm::Function::ExternalLinkage, "realloc", getModule()); - reallocFunc->setDoesNotThrow(); - reallocFunc->addAttribute(0, llvm::Attribute::NoAlias); - reallocFunc->addAttribute(1, llvm::Attribute::NoCapture); - return reallocFunc; + if (auto func = getModule()->getFunction("realloc")) + return func; + + llvm::Type* reallocArgTypes[] = {Type::BytePtr, Type::Size}; + auto reallocFunc = + llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, reallocArgTypes, false), + llvm::Function::ExternalLinkage, "realloc", getModule()); + reallocFunc->setDoesNotThrow(); + reallocFunc->addAttribute(0, llvm::Attribute::NoAlias); + reallocFunc->addAttribute(1, llvm::Attribute::NoCapture); + return reallocFunc; } llvm::Function* Array::createExtendFunc() { - llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.extend", getModule()); - func->setDoesNotThrow(); - func->addAttribute(1, llvm::Attribute::NoCapture); - - auto iter = func->arg_begin(); - llvm::Argument* arrayPtr = &(*iter++); - arrayPtr->setName("arrayPtr"); - llvm::Argument* newSize = &(*iter++); - newSize->setName("newSize"); - - InsertPointGuard guard{m_builder}; - m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); - auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");// TODO: Use byte* in Array - auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); - auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); - auto data = m_builder.CreateLoad(dataPtr, "data"); - auto size = m_builder.CreateLoad(sizePtr, "size"); - auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize"); - auto newData = m_reallocFunc.call(m_builder, {data, newSize}, "newData"); // TODO: Check realloc result for null - auto extPtr = m_builder.CreateGEP(newData, size, "extPtr"); - m_builder.CreateMemSet(extPtr, m_builder.getInt8(0), extSize, 16); - m_builder.CreateStore(newData, dataPtr); - m_builder.CreateStore(newSize, sizePtr); - m_builder.CreateStore(newSize, capPtr); - m_builder.CreateRetVoid(); - return func; + llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), + llvm::Function::PrivateLinkage, "array.extend", getModule()); + func->setDoesNotThrow(); + func->addAttribute(1, llvm::Attribute::NoCapture); + + auto iter = func->arg_begin(); + llvm::Argument* arrayPtr = &(*iter++); + arrayPtr->setName("arrayPtr"); + llvm::Argument* newSize = &(*iter++); + newSize->setName("newSize"); + + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); + auto dataPtr = m_builder.CreateBitCast( + arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr"); // TODO: Use byte* in Array + auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); + auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); + auto data = m_builder.CreateLoad(dataPtr, "data"); + auto size = m_builder.CreateLoad(sizePtr, "size"); + auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize"); + auto newData = m_reallocFunc.call( + m_builder, {data, newSize}, "newData"); // TODO: Check realloc result for null + auto extPtr = m_builder.CreateGEP(newData, size, "extPtr"); + m_builder.CreateMemSet(extPtr, m_builder.getInt8(0), extSize, 16); + m_builder.CreateStore(newData, dataPtr); + m_builder.CreateStore(newSize, sizePtr); + m_builder.CreateStore(newSize, capPtr); + m_builder.CreateRetVoid(); + return func; } llvm::Type* Array::getType() { - llvm::Type* elementTys[] = {Type::WordPtr, Type::Size, Type::Size}; - static auto arrayTy = llvm::StructType::create(elementTys, "Array"); - return arrayTy; + llvm::Type* elementTys[] = {Type::WordPtr, Type::Size, Type::Size}; + static auto arrayTy = llvm::StructType::create(elementTys, "Array"); + return arrayTy; } -Array::Array(IRBuilder& _builder, char const* _name) : - CompilerHelper(_builder) +Array::Array(IRBuilder& _builder, char const* _name) : CompilerHelper(_builder) { - m_array = m_builder.CreateAlloca(getType(), nullptr, _name); - m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); + m_array = m_builder.CreateAlloca(getType(), nullptr, _name); + m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); } -Array::Array(IRBuilder& _builder, llvm::Value* _array) : - CompilerHelper(_builder), - m_array(_array) +Array::Array(IRBuilder& _builder, llvm::Value* _array) : CompilerHelper(_builder), m_array(_array) { - m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); + m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); } void Array::pop(llvm::Value* _count) { - auto sizePtr = m_builder.CreateStructGEP(getType(), m_array, 1, "sizePtr"); - auto size = m_builder.CreateLoad(sizePtr, "size"); - auto newSize = m_builder.CreateNUWSub(size, _count, "newSize"); - m_builder.CreateStore(newSize, sizePtr); + auto sizePtr = m_builder.CreateStructGEP(getType(), m_array, 1, "sizePtr"); + auto size = m_builder.CreateLoad(sizePtr, "size"); + auto newSize = m_builder.CreateNUWSub(size, _count, "newSize"); + m_builder.CreateStore(newSize, sizePtr); } llvm::Value* Array::size(llvm::Value* _array) { - auto sizePtr = m_builder.CreateStructGEP(getType(), _array ? _array : m_array, 1, "sizePtr"); - return m_builder.CreateLoad(sizePtr, "array.size"); + auto sizePtr = m_builder.CreateStructGEP(getType(), _array ? _array : m_array, 1, "sizePtr"); + return m_builder.CreateLoad(sizePtr, "array.size"); } void Array::extend(llvm::Value* _arrayPtr, llvm::Value* _size) { - assert(_arrayPtr->getType() == m_array->getType()); - assert(_size->getType() == Type::Size); - m_extendFunc.call(m_builder, {_arrayPtr, _size}); + assert(_arrayPtr->getType() == m_array->getType()); + assert(_size->getType() == Type::Size); + m_extendFunc.call(m_builder, {_arrayPtr, _size}); } -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Array.h b/libevmjit/Array.h index a36de6c2..9d807699 100644 --- a/libevmjit/Array.h +++ b/libevmjit/Array.h @@ -10,63 +10,67 @@ namespace eth { namespace jit { - class LazyFunction { public: - using Creator = std::function; + using Creator = std::function; - LazyFunction(Creator _creator) : - m_creator(_creator) - {} + LazyFunction(Creator _creator) : m_creator(_creator) {} - llvm::Value* call(IRBuilder& _builder, std::initializer_list const& _args, llvm::Twine const& _name = ""); + llvm::Value* call(IRBuilder& _builder, std::initializer_list const& _args, + llvm::Twine const& _name = ""); private: - llvm::Function* m_func = nullptr; - Creator m_creator; + llvm::Function* m_func = nullptr; + Creator m_creator; }; class Array : public CompilerHelper { public: - Array(IRBuilder& _builder, char const* _name); - Array(IRBuilder& _builder, llvm::Value* _array); + Array(IRBuilder& _builder, char const* _name); + Array(IRBuilder& _builder, llvm::Value* _array); - void push(llvm::Value* _value) { m_pushFunc.call(m_builder, {m_array, _value}); } - void set(llvm::Value* _index, llvm::Value* _value) { m_setFunc.call(m_builder, {m_array, _index, _value}); } - llvm::Value* get(llvm::Value* _index) { return m_getFunc.call(m_builder, {m_array, _index}); } - void pop(llvm::Value* _count); - llvm::Value* size(llvm::Value* _array = nullptr); - void free() { m_freeFunc.call(m_builder, {m_array}); } + void push(llvm::Value* _value) { m_pushFunc.call(m_builder, {m_array, _value}); } + void set(llvm::Value* _index, llvm::Value* _value) + { + m_setFunc.call(m_builder, {m_array, _index, _value}); + } + llvm::Value* get(llvm::Value* _index) { return m_getFunc.call(m_builder, {m_array, _index}); } + void pop(llvm::Value* _count); + llvm::Value* size(llvm::Value* _array = nullptr); + void free() { m_freeFunc.call(m_builder, {m_array}); } - void extend(llvm::Value* _arrayPtr, llvm::Value* _size); - llvm::Value* getPtr(llvm::Value* _arrayPtr, llvm::Value* _index) { return m_getPtrFunc.call(m_builder, {_arrayPtr, _index}); } + void extend(llvm::Value* _arrayPtr, llvm::Value* _size); + llvm::Value* getPtr(llvm::Value* _arrayPtr, llvm::Value* _index) + { + return m_getPtrFunc.call(m_builder, {_arrayPtr, _index}); + } - llvm::Value* getPointerTo() const { return m_array; } + llvm::Value* getPointerTo() const { return m_array; } - static llvm::Type* getType(); + static llvm::Type* getType(); private: - llvm::Value* m_array = nullptr; - - llvm::Function* createArrayPushFunc(); - llvm::Function* createArraySetFunc(); - llvm::Function* createArrayGetFunc(); - llvm::Function* createGetPtrFunc(); - llvm::Function* createFreeFunc(); - llvm::Function* createExtendFunc(); - llvm::Function* getReallocFunc(); - - LazyFunction m_pushFunc = {[this](){ return createArrayPushFunc(); }}; - LazyFunction m_setFunc = {[this](){ return createArraySetFunc(); }}; - LazyFunction m_getPtrFunc = {[this](){ return createGetPtrFunc(); }}; - LazyFunction m_getFunc = {[this](){ return createArrayGetFunc(); }}; - LazyFunction m_freeFunc = {[this](){ return createFreeFunc(); }}; - LazyFunction m_extendFunc = {[this](){ return createExtendFunc(); }}; - LazyFunction m_reallocFunc = {[this](){ return getReallocFunc(); }}; + llvm::Value* m_array = nullptr; + + llvm::Function* createArrayPushFunc(); + llvm::Function* createArraySetFunc(); + llvm::Function* createArrayGetFunc(); + llvm::Function* createGetPtrFunc(); + llvm::Function* createFreeFunc(); + llvm::Function* createExtendFunc(); + llvm::Function* getReallocFunc(); + + LazyFunction m_pushFunc = {[this]() { return createArrayPushFunc(); }}; + LazyFunction m_setFunc = {[this]() { return createArraySetFunc(); }}; + LazyFunction m_getPtrFunc = {[this]() { return createGetPtrFunc(); }}; + LazyFunction m_getFunc = {[this]() { return createArrayGetFunc(); }}; + LazyFunction m_freeFunc = {[this]() { return createFreeFunc(); }}; + LazyFunction m_extendFunc = {[this]() { return createExtendFunc(); }}; + LazyFunction m_reallocFunc = {[this]() { return getReallocFunc(); }}; }; -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/BasicBlock.cpp b/libevmjit/BasicBlock.cpp index 14642057..82dd33ea 100644 --- a/libevmjit/BasicBlock.cpp +++ b/libevmjit/BasicBlock.cpp @@ -2,15 +2,15 @@ #include +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include -#include #include +#include #include #include -#include +#include #include -#include "preprocessor/llvm_includes_end.h" #include "RuntimeManager.h" #include "Type.h" @@ -22,184 +22,191 @@ namespace eth { namespace jit { - -BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc): - m_firstInstrIdx{_firstInstrIdx}, - m_begin(_begin), - m_end(_end), - m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {".", std::to_string(_firstInstrIdx)}, _mainFunc)) +BasicBlock::BasicBlock( + instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc) + : m_firstInstrIdx{_firstInstrIdx}, + m_begin(_begin), + m_end(_end), + m_llvmBB(llvm::BasicBlock::Create( + _mainFunc->getContext(), {".", std::to_string(_firstInstrIdx)}, _mainFunc)) {} -LocalStack::LocalStack(IRBuilder& _builder, RuntimeManager& _runtimeManager): - CompilerHelper(_builder) +LocalStack::LocalStack(IRBuilder& _builder, RuntimeManager& _runtimeManager) + : CompilerHelper(_builder) { - // Call stack.prepare. min, max, size args will be filled up in finalize(). - auto undef = llvm::UndefValue::get(Type::Size); - m_sp = m_builder.CreateCall(getStackPrepareFunc(), - {_runtimeManager.getStackBase(), _runtimeManager.getStackSize(), undef, undef, undef, _runtimeManager.getJmpBuf()}, - {"sp", m_builder.GetInsertBlock()->getName()}); + // Call stack.prepare. min, max, size args will be filled up in finalize(). + auto undef = llvm::UndefValue::get(Type::Size); + m_sp = m_builder.CreateCall(getStackPrepareFunc(), + {_runtimeManager.getStackBase(), _runtimeManager.getStackSize(), undef, undef, undef, + _runtimeManager.getJmpBuf()}, + {"sp", m_builder.GetInsertBlock()->getName()}); } void LocalStack::push(llvm::Value* _value) { - assert(_value->getType() == Type::Word); - m_local.push_back(_value); - m_maxSize = std::max(m_maxSize, size()); + assert(_value->getType() == Type::Word); + m_local.push_back(_value); + m_maxSize = std::max(m_maxSize, size()); } llvm::Value* LocalStack::pop() { - auto item = get(0); - assert(!m_local.empty() || !m_input.empty()); + auto item = get(0); + assert(!m_local.empty() || !m_input.empty()); - if (m_local.size() > 0) - m_local.pop_back(); - else - ++m_globalPops; + if (m_local.size() > 0) + m_local.pop_back(); + else + ++m_globalPops; - m_minSize = std::min(m_minSize, size()); - return item; + m_minSize = std::min(m_minSize, size()); + return item; } /// Copies the _index-th element of the local stack and pushes it back on the top. void LocalStack::dup(size_t _index) { - auto val = get(_index); - push(val); + auto val = get(_index); + push(val); } /// Swaps the top element with the _index-th element of the local stack. void LocalStack::swap(size_t _index) { - assert(_index > 0); ///< _index must not be 0. - auto val = get(_index); - auto tos = get(0); - set(_index, tos); - set(0, val); + assert(_index > 0); ///< _index must not be 0. + auto val = get(_index); + auto tos = get(0); + set(_index, tos); + set(0, val); } llvm::Value* LocalStack::get(size_t _index) { - if (_index < m_local.size()) - return *(m_local.rbegin() + _index); // count from back - - auto idx = _index - m_local.size() + m_globalPops; - if (idx >= m_input.size()) - m_input.resize(idx + 1); - auto& item = m_input[idx]; - - if (!item) - { - // Fetch an item from global stack - ssize_t globalIdx = -static_cast(idx) - 1; - auto slot = m_builder.CreateConstGEP1_64(m_sp, globalIdx); - item = m_builder.CreateAlignedLoad(slot, 16); // TODO: Handle malloc alignment. Also for 32-bit systems. - m_minSize = std::min(m_minSize, globalIdx); // remember required stack size - } - - return item; + if (_index < m_local.size()) + return *(m_local.rbegin() + _index); // count from back + + auto idx = _index - m_local.size() + m_globalPops; + if (idx >= m_input.size()) + m_input.resize(idx + 1); + auto& item = m_input[idx]; + + if (!item) + { + // Fetch an item from global stack + ssize_t globalIdx = -static_cast(idx) - 1; + auto slot = m_builder.CreateConstGEP1_64(m_sp, globalIdx); + item = m_builder.CreateAlignedLoad(slot, 16); // TODO: Handle malloc alignment. Also for + // 32-bit systems. + m_minSize = std::min(m_minSize, globalIdx); // remember required stack size + } + + return item; } void LocalStack::set(size_t _index, llvm::Value* _word) { - if (_index < m_local.size()) - { - *(m_local.rbegin() + _index) = _word; - return; - } - - auto idx = _index - m_local.size() + m_globalPops; - assert(idx < m_input.size()); - m_input[idx] = _word; + if (_index < m_local.size()) + { + *(m_local.rbegin() + _index) = _word; + return; + } + + auto idx = _index - m_local.size() + m_globalPops; + assert(idx < m_input.size()); + m_input[idx] = _word; } void LocalStack::finalize() { - m_sp->setArgOperand(2, m_builder.getInt64(minSize())); - m_sp->setArgOperand(3, m_builder.getInt64(maxSize())); - m_sp->setArgOperand(4, m_builder.getInt64(size())); - - if (auto term = m_builder.GetInsertBlock()->getTerminator()) - m_builder.SetInsertPoint(term); // Insert before terminator - - auto inputIt = m_input.rbegin(); - auto localIt = m_local.begin(); - for (auto globalIdx = -static_cast(m_input.size()); globalIdx < size(); ++globalIdx) - { - llvm::Value* item = nullptr; - if (globalIdx < -m_globalPops) - { - item = *inputIt++; // update input items (might contain original value) - if (!item) // some items are skipped - continue; - } - else - item = *localIt++; // store new items - - auto slot = m_builder.CreateConstGEP1_64(m_sp, globalIdx); - m_builder.CreateAlignedStore(item, slot, 16); // TODO: Handle malloc alignment. Also for 32-bit systems. - } + m_sp->setArgOperand(2, m_builder.getInt64(minSize())); + m_sp->setArgOperand(3, m_builder.getInt64(maxSize())); + m_sp->setArgOperand(4, m_builder.getInt64(size())); + + if (auto term = m_builder.GetInsertBlock()->getTerminator()) + m_builder.SetInsertPoint(term); // Insert before terminator + + auto inputIt = m_input.rbegin(); + auto localIt = m_local.begin(); + for (auto globalIdx = -static_cast(m_input.size()); globalIdx < size(); ++globalIdx) + { + llvm::Value* item = nullptr; + if (globalIdx < -m_globalPops) + { + item = *inputIt++; // update input items (might contain original value) + if (!item) // some items are skipped + continue; + } + else + item = *localIt++; // store new items + + auto slot = m_builder.CreateConstGEP1_64(m_sp, globalIdx); + m_builder.CreateAlignedStore(item, slot, 16); // TODO: Handle malloc alignment. Also for + // 32-bit systems. + } } llvm::Function* LocalStack::getStackPrepareFunc() { - static const auto c_funcName = "stack.prepare"; - if (auto func = getModule()->getFunction(c_funcName)) - return func; - - llvm::Type* argsTys[] = {Type::WordPtr, Type::Size->getPointerTo(), Type::Size, Type::Size, Type::Size, Type::BytePtr}; - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argsTys, false), llvm::Function::PrivateLinkage, c_funcName, getModule()); - func->setDoesNotThrow(); - func->addAttribute(1, llvm::Attribute::ReadNone); - func->addAttribute(2, llvm::Attribute::NoAlias); - func->addAttribute(2, llvm::Attribute::NoCapture); - - auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); - auto updateBB = llvm::BasicBlock::Create(func->getContext(), "Update", func); - auto outOfStackBB = llvm::BasicBlock::Create(func->getContext(), "OutOfStack", func); - - auto iter = func->arg_begin(); - llvm::Argument* base = &(*iter++); - base->setName("base"); - llvm::Argument* sizePtr = &(*iter++); - sizePtr->setName("size.ptr"); - llvm::Argument* min = &(*iter++); - min->setName("min"); - llvm::Argument* max = &(*iter++); - max->setName("max"); - llvm::Argument* diff = &(*iter++); - diff->setName("diff"); - llvm::Argument* jmpBuf = &(*iter); - jmpBuf->setName("jmpBuf"); - - InsertPointGuard guard{m_builder}; - m_builder.SetInsertPoint(checkBB); - auto sizeAlignment = getModule()->getDataLayout().getABITypeAlignment(Type::Size); - auto size = m_builder.CreateAlignedLoad(sizePtr, sizeAlignment, "size"); - auto minSize = m_builder.CreateAdd(size, min, "size.min", false, true); - auto maxSize = m_builder.CreateAdd(size, max, "size.max", true, true); - auto minOk = m_builder.CreateICmpSGE(minSize, m_builder.getInt64(0), "ok.min"); - auto maxOk = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(RuntimeManager::stackSizeLimit), "ok.max"); - auto ok = m_builder.CreateAnd(minOk, maxOk, "ok"); - m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); - - m_builder.SetInsertPoint(updateBB); - auto newSize = m_builder.CreateNSWAdd(size, diff, "size.next"); - m_builder.CreateAlignedStore(newSize, sizePtr, sizeAlignment); - auto sp = m_builder.CreateGEP(base, size, "sp"); - m_builder.CreateRet(sp); - - m_builder.SetInsertPoint(outOfStackBB); - auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); - m_builder.CreateCall(longjmp, {jmpBuf}); - m_builder.CreateUnreachable(); - - return func; + static const auto c_funcName = "stack.prepare"; + if (auto func = getModule()->getFunction(c_funcName)) + return func; + + llvm::Type* argsTys[] = {Type::WordPtr, Type::Size->getPointerTo(), Type::Size, Type::Size, + Type::Size, Type::BytePtr}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argsTys, false), + llvm::Function::PrivateLinkage, c_funcName, getModule()); + func->setDoesNotThrow(); + func->addAttribute(1, llvm::Attribute::ReadNone); + func->addAttribute(2, llvm::Attribute::NoAlias); + func->addAttribute(2, llvm::Attribute::NoCapture); + + auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); + auto updateBB = llvm::BasicBlock::Create(func->getContext(), "Update", func); + auto outOfStackBB = llvm::BasicBlock::Create(func->getContext(), "OutOfStack", func); + + auto iter = func->arg_begin(); + llvm::Argument* base = &(*iter++); + base->setName("base"); + llvm::Argument* sizePtr = &(*iter++); + sizePtr->setName("size.ptr"); + llvm::Argument* min = &(*iter++); + min->setName("min"); + llvm::Argument* max = &(*iter++); + max->setName("max"); + llvm::Argument* diff = &(*iter++); + diff->setName("diff"); + llvm::Argument* jmpBuf = &(*iter); + jmpBuf->setName("jmpBuf"); + + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(checkBB); + auto sizeAlignment = getModule()->getDataLayout().getABITypeAlignment(Type::Size); + auto size = m_builder.CreateAlignedLoad(sizePtr, sizeAlignment, "size"); + auto minSize = m_builder.CreateAdd(size, min, "size.min", false, true); + auto maxSize = m_builder.CreateAdd(size, max, "size.max", true, true); + auto minOk = m_builder.CreateICmpSGE(minSize, m_builder.getInt64(0), "ok.min"); + auto maxOk = m_builder.CreateICmpULE( + maxSize, m_builder.getInt64(RuntimeManager::stackSizeLimit), "ok.max"); + auto ok = m_builder.CreateAnd(minOk, maxOk, "ok"); + m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); + + m_builder.SetInsertPoint(updateBB); + auto newSize = m_builder.CreateNSWAdd(size, diff, "size.next"); + m_builder.CreateAlignedStore(newSize, sizePtr, sizeAlignment); + auto sp = m_builder.CreateGEP(base, size, "sp"); + m_builder.CreateRet(sp); + + m_builder.SetInsertPoint(outOfStackBB); + auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); + m_builder.CreateCall(longjmp, {jmpBuf}); + m_builder.CreateUnreachable(); + + return func; } -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/BasicBlock.h b/libevmjit/BasicBlock.h index 1c237903..61f8cf9e 100644 --- a/libevmjit/BasicBlock.h +++ b/libevmjit/BasicBlock.h @@ -16,73 +16,77 @@ using instr_idx = uint64_t; class RuntimeManager; -class LocalStack: public CompilerHelper +class LocalStack : public CompilerHelper { public: - explicit LocalStack(IRBuilder& _builder, RuntimeManager& _runtimeManager); + explicit LocalStack(IRBuilder& _builder, RuntimeManager& _runtimeManager); - /// Pushes value on stack - void push(llvm::Value* _value); + /// Pushes value on stack + void push(llvm::Value* _value); - /// Pops and returns top value - llvm::Value* pop(); + /// Pops and returns top value + llvm::Value* pop(); - /// Duplicates _index'th value on stack - void dup(size_t _index); + /// Duplicates _index'th value on stack + void dup(size_t _index); - /// Swaps _index'th value on stack with a value on stack top. - /// @param _index Index of value to be swaped. Must be > 0. - void swap(size_t _index); + /// Swaps _index'th value on stack with a value on stack top. + /// @param _index Index of value to be swaped. Must be > 0. + void swap(size_t _index); - ssize_t size() const { return static_cast(m_local.size()) - m_globalPops; } - ssize_t minSize() const { return m_minSize; } - ssize_t maxSize() const { return m_maxSize; } + ssize_t size() const { return static_cast(m_local.size()) - m_globalPops; } + ssize_t minSize() const { return m_minSize; } + ssize_t maxSize() const { return m_maxSize; } - /// Finalize local stack: check the requirements and update of the global stack. - void finalize(); + /// Finalize local stack: check the requirements and update of the global stack. + void finalize(); private: - /// Gets _index'th value from top (counting from 0) - llvm::Value* get(size_t _index); + /// Gets _index'th value from top (counting from 0) + llvm::Value* get(size_t _index); - /// Sets _index'th value from top (counting from 0) - void set(size_t _index, llvm::Value* _value); + /// Sets _index'th value from top (counting from 0) + void set(size_t _index, llvm::Value* _value); - llvm::Function* getStackPrepareFunc(); + llvm::Function* getStackPrepareFunc(); - /// Items fetched from global stack. First element matches the top of the global stack. - /// Can contain nulls if some items has been skipped. - std::vector m_input; + /// Items fetched from global stack. First element matches the top of the global stack. + /// Can contain nulls if some items has been skipped. + std::vector m_input; - /// Local stack items that has not been pushed to global stack. First item is just above global stack. - std::vector m_local; + /// Local stack items that has not been pushed to global stack. First item is just above global + /// stack. + std::vector m_local; - llvm::CallInst* m_sp = nullptr; ///< Call to stack.prepare function which returns stack pointer for current basic block. + llvm::CallInst* m_sp = nullptr; ///< Call to stack.prepare function which returns stack pointer + ///< for current basic block. - ssize_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap. - ssize_t m_minSize = 0; ///< Minimum reached local stack size. Can be negative. - ssize_t m_maxSize = 0; ///< Maximum reached local stack size. + ssize_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global + ///< - local stack overlap. + ssize_t m_minSize = 0; ///< Minimum reached local stack size. Can be negative. + ssize_t m_maxSize = 0; ///< Maximum reached local stack size. }; class BasicBlock { public: - explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc); + explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, + llvm::Function* _mainFunc); - llvm::BasicBlock* llvm() { return m_llvmBB; } + llvm::BasicBlock* llvm() { return m_llvmBB; } - instr_idx firstInstrIdx() const { return m_firstInstrIdx; } - code_iterator begin() const { return m_begin; } - code_iterator end() const { return m_end; } + instr_idx firstInstrIdx() const { return m_firstInstrIdx; } + code_iterator begin() const { return m_begin; } + code_iterator end() const { return m_end; } private: - instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block - code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block - code_iterator const m_end = {}; ///< Iterator pointing code end of the block + instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block + code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block + code_iterator const m_end = {}; ///< Iterator pointing code end of the block - llvm::BasicBlock* const m_llvmBB; ///< Reference to the LLVM BasicBlock + llvm::BasicBlock* const m_llvmBB; ///< Reference to the LLVM BasicBlock }; -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Cache.cpp b/libevmjit/Cache.cpp index 0a80f4ec..93c9c230 100644 --- a/libevmjit/Cache.cpp +++ b/libevmjit/Cache.cpp @@ -2,15 +2,15 @@ #include +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" -#include -#include -#include #include -#include +#include +#include +#include #include +#include #include -#include "preprocessor/llvm_includes_end.h" #include "ExecStats.h" #include "Utils.h" @@ -19,163 +19,167 @@ namespace dev { namespace evmjit { - namespace { - /// The ABI version of jitted codes. It reflects how a generated code - /// communicates with outside world. When this communication changes old - /// cached code must be invalidated. - const auto c_internalABIVersion = 4; - - using Guard = std::lock_guard; - std::mutex x_cacheMutex; - CacheMode g_mode; - std::unique_ptr g_lastObject; - JITListener* g_listener; - - std::string getVersionedCacheDir() - { - llvm::SmallString<256> path; - llvm::sys::path::user_cache_directory(path, "ethereum", "evmjit", - std::to_string(c_internalABIVersion)); - return path.str(); - } - +/// The ABI version of jitted codes. It reflects how a generated code +/// communicates with outside world. When this communication changes old +/// cached code must be invalidated. +const auto c_internalABIVersion = 4; + +using Guard = std::lock_guard; +std::mutex x_cacheMutex; +CacheMode g_mode; +std::unique_ptr g_lastObject; +JITListener* g_listener; + +std::string getVersionedCacheDir() +{ + llvm::SmallString<256> path; + llvm::sys::path::user_cache_directory( + path, "ethereum", "evmjit", std::to_string(c_internalABIVersion)); + return path.str(); } +} // namespace + ObjectCache* Cache::init(CacheMode _mode, JITListener* _listener) { - DLOG(cache) << "Cache dir: " << getVersionedCacheDir() << "\n"; + DLOG(cache) << "Cache dir: " << getVersionedCacheDir() << "\n"; - Guard g{x_cacheMutex}; + Guard g{x_cacheMutex}; - g_mode = _mode; - g_listener = _listener; + g_mode = _mode; + g_listener = _listener; - if (g_mode == CacheMode::clear) - { - Cache::clear(); - g_mode = CacheMode::off; - } + if (g_mode == CacheMode::clear) + { + Cache::clear(); + g_mode = CacheMode::off; + } - if (g_mode != CacheMode::off) - { - static ObjectCache objectCache; - return &objectCache; - } - return nullptr; + if (g_mode != CacheMode::off) + { + static ObjectCache objectCache; + return &objectCache; + } + return nullptr; } void Cache::clear() { - Guard g{x_cacheMutex}; + Guard g{x_cacheMutex}; - auto cachePath = getVersionedCacheDir(); - std::error_code err; - for (auto it = llvm::sys::fs::directory_iterator{cachePath, err}; it != decltype(it){}; it.increment(err)) - llvm::sys::fs::remove(it->path()); + auto cachePath = getVersionedCacheDir(); + std::error_code err; + for (auto it = llvm::sys::fs::directory_iterator{cachePath, err}; it != decltype(it){}; + it.increment(err)) + llvm::sys::fs::remove(it->path()); } -void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache, - llvm::LLVMContext& _llvmContext) +void Cache::preload(llvm::ExecutionEngine& _ee, + std::unordered_map& _funcCache, llvm::LLVMContext& _llvmContext) { - Guard g{x_cacheMutex}; - - // Disable listener - auto listener = g_listener; - g_listener = nullptr; - - auto cachePath = getVersionedCacheDir(); - std::error_code err; - for (auto it = llvm::sys::fs::directory_iterator{cachePath, err}; it != decltype(it){}; it.increment(err)) - { - auto name = it->path().substr(cachePath.size() + 1); - if (auto module = getObject(name, _llvmContext)) - { - DLOG(cache) << "Preload: " << name << "\n"; - _ee.addModule(std::move(module)); - auto addr = _ee.getFunctionAddress(name); - assert(addr); - _funcCache[std::move(name)] = addr; - } - } - - g_listener = listener; + Guard g{x_cacheMutex}; + + // Disable listener + auto listener = g_listener; + g_listener = nullptr; + + auto cachePath = getVersionedCacheDir(); + std::error_code err; + for (auto it = llvm::sys::fs::directory_iterator{cachePath, err}; it != decltype(it){}; + it.increment(err)) + { + auto name = it->path().substr(cachePath.size() + 1); + if (auto module = getObject(name, _llvmContext)) + { + DLOG(cache) << "Preload: " << name << "\n"; + _ee.addModule(std::move(module)); + auto addr = _ee.getFunctionAddress(name); + assert(addr); + _funcCache[std::move(name)] = addr; + } + } + + g_listener = listener; } -std::unique_ptr Cache::getObject(std::string const& id, llvm::LLVMContext& _llvmContext) +std::unique_ptr Cache::getObject( + std::string const& id, llvm::LLVMContext& _llvmContext) { - Guard g{x_cacheMutex}; - - if (g_mode != CacheMode::on && g_mode != CacheMode::read) - return nullptr; - - // TODO: Disabled because is not thread-safe. - //if (g_listener) - // g_listener->stateChanged(ExecState::CacheLoad); - - DLOG(cache) << id << ": search\n"; - if (!CHECK(!g_lastObject)) - g_lastObject = nullptr; - - llvm::SmallString<256> cachePath{getVersionedCacheDir()}; - llvm::sys::path::append(cachePath, id); - - if (auto r = llvm::MemoryBuffer::getFile(cachePath, -1, false)) - g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); - else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) - DLOG(cache) << r.getError().message(); // TODO: Add warning log - - if (g_lastObject) // if object found create fake module - { - DLOG(cache) << id << ": found\n"; - auto module = llvm::make_unique(id, _llvmContext); - auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(_llvmContext), {}, false); - auto mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get()); - auto bb = llvm::BasicBlock::Create(_llvmContext, {}, mainFunc); - bb->getInstList().push_back(new llvm::UnreachableInst{_llvmContext}); - return module; - } - DLOG(cache) << id << ": not found\n"; - return nullptr; + Guard g{x_cacheMutex}; + + if (g_mode != CacheMode::on && g_mode != CacheMode::read) + return nullptr; + + // TODO: Disabled because is not thread-safe. + // if (g_listener) + // g_listener->stateChanged(ExecState::CacheLoad); + + DLOG(cache) << id << ": search\n"; + if (!CHECK(!g_lastObject)) + g_lastObject = nullptr; + + llvm::SmallString<256> cachePath{getVersionedCacheDir()}; + llvm::sys::path::append(cachePath, id); + + if (auto r = llvm::MemoryBuffer::getFile(cachePath, -1, false)) + g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); + else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) + DLOG(cache) << r.getError().message(); // TODO: Add warning log + + if (g_lastObject) // if object found create fake module + { + DLOG(cache) << id << ": found\n"; + auto module = llvm::make_unique(id, _llvmContext); + auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(_llvmContext), {}, false); + auto mainFunc = + llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get()); + auto bb = llvm::BasicBlock::Create(_llvmContext, {}, mainFunc); + bb->getInstList().push_back(new llvm::UnreachableInst{_llvmContext}); + return module; + } + DLOG(cache) << id << ": not found\n"; + return nullptr; } void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) { - Guard g{x_cacheMutex}; - - // Only in "on" and "write" mode - if (g_mode != CacheMode::on && g_mode != CacheMode::write) - return; - - // TODO: Disabled because is not thread-safe. - // if (g_listener) - // g_listener->stateChanged(ExecState::CacheWrite); - - auto&& id = _module->getModuleIdentifier(); - llvm::SmallString<256> cachePath{getVersionedCacheDir()}; - if (auto err = llvm::sys::fs::create_directories(cachePath)) - { - DLOG(cache) << "Cannot create cache dir " << cachePath.str().str() << " (error: " << err.message() << "\n"; - return; - } - - llvm::sys::path::append(cachePath, id); - - DLOG(cache) << id << ": write\n"; - std::error_code error; - llvm::raw_fd_ostream cacheFile(cachePath, error, llvm::sys::fs::F_None); - cacheFile << _object.getBuffer(); + Guard g{x_cacheMutex}; + + // Only in "on" and "write" mode + if (g_mode != CacheMode::on && g_mode != CacheMode::write) + return; + + // TODO: Disabled because is not thread-safe. + // if (g_listener) + // g_listener->stateChanged(ExecState::CacheWrite); + + auto&& id = _module->getModuleIdentifier(); + llvm::SmallString<256> cachePath{getVersionedCacheDir()}; + if (auto err = llvm::sys::fs::create_directories(cachePath)) + { + DLOG(cache) << "Cannot create cache dir " << cachePath.str().str() + << " (error: " << err.message() << "\n"; + return; + } + + llvm::sys::path::append(cachePath, id); + + DLOG(cache) << id << ": write\n"; + std::error_code error; + llvm::raw_fd_ostream cacheFile(cachePath, error, llvm::sys::fs::F_None); + cacheFile << _object.getBuffer(); } std::unique_ptr ObjectCache::getObject(llvm::Module const* _module) { - Guard g{x_cacheMutex}; + Guard g{x_cacheMutex}; - DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; - return std::move(g_lastObject); + DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; + return std::move(g_lastObject); } -} -} +} // namespace evmjit +} // namespace dev diff --git a/libevmjit/Cache.h b/libevmjit/Cache.h index b2212491..a6468456 100644 --- a/libevmjit/Cache.h +++ b/libevmjit/Cache.h @@ -3,14 +3,14 @@ #include #include +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include #include -#include "preprocessor/llvm_includes_end.h" namespace llvm { - class ExecutionEngine; +class ExecutionEngine; } namespace dev @@ -21,41 +21,44 @@ class JITListener; enum class CacheMode { - off, - on, - read, - write, - clear, - preload + off, + on, + read, + write, + clear, + preload }; class ObjectCache : public llvm::ObjectCache { public: - /// notifyObjectCompiled - Provides a pointer to compiled code for Module M. - virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) final override; - - /// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that - /// contains the object which corresponds with Module M, or 0 if an object is - /// not available. The caller owns both the MemoryBuffer returned by this - /// and the memory it references. - virtual std::unique_ptr getObject(llvm::Module const* _module) final override; + /// notifyObjectCompiled - Provides a pointer to compiled code for Module M. + virtual void notifyObjectCompiled( + llvm::Module const* _module, llvm::MemoryBufferRef _object) final override; + + /// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that + /// contains the object which corresponds with Module M, or 0 if an object is + /// not available. The caller owns both the MemoryBuffer returned by this + /// and the memory it references. + virtual std::unique_ptr getObject( + llvm::Module const* _module) final override; }; class Cache { public: - static ObjectCache* init(CacheMode _mode, JITListener* _listener); - static std::unique_ptr getObject(std::string const& id, llvm::LLVMContext& _llvmContext); + static ObjectCache* init(CacheMode _mode, JITListener* _listener); + static std::unique_ptr getObject( + std::string const& id, llvm::LLVMContext& _llvmContext); - /// Clears cache storage - static void clear(); + /// Clears cache storage + static void clear(); - /// Loads all available cached objects to ExecutionEngine - static void preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache, - llvm::LLVMContext& _llvmContext); + /// Loads all available cached objects to ExecutionEngine + static void preload(llvm::ExecutionEngine& _ee, + std::unordered_map& _funcCache, llvm::LLVMContext& _llvmContext); }; -} -} +} // namespace evmjit +} // namespace dev diff --git a/libevmjit/Common.h b/libevmjit/Common.h index 4a6d7b4f..ccb0e445 100644 --- a/libevmjit/Common.h +++ b/libevmjit/Common.h @@ -6,11 +6,10 @@ namespace dev { namespace evmjit { - using byte = uint8_t; using code_iterator = byte const*; #define UNTESTED assert(false) -} -} +} // namespace evmjit +} // namespace dev diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index c92f2b3e..8c378110 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -1,38 +1,38 @@ #include "Compiler.h" -#include #include +#include #include +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include -#include #include -#include "preprocessor/llvm_includes_end.h" +#include -#include "JIT.h" -#include "Instruction.h" -#include "Type.h" -#include "Memory.h" +#include "Arith256.h" +#include "Endianness.h" #include "Ext.h" #include "GasMeter.h" -#include "Utils.h" -#include "Endianness.h" -#include "Arith256.h" +#include "Instruction.h" +#include "JIT.h" +#include "Memory.h" #include "RuntimeManager.h" +#include "Type.h" +#include "Utils.h" #ifndef __has_cpp_attribute - #define __has_cpp_attribute(x) 0 +#define __has_cpp_attribute(x) 0 #endif #if __has_cpp_attribute(fallthrough) - #define FALLTHROUGH [[fallthrough]] +#define FALLTHROUGH [[fallthrough]] #elif __has_cpp_attribute(clang::fallthrough) - #define FALLTHROUGH [[clang::fallthrough]] +#define FALLTHROUGH [[clang::fallthrough]] #elif __has_cpp_attribute(gnu::fallthrough) - #define FALLTHROUGH [[gnu::fallthrough]] +#define FALLTHROUGH [[gnu::fallthrough]] #else - #define FALLTHROUGH +#define FALLTHROUGH #endif namespace dev @@ -41,978 +41,987 @@ namespace eth { namespace jit { - static const auto c_destIdxLabel = "destIdx"; -Compiler::Compiler(Options const& _options, evmc_revision _rev, bool _staticCall, llvm::LLVMContext& _llvmContext): - m_options(_options), - m_rev(_rev), - m_staticCall(_staticCall), - m_builder(_llvmContext) +Compiler::Compiler( + Options const& _options, evmc_revision _rev, bool _staticCall, llvm::LLVMContext& _llvmContext) + : m_options(_options), m_rev(_rev), m_staticCall(_staticCall), m_builder(_llvmContext) { - Type::init(m_builder.getContext()); + Type::init(m_builder.getContext()); } -std::vector Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd) +std::vector Compiler::createBasicBlocks( + code_iterator _codeBegin, code_iterator _codeEnd) { - /// Helper function that skips push data and finds next iterator (can be the end) - auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end) - { - static const auto push1 = static_cast(Instruction::PUSH1); - static const auto push32 = static_cast(Instruction::PUSH32); - size_t offset = 1; - if (*_curr >= push1 && *_curr <= push32) - offset += std::min(*_curr - push1 + 1, (_end - _curr) - 1); - return _curr + offset; - }; - - std::vector blocks; - - bool isDead = false; - auto begin = _codeBegin; // begin of current block - for (auto curr = begin, next = begin; curr != _codeEnd; curr = next) - { - next = skipPushDataAndGetNext(curr, _codeEnd); - - if (isDead) - { - if (Instruction(*curr) == Instruction::JUMPDEST) - { - isDead = false; - begin = curr; - } - else - continue; - } - - bool isEnd = false; - switch (Instruction(*curr)) - { - case Instruction::JUMP: - case Instruction::RETURN: - case Instruction::REVERT: - case Instruction::STOP: - case Instruction::SUICIDE: - isDead = true; - FALLTHROUGH; - case Instruction::JUMPI: - isEnd = true; - break; - - default: - break; - } - - assert(next <= _codeEnd); - if (next == _codeEnd || Instruction(*next) == Instruction::JUMPDEST) - isEnd = true; - - if (isEnd) - { - auto beginIdx = begin - _codeBegin; - blocks.emplace_back(beginIdx, begin, next, m_mainFunc); - begin = next; - } - } - - return blocks; + /// Helper function that skips push data and finds next iterator (can be the end) + auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end) { + static const auto push1 = static_cast(Instruction::PUSH1); + static const auto push32 = static_cast(Instruction::PUSH32); + size_t offset = 1; + if (*_curr >= push1 && *_curr <= push32) + offset += std::min(*_curr - push1 + 1, (_end - _curr) - 1); + return _curr + offset; + }; + + std::vector blocks; + + bool isDead = false; + auto begin = _codeBegin; // begin of current block + for (auto curr = begin, next = begin; curr != _codeEnd; curr = next) + { + next = skipPushDataAndGetNext(curr, _codeEnd); + + if (isDead) + { + if (Instruction(*curr) == Instruction::JUMPDEST) + { + isDead = false; + begin = curr; + } + else + continue; + } + + bool isEnd = false; + switch (Instruction(*curr)) + { + case Instruction::JUMP: + case Instruction::RETURN: + case Instruction::REVERT: + case Instruction::STOP: + case Instruction::SUICIDE: + isDead = true; + FALLTHROUGH; + case Instruction::JUMPI: + isEnd = true; + break; + + default: + break; + } + + assert(next <= _codeEnd); + if (next == _codeEnd || Instruction(*next) == Instruction::JUMPDEST) + isEnd = true; + + if (isEnd) + { + auto beginIdx = begin - _codeBegin; + blocks.emplace_back(beginIdx, begin, next, m_mainFunc); + begin = next; + } + } + + return blocks; } void Compiler::resolveJumps() { - auto jumpTable = llvm::cast(m_jumpTableBB->getTerminator()); - auto jumpTableInput = llvm::cast(m_jumpTableBB->begin()); - - // Iterate through all EVM instructions blocks (skip first one and last 4 - special blocks). - for (auto it = std::next(m_mainFunc->begin()), end = std::prev(m_mainFunc->end(), 4); it != end; ++it) - { - auto nextBlockIter = it; - ++nextBlockIter; // If the last code block, that will be "stop" block. - auto currentBlockPtr = &(*it); - auto nextBlockPtr = &(*nextBlockIter); - - auto term = it->getTerminator(); - llvm::BranchInst* jump = nullptr; - - if (!term) // Block may have no terminator if the next instruction is a jump destination. - IRBuilder{currentBlockPtr}.CreateBr(nextBlockPtr); - else if ((jump = llvm::dyn_cast(term)) && jump->getSuccessor(0) == m_jumpTableBB) - { - auto destIdx = llvm::cast(jump->getMetadata(c_destIdxLabel)->getOperand(0))->getValue(); - if (auto constant = llvm::dyn_cast(destIdx)) - { - // If destination index is a constant do direct jump to the destination block. - auto bb = jumpTable->findCaseValue(constant)->getCaseSuccessor(); - jump->setSuccessor(0, bb); - } - else - jumpTableInput->addIncoming(destIdx, currentBlockPtr); // Fill up PHI node - - if (jump->isConditional()) - jump->setSuccessor(1, &(*nextBlockIter)); // Set next block for conditional jumps - } - } - - auto simplifiedInput = jumpTableInput->getNumIncomingValues() == 0 ? - llvm::UndefValue::get(jumpTableInput->getType()) : - jumpTableInput->hasConstantValue(); - if (simplifiedInput) - { - jumpTableInput->replaceAllUsesWith(simplifiedInput); - jumpTableInput->eraseFromParent(); - } + auto jumpTable = llvm::cast(m_jumpTableBB->getTerminator()); + auto jumpTableInput = llvm::cast(m_jumpTableBB->begin()); + + // Iterate through all EVM instructions blocks (skip first one and last 4 - special blocks). + for (auto it = std::next(m_mainFunc->begin()), end = std::prev(m_mainFunc->end(), 4); it != end; + ++it) + { + auto nextBlockIter = it; + ++nextBlockIter; // If the last code block, that will be "stop" block. + auto currentBlockPtr = &(*it); + auto nextBlockPtr = &(*nextBlockIter); + + auto term = it->getTerminator(); + llvm::BranchInst* jump = nullptr; + + if (!term) // Block may have no terminator if the next instruction is a jump destination. + IRBuilder{currentBlockPtr}.CreateBr(nextBlockPtr); + else if ((jump = llvm::dyn_cast(term)) && + jump->getSuccessor(0) == m_jumpTableBB) + { + auto destIdx = + llvm::cast(jump->getMetadata(c_destIdxLabel)->getOperand(0)) + ->getValue(); + if (auto constant = llvm::dyn_cast(destIdx)) + { + // If destination index is a constant do direct jump to the destination block. + auto bb = jumpTable->findCaseValue(constant)->getCaseSuccessor(); + jump->setSuccessor(0, bb); + } + else + jumpTableInput->addIncoming(destIdx, currentBlockPtr); // Fill up PHI node + + if (jump->isConditional()) + jump->setSuccessor(1, &(*nextBlockIter)); // Set next block for conditional jumps + } + } + + auto simplifiedInput = jumpTableInput->getNumIncomingValues() == 0 ? + llvm::UndefValue::get(jumpTableInput->getType()) : + jumpTableInput->hasConstantValue(); + if (simplifiedInput) + { + jumpTableInput->replaceAllUsesWith(simplifiedInput); + jumpTableInput->eraseFromParent(); + } } -std::unique_ptr Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id) +std::unique_ptr Compiler::compile( + code_iterator _begin, code_iterator _end, std::string const& _id) { - auto module = llvm::make_unique(_id, m_builder.getContext()); // TODO: Provide native DataLayout - - // Create main function - auto mainFuncType = llvm::FunctionType::get(Type::MainReturn, Type::RuntimePtr, false); - m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get()); - m_mainFunc->args().begin()->setName("rt"); - - // Create entry basic block - auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", m_mainFunc); - - auto blocks = createBasicBlocks(_begin, _end); - - // Special "Stop" block. Guarantees that there exists a next block after the code blocks (also when there are no code blocks). - auto stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); - m_jumpTableBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "JumpTable", m_mainFunc); - auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc); - - m_builder.SetInsertPoint(m_jumpTableBB); // Must be before basic blocks compilation - auto target = m_builder.CreatePHI(Type::Word, 16, "target"); - m_builder.CreateSwitch(target, abortBB); - - m_builder.SetInsertPoint(entryBB); - - - // Init runtime structures. - RuntimeManager runtimeManager(m_builder, _begin, _end); - GasMeter gasMeter(m_builder, runtimeManager, m_rev); - Memory memory(runtimeManager, gasMeter); - Ext ext(runtimeManager, memory); - Arith256 arith(m_builder); - - auto jmpBufWords = m_builder.CreateAlloca(Type::BytePtr, m_builder.getInt64(3), "jmpBuf.words"); - auto frameaddress = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::frameaddress); - auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp"); - m_builder.CreateStore(fp, jmpBufWords); - auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave); - auto sp = m_builder.CreateCall(stacksave, {}, "sp"); - auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp"); - m_builder.CreateStore(sp, jmpBufSp); - auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp); - auto jmpBuf = m_builder.CreateBitCast(jmpBufWords, Type::BytePtr, "jmpBuf"); - auto r = m_builder.CreateCall(setjmp, jmpBuf); - auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0)); - runtimeManager.setJmpBuf(jmpBuf); - m_builder.CreateCondBr(normalFlow, entryBB->getNextNode(), abortBB, Type::expectTrue); - - for (auto& block: blocks) - compileBasicBlock(block, runtimeManager, arith, memory, ext, gasMeter); - - // Code for special blocks: - m_builder.SetInsertPoint(stopBB); - runtimeManager.exit(ReturnCode::Stop); - - m_builder.SetInsertPoint(abortBB); - runtimeManager.exit(ReturnCode::OutOfGas); - - resolveJumps(); - - return module; + auto module = llvm::make_unique( + _id, m_builder.getContext()); // TODO: Provide native DataLayout + + // Create main function + auto mainFuncType = llvm::FunctionType::get(Type::MainReturn, Type::RuntimePtr, false); + m_mainFunc = + llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get()); + m_mainFunc->args().begin()->setName("rt"); + + // Create entry basic block + auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", m_mainFunc); + + auto blocks = createBasicBlocks(_begin, _end); + + // Special "Stop" block. Guarantees that there exists a next block after the code blocks (also + // when there are no code blocks). + auto stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); + m_jumpTableBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "JumpTable", m_mainFunc); + auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc); + + m_builder.SetInsertPoint(m_jumpTableBB); // Must be before basic blocks compilation + auto target = m_builder.CreatePHI(Type::Word, 16, "target"); + m_builder.CreateSwitch(target, abortBB); + + m_builder.SetInsertPoint(entryBB); + + + // Init runtime structures. + RuntimeManager runtimeManager(m_builder, _begin, _end); + GasMeter gasMeter(m_builder, runtimeManager, m_rev); + Memory memory(runtimeManager, gasMeter); + Ext ext(runtimeManager, memory); + Arith256 arith(m_builder); + + auto jmpBufWords = m_builder.CreateAlloca(Type::BytePtr, m_builder.getInt64(3), "jmpBuf.words"); + auto frameaddress = + llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::frameaddress); + auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp"); + m_builder.CreateStore(fp, jmpBufWords); + auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave); + auto sp = m_builder.CreateCall(stacksave, {}, "sp"); + auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp"); + m_builder.CreateStore(sp, jmpBufSp); + auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp); + auto jmpBuf = m_builder.CreateBitCast(jmpBufWords, Type::BytePtr, "jmpBuf"); + auto r = m_builder.CreateCall(setjmp, jmpBuf); + auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0)); + runtimeManager.setJmpBuf(jmpBuf); + m_builder.CreateCondBr(normalFlow, entryBB->getNextNode(), abortBB, Type::expectTrue); + + for (auto& block : blocks) + compileBasicBlock(block, runtimeManager, arith, memory, ext, gasMeter); + + // Code for special blocks: + m_builder.SetInsertPoint(stopBB); + runtimeManager.exit(ReturnCode::Stop); + + m_builder.SetInsertPoint(abortBB); + runtimeManager.exit(ReturnCode::OutOfGas); + + resolveJumps(); + + return module; } void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager, - Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter) + Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter) { - m_builder.SetInsertPoint(_basicBlock.llvm()); - LocalStack stack{m_builder, _runtimeManager}; - - for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it) - { - auto inst = Instruction(*it); - - _gasMeter.count(inst); - - switch (inst) - { - - case Instruction::ADD: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto result = m_builder.CreateAdd(lhs, rhs); - stack.push(result); - break; - } - - case Instruction::SUB: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto result = m_builder.CreateSub(lhs, rhs); - stack.push(result); - break; - } - - case Instruction::MUL: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = m_builder.CreateMul(lhs, rhs); - stack.push(res); - break; - } - - case Instruction::DIV: - { - auto d = stack.pop(); - auto n = stack.pop(); - auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); - n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal - auto r = m_builder.CreateUDiv(d, n); - r = m_builder.CreateSelect(divByZero, Constant::get(0), r); - stack.push(r); - break; - } - - case Instruction::SDIV: - { - auto d = stack.pop(); - auto n = stack.pop(); - auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); - auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1)); - n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal - auto r = m_builder.CreateSDiv(d, n); - r = m_builder.CreateSelect(divByZero, Constant::get(0), r); - auto dNeg = m_builder.CreateSub(Constant::get(0), d); - r = m_builder.CreateSelect(divByMinusOne, dNeg, r); // protect against undef i256.min / -1 - stack.push(r); - break; - } - - case Instruction::MOD: - { - auto d = stack.pop(); - auto n = stack.pop(); - auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); - n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal - auto r = m_builder.CreateURem(d, n); - r = m_builder.CreateSelect(divByZero, Constant::get(0), r); - stack.push(r); - break; - } - - case Instruction::SMOD: - { - auto d = stack.pop(); - auto n = stack.pop(); - auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); - auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1)); - n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal - auto r = m_builder.CreateSRem(d, n); - r = m_builder.CreateSelect(divByZero, Constant::get(0), r); - r = m_builder.CreateSelect(divByMinusOne, Constant::get(0), r); // protect against undef i256.min / -1 - stack.push(r); - break; - } - - case Instruction::ADDMOD: - { - auto i512Ty = m_builder.getIntNTy(512); - auto a = stack.pop(); - auto b = stack.pop(); - auto m = stack.pop(); - auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0)); - a = m_builder.CreateZExt(a, i512Ty); - b = m_builder.CreateZExt(b, i512Ty); - m = m_builder.CreateZExt(m, i512Ty); - auto s = m_builder.CreateNUWAdd(a, b); - s = m_builder.CreateURem(s, m); - s = m_builder.CreateTrunc(s, Type::Word); - s = m_builder.CreateSelect(divByZero, Constant::get(0), s); - stack.push(s); - break; - } - - case Instruction::MULMOD: - { - auto i512Ty = m_builder.getIntNTy(512); - auto a = stack.pop(); - auto b = stack.pop(); - auto m = stack.pop(); - auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0)); - a = m_builder.CreateZExt(a, i512Ty); - b = m_builder.CreateZExt(b, i512Ty); - m = m_builder.CreateZExt(m, i512Ty); - auto p = m_builder.CreateNUWMul(a, b); - p = m_builder.CreateURem(p, m); - p = m_builder.CreateTrunc(p, Type::Word); - p = m_builder.CreateSelect(divByZero, Constant::get(0), p); - stack.push(p); - break; - } - - case Instruction::EXP: - { - auto base = stack.pop(); - auto exponent = stack.pop(); - _gasMeter.countExp(exponent); - auto ret = _arith.exp(base, exponent); - stack.push(ret); - break; - } - - case Instruction::NOT: - { - auto value = stack.pop(); - auto ret = m_builder.CreateXor(value, Constant::get(-1), "bnot"); - stack.push(ret); - break; - } - - case Instruction::LT: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res1 = m_builder.CreateICmpULT(lhs, rhs); - auto res256 = m_builder.CreateZExt(res1, Type::Word); - stack.push(res256); - break; - } - - case Instruction::GT: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res1 = m_builder.CreateICmpUGT(lhs, rhs); - auto res256 = m_builder.CreateZExt(res1, Type::Word); - stack.push(res256); - break; - } - - case Instruction::SLT: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res1 = m_builder.CreateICmpSLT(lhs, rhs); - auto res256 = m_builder.CreateZExt(res1, Type::Word); - stack.push(res256); - break; - } - - case Instruction::SGT: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res1 = m_builder.CreateICmpSGT(lhs, rhs); - auto res256 = m_builder.CreateZExt(res1, Type::Word); - stack.push(res256); - break; - } - - case Instruction::EQ: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res1 = m_builder.CreateICmpEQ(lhs, rhs); - auto res256 = m_builder.CreateZExt(res1, Type::Word); - stack.push(res256); - break; - } - - case Instruction::ISZERO: - { - auto top = stack.pop(); - auto iszero = m_builder.CreateICmpEQ(top, Constant::get(0), "iszero"); - auto result = m_builder.CreateZExt(iszero, Type::Word); - stack.push(result); - break; - } - - case Instruction::AND: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = m_builder.CreateAnd(lhs, rhs); - stack.push(res); - break; - } - - case Instruction::OR: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = m_builder.CreateOr(lhs, rhs); - stack.push(res); - break; - } - - case Instruction::XOR: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = m_builder.CreateXor(lhs, rhs); - stack.push(res); - break; - } - - case Instruction::BYTE: - { - const auto idx = stack.pop(); - auto value = Endianness::toBE(m_builder, stack.pop()); - - auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid"); - auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); - // TODO: Workaround for LLVM bug. Using big value of index causes invalid memory access. - auto safeIdx = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5)); - // TODO: Workaround for LLVM bug. DAG Builder used sext on index instead of zext - safeIdx = m_builder.CreateZExt(safeIdx, Type::Size); - auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte"); - value = m_builder.CreateZExt(byte, Type::Word); - value = m_builder.CreateSelect(idxValid, value, Constant::get(0)); - stack.push(value); - break; - } - - case Instruction::SIGNEXTEND: - { - auto idx = stack.pop(); - auto word = stack.pop(); - - auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32"); - auto k32 = m_builder.CreateZExt(k32_, Type::Size); - auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8"); - - // test for word >> (k * 8 + 7) - auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos"); - auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word); - auto bitval = m_builder.CreateLShr(word, bitposEx, "bitval"); - auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest"); - - auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx); - auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); - - auto negmask = m_builder.CreateXor(mask, llvm::ConstantInt::getAllOnesValue(Type::Word), "negmask"); - auto val1 = m_builder.CreateOr(word, negmask); - auto val0 = m_builder.CreateAnd(word, mask); - - auto kInRange = m_builder.CreateICmpULE(idx, llvm::ConstantInt::get(Type::Word, 30)); - auto result = m_builder.CreateSelect(kInRange, - m_builder.CreateSelect(bittest, val1, val0), - word); - stack.push(result); - break; - } - - case Instruction::SHA3: - { - auto inOff = stack.pop(); - auto inSize = stack.pop(); - _memory.require(inOff, inSize); - _gasMeter.countSha3Data(inSize); - auto hash = _ext.sha3(inOff, inSize); - stack.push(hash); - break; - } - - case Instruction::POP: - { - stack.pop(); - break; - } - - case Instruction::ANY_PUSH: - { - auto value = readPushData(it, _basicBlock.end()); - stack.push(Constant::get(value)); - break; - } - - case Instruction::ANY_DUP: - { - auto index = static_cast(inst) - static_cast(Instruction::DUP1); - stack.dup(index); - break; - } - - case Instruction::ANY_SWAP: - { - auto index = static_cast(inst) - static_cast(Instruction::SWAP1) + 1; - stack.swap(index); - break; - } - - case Instruction::MLOAD: - { - auto addr = stack.pop(); - auto word = _memory.loadWord(addr); - stack.push(word); - break; - } - - case Instruction::MSTORE: - { - auto addr = stack.pop(); - auto word = stack.pop(); - _memory.storeWord(addr, word); - break; - } - - case Instruction::MSTORE8: - { - auto addr = stack.pop(); - auto word = stack.pop(); - _memory.storeByte(addr, word); - break; - } - - case Instruction::MSIZE: - { - auto word = _memory.getSize(); - stack.push(word); - break; - } - - case Instruction::SLOAD: - { - auto index = stack.pop(); - auto value = _ext.sload(index); - stack.push(value); - break; - } - - case Instruction::SSTORE: - { - if (m_staticCall) - goto invalidInstruction; - - auto index = stack.pop(); - auto value = stack.pop(); - _gasMeter.countSStore(_ext, index, value); - _ext.sstore(index, value); - break; - } - - case Instruction::JUMP: - case Instruction::JUMPI: - { - auto destIdx = llvm::MDNode::get(m_builder.getContext(), llvm::ValueAsMetadata::get(stack.pop())); - - // Create branch instruction, initially to jump table. - // Destination will be optimized with direct jump during jump resolving if destination index is a constant. - auto jumpInst = (inst == Instruction::JUMP) ? - m_builder.CreateBr(m_jumpTableBB) : - m_builder.CreateCondBr(m_builder.CreateICmpNE(stack.pop(), Constant::get(0), "jump.check"), m_jumpTableBB, nullptr); - - // Attach medatada to branch instruction with information about destination index. - jumpInst->setMetadata(c_destIdxLabel, destIdx); - break; - } - - case Instruction::JUMPDEST: - { - // Add the basic block to the jump table. - assert(it == _basicBlock.begin() && "JUMPDEST must be the first instruction of a basic block"); - auto jumpTable = llvm::cast(m_jumpTableBB->getTerminator()); - jumpTable->addCase(Constant::get(_basicBlock.firstInstrIdx()), _basicBlock.llvm()); - break; - } - - case Instruction::PC: - { - auto value = Constant::get(it - _basicBlock.begin() + _basicBlock.firstInstrIdx()); - stack.push(value); - break; - } - - case Instruction::GAS: - { - _gasMeter.commitCostBlock(); - stack.push(m_builder.CreateZExt(_runtimeManager.getGas(), Type::Word)); - break; - } - - case Instruction::ADDRESS: - stack.push(Endianness::toNative(m_builder, _runtimeManager.getAddress())); - break; - case Instruction::CALLER: - stack.push(Endianness::toNative(m_builder, _runtimeManager.getSender())); - break; - case Instruction::ORIGIN: - stack.push(m_builder.CreateZExt(Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(1)), Type::Word)); - break; - case Instruction::COINBASE: - stack.push(m_builder.CreateZExt(Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(2)), Type::Word)); - break; - - case Instruction::GASPRICE: - stack.push(Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(0))); - break; - - case Instruction::DIFFICULTY: - stack.push(Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(6))); - break; - - case Instruction::GASLIMIT: - stack.push(m_builder.CreateZExt(_runtimeManager.getTxContextItem(5), Type::Word)); - break; - - case Instruction::NUMBER: - stack.push(m_builder.CreateZExt(_runtimeManager.getTxContextItem(3), Type::Word)); - break; - - case Instruction::TIMESTAMP: - stack.push(m_builder.CreateZExt(_runtimeManager.getTxContextItem(4), Type::Word)); - break; - - case Instruction::CALLVALUE: - { - auto beValue = _runtimeManager.getValue(); - stack.push(Endianness::toNative(m_builder, beValue)); - break; - } - - case Instruction::CODESIZE: - stack.push(_runtimeManager.getCodeSize()); - break; - - case Instruction::CALLDATASIZE: - stack.push(_runtimeManager.getCallDataSize()); - break; - - case Instruction::RETURNDATASIZE: - { - if (m_rev < EVMC_BYZANTIUM) - goto invalidInstruction; - - auto returnBufSizePtr = _runtimeManager.getReturnBufSizePtr(); - auto returnBufSize = m_builder.CreateLoad(returnBufSizePtr); - stack.push(m_builder.CreateZExt(returnBufSize, Type::Word)); - break; - } - - case Instruction::BLOCKHASH: - { - auto number = stack.pop(); - // If number bigger than int64 assume the result is 0. - auto limitC = m_builder.getInt64(std::numeric_limits::max()); - auto limit = m_builder.CreateZExt(limitC, Type::Word); - auto isBigNumber = m_builder.CreateICmpUGT(number, limit); - auto hash = _ext.blockHash(number); - // TODO: Try to eliminate the call if the number is invalid. - hash = m_builder.CreateSelect(isBigNumber, Constant::get(0), hash); - stack.push(hash); - break; - } - - case Instruction::BALANCE: - { - auto address = stack.pop(); - auto value = _ext.balance(address); - stack.push(value); - break; - } - - case Instruction::EXTCODESIZE: - { - auto addr = stack.pop(); - auto codesize = _ext.extcodesize(addr); - stack.push(codesize); - break; - } - - case Instruction::CALLDATACOPY: - { - auto destMemIdx = stack.pop(); - auto srcIdx = stack.pop(); - auto reqBytes = stack.pop(); - - auto srcPtr = _runtimeManager.getCallData(); - auto srcSize = _runtimeManager.getCallDataSize(); - - _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); - break; - } - - case Instruction::RETURNDATACOPY: - { - if (m_rev < EVMC_BYZANTIUM) - goto invalidInstruction; - - auto destMemIdx = stack.pop(); - auto srcIdx = stack.pop(); - auto reqBytes = stack.pop(); - - auto srcPtr = m_builder.CreateLoad(_runtimeManager.getReturnBufDataPtr()); - auto srcSize = m_builder.CreateLoad(_runtimeManager.getReturnBufSizePtr()); - - _memory.copyBytesNoPadding(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); - break; - } - - case Instruction::CODECOPY: - { - auto destMemIdx = stack.pop(); - auto srcIdx = stack.pop(); - auto reqBytes = stack.pop(); - - auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, feature #80814234 - auto srcSize = _runtimeManager.getCodeSize(); - - _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); - break; - } - - case Instruction::EXTCODECOPY: - { - auto addr = stack.pop(); - auto destMemIdx = stack.pop(); - auto srcIdx = stack.pop(); - auto reqBytes = stack.pop(); - - auto codeRef = _ext.extcode(addr); - - _memory.copyBytes(codeRef.ptr, codeRef.size, srcIdx, destMemIdx, reqBytes); - break; - } - - case Instruction::CALLDATALOAD: - { - auto idx = stack.pop(); - auto value = _ext.calldataload(idx); - stack.push(value); - break; - } - - case Instruction::CREATE: - { - if (m_staticCall) - goto invalidInstruction; - - auto endowment = stack.pop(); - auto initOff = stack.pop(); - auto initSize = stack.pop(); - _memory.require(initOff, initSize); - - _gasMeter.commitCostBlock(); - auto gas = _runtimeManager.getGas(); - llvm::Value* gasKept = (m_rev >= EVMC_TANGERINE_WHISTLE) ? - m_builder.CreateLShr(gas, 6) : - m_builder.getInt64(0); - auto createGas = m_builder.CreateSub(gas, gasKept, "create.gas", true, true); - llvm::Value* r = nullptr; - llvm::Value* pAddr = nullptr; - std::tie(r, pAddr) = _ext.create(createGas, endowment, initOff, initSize); - - auto ret = - m_builder.CreateICmpSGE(r, m_builder.getInt64(0), "create.ret"); - auto rmagic = m_builder.CreateSelect( - ret, m_builder.getInt64(0), m_builder.getInt64(EVM_CALL_FAILURE), - "call.rmagic"); - // TODO: optimize - auto gasLeft = m_builder.CreateSub(r, rmagic, "create.gasleft"); - gas = m_builder.CreateAdd(gasLeft, gasKept); - _runtimeManager.setGas(gas); - - llvm::Value* addr = m_builder.CreateLoad(pAddr); - addr = Endianness::toNative(m_builder, addr); - addr = m_builder.CreateZExt(addr, Type::Word); - addr = m_builder.CreateSelect(ret, addr, Constant::get(0)); - stack.push(addr); - break; - } - - case Instruction::CALL: - case Instruction::CALLCODE: - case Instruction::DELEGATECALL: - case Instruction::STATICCALL: - { - // Handle invalid instructions. - if (inst == Instruction::DELEGATECALL && m_rev < EVMC_HOMESTEAD) - goto invalidInstruction; - - if (inst == Instruction::STATICCALL && m_rev < EVMC_BYZANTIUM) - goto invalidInstruction; - - auto callGas = stack.pop(); - auto address = stack.pop(); - bool hasValue = inst == Instruction::CALL || inst == Instruction::CALLCODE; - auto value = hasValue ? stack.pop() : Constant::get(0); - - auto inOff = stack.pop(); - auto inSize = stack.pop(); - auto outOff = stack.pop(); - auto outSize = stack.pop(); - - _gasMeter.commitCostBlock(); - - // Require memory for in and out buffers - _memory.require(outOff, outSize); // Out buffer first as we guess - // it will be after the in one - _memory.require(inOff, inSize); - - auto noTransfer = m_builder.CreateICmpEQ(value, Constant::get(0)); - - // For static call mode, select infinite penalty for CALL with - // value transfer. - auto const transferGas = (inst == Instruction::CALL && m_staticCall) ? - std::numeric_limits::max() : - JITSchedule::valueTransferGas::value; - - auto transferCost = m_builder.CreateSelect( - noTransfer, m_builder.getInt64(0), - m_builder.getInt64(transferGas)); - _gasMeter.count(transferCost, _runtimeManager.getJmpBuf(), - _runtimeManager.getGasPtr()); - - if (inst == Instruction::CALL) - { - auto accountExists = _ext.exists(address); - auto noPenaltyCond = accountExists; - if (m_rev >= EVMC_SPURIOUS_DRAGON) - noPenaltyCond = m_builder.CreateOr(accountExists, noTransfer); - auto penalty = m_builder.CreateSelect(noPenaltyCond, - m_builder.getInt64(0), - m_builder.getInt64(JITSchedule::callNewAccount::value)); - _gasMeter.count(penalty, _runtimeManager.getJmpBuf(), - _runtimeManager.getGasPtr()); - } - - if (m_rev >= EVMC_TANGERINE_WHISTLE) - { - auto gas = _runtimeManager.getGas(); - auto gas64th = m_builder.CreateLShr(gas, 6); - auto gasMaxAllowed = m_builder.CreateZExt( - m_builder.CreateSub(gas, gas64th, "gas.maxallowed", - true, true), Type::Word); - auto cmp = m_builder.CreateICmpUGT(callGas, gasMaxAllowed); - callGas = m_builder.CreateSelect(cmp, gasMaxAllowed, callGas); - } - - _gasMeter.count(callGas, _runtimeManager.getJmpBuf(), - _runtimeManager.getGasPtr()); - auto stipend = m_builder.CreateSelect( - noTransfer, m_builder.getInt64(0), - m_builder.getInt64(JITSchedule::callStipend::value)); - auto gas = m_builder.CreateTrunc(callGas, Type::Gas, "call.gas.declared"); - gas = m_builder.CreateAdd(gas, stipend, "call.gas", true, true); - int kind = [inst]() -> int - { - switch (inst) - { - case Instruction::CALL: return EVMC_CALL; - case Instruction::CALLCODE: return EVMC_CALLCODE; - case Instruction::DELEGATECALL: return EVMC_DELEGATECALL; - case Instruction::STATICCALL: return EVM_STATICCALL; - default: LLVM_BUILTIN_UNREACHABLE; - } - }(); - auto r = _ext.call(kind, gas, address, value, inOff, inSize, outOff, - outSize); - auto ret = - m_builder.CreateICmpSGE(r, m_builder.getInt64(0), "call.ret"); - auto rmagic = m_builder.CreateSelect( - ret, m_builder.getInt64(0), m_builder.getInt64(EVM_CALL_FAILURE), - "call.rmagic"); - // TODO: optimize - auto finalGas = m_builder.CreateSub(r, rmagic, "call.finalgas"); - _gasMeter.giveBack(finalGas); - stack.push(m_builder.CreateZExt(ret, Type::Word)); - break; - } - - case Instruction::RETURN: - case Instruction::REVERT: - { - auto const isRevert = inst == Instruction::REVERT; - if (isRevert && m_rev < EVMC_BYZANTIUM) - goto invalidInstruction; - - auto index = stack.pop(); - auto size = stack.pop(); - - _memory.require(index, size); - _runtimeManager.registerReturnData(index, size); - - _runtimeManager.exit(isRevert ? ReturnCode::Revert : ReturnCode::Return); - break; - } - - case Instruction::SUICIDE: - { - if (m_staticCall) - goto invalidInstruction; - - auto dest = stack.pop(); - if (m_rev >= EVMC_TANGERINE_WHISTLE) - { - auto destExists = _ext.exists(dest); - auto noPenaltyCond = destExists; - if (m_rev >= EVMC_SPURIOUS_DRAGON) - { - auto addr = Endianness::toNative(m_builder, _runtimeManager.getAddress()); - auto balance = _ext.balance(addr); - auto noTransfer = m_builder.CreateICmpEQ(balance, - Constant::get(0)); - noPenaltyCond = m_builder.CreateOr(destExists, noTransfer); - } - auto penalty = m_builder.CreateSelect( - noPenaltyCond, m_builder.getInt64(0), - m_builder.getInt64(JITSchedule::callNewAccount::value)); - _gasMeter.count(penalty, _runtimeManager.getJmpBuf(), - _runtimeManager.getGasPtr()); - } - _ext.selfdestruct(dest); - } - FALLTHROUGH; - case Instruction::STOP: - _runtimeManager.exit(ReturnCode::Stop); - break; - - case Instruction::LOG0: - case Instruction::LOG1: - case Instruction::LOG2: - case Instruction::LOG3: - case Instruction::LOG4: - { - if (m_staticCall) - goto invalidInstruction; - - auto beginIdx = stack.pop(); - auto numBytes = stack.pop(); - _memory.require(beginIdx, numBytes); - - // This will commit the current cost block - _gasMeter.countLogData(numBytes); - - llvm::SmallVector topics; - auto numTopics = static_cast(inst) - static_cast(Instruction::LOG0); - for (size_t i = 0; i < numTopics; ++i) - topics.emplace_back(stack.pop()); - - _ext.log(beginIdx, numBytes, topics); - break; - } - - invalidInstruction: - default: // Invalid instruction - abort - _runtimeManager.exit(ReturnCode::OutOfGas); - it = _basicBlock.end() - 1; // finish block compilation - } - } - - _gasMeter.commitCostBlock(); - - stack.finalize(); + m_builder.SetInsertPoint(_basicBlock.llvm()); + LocalStack stack{m_builder, _runtimeManager}; + + for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it) + { + auto inst = Instruction(*it); + + _gasMeter.count(inst); + + switch (inst) + { + case Instruction::ADD: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto result = m_builder.CreateAdd(lhs, rhs); + stack.push(result); + break; + } + + case Instruction::SUB: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto result = m_builder.CreateSub(lhs, rhs); + stack.push(result); + break; + } + + case Instruction::MUL: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = m_builder.CreateMul(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::DIV: + { + auto d = stack.pop(); + auto n = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware + // signal + auto r = m_builder.CreateUDiv(d, n); + r = m_builder.CreateSelect(divByZero, Constant::get(0), r); + stack.push(r); + break; + } + + case Instruction::SDIV: + { + auto d = stack.pop(); + auto n = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1)); + n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware + // signal + auto r = m_builder.CreateSDiv(d, n); + r = m_builder.CreateSelect(divByZero, Constant::get(0), r); + auto dNeg = m_builder.CreateSub(Constant::get(0), d); + r = m_builder.CreateSelect(divByMinusOne, dNeg, r); // protect against undef i256.min / + // -1 + stack.push(r); + break; + } + + case Instruction::MOD: + { + auto d = stack.pop(); + auto n = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware + // signal + auto r = m_builder.CreateURem(d, n); + r = m_builder.CreateSelect(divByZero, Constant::get(0), r); + stack.push(r); + break; + } + + case Instruction::SMOD: + { + auto d = stack.pop(); + auto n = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1)); + n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware + // signal + auto r = m_builder.CreateSRem(d, n); + r = m_builder.CreateSelect(divByZero, Constant::get(0), r); + r = m_builder.CreateSelect(divByMinusOne, Constant::get(0), r); // protect against + // undef i256.min / -1 + stack.push(r); + break; + } + + case Instruction::ADDMOD: + { + auto i512Ty = m_builder.getIntNTy(512); + auto a = stack.pop(); + auto b = stack.pop(); + auto m = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0)); + a = m_builder.CreateZExt(a, i512Ty); + b = m_builder.CreateZExt(b, i512Ty); + m = m_builder.CreateZExt(m, i512Ty); + auto s = m_builder.CreateNUWAdd(a, b); + s = m_builder.CreateURem(s, m); + s = m_builder.CreateTrunc(s, Type::Word); + s = m_builder.CreateSelect(divByZero, Constant::get(0), s); + stack.push(s); + break; + } + + case Instruction::MULMOD: + { + auto i512Ty = m_builder.getIntNTy(512); + auto a = stack.pop(); + auto b = stack.pop(); + auto m = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0)); + a = m_builder.CreateZExt(a, i512Ty); + b = m_builder.CreateZExt(b, i512Ty); + m = m_builder.CreateZExt(m, i512Ty); + auto p = m_builder.CreateNUWMul(a, b); + p = m_builder.CreateURem(p, m); + p = m_builder.CreateTrunc(p, Type::Word); + p = m_builder.CreateSelect(divByZero, Constant::get(0), p); + stack.push(p); + break; + } + + case Instruction::EXP: + { + auto base = stack.pop(); + auto exponent = stack.pop(); + _gasMeter.countExp(exponent); + auto ret = _arith.exp(base, exponent); + stack.push(ret); + break; + } + + case Instruction::NOT: + { + auto value = stack.pop(); + auto ret = m_builder.CreateXor(value, Constant::get(-1), "bnot"); + stack.push(ret); + break; + } + + case Instruction::LT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpULT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::GT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpUGT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::SLT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpSLT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::SGT: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpSGT(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::EQ: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res1 = m_builder.CreateICmpEQ(lhs, rhs); + auto res256 = m_builder.CreateZExt(res1, Type::Word); + stack.push(res256); + break; + } + + case Instruction::ISZERO: + { + auto top = stack.pop(); + auto iszero = m_builder.CreateICmpEQ(top, Constant::get(0), "iszero"); + auto result = m_builder.CreateZExt(iszero, Type::Word); + stack.push(result); + break; + } + + case Instruction::AND: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = m_builder.CreateAnd(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::OR: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = m_builder.CreateOr(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::XOR: + { + auto lhs = stack.pop(); + auto rhs = stack.pop(); + auto res = m_builder.CreateXor(lhs, rhs); + stack.push(res); + break; + } + + case Instruction::BYTE: + { + const auto idx = stack.pop(); + auto value = Endianness::toBE(m_builder, stack.pop()); + + auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid"); + auto bytes = + m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); + // TODO: Workaround for LLVM bug. Using big value of index causes invalid memory access. + auto safeIdx = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5)); + // TODO: Workaround for LLVM bug. DAG Builder used sext on index instead of zext + safeIdx = m_builder.CreateZExt(safeIdx, Type::Size); + auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte"); + value = m_builder.CreateZExt(byte, Type::Word); + value = m_builder.CreateSelect(idxValid, value, Constant::get(0)); + stack.push(value); + break; + } + + case Instruction::SIGNEXTEND: + { + auto idx = stack.pop(); + auto word = stack.pop(); + + auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32"); + auto k32 = m_builder.CreateZExt(k32_, Type::Size); + auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8"); + + // test for word >> (k * 8 + 7) + auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos"); + auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word); + auto bitval = m_builder.CreateLShr(word, bitposEx, "bitval"); + auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest"); + + auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx); + auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); + + auto negmask = m_builder.CreateXor( + mask, llvm::ConstantInt::getAllOnesValue(Type::Word), "negmask"); + auto val1 = m_builder.CreateOr(word, negmask); + auto val0 = m_builder.CreateAnd(word, mask); + + auto kInRange = m_builder.CreateICmpULE(idx, llvm::ConstantInt::get(Type::Word, 30)); + auto result = + m_builder.CreateSelect(kInRange, m_builder.CreateSelect(bittest, val1, val0), word); + stack.push(result); + break; + } + + case Instruction::SHA3: + { + auto inOff = stack.pop(); + auto inSize = stack.pop(); + _memory.require(inOff, inSize); + _gasMeter.countSha3Data(inSize); + auto hash = _ext.sha3(inOff, inSize); + stack.push(hash); + break; + } + + case Instruction::POP: + { + stack.pop(); + break; + } + + case Instruction::ANY_PUSH: + { + auto value = readPushData(it, _basicBlock.end()); + stack.push(Constant::get(value)); + break; + } + + case Instruction::ANY_DUP: + { + auto index = static_cast(inst) - static_cast(Instruction::DUP1); + stack.dup(index); + break; + } + + case Instruction::ANY_SWAP: + { + auto index = static_cast(inst) - static_cast(Instruction::SWAP1) + 1; + stack.swap(index); + break; + } + + case Instruction::MLOAD: + { + auto addr = stack.pop(); + auto word = _memory.loadWord(addr); + stack.push(word); + break; + } + + case Instruction::MSTORE: + { + auto addr = stack.pop(); + auto word = stack.pop(); + _memory.storeWord(addr, word); + break; + } + + case Instruction::MSTORE8: + { + auto addr = stack.pop(); + auto word = stack.pop(); + _memory.storeByte(addr, word); + break; + } + + case Instruction::MSIZE: + { + auto word = _memory.getSize(); + stack.push(word); + break; + } + + case Instruction::SLOAD: + { + auto index = stack.pop(); + auto value = _ext.sload(index); + stack.push(value); + break; + } + + case Instruction::SSTORE: + { + if (m_staticCall) + goto invalidInstruction; + + auto index = stack.pop(); + auto value = stack.pop(); + _gasMeter.countSStore(_ext, index, value); + _ext.sstore(index, value); + break; + } + + case Instruction::JUMP: + case Instruction::JUMPI: + { + auto destIdx = + llvm::MDNode::get(m_builder.getContext(), llvm::ValueAsMetadata::get(stack.pop())); + + // Create branch instruction, initially to jump table. + // Destination will be optimized with direct jump during jump resolving if destination + // index is a constant. + auto jumpInst = (inst == Instruction::JUMP) ? + m_builder.CreateBr(m_jumpTableBB) : + m_builder.CreateCondBr(m_builder.CreateICmpNE(stack.pop(), + Constant::get(0), "jump.check"), + m_jumpTableBB, nullptr); + + // Attach medatada to branch instruction with information about destination index. + jumpInst->setMetadata(c_destIdxLabel, destIdx); + break; + } + + case Instruction::JUMPDEST: + { + // Add the basic block to the jump table. + assert(it == _basicBlock.begin() && + "JUMPDEST must be the first instruction of a basic block"); + auto jumpTable = llvm::cast(m_jumpTableBB->getTerminator()); + jumpTable->addCase(Constant::get(_basicBlock.firstInstrIdx()), _basicBlock.llvm()); + break; + } + + case Instruction::PC: + { + auto value = Constant::get(it - _basicBlock.begin() + _basicBlock.firstInstrIdx()); + stack.push(value); + break; + } + + case Instruction::GAS: + { + _gasMeter.commitCostBlock(); + stack.push(m_builder.CreateZExt(_runtimeManager.getGas(), Type::Word)); + break; + } + + case Instruction::ADDRESS: + stack.push(Endianness::toNative(m_builder, _runtimeManager.getAddress())); + break; + case Instruction::CALLER: + stack.push(Endianness::toNative(m_builder, _runtimeManager.getSender())); + break; + case Instruction::ORIGIN: + stack.push(m_builder.CreateZExt( + Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(1)), Type::Word)); + break; + case Instruction::COINBASE: + stack.push(m_builder.CreateZExt( + Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(2)), Type::Word)); + break; + + case Instruction::GASPRICE: + stack.push(Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(0))); + break; + + case Instruction::DIFFICULTY: + stack.push(Endianness::toNative(m_builder, _runtimeManager.getTxContextItem(6))); + break; + + case Instruction::GASLIMIT: + stack.push(m_builder.CreateZExt(_runtimeManager.getTxContextItem(5), Type::Word)); + break; + + case Instruction::NUMBER: + stack.push(m_builder.CreateZExt(_runtimeManager.getTxContextItem(3), Type::Word)); + break; + + case Instruction::TIMESTAMP: + stack.push(m_builder.CreateZExt(_runtimeManager.getTxContextItem(4), Type::Word)); + break; + + case Instruction::CALLVALUE: + { + auto beValue = _runtimeManager.getValue(); + stack.push(Endianness::toNative(m_builder, beValue)); + break; + } + + case Instruction::CODESIZE: + stack.push(_runtimeManager.getCodeSize()); + break; + + case Instruction::CALLDATASIZE: + stack.push(_runtimeManager.getCallDataSize()); + break; + + case Instruction::RETURNDATASIZE: + { + if (m_rev < EVMC_BYZANTIUM) + goto invalidInstruction; + + auto returnBufSizePtr = _runtimeManager.getReturnBufSizePtr(); + auto returnBufSize = m_builder.CreateLoad(returnBufSizePtr); + stack.push(m_builder.CreateZExt(returnBufSize, Type::Word)); + break; + } + + case Instruction::BLOCKHASH: + { + auto number = stack.pop(); + // If number bigger than int64 assume the result is 0. + auto limitC = m_builder.getInt64(std::numeric_limits::max()); + auto limit = m_builder.CreateZExt(limitC, Type::Word); + auto isBigNumber = m_builder.CreateICmpUGT(number, limit); + auto hash = _ext.blockHash(number); + // TODO: Try to eliminate the call if the number is invalid. + hash = m_builder.CreateSelect(isBigNumber, Constant::get(0), hash); + stack.push(hash); + break; + } + + case Instruction::BALANCE: + { + auto address = stack.pop(); + auto value = _ext.balance(address); + stack.push(value); + break; + } + + case Instruction::EXTCODESIZE: + { + auto addr = stack.pop(); + auto codesize = _ext.extcodesize(addr); + stack.push(codesize); + break; + } + + case Instruction::CALLDATACOPY: + { + auto destMemIdx = stack.pop(); + auto srcIdx = stack.pop(); + auto reqBytes = stack.pop(); + + auto srcPtr = _runtimeManager.getCallData(); + auto srcSize = _runtimeManager.getCallDataSize(); + + _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); + break; + } + + case Instruction::RETURNDATACOPY: + { + if (m_rev < EVMC_BYZANTIUM) + goto invalidInstruction; + + auto destMemIdx = stack.pop(); + auto srcIdx = stack.pop(); + auto reqBytes = stack.pop(); + + auto srcPtr = m_builder.CreateLoad(_runtimeManager.getReturnBufDataPtr()); + auto srcSize = m_builder.CreateLoad(_runtimeManager.getReturnBufSizePtr()); + + _memory.copyBytesNoPadding(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); + break; + } + + case Instruction::CODECOPY: + { + auto destMemIdx = stack.pop(); + auto srcIdx = stack.pop(); + auto reqBytes = stack.pop(); + + auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, + // feature #80814234 + auto srcSize = _runtimeManager.getCodeSize(); + + _memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); + break; + } + + case Instruction::EXTCODECOPY: + { + auto addr = stack.pop(); + auto destMemIdx = stack.pop(); + auto srcIdx = stack.pop(); + auto reqBytes = stack.pop(); + + auto codeRef = _ext.extcode(addr); + + _memory.copyBytes(codeRef.ptr, codeRef.size, srcIdx, destMemIdx, reqBytes); + break; + } + + case Instruction::CALLDATALOAD: + { + auto idx = stack.pop(); + auto value = _ext.calldataload(idx); + stack.push(value); + break; + } + + case Instruction::CREATE: + { + if (m_staticCall) + goto invalidInstruction; + + auto endowment = stack.pop(); + auto initOff = stack.pop(); + auto initSize = stack.pop(); + _memory.require(initOff, initSize); + + _gasMeter.commitCostBlock(); + auto gas = _runtimeManager.getGas(); + llvm::Value* gasKept = (m_rev >= EVMC_TANGERINE_WHISTLE) ? + m_builder.CreateLShr(gas, 6) : + m_builder.getInt64(0); + auto createGas = m_builder.CreateSub(gas, gasKept, "create.gas", true, true); + llvm::Value* r = nullptr; + llvm::Value* pAddr = nullptr; + std::tie(r, pAddr) = _ext.create(createGas, endowment, initOff, initSize); + + auto ret = m_builder.CreateICmpSGE(r, m_builder.getInt64(0), "create.ret"); + auto rmagic = m_builder.CreateSelect( + ret, m_builder.getInt64(0), m_builder.getInt64(EVM_CALL_FAILURE), "call.rmagic"); + // TODO: optimize + auto gasLeft = m_builder.CreateSub(r, rmagic, "create.gasleft"); + gas = m_builder.CreateAdd(gasLeft, gasKept); + _runtimeManager.setGas(gas); + + llvm::Value* addr = m_builder.CreateLoad(pAddr); + addr = Endianness::toNative(m_builder, addr); + addr = m_builder.CreateZExt(addr, Type::Word); + addr = m_builder.CreateSelect(ret, addr, Constant::get(0)); + stack.push(addr); + break; + } + + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::DELEGATECALL: + case Instruction::STATICCALL: + { + // Handle invalid instructions. + if (inst == Instruction::DELEGATECALL && m_rev < EVMC_HOMESTEAD) + goto invalidInstruction; + + if (inst == Instruction::STATICCALL && m_rev < EVMC_BYZANTIUM) + goto invalidInstruction; + + auto callGas = stack.pop(); + auto address = stack.pop(); + bool hasValue = inst == Instruction::CALL || inst == Instruction::CALLCODE; + auto value = hasValue ? stack.pop() : Constant::get(0); + + auto inOff = stack.pop(); + auto inSize = stack.pop(); + auto outOff = stack.pop(); + auto outSize = stack.pop(); + + _gasMeter.commitCostBlock(); + + // Require memory for in and out buffers + _memory.require(outOff, outSize); // Out buffer first as we guess + // it will be after the in one + _memory.require(inOff, inSize); + + auto noTransfer = m_builder.CreateICmpEQ(value, Constant::get(0)); + + // For static call mode, select infinite penalty for CALL with + // value transfer. + auto const transferGas = (inst == Instruction::CALL && m_staticCall) ? + std::numeric_limits::max() : + JITSchedule::valueTransferGas::value; + + auto transferCost = m_builder.CreateSelect( + noTransfer, m_builder.getInt64(0), m_builder.getInt64(transferGas)); + _gasMeter.count(transferCost, _runtimeManager.getJmpBuf(), _runtimeManager.getGasPtr()); + + if (inst == Instruction::CALL) + { + auto accountExists = _ext.exists(address); + auto noPenaltyCond = accountExists; + if (m_rev >= EVMC_SPURIOUS_DRAGON) + noPenaltyCond = m_builder.CreateOr(accountExists, noTransfer); + auto penalty = m_builder.CreateSelect(noPenaltyCond, m_builder.getInt64(0), + m_builder.getInt64(JITSchedule::callNewAccount::value)); + _gasMeter.count(penalty, _runtimeManager.getJmpBuf(), _runtimeManager.getGasPtr()); + } + + if (m_rev >= EVMC_TANGERINE_WHISTLE) + { + auto gas = _runtimeManager.getGas(); + auto gas64th = m_builder.CreateLShr(gas, 6); + auto gasMaxAllowed = m_builder.CreateZExt( + m_builder.CreateSub(gas, gas64th, "gas.maxallowed", true, true), Type::Word); + auto cmp = m_builder.CreateICmpUGT(callGas, gasMaxAllowed); + callGas = m_builder.CreateSelect(cmp, gasMaxAllowed, callGas); + } + + _gasMeter.count(callGas, _runtimeManager.getJmpBuf(), _runtimeManager.getGasPtr()); + auto stipend = m_builder.CreateSelect(noTransfer, m_builder.getInt64(0), + m_builder.getInt64(JITSchedule::callStipend::value)); + auto gas = m_builder.CreateTrunc(callGas, Type::Gas, "call.gas.declared"); + gas = m_builder.CreateAdd(gas, stipend, "call.gas", true, true); + int kind = [inst]() -> int { + switch (inst) + { + case Instruction::CALL: + return EVMC_CALL; + case Instruction::CALLCODE: + return EVMC_CALLCODE; + case Instruction::DELEGATECALL: + return EVMC_DELEGATECALL; + case Instruction::STATICCALL: + return EVM_STATICCALL; + default: + LLVM_BUILTIN_UNREACHABLE; + } + }(); + auto r = _ext.call(kind, gas, address, value, inOff, inSize, outOff, outSize); + auto ret = m_builder.CreateICmpSGE(r, m_builder.getInt64(0), "call.ret"); + auto rmagic = m_builder.CreateSelect( + ret, m_builder.getInt64(0), m_builder.getInt64(EVM_CALL_FAILURE), "call.rmagic"); + // TODO: optimize + auto finalGas = m_builder.CreateSub(r, rmagic, "call.finalgas"); + _gasMeter.giveBack(finalGas); + stack.push(m_builder.CreateZExt(ret, Type::Word)); + break; + } + + case Instruction::RETURN: + case Instruction::REVERT: + { + auto const isRevert = inst == Instruction::REVERT; + if (isRevert && m_rev < EVMC_BYZANTIUM) + goto invalidInstruction; + + auto index = stack.pop(); + auto size = stack.pop(); + + _memory.require(index, size); + _runtimeManager.registerReturnData(index, size); + + _runtimeManager.exit(isRevert ? ReturnCode::Revert : ReturnCode::Return); + break; + } + + case Instruction::SUICIDE: + { + if (m_staticCall) + goto invalidInstruction; + + auto dest = stack.pop(); + if (m_rev >= EVMC_TANGERINE_WHISTLE) + { + auto destExists = _ext.exists(dest); + auto noPenaltyCond = destExists; + if (m_rev >= EVMC_SPURIOUS_DRAGON) + { + auto addr = Endianness::toNative(m_builder, _runtimeManager.getAddress()); + auto balance = _ext.balance(addr); + auto noTransfer = m_builder.CreateICmpEQ(balance, Constant::get(0)); + noPenaltyCond = m_builder.CreateOr(destExists, noTransfer); + } + auto penalty = m_builder.CreateSelect(noPenaltyCond, m_builder.getInt64(0), + m_builder.getInt64(JITSchedule::callNewAccount::value)); + _gasMeter.count(penalty, _runtimeManager.getJmpBuf(), _runtimeManager.getGasPtr()); + } + _ext.selfdestruct(dest); + } + FALLTHROUGH; + case Instruction::STOP: + _runtimeManager.exit(ReturnCode::Stop); + break; + + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + { + if (m_staticCall) + goto invalidInstruction; + + auto beginIdx = stack.pop(); + auto numBytes = stack.pop(); + _memory.require(beginIdx, numBytes); + + // This will commit the current cost block + _gasMeter.countLogData(numBytes); + + llvm::SmallVector topics; + auto numTopics = static_cast(inst) - static_cast(Instruction::LOG0); + for (size_t i = 0; i < numTopics; ++i) + topics.emplace_back(stack.pop()); + + _ext.log(beginIdx, numBytes, topics); + break; + } + + invalidInstruction: + default: // Invalid instruction - abort + _runtimeManager.exit(ReturnCode::OutOfGas); + it = _basicBlock.end() - 1; // finish block compilation + } + } + + _gasMeter.commitCostBlock(); + + stack.finalize(); } -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Compiler.h b/libevmjit/Compiler.h index b0e20989..2ec241ad 100644 --- a/libevmjit/Compiler.h +++ b/libevmjit/Compiler.h @@ -1,7 +1,7 @@ #pragma once -#include "JIT.h" #include "BasicBlock.h" +#include "JIT.h" namespace dev { @@ -9,50 +9,50 @@ namespace eth { namespace jit { - class Compiler { public: + struct Options + { + /// Rewrite switch instructions to sequences of branches + bool rewriteSwitchToBranches = true; - struct Options - { - /// Rewrite switch instructions to sequences of branches - bool rewriteSwitchToBranches = true; + /// Dump CFG as a .dot file for graphviz + bool dumpCFG = false; + }; - /// Dump CFG as a .dot file for graphviz - bool dumpCFG = false; - }; + Compiler(Options const& _options, evmc_revision _rev, bool _staticCall, + llvm::LLVMContext& _llvmContext); - Compiler(Options const& _options, evmc_revision _rev, bool _staticCall, llvm::LLVMContext& _llvmContext); - - std::unique_ptr compile(code_iterator _begin, code_iterator _end, std::string const& _id); + std::unique_ptr compile( + code_iterator _begin, code_iterator _end, std::string const& _id); private: + std::vector createBasicBlocks(code_iterator _begin, code_iterator _end); - std::vector createBasicBlocks(code_iterator _begin, code_iterator _end); - - void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter); + void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, + class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter); - void resolveJumps(); + void resolveJumps(); - /// Compiler options - Options const& m_options; + /// Compiler options + Options const& m_options; - /// EVM revision. - evmc_revision m_rev; + /// EVM revision. + evmc_revision m_rev; - bool const m_staticCall = false; + bool const m_staticCall = false; - /// Helper class for generating IR - IRBuilder m_builder; + /// Helper class for generating IR + IRBuilder m_builder; - /// Block with a jump table. - llvm::BasicBlock* m_jumpTableBB = nullptr; + /// Block with a jump table. + llvm::BasicBlock* m_jumpTableBB = nullptr; - /// Main program function - llvm::Function* m_mainFunc = nullptr; + /// Main program function + llvm::Function* m_mainFunc = nullptr; }; -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/CompilerHelper.cpp b/libevmjit/CompilerHelper.cpp index cc01580a..2aabc3a1 100644 --- a/libevmjit/CompilerHelper.cpp +++ b/libevmjit/CompilerHelper.cpp @@ -1,8 +1,8 @@ #include "CompilerHelper.h" +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include -#include "preprocessor/llvm_includes_end.h" #include "RuntimeManager.h" @@ -12,35 +12,32 @@ namespace eth { namespace jit { - -CompilerHelper::CompilerHelper(IRBuilder& _builder) : - m_builder(_builder) -{} +CompilerHelper::CompilerHelper(IRBuilder& _builder) : m_builder(_builder) {} llvm::Module* CompilerHelper::getModule() { - assert(m_builder.GetInsertBlock()); - assert(m_builder.GetInsertBlock()->getParent()); // BB must be in a function - return m_builder.GetInsertBlock()->getParent()->getParent(); + assert(m_builder.GetInsertBlock()); + assert(m_builder.GetInsertBlock()->getParent()); // BB must be in a function + return m_builder.GetInsertBlock()->getParent()->getParent(); } llvm::Function* CompilerHelper::getMainFunction() { - // TODO: Rename or change semantics of getMainFunction() function - assert(m_builder.GetInsertBlock()); - auto mainFunc = m_builder.GetInsertBlock()->getParent(); - assert(mainFunc); - if (mainFunc == &mainFunc->getParent()->getFunctionList().front()) // Main function is the first one in module - return mainFunc; - return nullptr; + // TODO: Rename or change semantics of getMainFunction() function + assert(m_builder.GetInsertBlock()); + auto mainFunc = m_builder.GetInsertBlock()->getParent(); + assert(mainFunc); + if (mainFunc == &mainFunc->getParent()->getFunctionList().front()) // Main function is the + // first one in module + return mainFunc; + return nullptr; } -RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager): - CompilerHelper(_runtimeManager.m_builder), - m_runtimeManager(_runtimeManager) +RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager) + : CompilerHelper(_runtimeManager.m_builder), m_runtimeManager(_runtimeManager) {} -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/CompilerHelper.h b/libevmjit/CompilerHelper.h index a730f7a3..82a6d81d 100644 --- a/libevmjit/CompilerHelper.h +++ b/libevmjit/CompilerHelper.h @@ -1,8 +1,8 @@ #pragma once +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include -#include "preprocessor/llvm_includes_end.h" namespace dev @@ -19,45 +19,47 @@ using IRBuilder = llvm::IRBuilder<>; class CompilerHelper { protected: - CompilerHelper(IRBuilder& _builder); + CompilerHelper(IRBuilder& _builder); - CompilerHelper(const CompilerHelper&) = delete; - CompilerHelper& operator=(CompilerHelper) = delete; + CompilerHelper(const CompilerHelper&) = delete; + CompilerHelper& operator=(CompilerHelper) = delete; - /// Reference to the IR module being compiled - llvm::Module* getModule(); + /// Reference to the IR module being compiled + llvm::Module* getModule(); - /// Reference to the main module function - llvm::Function* getMainFunction(); + /// Reference to the main module function + llvm::Function* getMainFunction(); - /// Reference to parent compiler IR builder - IRBuilder& m_builder; + /// Reference to parent compiler IR builder + IRBuilder& m_builder; - friend class RuntimeHelper; + friend class RuntimeHelper; }; /// Compiler helper that depends on runtime data class RuntimeHelper : public CompilerHelper { protected: - RuntimeHelper(RuntimeManager& _runtimeManager); + RuntimeHelper(RuntimeManager& _runtimeManager); - RuntimeManager& getRuntimeManager() { return m_runtimeManager; } + RuntimeManager& getRuntimeManager() { return m_runtimeManager; } private: - RuntimeManager& m_runtimeManager; + RuntimeManager& m_runtimeManager; }; struct InsertPointGuard { - explicit InsertPointGuard(llvm::IRBuilderBase& _builder): m_builder(_builder), m_insertPoint(_builder.saveIP()) {} - ~InsertPointGuard() { m_builder.restoreIP(m_insertPoint); } + explicit InsertPointGuard(llvm::IRBuilderBase& _builder) + : m_builder(_builder), m_insertPoint(_builder.saveIP()) + {} + ~InsertPointGuard() { m_builder.restoreIP(m_insertPoint); } private: - llvm::IRBuilderBase& m_builder; - llvm::IRBuilderBase::InsertPoint m_insertPoint; + llvm::IRBuilderBase& m_builder; + llvm::IRBuilderBase::InsertPoint m_insertPoint; }; -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Endianness.cpp b/libevmjit/Endianness.cpp index e1d28454..1f0a7ef6 100644 --- a/libevmjit/Endianness.cpp +++ b/libevmjit/Endianness.cpp @@ -1,9 +1,9 @@ #include "Endianness.h" +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include #include -#include "preprocessor/llvm_includes_end.h" #include "Type.h" @@ -13,21 +13,22 @@ namespace eth { namespace jit { - llvm::Value* Endianness::bswapIfLE(IRBuilder& _builder, llvm::Value* _value) { - if (llvm::sys::IsLittleEndianHost) - { - if (auto constant = llvm::dyn_cast(_value)) - return _builder.getInt(constant->getValue().byteSwap()); + if (llvm::sys::IsLittleEndianHost) + { + if (auto constant = llvm::dyn_cast(_value)) + return _builder.getInt(constant->getValue().byteSwap()); - // OPT: Cache func declaration? - auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, _value->getType()); - return _builder.CreateCall(bswapFunc, _value); - } - return _value; + // OPT: Cache func declaration? + auto bswapFunc = + llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), + llvm::Intrinsic::bswap, _value->getType()); + return _builder.CreateCall(bswapFunc, _value); + } + return _value; } -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Endianness.h b/libevmjit/Endianness.h index 285d7797..a3e3c87e 100644 --- a/libevmjit/Endianness.h +++ b/libevmjit/Endianness.h @@ -8,16 +8,21 @@ namespace eth { namespace jit { - struct Endianness { - static llvm::Value* toBE(IRBuilder& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } - static llvm::Value* toNative(IRBuilder& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } + static llvm::Value* toBE(IRBuilder& _builder, llvm::Value* _word) + { + return bswapIfLE(_builder, _word); + } + static llvm::Value* toNative(IRBuilder& _builder, llvm::Value* _word) + { + return bswapIfLE(_builder, _word); + } private: - static llvm::Value* bswapIfLE(IRBuilder& _builder, llvm::Value* _word); + static llvm::Value* bswapIfLE(IRBuilder& _builder, llvm::Value* _word); }; -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/ExecStats.cpp b/libevmjit/ExecStats.cpp index c7f6ef0c..d6d69e60 100644 --- a/libevmjit/ExecStats.cpp +++ b/libevmjit/ExecStats.cpp @@ -1,8 +1,8 @@ #include "ExecStats.h" -#include -#include #include +#include +#include #include "Utils.h" @@ -10,86 +10,92 @@ namespace dev { namespace evmjit { - void ExecStats::stateChanged(ExecState _state) { - if (!CHECK(m_state != ExecState::Finished)) - return; + if (!CHECK(m_state != ExecState::Finished)) + return; - auto now = clock::now(); - if (_state != ExecState::Started) - { - assert(time[(int)m_state] == ExecStats::duration::zero()); - time[(int)m_state] = now - m_tp; - } - m_state = _state; - m_tp = now; + auto now = clock::now(); + if (_state != ExecState::Started) + { + assert(time[(int)m_state] == ExecStats::duration::zero()); + time[(int)m_state] = now - m_tp; + } + m_state = _state; + m_tp = now; } namespace { struct StatsAgg { - using unit = std::chrono::microseconds; - ExecStats::duration tot = ExecStats::duration::zero(); - ExecStats::duration min = ExecStats::duration::max(); - ExecStats::duration max = ExecStats::duration::zero(); - size_t count = 0; + using unit = std::chrono::microseconds; + ExecStats::duration tot = ExecStats::duration::zero(); + ExecStats::duration min = ExecStats::duration::max(); + ExecStats::duration max = ExecStats::duration::zero(); + size_t count = 0; - void update(ExecStats::duration _d) - { - ++count; - tot += _d; - min = _d < min ? _d : min; - max = _d > max ? _d : max; - } + void update(ExecStats::duration _d) + { + ++count; + tot += _d; + min = _d < min ? _d : min; + max = _d > max ? _d : max; + } - void output(char const* _name, std::ostream& _os) - { - auto avg = tot / count; - _os << std::setfill(' ') - << std::setw(12) << std::left << _name - << std::setw(10) << std::right << std::chrono::duration_cast(tot).count() - << std::setw(10) << std::right << std::chrono::duration_cast(avg).count() - << std::setw(10) << std::right << std::chrono::duration_cast(min).count() - << std::setw(10) << std::right << std::chrono::duration_cast(max).count() - << std::endl; - } + void output(char const* _name, std::ostream& _os) + { + auto avg = tot / count; + _os << std::setfill(' ') << std::setw(12) << std::left << _name << std::setw(10) + << std::right << std::chrono::duration_cast(tot).count() << std::setw(10) + << std::right << std::chrono::duration_cast(avg).count() << std::setw(10) + << std::right << std::chrono::duration_cast(min).count() << std::setw(10) + << std::right << std::chrono::duration_cast(max).count() << std::endl; + } }; char const* getExecStateName(ExecState _state) { - switch (_state) - { - case ExecState::Started: return "Start"; - case ExecState::CacheLoad: return "CacheLoad"; - case ExecState::CacheWrite: return "CacheWrite"; - case ExecState::Compilation: return "Compilation"; - case ExecState::Optimization: return "Optimization"; - case ExecState::CodeGen: return "CodeGen"; - case ExecState::Execution: return "Execution"; - case ExecState::Return: return "Return"; - case ExecState::Finished: return "Finish"; - } - return nullptr; -} + switch (_state) + { + case ExecState::Started: + return "Start"; + case ExecState::CacheLoad: + return "CacheLoad"; + case ExecState::CacheWrite: + return "CacheWrite"; + case ExecState::Compilation: + return "Compilation"; + case ExecState::Optimization: + return "Optimization"; + case ExecState::CodeGen: + return "CodeGen"; + case ExecState::Execution: + return "Execution"; + case ExecState::Return: + return "Return"; + case ExecState::Finished: + return "Finish"; + } + return nullptr; } +} // namespace StatsCollector::~StatsCollector() { - if (stats.empty()) - return; + if (stats.empty()) + return; - std::cout << " [us] total avg min max\n"; - for (int i = 0; i < (int)ExecState::Finished; ++i) - { - StatsAgg agg; - for (auto&& s : stats) - agg.update(s->time[i]); + std::cout << " [us] total avg min max\n"; + for (int i = 0; i < (int)ExecState::Finished; ++i) + { + StatsAgg agg; + for (auto&& s : stats) + agg.update(s->time[i]); - agg.output(getExecStateName(ExecState(i)), std::cout); - } + agg.output(getExecStateName(ExecState(i)), std::cout); + } } -} -} +} // namespace evmjit +} // namespace dev diff --git a/libevmjit/ExecStats.h b/libevmjit/ExecStats.h index 4a5ae00e..3b70a8c8 100644 --- a/libevmjit/ExecStats.h +++ b/libevmjit/ExecStats.h @@ -1,68 +1,66 @@ #pragma once +#include #include -#include #include -#include +#include namespace dev { namespace evmjit { - enum class ExecState { - Started, - CacheLoad, - CacheWrite, - Compilation, - Optimization, - CodeGen, - Execution, - Return, - Finished + Started, + CacheLoad, + CacheWrite, + Compilation, + Optimization, + CodeGen, + Execution, + Return, + Finished }; class JITListener { public: - JITListener() = default; - JITListener(JITListener const&) = delete; - JITListener& operator=(JITListener) = delete; - virtual ~JITListener() {} + JITListener() = default; + JITListener(JITListener const&) = delete; + JITListener& operator=(JITListener) = delete; + virtual ~JITListener() {} - virtual void executionStarted() {} - virtual void executionEnded() {} + virtual void executionStarted() {} + virtual void executionEnded() {} - virtual void stateChanged(ExecState) {} + virtual void stateChanged(ExecState) {} }; class ExecStats : public JITListener { public: - using clock = std::chrono::high_resolution_clock; - using duration = clock::duration; - using time_point = clock::time_point; + using clock = std::chrono::high_resolution_clock; + using duration = clock::duration; + using time_point = clock::time_point; - std::string id; - duration time[(int)ExecState::Finished] = {}; + std::string id; + duration time[(int)ExecState::Finished] = {}; - void stateChanged(ExecState _state) override; + void stateChanged(ExecState _state) override; private: - ExecState m_state = {}; - time_point m_tp = {}; - + ExecState m_state = {}; + time_point m_tp = {}; }; class StatsCollector { public: - std::vector> stats; + std::vector> stats; - ~StatsCollector(); + ~StatsCollector(); }; -} -} +} // namespace evmjit +} // namespace dev diff --git a/libevmjit/Ext.cpp b/libevmjit/Ext.cpp index 81b6b6af..6ca51878 100644 --- a/libevmjit/Ext.cpp +++ b/libevmjit/Ext.cpp @@ -1,14 +1,14 @@ #include "Ext.h" +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include #include -#include "preprocessor/llvm_includes_end.h" -#include "RuntimeManager.h" +#include "Endianness.h" #include "Memory.h" +#include "RuntimeManager.h" #include "Type.h" -#include "Endianness.h" namespace dev { @@ -16,588 +16,598 @@ namespace eth { namespace jit { - -Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan) : - RuntimeHelper(_runtimeManager), - m_memoryMan(_memoryMan) +Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan) + : RuntimeHelper(_runtimeManager), m_memoryMan(_memoryMan) { - m_funcs = decltype(m_funcs)(); - m_argAllocas = decltype(m_argAllocas)(); - m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); + m_funcs = decltype(m_funcs)(); + m_argAllocas = decltype(m_argAllocas)(); + m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); } namespace { - using FuncDesc = std::tuple; -llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list const& _argsTypes) +llvm::FunctionType* getFunctionType( + llvm::Type* _returnType, std::initializer_list const& _argsTypes) { - return llvm::FunctionType::get(_returnType, llvm::ArrayRef{_argsTypes.begin(), _argsTypes.size()}, false); + return llvm::FunctionType::get( + _returnType, llvm::ArrayRef{_argsTypes.begin(), _argsTypes.size()}, false); } std::array::value> const& getEnvFuncDescs() { - static std::array::value> descs{{ - FuncDesc{"env_sload", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, - FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, - FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})}, - FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::WordPtr, Type::EnvPtr, Type::WordPtr})}, - FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})}, - FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::GasPtr, Type::Gas, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size})}, - FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})}, - FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, - FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, - }}; - - return descs; + static std::array::value> descs{{ + FuncDesc{ + "env_sload", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_sstore", + getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{ + "env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})}, + FuncDesc{"env_balance", + getFunctionType(Type::Void, {Type::WordPtr, Type::EnvPtr, Type::WordPtr})}, + FuncDesc{"env_create", + getFunctionType(Type::Void, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::BytePtr, + Type::Size, Type::WordPtr})}, + FuncDesc{"env_call", getFunctionType(Type::Bool, + {Type::EnvPtr, Type::GasPtr, Type::Gas, Type::WordPtr, + Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, + Type::BytePtr, Type::Size, Type::BytePtr, Type::Size})}, + FuncDesc{"env_log", + getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, + Type::WordPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_blockhash", + getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, + {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, + }}; + + return descs; } llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module) { - auto&& desc = getEnvFuncDescs()[static_cast(_id)]; - return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module); + auto&& desc = getEnvFuncDescs()[static_cast(_id)]; + return llvm::Function::Create( + std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module); } llvm::Function* getAccountExistsFunc(llvm::Module* _module) { - static const auto funcName = "evm.exists"; - auto func = _module->getFunction(funcName); - if (!func) - { - // TODO: Mark the function as pure to eliminate multiple calls. - auto i32 = llvm::IntegerType::getInt32Ty(_module->getContext()); - auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); - auto fty = llvm::FunctionType::get( - i32, {Type::EnvPtr, addrTy->getPointerTo()}, false); - func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); - } - return func; + static const auto funcName = "evm.exists"; + auto func = _module->getFunction(funcName); + if (!func) + { + // TODO: Mark the function as pure to eliminate multiple calls. + auto i32 = llvm::IntegerType::getInt32Ty(_module->getContext()); + auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); + auto fty = llvm::FunctionType::get(i32, {Type::EnvPtr, addrTy->getPointerTo()}, false); + func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); + } + return func; } llvm::Function* getGetStorageFunc(llvm::Module* _module) { - static const auto funcName = "evm.sload"; - auto func = _module->getFunction(funcName); - if (!func) - { - // TODO: Mark the function as pure to eliminate multiple calls. - auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); - auto fty = llvm::FunctionType::get( - Type::Void, {Type::WordPtr, Type::EnvPtr, addrTy->getPointerTo(), Type::WordPtr}, false); - func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); - func->addAttribute(1, llvm::Attribute::NoAlias); - func->addAttribute(1, llvm::Attribute::NoCapture); - func->addAttribute(3, llvm::Attribute::ReadOnly); - func->addAttribute(3, llvm::Attribute::NoAlias); - func->addAttribute(3, llvm::Attribute::NoCapture); - func->addAttribute(4, llvm::Attribute::ReadOnly); - func->addAttribute(4, llvm::Attribute::NoAlias); - func->addAttribute(4, llvm::Attribute::NoCapture); - } - return func; + static const auto funcName = "evm.sload"; + auto func = _module->getFunction(funcName); + if (!func) + { + // TODO: Mark the function as pure to eliminate multiple calls. + auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); + auto fty = llvm::FunctionType::get(Type::Void, + {Type::WordPtr, Type::EnvPtr, addrTy->getPointerTo(), Type::WordPtr}, false); + func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); + func->addAttribute(1, llvm::Attribute::NoAlias); + func->addAttribute(1, llvm::Attribute::NoCapture); + func->addAttribute(3, llvm::Attribute::ReadOnly); + func->addAttribute(3, llvm::Attribute::NoAlias); + func->addAttribute(3, llvm::Attribute::NoCapture); + func->addAttribute(4, llvm::Attribute::ReadOnly); + func->addAttribute(4, llvm::Attribute::NoAlias); + func->addAttribute(4, llvm::Attribute::NoCapture); + } + return func; } llvm::Function* getSetStorageFunc(llvm::Module* _module) { - static const auto funcName = "evm.sstore"; - auto func = _module->getFunction(funcName); - if (!func) - { - auto addrPtrTy = llvm::Type::getIntNPtrTy(_module->getContext(), 160); - auto fty = llvm::FunctionType::get(Type::Void, {Type::EnvPtr, addrPtrTy, Type::WordPtr, Type::WordPtr}, false); - func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); - func->addAttribute(2, llvm::Attribute::ReadOnly); - func->addAttribute(2, llvm::Attribute::NoAlias); - func->addAttribute(2, llvm::Attribute::NoCapture); - func->addAttribute(3, llvm::Attribute::ReadOnly); - func->addAttribute(3, llvm::Attribute::NoAlias); - func->addAttribute(3, llvm::Attribute::NoCapture); - } - return func; + static const auto funcName = "evm.sstore"; + auto func = _module->getFunction(funcName); + if (!func) + { + auto addrPtrTy = llvm::Type::getIntNPtrTy(_module->getContext(), 160); + auto fty = llvm::FunctionType::get( + Type::Void, {Type::EnvPtr, addrPtrTy, Type::WordPtr, Type::WordPtr}, false); + func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); + func->addAttribute(2, llvm::Attribute::ReadOnly); + func->addAttribute(2, llvm::Attribute::NoAlias); + func->addAttribute(2, llvm::Attribute::NoCapture); + func->addAttribute(3, llvm::Attribute::ReadOnly); + func->addAttribute(3, llvm::Attribute::NoAlias); + func->addAttribute(3, llvm::Attribute::NoCapture); + } + return func; } llvm::Function* getGetBalanceFunc(llvm::Module* _module) { - static const auto funcName = "evm.balance"; - auto func = _module->getFunction(funcName); - if (!func) - { - auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); - auto fty = llvm::FunctionType::get( - Type::Void, {Type::WordPtr, Type::EnvPtr, addrTy->getPointerTo()}, false); - func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); - func->addAttribute(1, llvm::Attribute::NoAlias); - func->addAttribute(1, llvm::Attribute::NoCapture); - func->addAttribute(3, llvm::Attribute::ReadOnly); - func->addAttribute(3, llvm::Attribute::NoAlias); - func->addAttribute(3, llvm::Attribute::NoCapture); - } - return func; + static const auto funcName = "evm.balance"; + auto func = _module->getFunction(funcName); + if (!func) + { + auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); + auto fty = llvm::FunctionType::get( + Type::Void, {Type::WordPtr, Type::EnvPtr, addrTy->getPointerTo()}, false); + func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); + func->addAttribute(1, llvm::Attribute::NoAlias); + func->addAttribute(1, llvm::Attribute::NoCapture); + func->addAttribute(3, llvm::Attribute::ReadOnly); + func->addAttribute(3, llvm::Attribute::NoAlias); + func->addAttribute(3, llvm::Attribute::NoCapture); + } + return func; } llvm::Function* getGetCodeSizeFunc(llvm::Module* _module) { - static const auto funcName = "evm.codesize"; - auto func = _module->getFunction(funcName); - if (!func) - { - auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); - auto fty = llvm::FunctionType::get( - Type::Size, {Type::EnvPtr, addrTy->getPointerTo()}, false); - func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); - func->addAttribute(2, llvm::Attribute::ReadOnly); - func->addAttribute(2, llvm::Attribute::NoAlias); - func->addAttribute(2, llvm::Attribute::NoCapture); - } - return func; + static const auto funcName = "evm.codesize"; + auto func = _module->getFunction(funcName); + if (!func) + { + auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); + auto fty = + llvm::FunctionType::get(Type::Size, {Type::EnvPtr, addrTy->getPointerTo()}, false); + func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); + func->addAttribute(2, llvm::Attribute::ReadOnly); + func->addAttribute(2, llvm::Attribute::NoAlias); + func->addAttribute(2, llvm::Attribute::NoCapture); + } + return func; } llvm::Function* getGetCodeFunc(llvm::Module* _module) { - static const auto funcName = "evm.code"; - auto func = _module->getFunction(funcName); - if (!func) - { - auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); - auto fty = llvm::FunctionType::get( - Type::Size, {Type::BytePtr->getPointerTo(), Type::EnvPtr, addrTy->getPointerTo()}, false); - func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); - func->addAttribute(1, llvm::Attribute::NoAlias); - func->addAttribute(1, llvm::Attribute::NoCapture); - func->addAttribute(3, llvm::Attribute::ReadOnly); - func->addAttribute(3, llvm::Attribute::NoAlias); - func->addAttribute(3, llvm::Attribute::NoCapture); - } - return func; + static const auto funcName = "evm.code"; + auto func = _module->getFunction(funcName); + if (!func) + { + auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); + auto fty = llvm::FunctionType::get(Type::Size, + {Type::BytePtr->getPointerTo(), Type::EnvPtr, addrTy->getPointerTo()}, false); + func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); + func->addAttribute(1, llvm::Attribute::NoAlias); + func->addAttribute(1, llvm::Attribute::NoCapture); + func->addAttribute(3, llvm::Attribute::ReadOnly); + func->addAttribute(3, llvm::Attribute::NoAlias); + func->addAttribute(3, llvm::Attribute::NoCapture); + } + return func; } llvm::Function* getSelfdestructFunc(llvm::Module* _module) { - static const auto funcName = "evm.selfdestruct"; - auto func = _module->getFunction(funcName); - if (!func) - { - auto addrPtrTy = llvm::Type::getIntNPtrTy(_module->getContext(), 160); - auto fty = llvm::FunctionType::get(Type::Void, {Type::EnvPtr, addrPtrTy, addrPtrTy}, false); - func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); - func->addAttribute(2, llvm::Attribute::ReadOnly); - func->addAttribute(2, llvm::Attribute::NoAlias); - func->addAttribute(2, llvm::Attribute::NoCapture); - func->addAttribute(3, llvm::Attribute::ReadOnly); - func->addAttribute(3, llvm::Attribute::NoAlias); - func->addAttribute(3, llvm::Attribute::NoCapture); - } - return func; + static const auto funcName = "evm.selfdestruct"; + auto func = _module->getFunction(funcName); + if (!func) + { + auto addrPtrTy = llvm::Type::getIntNPtrTy(_module->getContext(), 160); + auto fty = llvm::FunctionType::get(Type::Void, {Type::EnvPtr, addrPtrTy, addrPtrTy}, false); + func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); + func->addAttribute(2, llvm::Attribute::ReadOnly); + func->addAttribute(2, llvm::Attribute::NoAlias); + func->addAttribute(2, llvm::Attribute::NoCapture); + func->addAttribute(3, llvm::Attribute::ReadOnly); + func->addAttribute(3, llvm::Attribute::NoAlias); + func->addAttribute(3, llvm::Attribute::NoCapture); + } + return func; } llvm::Function* getLogFunc(llvm::Module* _module) { - static const auto funcName = "evm.log"; - auto func = _module->getFunction(funcName); - if (!func) - { - auto addrPtrTy = llvm::Type::getIntNPtrTy(_module->getContext(), 160); - auto fty = llvm::FunctionType::get(Type::Void, {Type::EnvPtr, addrPtrTy, Type::BytePtr, Type::Size, Type::WordPtr, Type::Size}, false); - func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); - func->addAttribute(3, llvm::Attribute::ReadOnly); - func->addAttribute(3, llvm::Attribute::NoAlias); - func->addAttribute(3, llvm::Attribute::NoCapture); - func->addAttribute(5, llvm::Attribute::ReadOnly); - func->addAttribute(5, llvm::Attribute::NoAlias); - func->addAttribute(5, llvm::Attribute::NoCapture); - } - return func; + static const auto funcName = "evm.log"; + auto func = _module->getFunction(funcName); + if (!func) + { + auto addrPtrTy = llvm::Type::getIntNPtrTy(_module->getContext(), 160); + auto fty = llvm::FunctionType::get(Type::Void, + {Type::EnvPtr, addrPtrTy, Type::BytePtr, Type::Size, Type::WordPtr, Type::Size}, false); + func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); + func->addAttribute(3, llvm::Attribute::ReadOnly); + func->addAttribute(3, llvm::Attribute::NoAlias); + func->addAttribute(3, llvm::Attribute::NoCapture); + func->addAttribute(5, llvm::Attribute::ReadOnly); + func->addAttribute(5, llvm::Attribute::NoAlias); + func->addAttribute(5, llvm::Attribute::NoCapture); + } + return func; } llvm::Function* getCallFunc(llvm::Module* _module) { - static const auto funcName = "call"; - auto func = _module->getFunction(funcName); - if (!func) - { - auto i32 = llvm::IntegerType::getInt32Ty(_module->getContext()); - auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); - auto addrPtrTy = addrTy->getPointerTo(); - auto fty = llvm::FunctionType::get( - Type::Gas, - {Type::EnvPtr, i32, Type::Gas, addrPtrTy, Type::WordPtr, - Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, - Type::BytePtr->getPointerTo(), Type::Size->getPointerTo()}, - false); - func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, "evm.call", _module); - func->addAttribute(4, llvm::Attribute::ReadOnly); - func->addAttribute(4, llvm::Attribute::NoAlias); - func->addAttribute(4, llvm::Attribute::NoCapture); - func->addAttribute(5, llvm::Attribute::ReadOnly); - func->addAttribute(5, llvm::Attribute::NoAlias); - func->addAttribute(5, llvm::Attribute::NoCapture); - func->addAttribute(6, llvm::Attribute::ReadOnly); - func->addAttribute(6, llvm::Attribute::NoCapture); - func->addAttribute(8, llvm::Attribute::NoCapture); - auto callFunc = func; - - // Create a call wrapper to handle additional checks. - fty = llvm::FunctionType::get( - Type::Gas, - {Type::EnvPtr, i32, Type::Gas, addrPtrTy, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, - Type::BytePtr->getPointerTo(), Type::Size->getPointerTo(), addrTy, Type::Size}, - false - ); - func = llvm::Function::Create(fty, llvm::Function::PrivateLinkage, funcName, _module); - func->addAttribute(4, llvm::Attribute::ReadOnly); - func->addAttribute(4, llvm::Attribute::NoAlias); - func->addAttribute(4, llvm::Attribute::NoCapture); - func->addAttribute(5, llvm::Attribute::ReadOnly); - func->addAttribute(5, llvm::Attribute::NoAlias); - func->addAttribute(5, llvm::Attribute::NoCapture); - func->addAttribute(6, llvm::Attribute::ReadOnly); - func->addAttribute(6, llvm::Attribute::NoCapture); - func->addAttribute(8, llvm::Attribute::NoCapture); - - auto iter = func->arg_begin(); - auto& env = *iter; - std::advance(iter, 1); - auto& callKind = *iter; - std::advance(iter, 1); - auto& gas = *iter; - std::advance(iter, 2); - auto& valuePtr = *iter; - std::advance(iter, 7); - auto& addr = *iter; - std::advance(iter, 1); - auto& depth = *iter; - - auto& ctx = _module->getContext(); - llvm::IRBuilder<> builder(ctx); - auto entryBB = llvm::BasicBlock::Create(ctx, "Entry", func); - auto checkTransferBB = llvm::BasicBlock::Create(ctx, "CheckTransfer", func); - auto checkBalanceBB = llvm::BasicBlock::Create(ctx, "CheckBalance", func); - auto callBB = llvm::BasicBlock::Create(ctx, "Call", func); - auto failBB = llvm::BasicBlock::Create(ctx, "Fail", func); - - builder.SetInsertPoint(entryBB); - auto v = builder.CreateAlloca(Type::Word); - auto addrAlloca = builder.CreateBitCast(builder.CreateAlloca(Type::Word), addrPtrTy); - auto getBalanceFn = getGetBalanceFunc(_module); - auto depthOk = builder.CreateICmpSLT(&depth, builder.getInt64(1024)); - builder.CreateCondBr(depthOk, checkTransferBB, failBB); - - builder.SetInsertPoint(checkTransferBB); - auto notDelegateCall = builder.CreateICmpNE(&callKind, builder.getInt32(EVMC_DELEGATECALL)); - llvm::Value* value = builder.CreateLoad(&valuePtr); - auto valueNonZero = builder.CreateICmpNE(value, Constant::get(0)); - auto transfer = builder.CreateAnd(notDelegateCall, valueNonZero); - builder.CreateCondBr(transfer, checkBalanceBB, callBB); - - builder.SetInsertPoint(checkBalanceBB); - builder.CreateStore(&addr, addrAlloca); - builder.CreateCall(getBalanceFn, {v, &env, addrAlloca}); - llvm::Value* balance = builder.CreateLoad(v); - balance = Endianness::toNative(builder, balance); - value = Endianness::toNative(builder, value); - auto balanceOk = builder.CreateICmpUGE(balance, value); - builder.CreateCondBr(balanceOk, callBB, failBB); - - builder.SetInsertPoint(callBB); - // Pass the first 11 args to the external call. - llvm::Value* args[11]; - auto it = func->arg_begin(); - for (auto outIt = std::begin(args); outIt != std::end(args); ++it, ++outIt) - *outIt = &*it; - auto ret = builder.CreateCall(callFunc, args); - builder.CreateRet(ret); - - builder.SetInsertPoint(failBB); - auto failRet = builder.CreateOr(&gas, builder.getInt64(EVM_CALL_FAILURE)); - builder.CreateRet(failRet); - } - return func; + static const auto funcName = "call"; + auto func = _module->getFunction(funcName); + if (!func) + { + auto i32 = llvm::IntegerType::getInt32Ty(_module->getContext()); + auto addrTy = llvm::IntegerType::get(_module->getContext(), 160); + auto addrPtrTy = addrTy->getPointerTo(); + auto fty = llvm::FunctionType::get(Type::Gas, + {Type::EnvPtr, i32, Type::Gas, addrPtrTy, Type::WordPtr, Type::BytePtr, Type::Size, + Type::BytePtr, Type::Size, Type::BytePtr->getPointerTo(), + Type::Size->getPointerTo()}, + false); + func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, "evm.call", _module); + func->addAttribute(4, llvm::Attribute::ReadOnly); + func->addAttribute(4, llvm::Attribute::NoAlias); + func->addAttribute(4, llvm::Attribute::NoCapture); + func->addAttribute(5, llvm::Attribute::ReadOnly); + func->addAttribute(5, llvm::Attribute::NoAlias); + func->addAttribute(5, llvm::Attribute::NoCapture); + func->addAttribute(6, llvm::Attribute::ReadOnly); + func->addAttribute(6, llvm::Attribute::NoCapture); + func->addAttribute(8, llvm::Attribute::NoCapture); + auto callFunc = func; + + // Create a call wrapper to handle additional checks. + fty = llvm::FunctionType::get(Type::Gas, + {Type::EnvPtr, i32, Type::Gas, addrPtrTy, Type::WordPtr, Type::BytePtr, Type::Size, + Type::BytePtr, Type::Size, Type::BytePtr->getPointerTo(), + Type::Size->getPointerTo(), addrTy, Type::Size}, + false); + func = llvm::Function::Create(fty, llvm::Function::PrivateLinkage, funcName, _module); + func->addAttribute(4, llvm::Attribute::ReadOnly); + func->addAttribute(4, llvm::Attribute::NoAlias); + func->addAttribute(4, llvm::Attribute::NoCapture); + func->addAttribute(5, llvm::Attribute::ReadOnly); + func->addAttribute(5, llvm::Attribute::NoAlias); + func->addAttribute(5, llvm::Attribute::NoCapture); + func->addAttribute(6, llvm::Attribute::ReadOnly); + func->addAttribute(6, llvm::Attribute::NoCapture); + func->addAttribute(8, llvm::Attribute::NoCapture); + + auto iter = func->arg_begin(); + auto& env = *iter; + std::advance(iter, 1); + auto& callKind = *iter; + std::advance(iter, 1); + auto& gas = *iter; + std::advance(iter, 2); + auto& valuePtr = *iter; + std::advance(iter, 7); + auto& addr = *iter; + std::advance(iter, 1); + auto& depth = *iter; + + auto& ctx = _module->getContext(); + llvm::IRBuilder<> builder(ctx); + auto entryBB = llvm::BasicBlock::Create(ctx, "Entry", func); + auto checkTransferBB = llvm::BasicBlock::Create(ctx, "CheckTransfer", func); + auto checkBalanceBB = llvm::BasicBlock::Create(ctx, "CheckBalance", func); + auto callBB = llvm::BasicBlock::Create(ctx, "Call", func); + auto failBB = llvm::BasicBlock::Create(ctx, "Fail", func); + + builder.SetInsertPoint(entryBB); + auto v = builder.CreateAlloca(Type::Word); + auto addrAlloca = builder.CreateBitCast(builder.CreateAlloca(Type::Word), addrPtrTy); + auto getBalanceFn = getGetBalanceFunc(_module); + auto depthOk = builder.CreateICmpSLT(&depth, builder.getInt64(1024)); + builder.CreateCondBr(depthOk, checkTransferBB, failBB); + + builder.SetInsertPoint(checkTransferBB); + auto notDelegateCall = builder.CreateICmpNE(&callKind, builder.getInt32(EVMC_DELEGATECALL)); + llvm::Value* value = builder.CreateLoad(&valuePtr); + auto valueNonZero = builder.CreateICmpNE(value, Constant::get(0)); + auto transfer = builder.CreateAnd(notDelegateCall, valueNonZero); + builder.CreateCondBr(transfer, checkBalanceBB, callBB); + + builder.SetInsertPoint(checkBalanceBB); + builder.CreateStore(&addr, addrAlloca); + builder.CreateCall(getBalanceFn, {v, &env, addrAlloca}); + llvm::Value* balance = builder.CreateLoad(v); + balance = Endianness::toNative(builder, balance); + value = Endianness::toNative(builder, value); + auto balanceOk = builder.CreateICmpUGE(balance, value); + builder.CreateCondBr(balanceOk, callBB, failBB); + + builder.SetInsertPoint(callBB); + // Pass the first 11 args to the external call. + llvm::Value* args[11]; + auto it = func->arg_begin(); + for (auto outIt = std::begin(args); outIt != std::end(args); ++it, ++outIt) + *outIt = &*it; + auto ret = builder.CreateCall(callFunc, args); + builder.CreateRet(ret); + + builder.SetInsertPoint(failBB); + auto failRet = builder.CreateOr(&gas, builder.getInt64(EVM_CALL_FAILURE)); + builder.CreateRet(failRet); + } + return func; } llvm::Function* getBlockHashFunc(llvm::Module* _module) { - static const auto funcName = "evm.blockhash"; - auto func = _module->getFunction(funcName); - if (!func) - { - // TODO: Mark the function as pure to eliminate multiple calls. - auto i64 = llvm::IntegerType::getInt64Ty(_module->getContext()); - auto fty = llvm::FunctionType::get(Type::Void, {Type::WordPtr, Type::EnvPtr, i64}, false); - func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); - func->addAttribute(1, llvm::Attribute::NoAlias); - func->addAttribute(1, llvm::Attribute::NoCapture); - } - return func; -} - + static const auto funcName = "evm.blockhash"; + auto func = _module->getFunction(funcName); + if (!func) + { + // TODO: Mark the function as pure to eliminate multiple calls. + auto i64 = llvm::IntegerType::getInt64Ty(_module->getContext()); + auto fty = llvm::FunctionType::get(Type::Void, {Type::WordPtr, Type::EnvPtr, i64}, false); + func = llvm::Function::Create(fty, llvm::Function::ExternalLinkage, funcName, _module); + func->addAttribute(1, llvm::Attribute::NoAlias); + func->addAttribute(1, llvm::Attribute::NoCapture); + } + return func; } +} // namespace llvm::Value* Ext::getArgAlloca() { - auto& a = m_argAllocas[m_argCounter]; - if (!a) - { - InsertPointGuard g{m_builder}; - auto allocaIt = getMainFunction()->front().begin(); - auto allocaPtr = &(*allocaIt); - std::advance(allocaIt, m_argCounter); // Skip already created allocas - m_builder.SetInsertPoint(allocaPtr); - a = m_builder.CreateAlloca(Type::Word, nullptr, {"a.", std::to_string(m_argCounter)}); - } - ++m_argCounter; - return a; + auto& a = m_argAllocas[m_argCounter]; + if (!a) + { + InsertPointGuard g{m_builder}; + auto allocaIt = getMainFunction()->front().begin(); + auto allocaPtr = &(*allocaIt); + std::advance(allocaIt, m_argCounter); // Skip already created allocas + m_builder.SetInsertPoint(allocaPtr); + a = m_builder.CreateAlloca(Type::Word, nullptr, {"a.", std::to_string(m_argCounter)}); + } + ++m_argCounter; + return a; } llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list const& _args) { - auto& func = m_funcs[static_cast(_funcId)]; - if (!func) - func = createFunc(_funcId, getModule()); + auto& func = m_funcs[static_cast(_funcId)]; + if (!func) + func = createFunc(_funcId, getModule()); - m_argCounter = 0; - return m_builder.CreateCall(func, {_args.begin(), _args.size()}); + m_argCounter = 0; + return m_builder.CreateCall(func, {_args.begin(), _args.size()}); } -llvm::Value* Ext::createCABICall(llvm::Function* _func, std::initializer_list const& _args) -{ - auto args = llvm::SmallVector{_args}; - for (auto&& farg: _func->args()) - { - if (farg.hasByValAttr() || farg.getType()->isPointerTy()) - { - auto& arg = args[farg.getArgNo()]; - // TODO: Remove defensive check and always use it this way. - if (!arg->getType()->isPointerTy()) - { - auto mem = getArgAlloca(); - // TODO: The bitcast may be redundant - mem = m_builder.CreateBitCast(mem, arg->getType()->getPointerTo()); - m_builder.CreateStore(arg, mem); - arg = mem; - } - } - } - - m_argCounter = 0; - return m_builder.CreateCall(_func, args); +llvm::Value* Ext::createCABICall( + llvm::Function* _func, std::initializer_list const& _args) +{ + auto args = llvm::SmallVector{_args}; + for (auto&& farg : _func->args()) + { + if (farg.hasByValAttr() || farg.getType()->isPointerTy()) + { + auto& arg = args[farg.getArgNo()]; + // TODO: Remove defensive check and always use it this way. + if (!arg->getType()->isPointerTy()) + { + auto mem = getArgAlloca(); + // TODO: The bitcast may be redundant + mem = m_builder.CreateBitCast(mem, arg->getType()->getPointerTo()); + m_builder.CreateStore(arg, mem); + arg = mem; + } + } + } + + m_argCounter = 0; + return m_builder.CreateCall(_func, args); } llvm::Value* Ext::sload(llvm::Value* _index) { - auto index = Endianness::toBE(m_builder, _index); - auto addrTy = m_builder.getIntNTy(160); - auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); - auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); - m_builder.CreateStore(myAddr, pAddr); - auto func = getGetStorageFunc(getModule()); - auto pValue = getArgAlloca(); - createCABICall(func, {pValue, getRuntimeManager().getEnvPtr(), pAddr, index}); - return Endianness::toNative(m_builder, m_builder.CreateLoad(pValue)); + auto index = Endianness::toBE(m_builder, _index); + auto addrTy = m_builder.getIntNTy(160); + auto myAddr = Endianness::toBE( + m_builder, m_builder.CreateTrunc( + Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); + auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); + m_builder.CreateStore(myAddr, pAddr); + auto func = getGetStorageFunc(getModule()); + auto pValue = getArgAlloca(); + createCABICall(func, {pValue, getRuntimeManager().getEnvPtr(), pAddr, index}); + return Endianness::toNative(m_builder, m_builder.CreateLoad(pValue)); } void Ext::sstore(llvm::Value* _index, llvm::Value* _value) { - auto addrTy = m_builder.getIntNTy(160); - auto index = Endianness::toBE(m_builder, _index); - auto value = Endianness::toBE(m_builder, _value); - auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); - auto func = getSetStorageFunc(getModule()); - createCABICall(func, {getRuntimeManager().getEnvPtr(), myAddr, index, value}); + auto addrTy = m_builder.getIntNTy(160); + auto index = Endianness::toBE(m_builder, _index); + auto value = Endianness::toBE(m_builder, _value); + auto myAddr = Endianness::toBE( + m_builder, m_builder.CreateTrunc( + Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); + auto func = getSetStorageFunc(getModule()); + createCABICall(func, {getRuntimeManager().getEnvPtr(), myAddr, index, value}); } void Ext::selfdestruct(llvm::Value* _beneficiary) { - auto addrTy = m_builder.getIntNTy(160); - auto func = getSelfdestructFunc(getModule()); - auto b = Endianness::toBE(m_builder, m_builder.CreateTrunc(_beneficiary, addrTy)); - auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); - createCABICall(func, {getRuntimeManager().getEnvPtr(), myAddr, b}); + auto addrTy = m_builder.getIntNTy(160); + auto func = getSelfdestructFunc(getModule()); + auto b = Endianness::toBE(m_builder, m_builder.CreateTrunc(_beneficiary, addrTy)); + auto myAddr = Endianness::toBE( + m_builder, m_builder.CreateTrunc( + Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); + createCABICall(func, {getRuntimeManager().getEnvPtr(), myAddr, b}); } llvm::Value* Ext::calldataload(llvm::Value* _idx) { - auto ret = getArgAlloca(); - auto result = m_builder.CreateBitCast(ret, Type::BytePtr); - - auto callDataSize = getRuntimeManager().getCallDataSize(); - auto callDataSize64 = m_builder.CreateTrunc(callDataSize, Type::Size); - auto idxValid = m_builder.CreateICmpULT(_idx, callDataSize); - auto idx = m_builder.CreateTrunc(m_builder.CreateSelect(idxValid, _idx, callDataSize), Type::Size, "idx"); - - auto end = m_builder.CreateNUWAdd(idx, m_builder.getInt64(32)); - end = m_builder.CreateSelect(m_builder.CreateICmpULE(end, callDataSize64), end, callDataSize64); - auto copySize = m_builder.CreateNUWSub(end, idx); - auto padSize = m_builder.CreateNUWSub(m_builder.getInt64(32), copySize); - auto dataBegin = m_builder.CreateGEP(Type::Byte, getRuntimeManager().getCallData(), idx); - m_builder.CreateMemCpy(result, dataBegin, copySize, 1); - auto pad = m_builder.CreateGEP(Type::Byte, result, copySize); - m_builder.CreateMemSet(pad, m_builder.getInt8(0), padSize, 1); - - m_argCounter = 0; // Release args allocas. TODO: This is a bad design - return Endianness::toNative(m_builder, m_builder.CreateLoad(ret)); + auto ret = getArgAlloca(); + auto result = m_builder.CreateBitCast(ret, Type::BytePtr); + + auto callDataSize = getRuntimeManager().getCallDataSize(); + auto callDataSize64 = m_builder.CreateTrunc(callDataSize, Type::Size); + auto idxValid = m_builder.CreateICmpULT(_idx, callDataSize); + auto idx = m_builder.CreateTrunc( + m_builder.CreateSelect(idxValid, _idx, callDataSize), Type::Size, "idx"); + + auto end = m_builder.CreateNUWAdd(idx, m_builder.getInt64(32)); + end = m_builder.CreateSelect(m_builder.CreateICmpULE(end, callDataSize64), end, callDataSize64); + auto copySize = m_builder.CreateNUWSub(end, idx); + auto padSize = m_builder.CreateNUWSub(m_builder.getInt64(32), copySize); + auto dataBegin = m_builder.CreateGEP(Type::Byte, getRuntimeManager().getCallData(), idx); + m_builder.CreateMemCpy(result, dataBegin, copySize, 1); + auto pad = m_builder.CreateGEP(Type::Byte, result, copySize); + m_builder.CreateMemSet(pad, m_builder.getInt8(0), padSize, 1); + + m_argCounter = 0; // Release args allocas. TODO: This is a bad design + return Endianness::toNative(m_builder, m_builder.CreateLoad(ret)); } llvm::Value* Ext::balance(llvm::Value* _address) { - auto func = getGetBalanceFunc(getModule()); - auto addrTy = m_builder.getIntNTy(160); - auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); - auto pResult = getArgAlloca(); - auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); - m_builder.CreateStore(address, pAddr); - createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), pAddr}); - return Endianness::toNative(m_builder, m_builder.CreateLoad(pResult)); + auto func = getGetBalanceFunc(getModule()); + auto addrTy = m_builder.getIntNTy(160); + auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); + auto pResult = getArgAlloca(); + auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); + m_builder.CreateStore(address, pAddr); + createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), pAddr}); + return Endianness::toNative(m_builder, m_builder.CreateLoad(pResult)); } llvm::Value* Ext::exists(llvm::Value* _address) { - auto func = getAccountExistsFunc(getModule()); - auto addrTy = m_builder.getIntNTy(160); - auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); - auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); - m_builder.CreateStore(address, pAddr); - auto r = createCABICall(func, {getRuntimeManager().getEnvPtr(), pAddr}); - return m_builder.CreateTrunc(r, m_builder.getInt1Ty()); + auto func = getAccountExistsFunc(getModule()); + auto addrTy = m_builder.getIntNTy(160); + auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); + auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); + m_builder.CreateStore(address, pAddr); + auto r = createCABICall(func, {getRuntimeManager().getEnvPtr(), pAddr}); + return m_builder.CreateTrunc(r, m_builder.getInt1Ty()); } llvm::Value* Ext::blockHash(llvm::Value* _number) { - auto func = getBlockHashFunc(getModule()); - auto number = m_builder.CreateTrunc(_number, m_builder.getInt64Ty()); - auto pResult = getArgAlloca(); - createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), number}); - return Endianness::toNative(m_builder, m_builder.CreateLoad(pResult)); + auto func = getBlockHashFunc(getModule()); + auto number = m_builder.CreateTrunc(_number, m_builder.getInt64Ty()); + auto pResult = getArgAlloca(); + createCABICall(func, {pResult, getRuntimeManager().getEnvPtr(), number}); + return Endianness::toNative(m_builder, m_builder.CreateLoad(pResult)); } llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) { - auto begin = m_memoryMan.getBytePtr(_inOff); - auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size"); - auto ret = getArgAlloca(); - createCall(EnvFunc::sha3, {begin, size, ret}); - llvm::Value* hash = m_builder.CreateLoad(ret); - return Endianness::toNative(m_builder, hash); + auto begin = m_memoryMan.getBytePtr(_inOff); + auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size"); + auto ret = getArgAlloca(); + createCall(EnvFunc::sha3, {begin, size, ret}); + llvm::Value* hash = m_builder.CreateLoad(ret); + return Endianness::toNative(m_builder, hash); } MemoryRef Ext::extcode(llvm::Value* _address) { - auto func = getGetCodeFunc(getModule()); - auto addrTy = m_builder.getIntNTy(160); - auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); - auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); - m_builder.CreateStore(address, pAddr); - auto a = getArgAlloca(); - auto codePtrPtr = m_builder.CreateBitCast(a, Type::BytePtr->getPointerTo()); - auto size = createCABICall(func, {codePtrPtr, getRuntimeManager().getEnvPtr(), pAddr}); - auto code = m_builder.CreateLoad(codePtrPtr, "code"); - auto size256 = m_builder.CreateZExt(size, Type::Word); - return {code, size256}; + auto func = getGetCodeFunc(getModule()); + auto addrTy = m_builder.getIntNTy(160); + auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); + auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); + m_builder.CreateStore(address, pAddr); + auto a = getArgAlloca(); + auto codePtrPtr = m_builder.CreateBitCast(a, Type::BytePtr->getPointerTo()); + auto size = createCABICall(func, {codePtrPtr, getRuntimeManager().getEnvPtr(), pAddr}); + auto code = m_builder.CreateLoad(codePtrPtr, "code"); + auto size256 = m_builder.CreateZExt(size, Type::Word); + return {code, size256}; } llvm::Value* Ext::extcodesize(llvm::Value* _address) { - auto func = getGetCodeSizeFunc(getModule()); - auto addrTy = m_builder.getIntNTy(160); - auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); - auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); - m_builder.CreateStore(address, pAddr); - auto size = createCABICall(func, {getRuntimeManager().getEnvPtr(), pAddr}); - return m_builder.CreateZExt(size, Type::Word); + auto func = getGetCodeSizeFunc(getModule()); + auto addrTy = m_builder.getIntNTy(160); + auto address = Endianness::toBE(m_builder, m_builder.CreateTrunc(_address, addrTy)); + auto pAddr = m_builder.CreateBitCast(getArgAlloca(), addrTy->getPointerTo()); + m_builder.CreateStore(address, pAddr); + auto size = createCABICall(func, {getRuntimeManager().getEnvPtr(), pAddr}); + return m_builder.CreateZExt(size, Type::Word); } void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, llvm::ArrayRef _topics) { - if (!m_topics) - { - InsertPointGuard g{m_builder}; - auto& entryBB = getMainFunction()->front(); - m_builder.SetInsertPoint(&entryBB, entryBB.begin()); - m_topics = m_builder.CreateAlloca(Type::Word, m_builder.getInt32(4), "topics"); - } - - auto dataPtr = m_memoryMan.getBytePtr(_memIdx); - auto dataSize = m_builder.CreateTrunc(_numBytes, Type::Size, "data.size"); - - for (size_t i = 0; i < _topics.size(); ++i) - { - auto t = Endianness::toBE(m_builder, _topics[i]); - auto p = m_builder.CreateConstGEP1_32(m_topics, static_cast(i)); - m_builder.CreateStore(t, p); - } - auto numTopics = m_builder.getInt64(_topics.size()); - - auto addrTy = m_builder.getIntNTy(160); - auto func = getLogFunc(getModule()); - - auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); - createCABICall(func, { - getRuntimeManager().getEnvPtr(), myAddr, dataPtr, dataSize, m_topics, numTopics - }); -} - -llvm::Value* Ext::call(int _kind, - llvm::Value* _gas, - llvm::Value* _addr, - llvm::Value* _value, - llvm::Value* _inOff, - llvm::Value* _inSize, - llvm::Value* _outOff, - llvm::Value* _outSize) -{ - auto gas = m_builder.CreateTrunc(_gas, Type::Size); - auto addrTy = m_builder.getIntNTy(160); - auto addr = m_builder.CreateTrunc(_addr, addrTy); - addr = Endianness::toBE(m_builder, addr); - auto inData = m_memoryMan.getBytePtr(_inOff); - auto inSize = m_builder.CreateTrunc(_inSize, Type::Size); - auto outData = m_memoryMan.getBytePtr(_outOff); - auto outSize = m_builder.CreateTrunc(_outSize, Type::Size); - - auto value = getArgAlloca(); - m_builder.CreateStore(Endianness::toBE(m_builder, _value), value); - - auto func = getCallFunc(getModule()); - auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); - getRuntimeManager().resetReturnBuf(); - return createCABICall( - func, - {getRuntimeManager().getEnvPtr(), m_builder.getInt32(_kind), gas, - addr, value, inData, inSize, outData, outSize, - getRuntimeManager().getReturnBufDataPtr(), getRuntimeManager().getReturnBufSizePtr(), - myAddr, getRuntimeManager().getDepth() - }); -} - -std::tuple Ext::create(llvm::Value* _gas, - llvm::Value* _endowment, - llvm::Value* _initOff, - llvm::Value* _initSize) -{ - auto addrTy = m_builder.getIntNTy(160); - auto value = getArgAlloca(); - m_builder.CreateStore(Endianness::toBE(m_builder, _endowment), value); - auto inData = m_memoryMan.getBytePtr(_initOff); - auto inSize = m_builder.CreateTrunc(_initSize, Type::Size); - auto pAddr = - m_builder.CreateBitCast(getArgAlloca(), m_builder.getInt8PtrTy()); - - auto func = getCallFunc(getModule()); - auto myAddr = Endianness::toBE(m_builder, m_builder.CreateTrunc(Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); - getRuntimeManager().resetReturnBuf(); - auto ret = createCABICall( - func, {getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVMC_CREATE), - _gas, llvm::UndefValue::get(addrTy), value, inData, inSize, pAddr, - m_builder.getInt64(20), - getRuntimeManager().getReturnBufDataPtr(), getRuntimeManager().getReturnBufSizePtr(), - myAddr, getRuntimeManager().getDepth() - }); - - pAddr = m_builder.CreateBitCast(pAddr, addrTy->getPointerTo()); - return std::tuple{ret, pAddr}; -} -} -} -} + if (!m_topics) + { + InsertPointGuard g{m_builder}; + auto& entryBB = getMainFunction()->front(); + m_builder.SetInsertPoint(&entryBB, entryBB.begin()); + m_topics = m_builder.CreateAlloca(Type::Word, m_builder.getInt32(4), "topics"); + } + + auto dataPtr = m_memoryMan.getBytePtr(_memIdx); + auto dataSize = m_builder.CreateTrunc(_numBytes, Type::Size, "data.size"); + + for (size_t i = 0; i < _topics.size(); ++i) + { + auto t = Endianness::toBE(m_builder, _topics[i]); + auto p = m_builder.CreateConstGEP1_32(m_topics, static_cast(i)); + m_builder.CreateStore(t, p); + } + auto numTopics = m_builder.getInt64(_topics.size()); + + auto addrTy = m_builder.getIntNTy(160); + auto func = getLogFunc(getModule()); + + auto myAddr = Endianness::toBE( + m_builder, m_builder.CreateTrunc( + Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); + createCABICall( + func, {getRuntimeManager().getEnvPtr(), myAddr, dataPtr, dataSize, m_topics, numTopics}); +} + +llvm::Value* Ext::call(int _kind, llvm::Value* _gas, llvm::Value* _addr, llvm::Value* _value, + llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize) +{ + auto gas = m_builder.CreateTrunc(_gas, Type::Size); + auto addrTy = m_builder.getIntNTy(160); + auto addr = m_builder.CreateTrunc(_addr, addrTy); + addr = Endianness::toBE(m_builder, addr); + auto inData = m_memoryMan.getBytePtr(_inOff); + auto inSize = m_builder.CreateTrunc(_inSize, Type::Size); + auto outData = m_memoryMan.getBytePtr(_outOff); + auto outSize = m_builder.CreateTrunc(_outSize, Type::Size); + + auto value = getArgAlloca(); + m_builder.CreateStore(Endianness::toBE(m_builder, _value), value); + + auto func = getCallFunc(getModule()); + auto myAddr = Endianness::toBE( + m_builder, m_builder.CreateTrunc( + Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); + getRuntimeManager().resetReturnBuf(); + return createCABICall(func, + {getRuntimeManager().getEnvPtr(), m_builder.getInt32(_kind), gas, addr, value, inData, + inSize, outData, outSize, getRuntimeManager().getReturnBufDataPtr(), + getRuntimeManager().getReturnBufSizePtr(), myAddr, getRuntimeManager().getDepth()}); +} + +std::tuple Ext::create( + llvm::Value* _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize) +{ + auto addrTy = m_builder.getIntNTy(160); + auto value = getArgAlloca(); + m_builder.CreateStore(Endianness::toBE(m_builder, _endowment), value); + auto inData = m_memoryMan.getBytePtr(_initOff); + auto inSize = m_builder.CreateTrunc(_initSize, Type::Size); + auto pAddr = m_builder.CreateBitCast(getArgAlloca(), m_builder.getInt8PtrTy()); + + auto func = getCallFunc(getModule()); + auto myAddr = Endianness::toBE( + m_builder, m_builder.CreateTrunc( + Endianness::toNative(m_builder, getRuntimeManager().getAddress()), addrTy)); + getRuntimeManager().resetReturnBuf(); + auto ret = createCABICall(func, + {getRuntimeManager().getEnvPtr(), m_builder.getInt32(EVMC_CREATE), _gas, + llvm::UndefValue::get(addrTy), value, inData, inSize, pAddr, m_builder.getInt64(20), + getRuntimeManager().getReturnBufDataPtr(), getRuntimeManager().getReturnBufSizePtr(), + myAddr, getRuntimeManager().getDepth()}); + + pAddr = m_builder.CreateBitCast(pAddr, addrTy->getPointerTo()); + return std::tuple{ret, pAddr}; +} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Ext.h b/libevmjit/Ext.h index e6f53db2..3cc5bfb9 100644 --- a/libevmjit/Ext.h +++ b/libevmjit/Ext.h @@ -2,8 +2,8 @@ #include -#include "JIT.h" #include "CompilerHelper.h" +#include "JIT.h" namespace dev { @@ -11,7 +11,6 @@ namespace eth { namespace jit { - /// The flag indicating call failure in evmc_call_fn() -- highest bit set. constexpr int64_t EVM_CALL_FAILURE = 0x8000000000000000; @@ -22,83 +21,77 @@ class Memory; struct MemoryRef { - llvm::Value* ptr; - llvm::Value* size; + llvm::Value* ptr; + llvm::Value* size; }; -template +template struct sizeOf { - static const size_t value = static_cast(_EnumT::_size); + static const size_t value = static_cast(_EnumT::_size); }; enum class EnvFunc { - sload, - sstore, - sha3, - balance, - create, - call, - log, - blockhash, - extcode, - - _size + sload, + sstore, + sha3, + balance, + create, + call, + log, + blockhash, + extcode, + + _size }; class Ext : public RuntimeHelper { public: - Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan); + Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan); - llvm::Value* sload(llvm::Value* _index); - void sstore(llvm::Value* _index, llvm::Value* _value); + llvm::Value* sload(llvm::Value* _index); + void sstore(llvm::Value* _index, llvm::Value* _value); - llvm::Value* balance(llvm::Value* _address); - llvm::Value* exists(llvm::Value* _address); - llvm::Value* calldataload(llvm::Value* _index); - std::tuple create(llvm::Value* _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); - llvm::Value* blockHash(llvm::Value* _number); + llvm::Value* balance(llvm::Value* _address); + llvm::Value* exists(llvm::Value* _address); + llvm::Value* calldataload(llvm::Value* _index); + std::tuple create( + llvm::Value* _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); + llvm::Value* blockHash(llvm::Value* _number); - llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); - MemoryRef extcode(llvm::Value* _addr); - llvm::Value* extcodesize(llvm::Value* _addr); + llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); + MemoryRef extcode(llvm::Value* _addr); + llvm::Value* extcodesize(llvm::Value* _addr); - void log(llvm::Value* _memIdx, llvm::Value* _numBytes, llvm::ArrayRef _topics); - void selfdestruct(llvm::Value* _beneficiary); + void log(llvm::Value* _memIdx, llvm::Value* _numBytes, llvm::ArrayRef _topics); + void selfdestruct(llvm::Value* _beneficiary); - llvm::Value* call(int _kind, - llvm::Value* _gas, - llvm::Value* _addr, - llvm::Value* _value, - llvm::Value* _inOff, - llvm::Value* _inSize, - llvm::Value* _outOff, - llvm::Value* _outSize); + llvm::Value* call(int _kind, llvm::Value* _gas, llvm::Value* _addr, llvm::Value* _value, + llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize); private: - Memory& m_memoryMan; + Memory& m_memoryMan; - llvm::Value* m_size; + llvm::Value* m_size; - std::array::value> m_funcs; - std::array m_argAllocas; - size_t m_argCounter = 0; + std::array::value> m_funcs; + std::array m_argAllocas; + size_t m_argCounter = 0; - /// Memory for array of up to 4 log topics - /// TODO: Merge this memory with args allocas. - llvm::Value* m_topics = nullptr; + /// Memory for array of up to 4 log topics + /// TODO: Merge this memory with args allocas. + llvm::Value* m_topics = nullptr; - llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list const& _args); - llvm::Value* getArgAlloca(); + llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list const& _args); + llvm::Value* getArgAlloca(); - llvm::Value* createCABICall(llvm::Function* _func, - std::initializer_list const& _args); + llvm::Value* createCABICall( + llvm::Function* _func, std::initializer_list const& _args); }; -} -} -} - +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/GasMeter.cpp b/libevmjit/GasMeter.cpp index b91ab5cb..97d11766 100644 --- a/libevmjit/GasMeter.cpp +++ b/libevmjit/GasMeter.cpp @@ -1,11 +1,11 @@ #include "GasMeter.h" +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include -#include "preprocessor/llvm_includes_end.h" -#include "JIT.h" #include "Ext.h" +#include "JIT.h" #include "RuntimeManager.h" namespace dev @@ -14,280 +14,293 @@ namespace eth { namespace jit { - -GasMeter::GasMeter(IRBuilder& _builder, RuntimeManager& _runtimeManager, evmc_revision rev): - CompilerHelper(_builder), - m_runtimeManager(_runtimeManager), - m_rev(rev) +GasMeter::GasMeter(IRBuilder& _builder, RuntimeManager& _runtimeManager, evmc_revision rev) + : CompilerHelper(_builder), m_runtimeManager(_runtimeManager), m_rev(rev) { - llvm::Type* gasCheckArgs[] = {Type::Gas->getPointerTo(), Type::Gas, Type::BytePtr}; - m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", getModule()); - m_gasCheckFunc->setDoesNotThrow(); - m_gasCheckFunc->addAttribute(1, llvm::Attribute::NoCapture); - - auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc); - auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc); - auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc); - - auto iter = m_gasCheckFunc->arg_begin(); - llvm::Argument* gasPtr = &(*iter++); - gasPtr->setName("gasPtr"); - llvm::Argument* cost = &(*iter++); - cost->setName("cost"); - llvm::Argument* jmpBuf = &(*iter); - jmpBuf->setName("jmpBuf"); - - InsertPointGuard guard(m_builder); - m_builder.SetInsertPoint(checkBB); - auto gas = m_builder.CreateLoad(gasPtr, "gas"); - auto gasUpdated = m_builder.CreateNSWSub(gas, cost, "gasUpdated"); - auto gasOk = m_builder.CreateICmpSGE(gasUpdated, m_builder.getInt64(0), "gasOk"); // gas >= 0, with gas == 0 we can still do 0 cost instructions - m_builder.CreateCondBr(gasOk, updateBB, outOfGasBB, Type::expectTrue); - - m_builder.SetInsertPoint(updateBB); - m_builder.CreateStore(gasUpdated, gasPtr); - m_builder.CreateRetVoid(); - - m_builder.SetInsertPoint(outOfGasBB); - m_runtimeManager.abort(jmpBuf); - m_builder.CreateUnreachable(); + llvm::Type* gasCheckArgs[] = {Type::Gas->getPointerTo(), Type::Gas, Type::BytePtr}; + m_gasCheckFunc = + llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), + llvm::Function::PrivateLinkage, "gas.check", getModule()); + m_gasCheckFunc->setDoesNotThrow(); + m_gasCheckFunc->addAttribute(1, llvm::Attribute::NoCapture); + + auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc); + auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc); + auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc); + + auto iter = m_gasCheckFunc->arg_begin(); + llvm::Argument* gasPtr = &(*iter++); + gasPtr->setName("gasPtr"); + llvm::Argument* cost = &(*iter++); + cost->setName("cost"); + llvm::Argument* jmpBuf = &(*iter); + jmpBuf->setName("jmpBuf"); + + InsertPointGuard guard(m_builder); + m_builder.SetInsertPoint(checkBB); + auto gas = m_builder.CreateLoad(gasPtr, "gas"); + auto gasUpdated = m_builder.CreateNSWSub(gas, cost, "gasUpdated"); + auto gasOk = m_builder.CreateICmpSGE(gasUpdated, m_builder.getInt64(0), + "gasOk"); // gas >= 0, with gas == 0 we can still do 0 cost instructions + m_builder.CreateCondBr(gasOk, updateBB, outOfGasBB, Type::expectTrue); + + m_builder.SetInsertPoint(updateBB); + m_builder.CreateStore(gasUpdated, gasPtr); + m_builder.CreateRetVoid(); + + m_builder.SetInsertPoint(outOfGasBB); + m_runtimeManager.abort(jmpBuf); + m_builder.CreateUnreachable(); } void GasMeter::count(Instruction _inst) { - if (!m_checkCall) - { - // Create gas check call with mocked block cost at begining of current cost-block - m_checkCall = m_builder.CreateCall(m_gasCheckFunc, {m_runtimeManager.getGasPtr(), llvm::UndefValue::get(Type::Gas), m_runtimeManager.getJmpBuf()}); - } - - m_blockCost += getStepCost(_inst); + if (!m_checkCall) + { + // Create gas check call with mocked block cost at begining of current cost-block + m_checkCall = m_builder.CreateCall( + m_gasCheckFunc, {m_runtimeManager.getGasPtr(), llvm::UndefValue::get(Type::Gas), + m_runtimeManager.getJmpBuf()}); + } + + m_blockCost += getStepCost(_inst); } void GasMeter::count(llvm::Value* _cost, llvm::Value* _jmpBuf, llvm::Value* _gasPtr) { - if (_cost->getType() == Type::Word) - { - auto gasMax256 = m_builder.CreateZExt(Constant::gasMax, Type::Word); - auto tooHigh = m_builder.CreateICmpUGT(_cost, gasMax256, "costTooHigh"); - auto cost64 = m_builder.CreateTrunc(_cost, Type::Gas); - _cost = m_builder.CreateSelect(tooHigh, Constant::gasMax, cost64, "cost"); - } - - assert(_cost->getType() == Type::Gas); - m_builder.CreateCall(m_gasCheckFunc, {_gasPtr ? _gasPtr : m_runtimeManager.getGasPtr(), _cost, _jmpBuf ? _jmpBuf : m_runtimeManager.getJmpBuf()}); + if (_cost->getType() == Type::Word) + { + auto gasMax256 = m_builder.CreateZExt(Constant::gasMax, Type::Word); + auto tooHigh = m_builder.CreateICmpUGT(_cost, gasMax256, "costTooHigh"); + auto cost64 = m_builder.CreateTrunc(_cost, Type::Gas); + _cost = m_builder.CreateSelect(tooHigh, Constant::gasMax, cost64, "cost"); + } + + assert(_cost->getType() == Type::Gas); + m_builder.CreateCall(m_gasCheckFunc, {_gasPtr ? _gasPtr : m_runtimeManager.getGasPtr(), _cost, + _jmpBuf ? _jmpBuf : m_runtimeManager.getJmpBuf()}); } void GasMeter::countExp(llvm::Value* _exponent) { - // Additional cost is 1 per significant byte of exponent - // lz - leading zeros - // cost = ((256 - lz) + 7) / 8 - - // OPT: Can gas update be done in exp algorithm? - auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); - auto lz256 = m_builder.CreateCall(ctlz, {_exponent, m_builder.getInt1(false)}); - auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz"); - auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits"); - auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8)); - auto exponentByteCost = m_rev >= EVMC_SPURIOUS_DRAGON ? 50 : JITSchedule::expByteGas::value; - count(m_builder.CreateNUWMul(sigBytes, m_builder.getInt64(exponentByteCost))); + // Additional cost is 1 per significant byte of exponent + // lz - leading zeros + // cost = ((256 - lz) + 7) / 8 + + // OPT: Can gas update be done in exp algorithm? + auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); + auto lz256 = m_builder.CreateCall(ctlz, {_exponent, m_builder.getInt1(false)}); + auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz"); + auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits"); + auto sigBytes = m_builder.CreateUDiv( + m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8)); + auto exponentByteCost = m_rev >= EVMC_SPURIOUS_DRAGON ? 50 : JITSchedule::expByteGas::value; + count(m_builder.CreateNUWMul(sigBytes, m_builder.getInt64(exponentByteCost))); } void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue) { - auto oldValue = _ext.sload(_index); - auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero"); - auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero"); - auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert"); - assert(JITSchedule::sstoreResetGas::value == JITSchedule::sstoreClearGas::value && "Update SSTORE gas cost"); - auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(JITSchedule::sstoreSetGas::value), m_builder.getInt64(JITSchedule::sstoreResetGas::value), "cost"); - count(cost); + auto oldValue = _ext.sload(_index); + auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero"); + auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero"); + auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert"); + assert(JITSchedule::sstoreResetGas::value == JITSchedule::sstoreClearGas::value && + "Update SSTORE gas cost"); + auto cost = + m_builder.CreateSelect(isInsert, m_builder.getInt64(JITSchedule::sstoreSetGas::value), + m_builder.getInt64(JITSchedule::sstoreResetGas::value), "cost"); + count(cost); } void GasMeter::countLogData(llvm::Value* _dataLength) { - assert(m_checkCall); - assert(m_blockCost > 0); // LOGn instruction is already counted - assert(JITSchedule::logDataGas::value != 1 && "Log data gas cost has changed. Update GasMeter."); - count(m_builder.CreateNUWMul(_dataLength, Constant::get(JITSchedule::logDataGas::value))); // TODO: Use i64 + assert(m_checkCall); + assert(m_blockCost > 0); // LOGn instruction is already counted + assert( + JITSchedule::logDataGas::value != 1 && "Log data gas cost has changed. Update GasMeter."); + count(m_builder.CreateNUWMul( + _dataLength, Constant::get(JITSchedule::logDataGas::value))); // TODO: Use i64 } void GasMeter::countSha3Data(llvm::Value* _dataLength) { - assert(m_checkCall); - assert(m_blockCost > 0); // SHA3 instruction is already counted - - // TODO: This round ups to 32 happens in many places - assert(JITSchedule::sha3WordGas::value != 1 && "SHA3 data cost has changed. Update GasMeter"); - auto dataLength64 = m_builder.CreateTrunc(_dataLength, Type::Gas); - auto words64 = m_builder.CreateUDiv(m_builder.CreateNUWAdd(dataLength64, m_builder.getInt64(31)), m_builder.getInt64(32)); - auto cost64 = m_builder.CreateNUWMul(m_builder.getInt64(JITSchedule::sha3WordGas::value), words64); - count(cost64); + assert(m_checkCall); + assert(m_blockCost > 0); // SHA3 instruction is already counted + + // TODO: This round ups to 32 happens in many places + assert(JITSchedule::sha3WordGas::value != 1 && "SHA3 data cost has changed. Update GasMeter"); + auto dataLength64 = m_builder.CreateTrunc(_dataLength, Type::Gas); + auto words64 = m_builder.CreateUDiv( + m_builder.CreateNUWAdd(dataLength64, m_builder.getInt64(31)), m_builder.getInt64(32)); + auto cost64 = + m_builder.CreateNUWMul(m_builder.getInt64(JITSchedule::sha3WordGas::value), words64); + count(cost64); } void GasMeter::giveBack(llvm::Value* _gas) { - assert(_gas->getType() == Type::Gas); - m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas)); + assert(_gas->getType() == Type::Gas); + m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas)); } void GasMeter::commitCostBlock() { - // If any uncommited block - if (m_checkCall) - { - if (m_blockCost == 0) // Do not check 0 - { - m_checkCall->eraseFromParent(); // Remove the gas check call - m_checkCall = nullptr; - return; - } - - m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas check call - m_checkCall = nullptr; // End cost-block - m_blockCost = 0; - } - assert(m_blockCost == 0); + // If any uncommited block + if (m_checkCall) + { + if (m_blockCost == 0) // Do not check 0 + { + m_checkCall->eraseFromParent(); // Remove the gas check call + m_checkCall = nullptr; + return; + } + + m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas + // check call + m_checkCall = nullptr; // End cost-block + m_blockCost = 0; + } + assert(m_blockCost == 0); } -void GasMeter::countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr) +void GasMeter::countMemory( + llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr) { - assert(JITSchedule::memoryGas::value != 1 && "Memory gas cost has changed. Update GasMeter."); - count(_additionalMemoryInWords, _jmpBuf, _gasPtr); + assert(JITSchedule::memoryGas::value != 1 && "Memory gas cost has changed. Update GasMeter."); + count(_additionalMemoryInWords, _jmpBuf, _gasPtr); } void GasMeter::countCopy(llvm::Value* _copyWords) { - assert(JITSchedule::copyGas::value != 1 && "Copy gas cost has changed. Update GasMeter."); - count(m_builder.CreateNUWMul(_copyWords, m_builder.getInt64(JITSchedule::copyGas::value))); + assert(JITSchedule::copyGas::value != 1 && "Copy gas cost has changed. Update GasMeter."); + count(m_builder.CreateNUWMul(_copyWords, m_builder.getInt64(JITSchedule::copyGas::value))); } int64_t GasMeter::getStepCost(Instruction inst) const { - switch (inst) - { - // Tier 0 - case Instruction::STOP: - case Instruction::RETURN: - case Instruction::REVERT: - case Instruction::SSTORE: // Handle cost of SSTORE separately in GasMeter::countSStore() - return JITSchedule::stepGas0::value; - - // Tier 1 - case Instruction::ADDRESS: - case Instruction::ORIGIN: - case Instruction::CALLER: - case Instruction::CALLVALUE: - case Instruction::CALLDATASIZE: - case Instruction::RETURNDATASIZE: - case Instruction::CODESIZE: - case Instruction::GASPRICE: - case Instruction::COINBASE: - case Instruction::TIMESTAMP: - case Instruction::NUMBER: - case Instruction::DIFFICULTY: - case Instruction::GASLIMIT: - case Instruction::POP: - case Instruction::PC: - case Instruction::MSIZE: - case Instruction::GAS: - return JITSchedule::stepGas1::value; - - // Tier 2 - case Instruction::ADD: - case Instruction::SUB: - case Instruction::LT: - case Instruction::GT: - case Instruction::SLT: - case Instruction::SGT: - case Instruction::EQ: - case Instruction::ISZERO: - case Instruction::AND: - case Instruction::OR: - case Instruction::XOR: - case Instruction::NOT: - case Instruction::BYTE: - case Instruction::CALLDATALOAD: - case Instruction::CALLDATACOPY: - case Instruction::RETURNDATACOPY: - case Instruction::CODECOPY: - case Instruction::MLOAD: - case Instruction::MSTORE: - case Instruction::MSTORE8: - case Instruction::ANY_PUSH: - case Instruction::ANY_DUP: - case Instruction::ANY_SWAP: - return JITSchedule::stepGas2::value; - - // Tier 3 - case Instruction::MUL: - case Instruction::DIV: - case Instruction::SDIV: - case Instruction::MOD: - case Instruction::SMOD: - case Instruction::SIGNEXTEND: - return JITSchedule::stepGas3::value; - - // Tier 4 - case Instruction::ADDMOD: - case Instruction::MULMOD: - case Instruction::JUMP: - return JITSchedule::stepGas4::value; - - // Tier 5 - case Instruction::EXP: - case Instruction::JUMPI: - return JITSchedule::stepGas5::value; - - // Tier 6 - case Instruction::BALANCE: - return m_rev >= EVMC_TANGERINE_WHISTLE ? 400 : JITSchedule::stepGas6::value; - - case Instruction::EXTCODESIZE: - case Instruction::EXTCODECOPY: - return m_rev >= EVMC_TANGERINE_WHISTLE ? 700 : JITSchedule::stepGas6::value; - - case Instruction::BLOCKHASH: - return m_rev >= EVMC_CONSTANTINOPLE ? 800 : JITSchedule::stepGas6::value; - - case Instruction::SHA3: - return JITSchedule::sha3Gas::value; - - case Instruction::SLOAD: - return m_rev >= EVMC_TANGERINE_WHISTLE ? 200 : JITSchedule::sloadGas::value; - - case Instruction::JUMPDEST: - return JITSchedule::jumpdestGas::value; - - case Instruction::LOG0: - case Instruction::LOG1: - case Instruction::LOG2: - case Instruction::LOG3: - case Instruction::LOG4: - { - auto numTopics = static_cast(inst) - static_cast(Instruction::LOG0); - return JITSchedule::logGas::value + numTopics * JITSchedule::logTopicGas::value; - } - - case Instruction::CALL: - case Instruction::CALLCODE: - case Instruction::DELEGATECALL: - case Instruction::STATICCALL: - return m_rev >= EVMC_TANGERINE_WHISTLE ? 700 : JITSchedule::callGas::value; - - case Instruction::CREATE: - return JITSchedule::createGas::value; - - case Instruction::SUICIDE: - return m_rev >= EVMC_TANGERINE_WHISTLE ? 5000 : JITSchedule::stepGas0::value; - - default: - // For invalid instruction just return 0. - return 0; - } + switch (inst) + { + // Tier 0 + case Instruction::STOP: + case Instruction::RETURN: + case Instruction::REVERT: + case Instruction::SSTORE: // Handle cost of SSTORE separately in GasMeter::countSStore() + return JITSchedule::stepGas0::value; + + // Tier 1 + case Instruction::ADDRESS: + case Instruction::ORIGIN: + case Instruction::CALLER: + case Instruction::CALLVALUE: + case Instruction::CALLDATASIZE: + case Instruction::RETURNDATASIZE: + case Instruction::CODESIZE: + case Instruction::GASPRICE: + case Instruction::COINBASE: + case Instruction::TIMESTAMP: + case Instruction::NUMBER: + case Instruction::DIFFICULTY: + case Instruction::GASLIMIT: + case Instruction::POP: + case Instruction::PC: + case Instruction::MSIZE: + case Instruction::GAS: + return JITSchedule::stepGas1::value; + + // Tier 2 + case Instruction::ADD: + case Instruction::SUB: + case Instruction::LT: + case Instruction::GT: + case Instruction::SLT: + case Instruction::SGT: + case Instruction::EQ: + case Instruction::ISZERO: + case Instruction::AND: + case Instruction::OR: + case Instruction::XOR: + case Instruction::NOT: + case Instruction::BYTE: + case Instruction::CALLDATALOAD: + case Instruction::CALLDATACOPY: + case Instruction::RETURNDATACOPY: + case Instruction::CODECOPY: + case Instruction::MLOAD: + case Instruction::MSTORE: + case Instruction::MSTORE8: + case Instruction::ANY_PUSH: + case Instruction::ANY_DUP: + case Instruction::ANY_SWAP: + return JITSchedule::stepGas2::value; + + // Tier 3 + case Instruction::MUL: + case Instruction::DIV: + case Instruction::SDIV: + case Instruction::MOD: + case Instruction::SMOD: + case Instruction::SIGNEXTEND: + return JITSchedule::stepGas3::value; + + // Tier 4 + case Instruction::ADDMOD: + case Instruction::MULMOD: + case Instruction::JUMP: + return JITSchedule::stepGas4::value; + + // Tier 5 + case Instruction::EXP: + case Instruction::JUMPI: + return JITSchedule::stepGas5::value; + + // Tier 6 + case Instruction::BALANCE: + return m_rev >= EVMC_TANGERINE_WHISTLE ? 400 : JITSchedule::stepGas6::value; + + case Instruction::EXTCODESIZE: + case Instruction::EXTCODECOPY: + return m_rev >= EVMC_TANGERINE_WHISTLE ? 700 : JITSchedule::stepGas6::value; + + case Instruction::BLOCKHASH: + return m_rev >= EVMC_CONSTANTINOPLE ? 800 : JITSchedule::stepGas6::value; + + case Instruction::SHA3: + return JITSchedule::sha3Gas::value; + + case Instruction::SLOAD: + return m_rev >= EVMC_TANGERINE_WHISTLE ? 200 : JITSchedule::sloadGas::value; + + case Instruction::JUMPDEST: + return JITSchedule::jumpdestGas::value; + + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + { + auto numTopics = static_cast(inst) - static_cast(Instruction::LOG0); + return JITSchedule::logGas::value + numTopics * JITSchedule::logTopicGas::value; + } + + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::DELEGATECALL: + case Instruction::STATICCALL: + return m_rev >= EVMC_TANGERINE_WHISTLE ? 700 : JITSchedule::callGas::value; + + case Instruction::CREATE: + return JITSchedule::createGas::value; + + case Instruction::SUICIDE: + return m_rev >= EVMC_TANGERINE_WHISTLE ? 5000 : JITSchedule::stepGas0::value; + + default: + // For invalid instruction just return 0. + return 0; + } } -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/GasMeter.h b/libevmjit/GasMeter.h index e57d7aea..a8ee4ec6 100644 --- a/libevmjit/GasMeter.h +++ b/libevmjit/GasMeter.h @@ -1,8 +1,8 @@ #pragma once -#include #include "CompilerHelper.h" #include "Instruction.h" +#include namespace dev { @@ -13,58 +13,58 @@ namespace jit class RuntimeManager; using namespace evmjit; -class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper +class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper { public: - GasMeter(IRBuilder& _builder, RuntimeManager& _runtimeManager, evmc_revision rev); + GasMeter(IRBuilder& _builder, RuntimeManager& _runtimeManager, evmc_revision rev); - /// Count step cost of instruction - void count(Instruction _inst); + /// Count step cost of instruction + void count(Instruction _inst); - /// Count additional cost - void count(llvm::Value* _cost, llvm::Value* _jmpBuf = nullptr, llvm::Value* _gasPtr = nullptr); + /// Count additional cost + void count(llvm::Value* _cost, llvm::Value* _jmpBuf = nullptr, llvm::Value* _gasPtr = nullptr); - /// Calculate & count gas cost for SSTORE instruction - void countSStore(class Ext& _ext, llvm::Value* _index, llvm::Value* _newValue); + /// Calculate & count gas cost for SSTORE instruction + void countSStore(class Ext& _ext, llvm::Value* _index, llvm::Value* _newValue); - /// Calculate & count additional gas cost for EXP instruction - void countExp(llvm::Value* _exponent); + /// Calculate & count additional gas cost for EXP instruction + void countExp(llvm::Value* _exponent); - /// Count gas cost of LOG data - void countLogData(llvm::Value* _dataLength); + /// Count gas cost of LOG data + void countLogData(llvm::Value* _dataLength); - /// Count gas cost of SHA3 data - void countSha3Data(llvm::Value* _dataLength); + /// Count gas cost of SHA3 data + void countSha3Data(llvm::Value* _dataLength); - /// Finalize cost-block by checking gas needed for the block before the block - void commitCostBlock(); + /// Finalize cost-block by checking gas needed for the block before the block + void commitCostBlock(); - /// Give back an amount of gas not used by a call - void giveBack(llvm::Value* _gas); + /// Give back an amount of gas not used by a call + void giveBack(llvm::Value* _gas); - /// Generate code that checks the cost of additional memory used by program - void countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr); + /// Generate code that checks the cost of additional memory used by program + void countMemory( + llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr); - /// Count addional gas cost for memory copy - void countCopy(llvm::Value* _copyWords); + /// Count addional gas cost for memory copy + void countCopy(llvm::Value* _copyWords); private: - int64_t getStepCost(Instruction inst) const; + int64_t getStepCost(Instruction inst) const; - /// Cumulative gas cost of a block of instructions - /// @TODO Handle overflow - int64_t m_blockCost = 0; + /// Cumulative gas cost of a block of instructions + /// @TODO Handle overflow + int64_t m_blockCost = 0; - llvm::CallInst* m_checkCall = nullptr; - llvm::Function* m_gasCheckFunc = nullptr; + llvm::CallInst* m_checkCall = nullptr; + llvm::Function* m_gasCheckFunc = nullptr; - RuntimeManager& m_runtimeManager; + RuntimeManager& m_runtimeManager; - /// EVM revision. - evmc_revision m_rev; + /// EVM revision. + evmc_revision m_rev; }; -} -} -} - +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Instruction.cpp b/libevmjit/Instruction.cpp index c2e267cd..00dccc4d 100644 --- a/libevmjit/Instruction.cpp +++ b/libevmjit/Instruction.cpp @@ -1,39 +1,42 @@ #include "Instruction.h" +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include -#include "preprocessor/llvm_includes_end.h" namespace dev { namespace evmjit { - llvm::APInt readPushData(code_iterator& _curr, code_iterator _end) { - auto pushInst = *_curr; - assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); - auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; - llvm::APInt value(256, 0); - ++_curr; // Point the data - for (decltype(numBytes) i = 0; i < numBytes; ++i) - { - byte b = (_curr != _end) ? *_curr++ : 0; - value <<= 8; - value |= b; - } - --_curr; // Point the last real byte read - return value; + auto pushInst = *_curr; + assert(Instruction(pushInst) >= Instruction::PUSH1 && + Instruction(pushInst) <= Instruction::PUSH32); + auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; + llvm::APInt value(256, 0); + ++_curr; // Point the data + for (decltype(numBytes) i = 0; i < numBytes; ++i) + { + byte b = (_curr != _end) ? *_curr++ : 0; + value <<= 8; + value |= b; + } + --_curr; // Point the last real byte read + return value; } void skipPushData(code_iterator& _curr, code_iterator _end) { - auto pushInst = *_curr; - assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); - auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; - --_end; - for (decltype(numBytes) i = 0; i < numBytes && _curr < _end; ++i, ++_curr) {} + auto pushInst = *_curr; + assert(Instruction(pushInst) >= Instruction::PUSH1 && + Instruction(pushInst) <= Instruction::PUSH32); + auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; + --_end; + for (decltype(numBytes) i = 0; i < numBytes && _curr < _end; ++i, ++_curr) + { + } } -} -} +} // namespace evmjit +} // namespace dev diff --git a/libevmjit/Instruction.h b/libevmjit/Instruction.h index 79276f65..32e2ed35 100644 --- a/libevmjit/Instruction.h +++ b/libevmjit/Instruction.h @@ -4,163 +4,163 @@ namespace llvm { - class APInt; +class APInt; } namespace dev { namespace evmjit { - /// Virtual machine bytecode instruction. -enum class Instruction: uint8_t +enum class Instruction : uint8_t { - STOP = 0x00, ///< halts execution - ADD, ///< addition operation - MUL, ///< mulitplication operation - SUB, ///< subtraction operation - DIV, ///< integer division operation - SDIV, ///< signed integer division operation - MOD, ///< modulo remainder operation - SMOD, ///< signed modulo remainder operation - ADDMOD, ///< unsigned modular addition - MULMOD, ///< unsigned modular multiplication - EXP, ///< exponential operation - SIGNEXTEND, ///< extend length of signed integer - - LT = 0x10, ///< less-than comparision - GT, ///< greater-than comparision - SLT, ///< signed less-than comparision - SGT, ///< signed greater-than comparision - EQ, ///< equality comparision - ISZERO, ///< simple not operator - AND, ///< bitwise AND operation - OR, ///< bitwise OR operation - XOR, ///< bitwise XOR operation - NOT, ///< bitwise NOT opertation - BYTE, ///< retrieve single byte from word - - SHA3 = 0x20, ///< compute SHA3-256 hash - - ADDRESS = 0x30, ///< get address of currently executing account - BALANCE, ///< get balance of the given account - ORIGIN, ///< get execution origination address - CALLER, ///< get caller address - CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution - CALLDATALOAD, ///< get input data of current environment - CALLDATASIZE, ///< get size of input data in current environment - CALLDATACOPY, ///< copy input data in current environment to memory - CODESIZE, ///< get size of code running in current environment - CODECOPY, ///< copy code running in current environment to memory - GASPRICE, ///< get price of gas in current environment - EXTCODESIZE, ///< get external code size (from another contract) - EXTCODECOPY, ///< copy external code (from another contract) - RETURNDATASIZE = 0x3d, - RETURNDATACOPY = 0x3e, - - BLOCKHASH = 0x40, ///< get hash of most recent complete block - COINBASE, ///< get the block's coinbase address - TIMESTAMP, ///< get the block's timestamp - NUMBER, ///< get the block's number - DIFFICULTY, ///< get the block's difficulty - GASLIMIT, ///< get the block's gas limit - - POP = 0x50, ///< remove item from stack - MLOAD, ///< load word from memory - MSTORE, ///< save word to memory - MSTORE8, ///< save byte to memory - SLOAD, ///< load word from storage - SSTORE, ///< save word to storage - JUMP, ///< alter the program counter - JUMPI, ///< conditionally alter the program counter - PC, ///< get the program counter - MSIZE, ///< get the size of active memory - GAS, ///< get the amount of available gas - JUMPDEST, ///< set a potential jump destination - - PUSH1 = 0x60, ///< place 1 byte item on stack - PUSH2, ///< place 2 byte item on stack - PUSH3, ///< place 3 byte item on stack - PUSH4, ///< place 4 byte item on stack - PUSH5, ///< place 5 byte item on stack - PUSH6, ///< place 6 byte item on stack - PUSH7, ///< place 7 byte item on stack - PUSH8, ///< place 8 byte item on stack - PUSH9, ///< place 9 byte item on stack - PUSH10, ///< place 10 byte item on stack - PUSH11, ///< place 11 byte item on stack - PUSH12, ///< place 12 byte item on stack - PUSH13, ///< place 13 byte item on stack - PUSH14, ///< place 14 byte item on stack - PUSH15, ///< place 15 byte item on stack - PUSH16, ///< place 16 byte item on stack - PUSH17, ///< place 17 byte item on stack - PUSH18, ///< place 18 byte item on stack - PUSH19, ///< place 19 byte item on stack - PUSH20, ///< place 20 byte item on stack - PUSH21, ///< place 21 byte item on stack - PUSH22, ///< place 22 byte item on stack - PUSH23, ///< place 23 byte item on stack - PUSH24, ///< place 24 byte item on stack - PUSH25, ///< place 25 byte item on stack - PUSH26, ///< place 26 byte item on stack - PUSH27, ///< place 27 byte item on stack - PUSH28, ///< place 28 byte item on stack - PUSH29, ///< place 29 byte item on stack - PUSH30, ///< place 30 byte item on stack - PUSH31, ///< place 31 byte item on stack - PUSH32, ///< place 32 byte item on stack - - DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack - DUP2, ///< copies the second highest item in the stack to the top of the stack - DUP3, ///< copies the third highest item in the stack to the top of the stack - DUP4, ///< copies the 4th highest item in the stack to the top of the stack - DUP5, ///< copies the 5th highest item in the stack to the top of the stack - DUP6, ///< copies the 6th highest item in the stack to the top of the stack - DUP7, ///< copies the 7th highest item in the stack to the top of the stack - DUP8, ///< copies the 8th highest item in the stack to the top of the stack - DUP9, ///< copies the 9th highest item in the stack to the top of the stack - DUP10, ///< copies the 10th highest item in the stack to the top of the stack - DUP11, ///< copies the 11th highest item in the stack to the top of the stack - DUP12, ///< copies the 12th highest item in the stack to the top of the stack - DUP13, ///< copies the 13th highest item in the stack to the top of the stack - DUP14, ///< copies the 14th highest item in the stack to the top of the stack - DUP15, ///< copies the 15th highest item in the stack to the top of the stack - DUP16, ///< copies the 16th highest item in the stack to the top of the stack - - SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack - SWAP2, ///< swaps the highest and third highest value on the stack - SWAP3, ///< swaps the highest and 4th highest value on the stack - SWAP4, ///< swaps the highest and 5th highest value on the stack - SWAP5, ///< swaps the highest and 6th highest value on the stack - SWAP6, ///< swaps the highest and 7th highest value on the stack - SWAP7, ///< swaps the highest and 8th highest value on the stack - SWAP8, ///< swaps the highest and 9th highest value on the stack - SWAP9, ///< swaps the highest and 10th highest value on the stack - SWAP10, ///< swaps the highest and 11th highest value on the stack - SWAP11, ///< swaps the highest and 12th highest value on the stack - SWAP12, ///< swaps the highest and 13th highest value on the stack - SWAP13, ///< swaps the highest and 14th highest value on the stack - SWAP14, ///< swaps the highest and 15th highest value on the stack - SWAP15, ///< swaps the highest and 16th highest value on the stack - SWAP16, ///< swaps the highest and 17th highest value on the stack - - LOG0 = 0xa0, ///< Makes a log entry; no topics. - LOG1, ///< Makes a log entry; 1 topic. - LOG2, ///< Makes a log entry; 2 topics. - LOG3, ///< Makes a log entry; 3 topics. - LOG4, ///< Makes a log entry; 4 topics. - - CREATE = 0xf0, ///< create a new account with associated code - CALL, ///< message-call into an account - CALLCODE, ///< message-call with another account's code only - RETURN, ///< halt execution returning output data - DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender (only from homestead on) - - STATICCALL = 0xfa, ///< Like CALL but does not allow state modification. - - REVERT = 0xfd, ///< stop execution and revert state changes, without consuming all provided gas - SUICIDE = 0xff ///< halt execution and register account for later deletion + STOP = 0x00, ///< halts execution + ADD, ///< addition operation + MUL, ///< mulitplication operation + SUB, ///< subtraction operation + DIV, ///< integer division operation + SDIV, ///< signed integer division operation + MOD, ///< modulo remainder operation + SMOD, ///< signed modulo remainder operation + ADDMOD, ///< unsigned modular addition + MULMOD, ///< unsigned modular multiplication + EXP, ///< exponential operation + SIGNEXTEND, ///< extend length of signed integer + + LT = 0x10, ///< less-than comparision + GT, ///< greater-than comparision + SLT, ///< signed less-than comparision + SGT, ///< signed greater-than comparision + EQ, ///< equality comparision + ISZERO, ///< simple not operator + AND, ///< bitwise AND operation + OR, ///< bitwise OR operation + XOR, ///< bitwise XOR operation + NOT, ///< bitwise NOT opertation + BYTE, ///< retrieve single byte from word + + SHA3 = 0x20, ///< compute SHA3-256 hash + + ADDRESS = 0x30, ///< get address of currently executing account + BALANCE, ///< get balance of the given account + ORIGIN, ///< get execution origination address + CALLER, ///< get caller address + CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this + ///< execution + CALLDATALOAD, ///< get input data of current environment + CALLDATASIZE, ///< get size of input data in current environment + CALLDATACOPY, ///< copy input data in current environment to memory + CODESIZE, ///< get size of code running in current environment + CODECOPY, ///< copy code running in current environment to memory + GASPRICE, ///< get price of gas in current environment + EXTCODESIZE, ///< get external code size (from another contract) + EXTCODECOPY, ///< copy external code (from another contract) + RETURNDATASIZE = 0x3d, + RETURNDATACOPY = 0x3e, + + BLOCKHASH = 0x40, ///< get hash of most recent complete block + COINBASE, ///< get the block's coinbase address + TIMESTAMP, ///< get the block's timestamp + NUMBER, ///< get the block's number + DIFFICULTY, ///< get the block's difficulty + GASLIMIT, ///< get the block's gas limit + + POP = 0x50, ///< remove item from stack + MLOAD, ///< load word from memory + MSTORE, ///< save word to memory + MSTORE8, ///< save byte to memory + SLOAD, ///< load word from storage + SSTORE, ///< save word to storage + JUMP, ///< alter the program counter + JUMPI, ///< conditionally alter the program counter + PC, ///< get the program counter + MSIZE, ///< get the size of active memory + GAS, ///< get the amount of available gas + JUMPDEST, ///< set a potential jump destination + + PUSH1 = 0x60, ///< place 1 byte item on stack + PUSH2, ///< place 2 byte item on stack + PUSH3, ///< place 3 byte item on stack + PUSH4, ///< place 4 byte item on stack + PUSH5, ///< place 5 byte item on stack + PUSH6, ///< place 6 byte item on stack + PUSH7, ///< place 7 byte item on stack + PUSH8, ///< place 8 byte item on stack + PUSH9, ///< place 9 byte item on stack + PUSH10, ///< place 10 byte item on stack + PUSH11, ///< place 11 byte item on stack + PUSH12, ///< place 12 byte item on stack + PUSH13, ///< place 13 byte item on stack + PUSH14, ///< place 14 byte item on stack + PUSH15, ///< place 15 byte item on stack + PUSH16, ///< place 16 byte item on stack + PUSH17, ///< place 17 byte item on stack + PUSH18, ///< place 18 byte item on stack + PUSH19, ///< place 19 byte item on stack + PUSH20, ///< place 20 byte item on stack + PUSH21, ///< place 21 byte item on stack + PUSH22, ///< place 22 byte item on stack + PUSH23, ///< place 23 byte item on stack + PUSH24, ///< place 24 byte item on stack + PUSH25, ///< place 25 byte item on stack + PUSH26, ///< place 26 byte item on stack + PUSH27, ///< place 27 byte item on stack + PUSH28, ///< place 28 byte item on stack + PUSH29, ///< place 29 byte item on stack + PUSH30, ///< place 30 byte item on stack + PUSH31, ///< place 31 byte item on stack + PUSH32, ///< place 32 byte item on stack + + DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack + DUP2, ///< copies the second highest item in the stack to the top of the stack + DUP3, ///< copies the third highest item in the stack to the top of the stack + DUP4, ///< copies the 4th highest item in the stack to the top of the stack + DUP5, ///< copies the 5th highest item in the stack to the top of the stack + DUP6, ///< copies the 6th highest item in the stack to the top of the stack + DUP7, ///< copies the 7th highest item in the stack to the top of the stack + DUP8, ///< copies the 8th highest item in the stack to the top of the stack + DUP9, ///< copies the 9th highest item in the stack to the top of the stack + DUP10, ///< copies the 10th highest item in the stack to the top of the stack + DUP11, ///< copies the 11th highest item in the stack to the top of the stack + DUP12, ///< copies the 12th highest item in the stack to the top of the stack + DUP13, ///< copies the 13th highest item in the stack to the top of the stack + DUP14, ///< copies the 14th highest item in the stack to the top of the stack + DUP15, ///< copies the 15th highest item in the stack to the top of the stack + DUP16, ///< copies the 16th highest item in the stack to the top of the stack + + SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack + SWAP2, ///< swaps the highest and third highest value on the stack + SWAP3, ///< swaps the highest and 4th highest value on the stack + SWAP4, ///< swaps the highest and 5th highest value on the stack + SWAP5, ///< swaps the highest and 6th highest value on the stack + SWAP6, ///< swaps the highest and 7th highest value on the stack + SWAP7, ///< swaps the highest and 8th highest value on the stack + SWAP8, ///< swaps the highest and 9th highest value on the stack + SWAP9, ///< swaps the highest and 10th highest value on the stack + SWAP10, ///< swaps the highest and 11th highest value on the stack + SWAP11, ///< swaps the highest and 12th highest value on the stack + SWAP12, ///< swaps the highest and 13th highest value on the stack + SWAP13, ///< swaps the highest and 14th highest value on the stack + SWAP14, ///< swaps the highest and 15th highest value on the stack + SWAP15, ///< swaps the highest and 16th highest value on the stack + SWAP16, ///< swaps the highest and 17th highest value on the stack + + LOG0 = 0xa0, ///< Makes a log entry; no topics. + LOG1, ///< Makes a log entry; 1 topic. + LOG2, ///< Makes a log entry; 2 topics. + LOG3, ///< Makes a log entry; 3 topics. + LOG4, ///< Makes a log entry; 4 topics. + + CREATE = 0xf0, ///< create a new account with associated code + CALL, ///< message-call into an account + CALLCODE, ///< message-call with another account's code only + RETURN, ///< halt execution returning output data + DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender (only from homestead on) + + STATICCALL = 0xfa, ///< Like CALL but does not allow state modification. + + REVERT = 0xfd, ///< stop execution and revert state changes, without consuming all provided gas + SUICIDE = 0xff ///< halt execution and register account for later deletion }; /// Reads PUSH data from pointed fragment of bytecode and constructs number out of it @@ -172,72 +172,75 @@ llvm::APInt readPushData(code_iterator& _curr, code_iterator _end); /// @param _curr is updated and points the last real byte skipped void skipPushData(code_iterator& _curr, code_iterator _end); -#define ANY_PUSH PUSH1: \ - case Instruction::PUSH2: \ - case Instruction::PUSH3: \ - case Instruction::PUSH4: \ - case Instruction::PUSH5: \ - case Instruction::PUSH6: \ - case Instruction::PUSH7: \ - case Instruction::PUSH8: \ - case Instruction::PUSH9: \ - case Instruction::PUSH10: \ - case Instruction::PUSH11: \ - case Instruction::PUSH12: \ - case Instruction::PUSH13: \ - case Instruction::PUSH14: \ - case Instruction::PUSH15: \ - case Instruction::PUSH16: \ - case Instruction::PUSH17: \ - case Instruction::PUSH18: \ - case Instruction::PUSH19: \ - case Instruction::PUSH20: \ - case Instruction::PUSH21: \ - case Instruction::PUSH22: \ - case Instruction::PUSH23: \ - case Instruction::PUSH24: \ - case Instruction::PUSH25: \ - case Instruction::PUSH26: \ - case Instruction::PUSH27: \ - case Instruction::PUSH28: \ - case Instruction::PUSH29: \ - case Instruction::PUSH30: \ - case Instruction::PUSH31: \ - case Instruction::PUSH32 - -#define ANY_DUP DUP1: \ - case Instruction::DUP2: \ - case Instruction::DUP3: \ - case Instruction::DUP4: \ - case Instruction::DUP5: \ - case Instruction::DUP6: \ - case Instruction::DUP7: \ - case Instruction::DUP8: \ - case Instruction::DUP9: \ - case Instruction::DUP10: \ - case Instruction::DUP11: \ - case Instruction::DUP12: \ - case Instruction::DUP13: \ - case Instruction::DUP14: \ - case Instruction::DUP15: \ - case Instruction::DUP16 - -#define ANY_SWAP SWAP1: \ - case Instruction::SWAP2: \ - case Instruction::SWAP3: \ - case Instruction::SWAP4: \ - case Instruction::SWAP5: \ - case Instruction::SWAP6: \ - case Instruction::SWAP7: \ - case Instruction::SWAP8: \ - case Instruction::SWAP9: \ - case Instruction::SWAP10: \ - case Instruction::SWAP11: \ - case Instruction::SWAP12: \ - case Instruction::SWAP13: \ - case Instruction::SWAP14: \ - case Instruction::SWAP15: \ - case Instruction::SWAP16 - -} -} +#define ANY_PUSH \ + PUSH1: \ + case Instruction::PUSH2: \ + case Instruction::PUSH3: \ + case Instruction::PUSH4: \ + case Instruction::PUSH5: \ + case Instruction::PUSH6: \ + case Instruction::PUSH7: \ + case Instruction::PUSH8: \ + case Instruction::PUSH9: \ + case Instruction::PUSH10: \ + case Instruction::PUSH11: \ + case Instruction::PUSH12: \ + case Instruction::PUSH13: \ + case Instruction::PUSH14: \ + case Instruction::PUSH15: \ + case Instruction::PUSH16: \ + case Instruction::PUSH17: \ + case Instruction::PUSH18: \ + case Instruction::PUSH19: \ + case Instruction::PUSH20: \ + case Instruction::PUSH21: \ + case Instruction::PUSH22: \ + case Instruction::PUSH23: \ + case Instruction::PUSH24: \ + case Instruction::PUSH25: \ + case Instruction::PUSH26: \ + case Instruction::PUSH27: \ + case Instruction::PUSH28: \ + case Instruction::PUSH29: \ + case Instruction::PUSH30: \ + case Instruction::PUSH31: \ + case Instruction::PUSH32 + +#define ANY_DUP \ + DUP1: \ + case Instruction::DUP2: \ + case Instruction::DUP3: \ + case Instruction::DUP4: \ + case Instruction::DUP5: \ + case Instruction::DUP6: \ + case Instruction::DUP7: \ + case Instruction::DUP8: \ + case Instruction::DUP9: \ + case Instruction::DUP10: \ + case Instruction::DUP11: \ + case Instruction::DUP12: \ + case Instruction::DUP13: \ + case Instruction::DUP14: \ + case Instruction::DUP15: \ + case Instruction::DUP16 + +#define ANY_SWAP \ + SWAP1: \ + case Instruction::SWAP2: \ + case Instruction::SWAP3: \ + case Instruction::SWAP4: \ + case Instruction::SWAP5: \ + case Instruction::SWAP6: \ + case Instruction::SWAP7: \ + case Instruction::SWAP8: \ + case Instruction::SWAP9: \ + case Instruction::SWAP10: \ + case Instruction::SWAP11: \ + case Instruction::SWAP12: \ + case Instruction::SWAP13: \ + case Instruction::SWAP14: \ + case Instruction::SWAP15: \ + case Instruction::SWAP16 + +} // namespace evmjit +} // namespace dev diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp index f82f677c..1f3b2197 100644 --- a/libevmjit/JIT.cpp +++ b/libevmjit/JIT.cpp @@ -3,40 +3,41 @@ #include #include +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" -#include +#include +#include #include #include #include #include +#include #include #include -#include -#include "preprocessor/llvm_includes_end.h" -#include "Ext.h" -#include "Compiler.h" -#include "Optimizer.h" +#include "BuildInfo.gen.h" #include "Cache.h" +#include "Compiler.h" #include "ExecStats.h" +#include "Ext.h" +#include "Optimizer.h" #include "Utils.h" -#include "BuildInfo.gen.h" // FIXME: Move these checks to evmc tests. static_assert(sizeof(evmc_uint256be) == 32, "evmc_uint256be is too big"); static_assert(sizeof(evmc_address) == 20, "evmc_address is too big"); static_assert(sizeof(evmc_result) == 64, "evmc_result does not fit cache line"); -static_assert(sizeof(evmc_message) <= 18*8, "evmc_message not optimally packed"); -static_assert(offsetof(evmc_message, code_hash) % 8 == 0, "evmc_message.code_hash not aligned"); +static_assert(sizeof(evmc_message) <= 18 * 8, "evmc_message not optimally packed"); // Check enums match int size. // On GCC/clang the underlying type should be unsigned int, on MSVC int -static_assert(sizeof(evmc_call_kind) == sizeof(int), "Enum `evmc_call_kind` is not the size of int"); -static_assert(sizeof(evmc_revision) == sizeof(int), "Enum `evmc_revision` is not the size of int"); +static_assert( + sizeof(evmc_call_kind) == sizeof(int), "Enum `evmc_call_kind` is not the size of int"); +static_assert(sizeof(evmc_revision) == sizeof(int), "Enum `evmc_revision` is not the size of int"); constexpr size_t optionalDataSize = sizeof(evmc_result) - offsetof(evmc_result, create_address); -static_assert(optionalDataSize == sizeof(evmc_result_optional_data), ""); +static_assert(optionalDataSize == sizeof(evmc_result_optional_storage), ""); namespace dev @@ -47,7 +48,7 @@ using namespace eth::jit; namespace { -using ExecFunc = ReturnCode(*)(ExecutionContext*); +using ExecFunc = ReturnCode (*)(ExecutionContext*); struct CodeMapEntry { @@ -60,111 +61,121 @@ struct CodeMapEntry char toChar(evmc_revision rev) { - switch (rev) - { - case EVMC_FRONTIER: return 'F'; - case EVMC_HOMESTEAD: return 'H'; - case EVMC_TANGERINE_WHISTLE: return 'T'; - case EVMC_SPURIOUS_DRAGON: return 'S'; - case EVMC_BYZANTIUM: return 'B'; - case EVMC_CONSTANTINOPLE: return 'C'; - } - LLVM_BUILTIN_UNREACHABLE; + switch (rev) + { + case EVMC_FRONTIER: + return 'F'; + case EVMC_HOMESTEAD: + return 'H'; + case EVMC_TANGERINE_WHISTLE: + return 'T'; + case EVMC_SPURIOUS_DRAGON: + return 'S'; + case EVMC_BYZANTIUM: + return 'B'; + case EVMC_CONSTANTINOPLE: + return 'C'; + case EVMC_PETERSBURG: + return 'P'; + case EVMC_ISTANBUL: + return 'I'; + } + LLVM_BUILTIN_UNREACHABLE; } /// Combine code hash and EVM revision into a printable code identifier. std::string makeCodeId(evmc_uint256be codeHash, evmc_revision rev, uint32_t flags) { - static const auto hexChars = "0123456789abcdef"; - std::string str; - str.reserve(sizeof(codeHash) * 2 + 1); - for (auto b: codeHash.bytes) - { - str.push_back(hexChars[b >> 4]); - str.push_back(hexChars[b & 0xf]); - } - str.push_back(toChar(rev)); - if (flags & EVMC_STATIC) - str.push_back('S'); - return str; + static const auto hexChars = "0123456789abcdef"; + std::string str; + str.reserve(sizeof(codeHash) * 2 + 1); + for (auto b : codeHash.bytes) + { + str.push_back(hexChars[b >> 4]); + str.push_back(hexChars[b & 0xf]); + } + str.push_back(toChar(rev)); + if (flags & EVMC_STATIC) + str.push_back('S'); + return str; } void printVersion() { - std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n" - << " EVMJIT version " << EVMJIT_VERSION << "\n" + std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n" + << " EVMJIT version " << EVMJIT_VERSION << "\n" #ifdef NDEBUG - << " Optimized build, " + << " Optimized build, " #else - << " DEBUG build, " + << " DEBUG build, " #endif - << __DATE__ << " (" << __TIME__ << ")\n" - << std::endl; + << __DATE__ << " (" << __TIME__ << ")\n" + << std::endl; } namespace cl = llvm::cl; cl::opt g_optimize{"O", cl::desc{"Optimize"}}; cl::opt g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"}, - cl::values( - clEnumValN(CacheMode::off, "0", "Disabled"), - clEnumValN(CacheMode::on, "1", "Enabled"), - clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."), - clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."), - clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."), - clEnumValN(CacheMode::preload, "p", "Preload all cached objects."))}; + cl::values(clEnumValN(CacheMode::off, "0", "Disabled"), + clEnumValN(CacheMode::on, "1", "Enabled"), + clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."), + clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."), + clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."), + clEnumValN(CacheMode::preload, "p", "Preload all cached objects."))}; cl::opt g_stats{"st", cl::desc{"Statistics"}}; cl::opt g_dump{"dump", cl::desc{"Dump LLVM IR module"}}; void parseOptions() { - static llvm::llvm_shutdown_obj shutdownObj{}; - cl::AddExtraVersionPrinter(printVersion); - cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); + static llvm::llvm_shutdown_obj shutdownObj{}; + cl::AddExtraVersionPrinter(printVersion); + cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); } class SymbolResolver; -class JITImpl: public evmc_instance +class JITImpl : public evmc_instance { - std::unique_ptr m_engine; - SymbolResolver const* m_memoryMgr = nullptr; - mutable std::mutex x_codeMap; - std::unordered_map m_codeMap; + std::unique_ptr m_engine; + SymbolResolver const* m_memoryMgr = nullptr; + mutable std::mutex x_codeMap; + std::unordered_map m_codeMap; - static llvm::LLVMContext& getLLVMContext() - { - // TODO: This probably should be thread_local, but for now that causes - // a crash when MCJIT is destroyed. - static llvm::LLVMContext llvmContext; - return llvmContext; - } + static llvm::LLVMContext& getLLVMContext() + { + // TODO: This probably should be thread_local, but for now that causes + // a crash when MCJIT is destroyed. + static llvm::LLVMContext llvmContext; + return llvmContext; + } - void createEngine(); + void createEngine(); public: - static JITImpl& instance() - { - // We need to keep this a singleton. - // so we only call changeVersion on it. - static JITImpl s_instance; - return s_instance; - } + static JITImpl& instance() + { + // We need to keep this a singleton. + // so we only call changeVersion on it. + static JITImpl s_instance; + return s_instance; + } - JITImpl(); + JITImpl(); - void checkMemorySize(); + void checkMemorySize(); - llvm::ExecutionEngine& engine() { return *m_engine; } + llvm::ExecutionEngine& engine() { return *m_engine; } - CodeMapEntry getExecFunc(std::string const& _codeIdentifier); - void mapExecFunc(std::string const& _codeIdentifier, ExecFunc _funcAddr); + CodeMapEntry getExecFunc(std::string const& _codeIdentifier); + void mapExecFunc(std::string const& _codeIdentifier, ExecFunc _funcAddr); - ExecFunc compile(evmc_revision _rev, bool _staticCall, byte const* _code, uint64_t _codeSize, std::string const& _codeIdentifier); + ExecFunc compile(evmc_revision _rev, bool _staticCall, byte const* _code, uint64_t _codeSize, + std::string const& _codeIdentifier); - evmc_context_fn_table const* host = nullptr; + evmc_host_interface const* host = nullptr; - evmc_message const* currentMsg = nullptr; - std::vector returnBuffer; + evmc_message const* currentMsg = nullptr; + std::vector returnBuffer; std::vector codeBuffer; @@ -172,54 +183,53 @@ class JITImpl: public evmc_instance }; int64_t call(evmc_context* _ctx, int _kind, int64_t _gas, evmc_address const* _address, - evmc_uint256be const* _value, uint8_t const* _inputData, size_t _inputSize, uint8_t* _outputData, - size_t _outputSize, uint8_t const** o_bufData, size_t* o_bufSize) noexcept + evmc_uint256be const* _value, uint8_t const* _inputData, size_t _inputSize, + uint8_t* _outputData, size_t _outputSize, uint8_t const** o_bufData, size_t* o_bufSize) noexcept { - // FIXME: Handle unexpected exceptions. - auto& jit = JITImpl::instance(); - - evmc_message msg; - msg.destination = *_address; - msg.sender = _kind != EVMC_DELEGATECALL ? jit.currentMsg->destination : jit.currentMsg->sender; - msg.value = _kind != EVMC_DELEGATECALL ? *_value : jit.currentMsg->value; - msg.input_data = _inputData; - msg.input_size = _inputSize; - msg.gas = _gas; - msg.depth = jit.currentMsg->depth + 1; - msg.flags = jit.currentMsg->flags; - if (_kind == EVM_STATICCALL) - { - msg.kind = EVMC_CALL; - msg.flags |= EVMC_STATIC; - } - else - msg.kind = static_cast(_kind); - - // FIXME: Handle code hash. - evmc_result result; - jit.host->call(&result, _ctx, &msg); - // FIXME: Clarify when gas_left is valid. - int64_t r = result.gas_left; - - // Handle output. It can contain data from RETURN or REVERT opcodes. - auto size = std::min(_outputSize, result.output_size); - std::copy_n(result.output_data, size, _outputData); - - // Update RETURNDATA buffer. - // The buffer is already cleared. - jit.returnBuffer = {result.output_data, result.output_data + result.output_size}; - *o_bufData = jit.returnBuffer.data(); - *o_bufSize = jit.returnBuffer.size(); - - if (_kind == EVMC_CREATE && result.status_code == EVMC_SUCCESS) - std::copy_n(result.create_address.bytes, sizeof(result.create_address), _outputData); - - if (result.status_code != EVMC_SUCCESS) - r |= EVM_CALL_FAILURE; - - if (result.release) - result.release(&result); - return r; + // FIXME: Handle unexpected exceptions. + auto& jit = JITImpl::instance(); + + evmc_message msg; + msg.destination = *_address; + msg.sender = _kind != EVMC_DELEGATECALL ? jit.currentMsg->destination : jit.currentMsg->sender; + msg.value = _kind != EVMC_DELEGATECALL ? *_value : jit.currentMsg->value; + msg.input_data = _inputData; + msg.input_size = _inputSize; + msg.gas = _gas; + msg.depth = jit.currentMsg->depth + 1; + msg.flags = jit.currentMsg->flags; + if (_kind == EVM_STATICCALL) + { + msg.kind = EVMC_CALL; + msg.flags |= EVMC_STATIC; + } + else + msg.kind = static_cast(_kind); + + // FIXME: Handle code hash. + evmc_result result = jit.host->call(_ctx, &msg); + // FIXME: Clarify when gas_left is valid. + int64_t r = result.gas_left; + + // Handle output. It can contain data from RETURN or REVERT opcodes. + auto size = std::min(_outputSize, result.output_size); + std::copy_n(result.output_data, size, _outputData); + + // Update RETURNDATA buffer. + // The buffer is already cleared. + jit.returnBuffer = {result.output_data, result.output_data + result.output_size}; + *o_bufData = jit.returnBuffer.data(); + *o_bufSize = jit.returnBuffer.size(); + + if (_kind == EVMC_CREATE && result.status_code == EVMC_SUCCESS) + std::copy_n(result.create_address.bytes, sizeof(result.create_address), _outputData); + + if (result.status_code != EVMC_SUCCESS) + r |= EVM_CALL_FAILURE; + + if (result.release) + result.release(&result); + return r; } @@ -239,16 +249,17 @@ size_t getCode(uint8_t** o_pCode, evmc_context* _ctx, evmc_address const* _addre class SymbolResolver : public llvm::SectionMemoryManager { - llvm::JITSymbol findSymbol(std::string const& _name) override - { - auto& jit = JITImpl::instance(); + llvm::JITSymbol findSymbol(std::string const& _name) override + { + auto& jit = JITImpl::instance(); - // Handle symbols' global prefix. - // If in current DataLayout global symbols are prefixed, drop the - // prefix from the name for local search. - char prefix = jit.engine().getDataLayout().getGlobalPrefix(); - llvm::StringRef unprefixedName = (prefix != '\0' && _name[0] == prefix) - ? llvm::StringRef{_name}.drop_front() : llvm::StringRef{_name}; + // Handle symbols' global prefix. + // If in current DataLayout global symbols are prefixed, drop the + // prefix from the name for local search. + char prefix = jit.engine().getDataLayout().getGlobalPrefix(); + llvm::StringRef unprefixedName = (prefix != '\0' && _name[0] == prefix) ? + llvm::StringRef{_name}.drop_front() : + llvm::StringRef{_name}; auto addr = llvm::StringSwitch(unprefixedName) @@ -268,48 +279,48 @@ class SymbolResolver : public llvm::SectionMemoryManager if (addr) return {addr, llvm::JITSymbolFlags::Exported}; - // Fallback to default implementation that would search for the symbol - // in the current process. Use the original prefixed symbol name. - // TODO: In the future we should control the whole set of requested - // symbols (like memcpy, memset, etc) to improve performance. - return llvm::SectionMemoryManager::findSymbol(_name); - } - - void reportMemorySize(size_t _addedSize) - { - m_totalMemorySize += _addedSize; - - if (!g_stats) - return; - - if (m_totalMemorySize >= m_printMemoryLimit) - { - constexpr size_t printMemoryStep = 10 * 1024 * 1024; - auto value = double(m_totalMemorySize) / printMemoryStep; - std::cerr << "EVMJIT total memory size: " << (10 * value) << " MB\n"; - m_printMemoryLimit += printMemoryStep; - } - } - - uint8_t* allocateCodeSection(uintptr_t _size, unsigned _a, unsigned _id, - llvm::StringRef _name) override - { - reportMemorySize(_size); - return llvm::SectionMemoryManager::allocateCodeSection(_size, _a, _id, _name); - } - - uint8_t* allocateDataSection(uintptr_t _size, unsigned _a, unsigned _id, - llvm::StringRef _name, bool _ro) override - { - reportMemorySize(_size); - return llvm::SectionMemoryManager::allocateDataSection(_size, _a, _id, _name, _ro); - } - - size_t m_totalMemorySize = 0; - size_t m_printMemoryLimit = 1024 * 1024; + // Fallback to default implementation that would search for the symbol + // in the current process. Use the original prefixed symbol name. + // TODO: In the future we should control the whole set of requested + // symbols (like memcpy, memset, etc) to improve performance. + return llvm::SectionMemoryManager::findSymbol(_name); + } + + void reportMemorySize(size_t _addedSize) + { + m_totalMemorySize += _addedSize; + + if (!g_stats) + return; + + if (m_totalMemorySize >= m_printMemoryLimit) + { + constexpr size_t printMemoryStep = 10 * 1024 * 1024; + auto value = double(m_totalMemorySize) / printMemoryStep; + std::cerr << "EVMJIT total memory size: " << (10 * value) << " MB\n"; + m_printMemoryLimit += printMemoryStep; + } + } + + uint8_t* allocateCodeSection( + uintptr_t _size, unsigned _a, unsigned _id, llvm::StringRef _name) override + { + reportMemorySize(_size); + return llvm::SectionMemoryManager::allocateCodeSection(_size, _a, _id, _name); + } + + uint8_t* allocateDataSection( + uintptr_t _size, unsigned _a, unsigned _id, llvm::StringRef _name, bool _ro) override + { + reportMemorySize(_size); + return llvm::SectionMemoryManager::allocateDataSection(_size, _a, _id, _name, _ro); + } + + size_t m_totalMemorySize = 0; + size_t m_printMemoryLimit = 1024 * 1024; public: - size_t totalMemorySize() const { return m_totalMemorySize; } + size_t totalMemorySize() const { return m_totalMemorySize; } }; @@ -327,123 +338,130 @@ void JITImpl::mapExecFunc(std::string const& _codeIdentifier, ExecFunc _funcAddr m_codeMap[_codeIdentifier].func = _funcAddr; } -ExecFunc JITImpl::compile(evmc_revision _rev, bool _staticCall, byte const* _code, uint64_t _codeSize, - std::string const& _codeIdentifier) +ExecFunc JITImpl::compile(evmc_revision _rev, bool _staticCall, byte const* _code, + uint64_t _codeSize, std::string const& _codeIdentifier) { - auto module = Cache::getObject(_codeIdentifier, getLLVMContext()); - if (!module) - { - // TODO: Listener support must be redesigned. These should be a feature of JITImpl - //listener->stateChanged(ExecState::Compilation); - assert(_code || !_codeSize); - //TODO: Can the Compiler be stateless? - module = Compiler({}, _rev, _staticCall, getLLVMContext()).compile(_code, _code + _codeSize, _codeIdentifier); - - if (g_optimize) - { - //listener->stateChanged(ExecState::Optimization); - optimize(*module); - } - - prepare(*module); - } - - if (g_dump) - { - llvm::raw_os_ostream cerr{std::cerr}; - module->print(cerr, nullptr); - } - - - m_engine->addModule(std::move(module)); - //listener->stateChanged(ExecState::CodeGen); - return (ExecFunc)m_engine->getFunctionAddress(_codeIdentifier); + auto module = Cache::getObject(_codeIdentifier, getLLVMContext()); + if (!module) + { + // TODO: Listener support must be redesigned. These should be a feature of JITImpl + // listener->stateChanged(ExecState::Compilation); + assert(_code || !_codeSize); + // TODO: Can the Compiler be stateless? + module = Compiler({}, _rev, _staticCall, getLLVMContext()) + .compile(_code, _code + _codeSize, _codeIdentifier); + + if (g_optimize) + { + // listener->stateChanged(ExecState::Optimization); + optimize(*module); + } + + prepare(*module); + } + + if (g_dump) + { + llvm::raw_os_ostream cerr{std::cerr}; + module->print(cerr, nullptr); + } + + + m_engine->addModule(std::move(module)); + // listener->stateChanged(ExecState::CodeGen); + return (ExecFunc)m_engine->getFunctionAddress(_codeIdentifier); } -} // anonymous namespace +} // anonymous namespace ExecutionContext::~ExecutionContext() noexcept { - if (m_memData) - std::free(m_memData); + if (m_memData) + std::free(m_memData); } bytes_ref ExecutionContext::getReturnData() const { - auto data = m_data->callData; - auto size = static_cast(m_data->callDataSize); + auto data = m_data->callData; + auto size = static_cast(m_data->callDataSize); - if (data < m_memData || data >= m_memData + m_memSize || size == 0) - { - assert(size == 0); // data can be an invalid pointer only if size is 0 - m_data->callData = nullptr; - return {}; - } + if (data < m_memData || data >= m_memData + m_memSize || size == 0) + { + assert(size == 0); // data can be an invalid pointer only if size is 0 + m_data->callData = nullptr; + return {}; + } - return bytes_ref{data, size}; + return bytes_ref{data, size}; } -extern "C" -{ +extern "C" { EXPORT evmc_instance* evmjit_create() { - // Let's always return the same instance. It's a bit of faking, but actually - // this might be a compliant implementation. - return &JITImpl::instance(); + // Let's always return the same instance. It's a bit of faking, but actually + // this might be a compliant implementation. + return &JITImpl::instance(); } static void destroy(evmc_instance* instance) { - (void)instance; - assert(instance == static_cast(&JITImpl::instance())); + (void)instance; + assert(instance == static_cast(&JITImpl::instance())); +} + +static evmc_capabilities_flagset getCapabilities(evmc_instance* instance) +{ + (void)instance; + return EVMC_CAPABILITY_EVM1; } static evmc_result execute(evmc_instance* instance, evmc_context* context, evmc_revision rev, - evmc_message const* msg, uint8_t const* code, size_t code_size) + evmc_message const* msg, uint8_t const* code, size_t code_size) { - auto& jit = *reinterpret_cast(instance); - - if (msg->depth == 0) - jit.checkMemorySize(); - - if (!jit.host) - jit.host = context->fn_table; - assert(jit.host == context->fn_table); // Require the fn_table not to change. - - // TODO: Temporary keep track of the current message. - evmc_message const* prevMsg = jit.currentMsg; - jit.currentMsg = msg; - - RuntimeData rt; - rt.code = code; - rt.codeSize = code_size; - rt.gas = msg->gas; - rt.callData = msg->input_data; - rt.callDataSize = msg->input_size; - std::memcpy(&rt.apparentValue, &msg->value, sizeof(msg->value)); - std::memset(&rt.address, 0, 12); - std::memcpy(&rt.address[12], &msg->destination, sizeof(msg->destination)); - std::memset(&rt.caller, 0, 12); - std::memcpy(&rt.caller[12], &msg->sender, sizeof(msg->sender)); - rt.depth = msg->depth; - - ExecutionContext ctx{rt, context}; - - evmc_result result; - result.status_code = EVMC_SUCCESS; - result.gas_left = 0; - result.output_data = nullptr; - result.output_size = 0; - result.release = nullptr; - - auto codeIdentifier = makeCodeId(msg->code_hash, rev, msg->flags); + auto& jit = *reinterpret_cast(instance); + + if (msg->depth == 0) + jit.checkMemorySize(); + + if (!jit.host) + jit.host = context->host; + assert(jit.host == context->host); // Require the host_interface not to change. + + // TODO: Temporary keep track of the current message. + evmc_message const* prevMsg = jit.currentMsg; + jit.currentMsg = msg; + + RuntimeData rt; + rt.code = code; + rt.codeSize = code_size; + rt.gas = msg->gas; + rt.callData = msg->input_data; + rt.callDataSize = msg->input_size; + std::memcpy(&rt.apparentValue, &msg->value, sizeof(msg->value)); + std::memset(&rt.address, 0, 12); + std::memcpy(&rt.address[12], &msg->destination, sizeof(msg->destination)); + std::memset(&rt.caller, 0, 12); + std::memcpy(&rt.caller[12], &msg->sender, sizeof(msg->sender)); + rt.depth = msg->depth; + + ExecutionContext ctx{rt, context}; + + evmc_result result; + result.status_code = EVMC_SUCCESS; + result.gas_left = 0; + result.output_data = nullptr; + result.output_size = 0; + result.release = nullptr; + + auto codeIdentifier = + makeCodeId(context->host->get_code_hash(context, &msg->destination), rev, msg->flags); auto codeEntry = jit.getExecFunc(codeIdentifier); auto func = codeEntry.func; if (!func) { - //FIXME: We have a race condition here! + // FIXME: We have a race condition here! if (codeEntry.hits <= jit.hitThreshold) { @@ -466,50 +484,50 @@ static evmc_result execute(evmc_instance* instance, evmc_context* context, evmc_ auto returnCode = func(&ctx); - if (returnCode == ReturnCode::Revert) - { - result.status_code = EVMC_REVERT; - result.gas_left = rt.gas; - } - else if (returnCode == ReturnCode::OutOfGas) - { - // EVMJIT does not provide information what exactly type of failure - // it was, so use generic EVM_FAILURE. - result.status_code = EVMC_FAILURE; - } - else - { - // In case of success return the amount of gas left. - result.gas_left = rt.gas; - } - - if (returnCode == ReturnCode::Return || returnCode == ReturnCode::Revert) - { - auto out = ctx.getReturnData(); - result.output_data = std::get<0>(out); - result.output_size = std::get<1>(out); - } - - // Take care of the internal memory. - if (ctx.m_memData) - { - // Use result's reserved data to store the memory pointer. - - evmc_get_optional_data(&result)->pointer = ctx.m_memData; - - // Set pointer to the destructor that will release the memory. - result.release = [](evmc_result const* r) - { - std::free(evmc_get_const_optional_data(r)->pointer); - }; - ctx.m_memData = nullptr; - } - - jit.currentMsg = prevMsg; - return result; + if (returnCode == ReturnCode::Revert) + { + result.status_code = EVMC_REVERT; + result.gas_left = rt.gas; + } + else if (returnCode == ReturnCode::OutOfGas) + { + // EVMJIT does not provide information what exactly type of failure + // it was, so use generic EVM_FAILURE. + result.status_code = EVMC_FAILURE; + } + else + { + // In case of success return the amount of gas left. + result.gas_left = rt.gas; + } + + if (returnCode == ReturnCode::Return || returnCode == ReturnCode::Revert) + { + auto out = ctx.getReturnData(); + result.output_data = std::get<0>(out); + result.output_size = std::get<1>(out); + } + + // Take care of the internal memory. + if (ctx.m_memData) + { + // Use result's reserved data to store the memory pointer. + + evmc_get_optional_storage(&result)->pointer = ctx.m_memData; + + // Set pointer to the destructor that will release the memory. + result.release = [](evmc_result const* r) { + std::free(evmc_get_const_optional_storage(r)->pointer); + }; + ctx.m_memData = nullptr; + } + + jit.currentMsg = prevMsg; + return result; } -static int setOption(evmc_instance* instance, const char* name, const char* value) noexcept +static evmc_set_option_result setOption( + evmc_instance* instance, const char* name, const char* value) noexcept { try { @@ -517,13 +535,13 @@ static int setOption(evmc_instance* instance, const char* name, const char* valu { auto& jit = static_cast(*instance); jit.hitThreshold = std::stoul(value); - return 1; + return EVMC_SET_OPTION_INVALID_NAME; } - return 0; + return EVMC_SET_OPTION_SUCCESS; } catch (...) { - return 0; + return EVMC_SET_OPTION_SUCCESS; } } @@ -531,71 +549,79 @@ static int setOption(evmc_instance* instance, const char* name, const char* valu void JITImpl::createEngine() { - auto module = llvm::make_unique("", getLLVMContext()); - - // FIXME: LLVM 3.7: test on Windows - auto triple = llvm::Triple(llvm::sys::getProcessTriple()); - if (triple.getOS() == llvm::Triple::OSType::Win32) - triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format - module->setTargetTriple(triple.str()); - - llvm::EngineBuilder builder(std::move(module)); - builder.setEngineKind(llvm::EngineKind::JIT); - auto memoryMgr = llvm::make_unique(); - m_memoryMgr = memoryMgr.get(); - builder.setMCJITMemoryManager(std::move(memoryMgr)); - builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); + auto module = llvm::make_unique("", getLLVMContext()); + + // FIXME: LLVM 3.7: test on Windows + auto triple = llvm::Triple(llvm::sys::getProcessTriple()); + if (triple.getOS() == llvm::Triple::OSType::Win32) + triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF + // format + module->setTargetTriple(triple.str()); + + llvm::EngineBuilder builder(std::move(module)); + builder.setEngineKind(llvm::EngineKind::JIT); + auto memoryMgr = llvm::make_unique(); + m_memoryMgr = memoryMgr.get(); + builder.setMCJITMemoryManager(std::move(memoryMgr)); + builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); #ifndef NDEBUG - builder.setVerifyModules(true); + builder.setVerifyModules(true); #endif - m_engine.reset(builder.create()); + m_engine.reset(builder.create()); + + // TODO: Update cache listener + m_engine->setObjectCache(Cache::init(g_cache, nullptr)); - // TODO: Update cache listener - m_engine->setObjectCache(Cache::init(g_cache, nullptr)); + // FIXME: Disabled during API changes + // if (preloadCache) + // Cache::preload(*m_engine, funcCache); +} - // FIXME: Disabled during API changes - //if (preloadCache) - // Cache::preload(*m_engine, funcCache); +evmc_instance createInstance() +{ + evmc_instance init = { + .abi_version = EVMC_ABI_VERSION, + .name = "evmjit", + .version = EVMJIT_VERSION, + .destroy = evmjit::destroy, + .execute = evmjit::execute, + .get_capabilities = evmjit::getCapabilities, + .set_tracer = nullptr, + .set_option = evmjit::setOption, + }; + return init; } -JITImpl::JITImpl() - : evmc_instance({ - EVMC_ABI_VERSION, - "evmjit", - EVMJIT_VERSION, - evmjit::destroy, - evmjit::execute, - evmjit::setOption, - }) +JITImpl::JITImpl() : evmc_instance(createInstance()) { - parseOptions(); + parseOptions(); - bool preloadCache = g_cache == CacheMode::preload; - if (preloadCache) - g_cache = CacheMode::on; + bool preloadCache = g_cache == CacheMode::preload; + if (preloadCache) + g_cache = CacheMode::on; - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); - createEngine(); + createEngine(); } void JITImpl::checkMemorySize() { - constexpr size_t memoryLimit = 1000 * 1024 * 1024; - - if (m_memoryMgr->totalMemorySize() > memoryLimit) - { - if (g_stats) - std::cerr << "EVMJIT reset!\n"; - - std::lock_guard lock{x_codeMap}; - m_codeMap.clear(); - m_engine.reset(); - createEngine(); - } -} + constexpr size_t memoryLimit = 1000 * 1024 * 1024; + if (m_memoryMgr->totalMemorySize() > memoryLimit) + { + if (g_stats) + std::cerr << "EVMJIT reset!\n"; + + std::lock_guard lock{x_codeMap}; + m_codeMap.clear(); + m_engine.reset(); + createEngine(); + } } -} + +} // namespace evmjit +} // namespace dev diff --git a/libevmjit/JIT.h b/libevmjit/JIT.h index fa1b9cf1..94e36cdb 100644 --- a/libevmjit/JIT.h +++ b/libevmjit/JIT.h @@ -11,127 +11,131 @@ namespace dev { namespace evmjit { - using byte = uint8_t; using bytes_ref = std::tuple; /// Representation of 256-bit value binary compatible with LLVM i256 struct i256 { - uint64_t words[4]; + uint64_t words[4]; - i256() = default; + i256() = default; }; // TODO: Merge with ExecutionContext struct RuntimeData { - enum Index - { - Gas, - GasPrice, - CallData, - CallDataSize, - Value, // Value of msg.value - different during DELEGATECALL. - Code, - CodeSize, - Address, - Sender, - Depth, - - ReturnData = CallData, ///< Return data pointer (set only in case of RETURN) - ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN) - }; - - static size_t const numElements = Depth + 1; - - int64_t gas = 0; - int64_t gasPrice = 0; - byte const* callData = nullptr; - uint64_t callDataSize = 0; - i256 apparentValue; - byte const* code = nullptr; - uint64_t codeSize = 0; - byte address[32]; - byte caller[32]; - int64_t depth; + enum Index + { + Gas, + GasPrice, + CallData, + CallDataSize, + Value, // Value of msg.value - different during DELEGATECALL. + Code, + CodeSize, + Address, + Sender, + Depth, + + ReturnData = CallData, ///< Return data pointer (set only in case of RETURN) + ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN) + }; + + static size_t const numElements = Depth + 1; + + int64_t gas = 0; + int64_t gasPrice = 0; + byte const* callData = nullptr; + uint64_t callDataSize = 0; + i256 apparentValue; + byte const* code = nullptr; + uint64_t codeSize = 0; + byte address[32]; + byte caller[32]; + int64_t depth; }; struct JITSchedule { - // TODO: Move to constexpr once all our target compilers support it. - typedef std::integral_constant stackLimit; - typedef std::integral_constant stepGas0; - typedef std::integral_constant stepGas1; - typedef std::integral_constant stepGas2; - typedef std::integral_constant stepGas3; - typedef std::integral_constant stepGas4; - typedef std::integral_constant stepGas5; - typedef std::integral_constant stepGas6; - typedef std::integral_constant stepGas7; - typedef std::integral_constant expByteGas; - typedef std::integral_constant sha3Gas; - typedef std::integral_constant sha3WordGas; - typedef std::integral_constant sloadGas; - typedef std::integral_constant sstoreSetGas; - typedef std::integral_constant sstoreResetGas; - typedef std::integral_constant sstoreClearGas; - typedef std::integral_constant jumpdestGas; - typedef std::integral_constant logGas; - typedef std::integral_constant logDataGas; - typedef std::integral_constant logTopicGas; - typedef std::integral_constant createGas; - typedef std::integral_constant callGas; - typedef std::integral_constant memoryGas; - typedef std::integral_constant copyGas; - typedef std::integral_constant valueTransferGas; - typedef std::integral_constant callStipend; - typedef std::integral_constant callNewAccount; + // TODO: Move to constexpr once all our target compilers support it. + typedef std::integral_constant stackLimit; + typedef std::integral_constant stepGas0; + typedef std::integral_constant stepGas1; + typedef std::integral_constant stepGas2; + typedef std::integral_constant stepGas3; + typedef std::integral_constant stepGas4; + typedef std::integral_constant stepGas5; + typedef std::integral_constant stepGas6; + typedef std::integral_constant stepGas7; + typedef std::integral_constant expByteGas; + typedef std::integral_constant sha3Gas; + typedef std::integral_constant sha3WordGas; + typedef std::integral_constant sloadGas; + typedef std::integral_constant sstoreSetGas; + typedef std::integral_constant sstoreResetGas; + typedef std::integral_constant sstoreClearGas; + typedef std::integral_constant jumpdestGas; + typedef std::integral_constant logGas; + typedef std::integral_constant logDataGas; + typedef std::integral_constant logTopicGas; + typedef std::integral_constant createGas; + typedef std::integral_constant callGas; + typedef std::integral_constant memoryGas; + typedef std::integral_constant copyGas; + typedef std::integral_constant valueTransferGas; + typedef std::integral_constant callStipend; + typedef std::integral_constant callNewAccount; }; enum class ReturnCode { - // Success codes - Stop = 0, - Return = 1, - Revert = 2, + // Success codes + Stop = 0, + Return = 1, + Revert = 2, - // Standard error codes - OutOfGas = -1, + // Standard error codes + OutOfGas = -1, - // Internal error codes - LLVMError = -101, + // Internal error codes + LLVMError = -101, - UnexpectedException = -111, + UnexpectedException = -111, }; class ExecutionContext { public: - ExecutionContext() = default; - ExecutionContext(RuntimeData& _data, evmc_context* _ctx) { init(_data, _ctx); } - ExecutionContext(ExecutionContext const&) = delete; - ExecutionContext& operator=(ExecutionContext const&) = delete; - ~ExecutionContext() noexcept; + ExecutionContext() = default; + ExecutionContext(RuntimeData& _data, evmc_context* _ctx) { init(_data, _ctx); } + ExecutionContext(ExecutionContext const&) = delete; + ExecutionContext& operator=(ExecutionContext const&) = delete; + ~ExecutionContext() noexcept; - void init(RuntimeData& _data, evmc_context* _ctx) { m_data = &_data; m_ctx = _ctx; } + void init(RuntimeData& _data, evmc_context* _ctx) + { + m_data = &_data; + m_ctx = _ctx; + } - byte const* code() const { return m_data->code; } - uint64_t codeSize() const { return m_data->codeSize; } + byte const* code() const { return m_data->code; } + uint64_t codeSize() const { return m_data->codeSize; } - bytes_ref getReturnData() const; + bytes_ref getReturnData() const; public: - RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract. - evmc_context* m_ctx = nullptr; ///< Pointer to Host execution context. Expected by compiled contract. - byte* m_memData = nullptr; - uint64_t m_memSize = 0; - uint64_t m_memCap = 0; + RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract. + evmc_context* m_ctx = + nullptr; ///< Pointer to Host execution context. Expected by compiled contract. + byte* m_memData = nullptr; + uint64_t m_memSize = 0; + uint64_t m_memCap = 0; public: - /// Reference to returned data (RETURN opcode used) - bytes_ref returnData; + /// Reference to returned data (RETURN opcode used) + bytes_ref returnData; }; -} -} +} // namespace evmjit +} // namespace dev diff --git a/libevmjit/Memory.cpp b/libevmjit/Memory.cpp index f15938b4..c32885c8 100644 --- a/libevmjit/Memory.cpp +++ b/libevmjit/Memory.cpp @@ -1,13 +1,13 @@ #include "Memory.h" +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include -#include "preprocessor/llvm_includes_end.h" -#include "Type.h" -#include "GasMeter.h" #include "Endianness.h" +#include "GasMeter.h" #include "RuntimeManager.h" +#include "Type.h" namespace dev { @@ -15,261 +15,279 @@ namespace eth { namespace jit { - -Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): - RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed - m_memory{m_builder, _runtimeManager.getMem()}, - m_gasMeter(_gasMeter) +Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter) + : RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed + m_memory{m_builder, _runtimeManager.getMem()}, + m_gasMeter(_gasMeter) {} llvm::Function* Memory::getRequireFunc() { - auto& func = m_require; - if (!func) - { - llvm::Type* argTypes[] = {Array::getType()->getPointerTo(), Type::Word, Type::Word, Type::BytePtr, Type::GasPtr}; - func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); - func->setDoesNotThrow(); - - auto iter = func->arg_begin(); - llvm::Argument* mem = &(*iter++); - mem->setName("mem"); - llvm::Argument* blkOffset = &(*iter++); - blkOffset->setName("blkOffset"); - llvm::Argument* blkSize = &(*iter++); - blkSize->setName("blkSize"); - llvm::Argument* jmpBuf = &(*iter++); - jmpBuf->setName("jmpBuf"); - llvm::Argument* gas = &(*iter); - gas->setName("gas"); - - auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); - auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); - auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); - auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); - - InsertPointGuard guard(m_builder); // Restores insert point at function exit - - // BB "Pre": Ignore checks with size 0 - m_builder.SetInsertPoint(preBB); - m_builder.CreateCondBr(m_builder.CreateICmpNE(blkSize, Constant::get(0)), checkBB, returnBB, Type::expectTrue); - - // BB "Check" - m_builder.SetInsertPoint(checkBB); - 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 - auto blkOffsetOk = m_builder.CreateICmpULE(blkOffset, Constant::get(c_inputMax), "blkOffsetOk"); - auto blkO = m_builder.CreateSelect(blkOffsetOk, m_builder.CreateTrunc(blkOffset, Type::Size), m_builder.getInt64(c_inputMax), "bklO"); - auto blkSizeOk = m_builder.CreateICmpULE(blkSize, Constant::get(c_inputMax), "blkSizeOk"); - auto blkS = m_builder.CreateSelect(blkSizeOk, m_builder.CreateTrunc(blkSize, Type::Size), m_builder.getInt64(c_inputMax), "bklS"); - - auto sizeReq0 = m_builder.CreateNUWAdd(blkO, blkS, "sizeReq0"); - auto sizeReq = m_builder.CreateAnd(m_builder.CreateNUWAdd(sizeReq0, m_builder.getInt64(31)), uint64_t(-1) << 5, "sizeReq"); // s' = ((s0 + 31) / 32) * 32 - auto sizeCur = m_memory.size(mem); - auto sizeOk = m_builder.CreateICmpULE(sizeReq, sizeCur, "sizeOk"); - - m_builder.CreateCondBr(sizeOk, returnBB, resizeBB, Type::expectTrue); - - // BB "Resize" - m_builder.SetInsertPoint(resizeBB); - // Check gas first - auto w1 = m_builder.CreateLShr(sizeReq, 5); - auto w1s = m_builder.CreateNUWMul(w1, w1); - auto c1 = m_builder.CreateAdd(m_builder.CreateNUWMul(w1, m_builder.getInt64(3)), m_builder.CreateLShr(w1s, 9)); - auto w0 = m_builder.CreateLShr(sizeCur, 5); - auto w0s = m_builder.CreateNUWMul(w0, w0); - auto c0 = m_builder.CreateAdd(m_builder.CreateNUWMul(w0, m_builder.getInt64(3)), m_builder.CreateLShr(w0s, 9)); - auto cc = m_builder.CreateNUWSub(c1, c0); - auto costOk = m_builder.CreateAnd(blkOffsetOk, blkSizeOk, "costOk"); - auto c = m_builder.CreateSelect(costOk, cc, m_builder.getInt64(std::numeric_limits::max()), "c"); - m_gasMeter.count(c, jmpBuf, gas); - // Resize - m_memory.extend(mem, sizeReq); - m_builder.CreateBr(returnBB); - - // BB "Return" - m_builder.SetInsertPoint(returnBB); - m_builder.CreateRetVoid(); - } - return func; + auto& func = m_require; + if (!func) + { + llvm::Type* argTypes[] = { + Array::getType()->getPointerTo(), Type::Word, Type::Word, Type::BytePtr, Type::GasPtr}; + func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), + llvm::Function::PrivateLinkage, "mem.require", getModule()); + func->setDoesNotThrow(); + + auto iter = func->arg_begin(); + llvm::Argument* mem = &(*iter++); + mem->setName("mem"); + llvm::Argument* blkOffset = &(*iter++); + blkOffset->setName("blkOffset"); + llvm::Argument* blkSize = &(*iter++); + blkSize->setName("blkSize"); + llvm::Argument* jmpBuf = &(*iter++); + jmpBuf->setName("jmpBuf"); + llvm::Argument* gas = &(*iter); + gas->setName("gas"); + + auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); + auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); + auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); + auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); + + InsertPointGuard guard(m_builder); // Restores insert point at function exit + + // BB "Pre": Ignore checks with size 0 + m_builder.SetInsertPoint(preBB); + m_builder.CreateCondBr( + m_builder.CreateICmpNE(blkSize, Constant::get(0)), checkBB, returnBB, Type::expectTrue); + + // BB "Check" + m_builder.SetInsertPoint(checkBB); + 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 + auto blkOffsetOk = + m_builder.CreateICmpULE(blkOffset, Constant::get(c_inputMax), "blkOffsetOk"); + auto blkO = m_builder.CreateSelect(blkOffsetOk, + m_builder.CreateTrunc(blkOffset, Type::Size), m_builder.getInt64(c_inputMax), "bklO"); + auto blkSizeOk = m_builder.CreateICmpULE(blkSize, Constant::get(c_inputMax), "blkSizeOk"); + auto blkS = m_builder.CreateSelect(blkSizeOk, m_builder.CreateTrunc(blkSize, Type::Size), + m_builder.getInt64(c_inputMax), "bklS"); + + auto sizeReq0 = m_builder.CreateNUWAdd(blkO, blkS, "sizeReq0"); + auto sizeReq = m_builder.CreateAnd(m_builder.CreateNUWAdd(sizeReq0, m_builder.getInt64(31)), + uint64_t(-1) << 5, "sizeReq"); // s' = ((s0 + 31) / 32) * 32 + auto sizeCur = m_memory.size(mem); + auto sizeOk = m_builder.CreateICmpULE(sizeReq, sizeCur, "sizeOk"); + + m_builder.CreateCondBr(sizeOk, returnBB, resizeBB, Type::expectTrue); + + // BB "Resize" + m_builder.SetInsertPoint(resizeBB); + // Check gas first + auto w1 = m_builder.CreateLShr(sizeReq, 5); + auto w1s = m_builder.CreateNUWMul(w1, w1); + auto c1 = m_builder.CreateAdd( + m_builder.CreateNUWMul(w1, m_builder.getInt64(3)), m_builder.CreateLShr(w1s, 9)); + auto w0 = m_builder.CreateLShr(sizeCur, 5); + auto w0s = m_builder.CreateNUWMul(w0, w0); + auto c0 = m_builder.CreateAdd( + m_builder.CreateNUWMul(w0, m_builder.getInt64(3)), m_builder.CreateLShr(w0s, 9)); + auto cc = m_builder.CreateNUWSub(c1, c0); + auto costOk = m_builder.CreateAnd(blkOffsetOk, blkSizeOk, "costOk"); + auto c = m_builder.CreateSelect( + costOk, cc, m_builder.getInt64(std::numeric_limits::max()), "c"); + m_gasMeter.count(c, jmpBuf, gas); + // Resize + m_memory.extend(mem, sizeReq); + m_builder.CreateBr(returnBB); + + // BB "Return" + m_builder.SetInsertPoint(returnBB); + m_builder.CreateRetVoid(); + } + return func; } llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType) { - auto isWord = _valueType == Type::Word; - - llvm::Type* storeArgs[] = {Array::getType()->getPointerTo(), Type::Word, _valueType}; - llvm::Type* loadArgs[] = {Array::getType()->getPointerTo(), Type::Word}; - auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; - auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false); - auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); - - InsertPointGuard guard(m_builder); // Restores insert point at function exit - - m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); - - auto iter = func->arg_begin(); - llvm::Argument* mem = &(*iter++); - mem->setName("mem"); - llvm::Argument* index = &(*iter++); - index->setName("index"); - - if (_isStore) - { - llvm::Argument* valueArg = &(*iter); - valueArg->setName("value"); - auto value = isWord ? Endianness::toBE(m_builder, valueArg) : valueArg; - auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size)); - auto valuePtr = m_builder.CreateBitCast(memPtr, _valueType->getPointerTo(), "valuePtr"); - m_builder.CreateStore(value, valuePtr); - m_builder.CreateRetVoid(); - } - else - { - auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size)); - llvm::Value* ret = m_builder.CreateLoad(memPtr); - ret = Endianness::toNative(m_builder, ret); - m_builder.CreateRet(ret); - } - - return func; + auto isWord = _valueType == Type::Word; + + llvm::Type* storeArgs[] = {Array::getType()->getPointerTo(), Type::Word, _valueType}; + llvm::Type* loadArgs[] = {Array::getType()->getPointerTo(), Type::Word}; + auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; + auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : + llvm::FunctionType::get(Type::Word, loadArgs, false); + auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); + + InsertPointGuard guard(m_builder); // Restores insert point at function exit + + m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); + + auto iter = func->arg_begin(); + llvm::Argument* mem = &(*iter++); + mem->setName("mem"); + llvm::Argument* index = &(*iter++); + index->setName("index"); + + if (_isStore) + { + llvm::Argument* valueArg = &(*iter); + valueArg->setName("value"); + auto value = isWord ? Endianness::toBE(m_builder, valueArg) : valueArg; + auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size)); + auto valuePtr = m_builder.CreateBitCast(memPtr, _valueType->getPointerTo(), "valuePtr"); + m_builder.CreateStore(value, valuePtr); + m_builder.CreateRetVoid(); + } + else + { + auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size)); + llvm::Value* ret = m_builder.CreateLoad(memPtr); + ret = Endianness::toNative(m_builder, ret); + m_builder.CreateRet(ret); + } + + return func; } llvm::Function* Memory::getLoadWordFunc() { - auto& func = m_loadWord; - if (!func) - func = createFunc(false, Type::Word); - return func; + auto& func = m_loadWord; + if (!func) + func = createFunc(false, Type::Word); + return func; } llvm::Function* Memory::getStoreWordFunc() { - auto& func = m_storeWord; - if (!func) - func = createFunc(true, Type::Word); - return func; + auto& func = m_storeWord; + if (!func) + func = createFunc(true, Type::Word); + return func; } llvm::Function* Memory::getStoreByteFunc() { - auto& func = m_storeByte; - if (!func) - func = createFunc(true, Type::Byte); - return func; + auto& func = m_storeByte; + if (!func) + func = createFunc(true, Type::Byte); + return func; } llvm::Value* Memory::loadWord(llvm::Value* _addr) { - require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8)); - return m_builder.CreateCall(getLoadWordFunc(), {getRuntimeManager().getMem(), _addr}); + require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8)); + return m_builder.CreateCall(getLoadWordFunc(), {getRuntimeManager().getMem(), _addr}); } void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) { - require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8)); - m_builder.CreateCall(getStoreWordFunc(), {getRuntimeManager().getMem(), _addr, _word}); + require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8)); + m_builder.CreateCall(getStoreWordFunc(), {getRuntimeManager().getMem(), _addr, _word}); } void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) { - require(_addr, Constant::get(Type::Byte->getPrimitiveSizeInBits() / 8)); - auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); - m_builder.CreateCall(getStoreByteFunc(), {getRuntimeManager().getMem(), _addr, byte}); + require(_addr, Constant::get(Type::Byte->getPrimitiveSizeInBits() / 8)); + auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); + m_builder.CreateCall(getStoreByteFunc(), {getRuntimeManager().getMem(), _addr, byte}); } llvm::Value* Memory::getData() { - auto memPtr = m_builder.CreateBitCast(getRuntimeManager().getMem(), Type::BytePtr->getPointerTo()); - auto data = m_builder.CreateLoad(memPtr, "data"); - assert(data->getType() == Type::BytePtr); - return data; + auto memPtr = + m_builder.CreateBitCast(getRuntimeManager().getMem(), Type::BytePtr->getPointerTo()); + auto data = m_builder.CreateLoad(memPtr, "data"); + assert(data->getType() == Type::BytePtr); + return data; } llvm::Value* Memory::getSize() { - return m_builder.CreateZExt(m_memory.size(), Type::Word, "msize"); + return m_builder.CreateZExt(m_memory.size(), Type::Word, "msize"); } llvm::Value* Memory::getBytePtr(llvm::Value* _index) { - return m_builder.CreateGEP(getData(), _index, "ptr"); + return m_builder.CreateGEP(getData(), _index, "ptr"); } void Memory::require(llvm::Value* _offset, llvm::Value* _size) { - if (auto constant = llvm::dyn_cast(_size)) - { - if (!constant->getValue()) - return; - } - m_builder.CreateCall(getRequireFunc(), {getRuntimeManager().getMem(), _offset, _size, getRuntimeManager().getJmpBuf(), getRuntimeManager().getGasPtr()}); + if (auto constant = llvm::dyn_cast(_size)) + { + if (!constant->getValue()) + return; + } + m_builder.CreateCall( + getRequireFunc(), {getRuntimeManager().getMem(), _offset, _size, + getRuntimeManager().getJmpBuf(), getRuntimeManager().getGasPtr()}); } void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, - llvm::Value* _destMemIdx, llvm::Value* _reqBytes) + llvm::Value* _destMemIdx, llvm::Value* _reqBytes) { - require(_destMemIdx, _reqBytes); - - // Additional copy cost - // TODO: This round ups to 32 happens in many places - auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Gas); - auto copyWords = m_builder.CreateUDiv(m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32)); - m_gasMeter.countCopy(copyWords); - - // Algorithm: - // isOutsideData = idx256 >= size256 - // idx64 = trunc idx256 - // size64 = trunc size256 - // dataLeftSize = size64 - idx64 // safe if not isOutsideData - // reqBytes64 = trunc _reqBytes // require() handles large values - // bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min - // bytesToCopy = select(isOutsideData, 0, bytesToCopy0) - - auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); - auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size); - auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size); - auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); - auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize); - auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes); - auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner, "bytesToCopy"); - auto bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero"); - - auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); - auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); - auto padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx"); - auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx); - auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx); - m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); - m_builder.CreateMemSet(pad, m_builder.getInt8(0), bytesToZero, 0); + require(_destMemIdx, _reqBytes); + + // Additional copy cost + // TODO: This round ups to 32 happens in many places + auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Gas); + auto copyWords = m_builder.CreateUDiv( + m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32)); + m_gasMeter.countCopy(copyWords); + + // Algorithm: + // isOutsideData = idx256 >= size256 + // idx64 = trunc idx256 + // size64 = trunc size256 + // dataLeftSize = size64 - idx64 // safe if not isOutsideData + // reqBytes64 = trunc _reqBytes // require() handles large values + // bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min + // bytesToCopy = select(isOutsideData, 0, bytesToCopy0) + + auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); + auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size); + auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size); + auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); + auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize); + auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes); + auto bytesToCopy = m_builder.CreateSelect( + isOutsideData, m_builder.getInt64(0), bytesToCopyInner, "bytesToCopy"); + auto bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero"); + + auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); + auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); + auto padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx"); + auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx); + auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx); + m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); + m_builder.CreateMemSet(pad, m_builder.getInt8(0), bytesToZero, 0); } void Memory::copyBytesNoPadding(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, - llvm::Value* _destMemIdx, llvm::Value* _reqBytes) + llvm::Value* _destMemIdx, llvm::Value* _reqBytes) { - require(_destMemIdx, _reqBytes); - - // Additional copy cost - // TODO: This round ups to 32 happens in many places - auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Size); - auto copyWords = m_builder.CreateUDiv(m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32)); - - auto reqSize = m_builder.CreateAdd(_srcIdx, _reqBytes); - auto overflow = m_builder.CreateICmpULT(reqSize, _reqBytes); - auto outOfRange = m_builder.CreateICmpUGT(reqSize, m_builder.CreateZExt(_srcSize, Type::Word)); - auto bufferOverrun = m_builder.CreateOr(overflow, outOfRange); - auto penalty = m_builder.getInt64(std::numeric_limits::max()); - auto cost = m_builder.CreateSelect(bufferOverrun, penalty, copyWords); - m_gasMeter.countCopy(cost); - - auto srcIdx = m_builder.CreateTrunc(_srcIdx, Type::Size); - - auto src = m_builder.CreateGEP(_srcPtr, srcIdx, "src"); - auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); - auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx); - m_builder.CreateMemCpy(dst, src, reqBytes, 0); + require(_destMemIdx, _reqBytes); + + // Additional copy cost + // TODO: This round ups to 32 happens in many places + auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Size); + auto copyWords = m_builder.CreateUDiv( + m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32)); + + auto reqSize = m_builder.CreateAdd(_srcIdx, _reqBytes); + auto overflow = m_builder.CreateICmpULT(reqSize, _reqBytes); + auto outOfRange = m_builder.CreateICmpUGT(reqSize, m_builder.CreateZExt(_srcSize, Type::Word)); + auto bufferOverrun = m_builder.CreateOr(overflow, outOfRange); + auto penalty = m_builder.getInt64(std::numeric_limits::max()); + auto cost = m_builder.CreateSelect(bufferOverrun, penalty, copyWords); + m_gasMeter.countCopy(cost); + + auto srcIdx = m_builder.CreateTrunc(_srcIdx, Type::Size); + + auto src = m_builder.CreateGEP(_srcPtr, srcIdx, "src"); + auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); + auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx); + m_builder.CreateMemCpy(dst, src, reqBytes, 0); } -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Memory.h b/libevmjit/Memory.h index bf33ebb2..690b6d68 100644 --- a/libevmjit/Memory.h +++ b/libevmjit/Memory.h @@ -13,41 +13,41 @@ class GasMeter; class Memory : public RuntimeHelper { public: - Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter); - - llvm::Value* loadWord(llvm::Value* _addr); - void storeWord(llvm::Value* _addr, llvm::Value* _word); - void storeByte(llvm::Value* _addr, llvm::Value* _byte); - llvm::Value* getData(); - llvm::Value* getSize(); - llvm::Value* getBytePtr(llvm::Value* _index); - void copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIndex, - llvm::Value* _destMemIdx, llvm::Value* _byteCount); - void copyBytesNoPadding(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIndex, - llvm::Value* _destMemIdx, llvm::Value* _byteCount); - - /// Requires the amount of memory to for data defined by offset and size. And counts gas fee for that memory. - void require(llvm::Value* _offset, llvm::Value* _size); + Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter); + + llvm::Value* loadWord(llvm::Value* _addr); + void storeWord(llvm::Value* _addr, llvm::Value* _word); + void storeByte(llvm::Value* _addr, llvm::Value* _byte); + llvm::Value* getData(); + llvm::Value* getSize(); + llvm::Value* getBytePtr(llvm::Value* _index); + void copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIndex, + llvm::Value* _destMemIdx, llvm::Value* _byteCount); + void copyBytesNoPadding(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIndex, + llvm::Value* _destMemIdx, llvm::Value* _byteCount); + + /// Requires the amount of memory to for data defined by offset and size. And counts gas fee for + /// that memory. + void require(llvm::Value* _offset, llvm::Value* _size); private: - Array m_memory; + Array m_memory; - GasMeter& m_gasMeter; + GasMeter& m_gasMeter; - llvm::Function* createFunc(bool _isStore, llvm::Type* _type); + llvm::Function* createFunc(bool _isStore, llvm::Type* _type); - llvm::Function* getRequireFunc(); - llvm::Function* getLoadWordFunc(); - llvm::Function* getStoreWordFunc(); - llvm::Function* getStoreByteFunc(); + llvm::Function* getRequireFunc(); + llvm::Function* getLoadWordFunc(); + llvm::Function* getStoreWordFunc(); + llvm::Function* getStoreByteFunc(); - llvm::Function* m_require = nullptr; - llvm::Function* m_loadWord = nullptr; - llvm::Function* m_storeWord = nullptr; - llvm::Function* m_storeByte = nullptr; + llvm::Function* m_require = nullptr; + llvm::Function* m_loadWord = nullptr; + llvm::Function* m_storeWord = nullptr; + llvm::Function* m_storeByte = nullptr; }; -} -} -} - +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index b640fd65..cde81296 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -1,14 +1,14 @@ #include "Optimizer.h" +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include #include -#include #include -#include +#include #include +#include #include -#include "preprocessor/llvm_includes_end.h" #include "Arith256.h" #include "Type.h" @@ -19,156 +19,149 @@ namespace eth { namespace jit { - namespace { - -class LongJmpEliminationPass: public llvm::FunctionPass +class LongJmpEliminationPass : public llvm::FunctionPass { - static char ID; + static char ID; public: - LongJmpEliminationPass(): - llvm::FunctionPass(ID) - {} + LongJmpEliminationPass() : llvm::FunctionPass(ID) {} - virtual bool runOnFunction(llvm::Function& _func) override; + virtual bool runOnFunction(llvm::Function& _func) override; }; char LongJmpEliminationPass::ID = 0; bool LongJmpEliminationPass::runOnFunction(llvm::Function& _func) { - auto iter = _func.getParent()->begin(); - if (&_func != &(*iter)) - return false; - - auto& mainFunc = _func; - auto& ctx = _func.getContext(); - auto abortCode = llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), -1); - - auto& exitBB = mainFunc.back(); - assert(exitBB.getName() == "Exit"); - auto retPhi = llvm::cast(&exitBB.front()); - - auto modified = false; - for (auto bbIt = mainFunc.begin(); bbIt != mainFunc.end(); ++bbIt) - { - if (auto term = llvm::dyn_cast(bbIt->getTerminator())) - { - auto longjmp = term->getPrevNode(); - assert(llvm::isa(longjmp)); - auto bbPtr = &(*bbIt); - retPhi->addIncoming(abortCode, bbPtr); - llvm::ReplaceInstWithInst(term, llvm::BranchInst::Create(&exitBB)); - longjmp->eraseFromParent(); - modified = true; - } - } - - return modified; + auto iter = _func.getParent()->begin(); + if (&_func != &(*iter)) + return false; + + auto& mainFunc = _func; + auto& ctx = _func.getContext(); + auto abortCode = llvm::ConstantInt::get(llvm::Type::getInt32Ty(ctx), -1); + + auto& exitBB = mainFunc.back(); + assert(exitBB.getName() == "Exit"); + auto retPhi = llvm::cast(&exitBB.front()); + + auto modified = false; + for (auto bbIt = mainFunc.begin(); bbIt != mainFunc.end(); ++bbIt) + { + if (auto term = llvm::dyn_cast(bbIt->getTerminator())) + { + auto longjmp = term->getPrevNode(); + assert(llvm::isa(longjmp)); + auto bbPtr = &(*bbIt); + retPhi->addIncoming(abortCode, bbPtr); + llvm::ReplaceInstWithInst(term, llvm::BranchInst::Create(&exitBB)); + longjmp->eraseFromParent(); + modified = true; + } + } + + return modified; } -} +} // namespace bool optimize(llvm::Module& _module) { - auto pm = llvm::legacy::PassManager{}; - pm.add(llvm::createFunctionInliningPass(2, 2, false)); - pm.add(new LongJmpEliminationPass{}); // TODO: Takes a lot of time with little effect - pm.add(llvm::createCFGSimplificationPass()); - pm.add(llvm::createInstructionCombiningPass()); - pm.add(llvm::createAggressiveDCEPass()); - pm.add(llvm::createLowerSwitchPass()); - return pm.run(_module); + auto pm = llvm::legacy::PassManager{}; + pm.add(llvm::createFunctionInliningPass(2, 2, false)); + pm.add(new LongJmpEliminationPass{}); // TODO: Takes a lot of time with little effect + pm.add(llvm::createCFGSimplificationPass()); + pm.add(llvm::createInstructionCombiningPass()); + pm.add(llvm::createAggressiveDCEPass()); + pm.add(llvm::createLowerSwitchPass()); + return pm.run(_module); } namespace { - -class LowerEVMPass: public llvm::BasicBlockPass +class LowerEVMPass : public llvm::BasicBlockPass { - static char ID; + static char ID; public: - LowerEVMPass(): - llvm::BasicBlockPass(ID) - {} + LowerEVMPass() : llvm::BasicBlockPass(ID) {} - virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override; + virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override; - using llvm::BasicBlockPass::doFinalization; - virtual bool doFinalization(llvm::Module& _module) override; + using llvm::BasicBlockPass::doFinalization; + virtual bool doFinalization(llvm::Module& _module) override; }; char LowerEVMPass::ID = 0; bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb) { - auto modified = false; - auto module = _bb.getParent()->getParent(); - auto i512Ty = llvm::IntegerType::get(_bb.getContext(), 512); - for (auto it = _bb.begin(); it != _bb.end(); ++it) - { - auto& inst = *it; - llvm::Function* func = nullptr; - if (inst.getType() == Type::Word) - { - switch (inst.getOpcode()) - { - case llvm::Instruction::UDiv: - func = Arith256::getUDiv256Func(*module); - break; - - case llvm::Instruction::URem: - func = Arith256::getURem256Func(*module); - break; - - case llvm::Instruction::SDiv: - func = Arith256::getSDiv256Func(*module); - break; - - case llvm::Instruction::SRem: - func = Arith256::getSRem256Func(*module); - break; - } - } - else if (inst.getType() == i512Ty) - { - switch (inst.getOpcode()) - { - case llvm::Instruction::URem: - func = Arith256::getURem512Func(*module); - break; - } - } - - if (func) - { - auto call = llvm::CallInst::Create(func, {inst.getOperand(0), inst.getOperand(1)}); - llvm::ReplaceInstWithInst(_bb.getInstList(), it, call); - modified = true; - } - } - return modified; + auto modified = false; + auto module = _bb.getParent()->getParent(); + auto i512Ty = llvm::IntegerType::get(_bb.getContext(), 512); + for (auto it = _bb.begin(); it != _bb.end(); ++it) + { + auto& inst = *it; + llvm::Function* func = nullptr; + if (inst.getType() == Type::Word) + { + switch (inst.getOpcode()) + { + case llvm::Instruction::UDiv: + func = Arith256::getUDiv256Func(*module); + break; + + case llvm::Instruction::URem: + func = Arith256::getURem256Func(*module); + break; + + case llvm::Instruction::SDiv: + func = Arith256::getSDiv256Func(*module); + break; + + case llvm::Instruction::SRem: + func = Arith256::getSRem256Func(*module); + break; + } + } + else if (inst.getType() == i512Ty) + { + switch (inst.getOpcode()) + { + case llvm::Instruction::URem: + func = Arith256::getURem512Func(*module); + break; + } + } + + if (func) + { + auto call = llvm::CallInst::Create(func, {inst.getOperand(0), inst.getOperand(1)}); + llvm::ReplaceInstWithInst(_bb.getInstList(), it, call); + modified = true; + } + } + return modified; } bool LowerEVMPass::doFinalization(llvm::Module&) { - return false; + return false; } -} +} // namespace bool prepare(llvm::Module& _module) { - auto pm = llvm::legacy::PassManager{}; - pm.add(llvm::createCFGSimplificationPass()); - pm.add(llvm::createDeadCodeEliminationPass()); - pm.add(new LowerEVMPass{}); - return pm.run(_module); + auto pm = llvm::legacy::PassManager{}; + pm.add(llvm::createCFGSimplificationPass()); + pm.add(llvm::createDeadCodeEliminationPass()); + pm.add(new LowerEVMPass{}); + return pm.run(_module); } -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Optimizer.h b/libevmjit/Optimizer.h index 4b7ab7e9..42d19bd2 100644 --- a/libevmjit/Optimizer.h +++ b/libevmjit/Optimizer.h @@ -2,7 +2,7 @@ namespace llvm { - class Module; +class Module; } namespace dev @@ -11,11 +11,10 @@ namespace eth { namespace jit { - bool optimize(llvm::Module& _module); bool prepare(llvm::Module& _module); -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/RuntimeManager.cpp b/libevmjit/RuntimeManager.cpp index 4ca8193c..aa7ebc53 100644 --- a/libevmjit/RuntimeManager.cpp +++ b/libevmjit/RuntimeManager.cpp @@ -1,9 +1,9 @@ #include "RuntimeManager.h" +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" #include #include -#include "preprocessor/llvm_includes_end.h" #include "Array.h" #include "Utils.h" @@ -14,314 +14,336 @@ namespace eth { namespace jit { - llvm::StructType* RuntimeManager::getRuntimeDataType() { - static llvm::StructType* type = nullptr; - if (!type) - { - llvm::Type* elems[] = - { - Type::Size, // gas - Type::Size, // gasPrice - Type::BytePtr, // callData - Type::Size, // callDataSize - Type::Word, // apparentValue - Type::BytePtr, // code - Type::Size, // codeSize - Type::Word, // adddress - Type::Word, // caller - Type::Size, // depth - }; - type = llvm::StructType::create(elems, "RuntimeData"); - } - return type; + static llvm::StructType* type = nullptr; + if (!type) + { + llvm::Type* elems[] = { + Type::Size, // gas + Type::Size, // gasPrice + Type::BytePtr, // callData + Type::Size, // callDataSize + Type::Word, // apparentValue + Type::BytePtr, // code + Type::Size, // codeSize + Type::Word, // adddress + Type::Word, // caller + Type::Size, // depth + }; + type = llvm::StructType::create(elems, "RuntimeData"); + } + return type; } llvm::StructType* RuntimeManager::getRuntimeType() { - static llvm::StructType* type = nullptr; - if (!type) - { - llvm::Type* elems[] = - { - Type::RuntimeDataPtr, // data - Type::EnvPtr, // Env* - Array::getType() // memory - }; - type = llvm::StructType::create(elems, "Runtime"); - } - return type; + static llvm::StructType* type = nullptr; + if (!type) + { + llvm::Type* elems[] = { + Type::RuntimeDataPtr, // data + Type::EnvPtr, // Env* + Array::getType() // memory + }; + type = llvm::StructType::create(elems, "Runtime"); + } + return type; } llvm::StructType* RuntimeManager::getTxContextType() { - auto name = "evm.txctx"; - auto type = getModule()->getTypeByName(name); - if (type) - return type; - - auto& ctx = getModule()->getContext(); - auto i256 = llvm::IntegerType::get(ctx, 256); - auto h160 = llvm::ArrayType::get(llvm::IntegerType::get(ctx, 8), 20); - auto i64 = llvm::IntegerType::get(ctx, 64); - return llvm::StructType::create({i256, h160, h160, i64, i64, i64, i256}, name); + auto name = "evm.txctx"; + auto type = getModule()->getTypeByName(name); + if (type) + return type; + + auto& ctx = getModule()->getContext(); + auto i256 = llvm::IntegerType::get(ctx, 256); + auto h160 = llvm::ArrayType::get(llvm::IntegerType::get(ctx, 8), 20); + auto i64 = llvm::IntegerType::get(ctx, 64); + return llvm::StructType::create({i256, h160, h160, i64, i64, i64, i256}, name); } llvm::Value* RuntimeManager::getTxContextItem(unsigned _index) { - auto call = m_builder.CreateCall(m_loadTxCtxFn, {m_txCtxLoaded, m_txCtx, m_envPtr}); - call->setCallingConv(llvm::CallingConv::Fast); - auto ptr = m_builder.CreateStructGEP(getTxContextType(), m_txCtx, _index); - if (_index == 1 || _index == 2) - { - // In struct addresses are represented as char[20] to fix alignment - // issues (i160 has alignment of 8). Here we convert them back to i160. - ptr = m_builder.CreateBitCast(ptr, m_builder.getIntNTy(160)->getPointerTo()); - } - return m_builder.CreateLoad(ptr); + auto call = m_builder.CreateCall(m_loadTxCtxFn, {m_txCtxLoaded, m_txCtx, m_envPtr}); + call->setCallingConv(llvm::CallingConv::Fast); + auto ptr = m_builder.CreateStructGEP(getTxContextType(), m_txCtx, _index); + if (_index == 1 || _index == 2) + { + // In struct addresses are represented as char[20] to fix alignment + // issues (i160 has alignment of 8). Here we convert them back to i160. + ptr = m_builder.CreateBitCast(ptr, m_builder.getIntNTy(160)->getPointerTo()); + } + return m_builder.CreateLoad(ptr); } namespace { llvm::Twine getName(RuntimeData::Index _index) { - switch (_index) - { - default: return ""; - case RuntimeData::Gas: return "msg.gas"; - case RuntimeData::GasPrice: return "tx.gasprice"; - case RuntimeData::CallData: return "msg.data.ptr"; - case RuntimeData::CallDataSize: return "msg.data.size"; - case RuntimeData::Value: return "msg.value"; - case RuntimeData::Code: return "code.ptr"; - case RuntimeData::CodeSize: return "code.size"; - case RuntimeData::Address: return "msg.address"; - case RuntimeData::Sender: return "msg.sender"; - case RuntimeData::Depth: return "msg.depth"; - } -} + switch (_index) + { + default: + return ""; + case RuntimeData::Gas: + return "msg.gas"; + case RuntimeData::GasPrice: + return "tx.gasprice"; + case RuntimeData::CallData: + return "msg.data.ptr"; + case RuntimeData::CallDataSize: + return "msg.data.size"; + case RuntimeData::Value: + return "msg.value"; + case RuntimeData::Code: + return "code.ptr"; + case RuntimeData::CodeSize: + return "code.size"; + case RuntimeData::Address: + return "msg.address"; + case RuntimeData::Sender: + return "msg.sender"; + case RuntimeData::Depth: + return "msg.depth"; + } } +} // namespace -RuntimeManager::RuntimeManager(IRBuilder& _builder, code_iterator _codeBegin, code_iterator _codeEnd): - CompilerHelper(_builder), - m_codeBegin(_codeBegin), - m_codeEnd(_codeEnd) +RuntimeManager::RuntimeManager( + IRBuilder& _builder, code_iterator _codeBegin, code_iterator _codeEnd) + : CompilerHelper(_builder), m_codeBegin(_codeBegin), m_codeEnd(_codeEnd) { - m_txCtxLoaded = m_builder.CreateAlloca(m_builder.getInt1Ty(), nullptr, "txctx.loaded"); - m_builder.CreateStore(m_builder.getInt1(false), m_txCtxLoaded); - m_txCtx = m_builder.CreateAlloca(getTxContextType(), nullptr, "txctx"); - - auto getTxCtxFnTy = llvm::FunctionType::get(Type::Void, {m_txCtx->getType(), Type::EnvPtr}, false); - auto getTxCtxFn = llvm::Function::Create(getTxCtxFnTy, llvm::Function::ExternalLinkage, "evm.get_tx_context", getModule()); - auto loadTxCtxFnTy = llvm::FunctionType::get(Type::Void, {m_txCtxLoaded->getType(), m_txCtx->getType(), Type::EnvPtr}, false); - m_loadTxCtxFn = llvm::Function::Create(loadTxCtxFnTy, llvm::Function::PrivateLinkage, "loadTxCtx", getModule()); - m_loadTxCtxFn->setCallingConv(llvm::CallingConv::Fast); - auto checkBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Check", m_loadTxCtxFn); - auto loadBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Load", m_loadTxCtxFn); - auto exitBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Exit", m_loadTxCtxFn); - - auto iter = m_loadTxCtxFn->arg_begin(); - llvm::Argument* flag = &(*iter++); - flag->setName("flag"); - llvm::Argument* txCtx = &(*iter++); - txCtx->setName("txctx"); - llvm::Argument* env = &(*iter); - env->setName("env"); - - auto b = IRBuilder{checkBB}; - auto f = b.CreateLoad(flag); - b.CreateCondBr(f, exitBB, loadBB); - b.SetInsertPoint(loadBB); - b.CreateStore(b.getInt1(true), flag); - b.CreateCall(getTxCtxFn, {txCtx, env}); - b.CreateBr(exitBB); - b.SetInsertPoint(exitBB); - b.CreateRetVoid(); - - // Unpack data - auto rtPtr = getRuntimePtr(); - m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr"); - assert(m_dataPtr->getType() == Type::RuntimeDataPtr); - m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem"); - assert(m_memPtr->getType() == Array::getType()->getPointerTo()); - m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env"); - assert(m_envPtr->getType() == Type::EnvPtr); - - auto mallocFunc = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, {Type::Size}, false), llvm::Function::ExternalLinkage, "malloc", getModule()); - mallocFunc->setDoesNotThrow(); - mallocFunc->addAttribute(0, llvm::Attribute::NoAlias); - - m_stackBase = m_builder.CreateCall(mallocFunc, m_builder.getInt64(Type::Word->getPrimitiveSizeInBits() / 8 * stackSizeLimit), "stack.base"); // TODO: Use Type::SizeT type - m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stack.size"); - m_builder.CreateStore(m_builder.getInt64(0), m_stackSize); - - auto data = m_builder.CreateLoad(m_dataPtr, "data"); - for (unsigned i = 0; i < m_dataElts.size(); ++i) - m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i))); - - m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr"); - m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr); - - m_returnBufDataPtr = m_builder.CreateAlloca(Type::BytePtr, nullptr, "returndata.ptr"); - m_returnBufSizePtr = m_builder.CreateAlloca(Type::Size, nullptr, "returndatasize.ptr"); - resetReturnBuf(); - - m_exitBB = llvm::BasicBlock::Create(m_builder.getContext(), "Exit", getMainFunction()); - InsertPointGuard guard{m_builder}; - m_builder.SetInsertPoint(m_exitBB); - auto retPhi = m_builder.CreatePHI(Type::MainReturn, 16, "ret"); - auto freeFunc = getModule()->getFunction("free"); - if (!freeFunc) - { - freeFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::WordPtr, false), llvm::Function::ExternalLinkage, "free", getModule()); - freeFunc->setDoesNotThrow(); - freeFunc->addAttribute(1, llvm::Attribute::NoCapture); - } - m_builder.CreateCall(freeFunc, {m_stackBase}); - auto extGasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), RuntimeData::Index::Gas, "msg.gas.ptr"); - m_builder.CreateStore(getGas(), extGasPtr); - m_builder.CreateRet(retPhi); + m_txCtxLoaded = m_builder.CreateAlloca(m_builder.getInt1Ty(), nullptr, "txctx.loaded"); + m_builder.CreateStore(m_builder.getInt1(false), m_txCtxLoaded); + m_txCtx = m_builder.CreateAlloca(getTxContextType(), nullptr, "txctx"); + + auto getTxCtxFnTy = + llvm::FunctionType::get(Type::Void, {m_txCtx->getType(), Type::EnvPtr}, false); + auto getTxCtxFn = llvm::Function::Create( + getTxCtxFnTy, llvm::Function::ExternalLinkage, "evm.get_tx_context", getModule()); + auto loadTxCtxFnTy = llvm::FunctionType::get( + Type::Void, {m_txCtxLoaded->getType(), m_txCtx->getType(), Type::EnvPtr}, false); + m_loadTxCtxFn = llvm::Function::Create( + loadTxCtxFnTy, llvm::Function::PrivateLinkage, "loadTxCtx", getModule()); + m_loadTxCtxFn->setCallingConv(llvm::CallingConv::Fast); + auto checkBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Check", m_loadTxCtxFn); + auto loadBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Load", m_loadTxCtxFn); + auto exitBB = llvm::BasicBlock::Create(m_loadTxCtxFn->getContext(), "Exit", m_loadTxCtxFn); + + auto iter = m_loadTxCtxFn->arg_begin(); + llvm::Argument* flag = &(*iter++); + flag->setName("flag"); + llvm::Argument* txCtx = &(*iter++); + txCtx->setName("txctx"); + llvm::Argument* env = &(*iter); + env->setName("env"); + + auto b = IRBuilder{checkBB}; + auto f = b.CreateLoad(flag); + b.CreateCondBr(f, exitBB, loadBB); + b.SetInsertPoint(loadBB); + b.CreateStore(b.getInt1(true), flag); + b.CreateCall(getTxCtxFn, {txCtx, env}); + b.CreateBr(exitBB); + b.SetInsertPoint(exitBB); + b.CreateRetVoid(); + + // Unpack data + auto rtPtr = getRuntimePtr(); + m_dataPtr = + m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr"); + assert(m_dataPtr->getType() == Type::RuntimeDataPtr); + m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem"); + assert(m_memPtr->getType() == Array::getType()->getPointerTo()); + m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env"); + assert(m_envPtr->getType() == Type::EnvPtr); + + auto mallocFunc = + llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, {Type::Size}, false), + llvm::Function::ExternalLinkage, "malloc", getModule()); + mallocFunc->setDoesNotThrow(); + mallocFunc->addAttribute(0, llvm::Attribute::NoAlias); + + m_stackBase = m_builder.CreateCall(mallocFunc, + m_builder.getInt64(Type::Word->getPrimitiveSizeInBits() / 8 * stackSizeLimit), + "stack.base"); // TODO: Use Type::SizeT type + m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stack.size"); + m_builder.CreateStore(m_builder.getInt64(0), m_stackSize); + + auto data = m_builder.CreateLoad(m_dataPtr, "data"); + for (unsigned i = 0; i < m_dataElts.size(); ++i) + m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i))); + + m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr"); + m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr); + + m_returnBufDataPtr = m_builder.CreateAlloca(Type::BytePtr, nullptr, "returndata.ptr"); + m_returnBufSizePtr = m_builder.CreateAlloca(Type::Size, nullptr, "returndatasize.ptr"); + resetReturnBuf(); + + m_exitBB = llvm::BasicBlock::Create(m_builder.getContext(), "Exit", getMainFunction()); + InsertPointGuard guard{m_builder}; + m_builder.SetInsertPoint(m_exitBB); + auto retPhi = m_builder.CreatePHI(Type::MainReturn, 16, "ret"); + auto freeFunc = getModule()->getFunction("free"); + if (!freeFunc) + { + freeFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::WordPtr, false), + llvm::Function::ExternalLinkage, "free", getModule()); + freeFunc->setDoesNotThrow(); + freeFunc->addAttribute(1, llvm::Attribute::NoCapture); + } + m_builder.CreateCall(freeFunc, {m_stackBase}); + auto extGasPtr = m_builder.CreateStructGEP( + getRuntimeDataType(), getDataPtr(), RuntimeData::Index::Gas, "msg.gas.ptr"); + m_builder.CreateStore(getGas(), extGasPtr); + m_builder.CreateRet(retPhi); } llvm::Value* RuntimeManager::getRuntimePtr() { - // Expect first argument of a function to be a pointer to Runtime - auto func = m_builder.GetInsertBlock()->getParent(); - auto rtPtr = func->args().begin(); - assert(rtPtr->getType() == Type::RuntimePtr); - return rtPtr; + // Expect first argument of a function to be a pointer to Runtime + auto func = m_builder.GetInsertBlock()->getParent(); + auto rtPtr = func->args().begin(); + assert(rtPtr->getType() == Type::RuntimePtr); + return rtPtr; } llvm::Value* RuntimeManager::getDataPtr() { - if (getMainFunction()) - return m_dataPtr; - - auto rtPtr = getRuntimePtr(); - auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data"); - assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo()); - return dataPtr; + if (getMainFunction()) + return m_dataPtr; + + auto rtPtr = getRuntimePtr(); + auto dataPtr = + m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data"); + assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo()); + return dataPtr; } llvm::Value* RuntimeManager::getEnvPtr() { - assert(getMainFunction()); // Available only in main function - return m_envPtr; + assert(getMainFunction()); // Available only in main function + return m_envPtr; } llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) { - auto ptr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), _index); - assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType()); - return ptr; + auto ptr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), _index); + assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType()); + return ptr; } llvm::Value* RuntimeManager::getAddress() { - return m_dataElts[RuntimeData::Address]; + return m_dataElts[RuntimeData::Address]; } llvm::Value* RuntimeManager::getSender() { - return m_dataElts[RuntimeData::Sender]; + return m_dataElts[RuntimeData::Sender]; } llvm::Value* RuntimeManager::getValue() { - return m_dataElts[RuntimeData::Value]; + return m_dataElts[RuntimeData::Value]; } llvm::Value* RuntimeManager::getDepth() { - return m_dataElts[RuntimeData::Depth]; + return m_dataElts[RuntimeData::Depth]; } void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value) { - auto ptr = getPtr(_index); - assert(ptr->getType() == _value->getType()->getPointerTo()); - m_builder.CreateStore(_value, ptr); + auto ptr = getPtr(_index); + assert(ptr->getType() == _value->getType()->getPointerTo()); + m_builder.CreateStore(_value, ptr); } void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size) { - auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo()); - auto mem = m_builder.CreateLoad(memPtr, "memory"); - auto returnDataPtr = m_builder.CreateGEP(mem, _offset); - set(RuntimeData::ReturnData, returnDataPtr); + auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo()); + auto mem = m_builder.CreateLoad(memPtr, "memory"); + auto returnDataPtr = m_builder.CreateGEP(mem, _offset); + set(RuntimeData::ReturnData, returnDataPtr); - auto size64 = m_builder.CreateTrunc(_size, Type::Size); - set(RuntimeData::ReturnDataSize, size64); + auto size64 = m_builder.CreateTrunc(_size, Type::Size); + set(RuntimeData::ReturnDataSize, size64); } void RuntimeManager::exit(ReturnCode _returnCode) { - m_builder.CreateBr(m_exitBB); - auto retPhi = llvm::cast(&m_exitBB->front()); - retPhi->addIncoming(Constant::get(_returnCode), m_builder.GetInsertBlock()); + m_builder.CreateBr(m_exitBB); + auto retPhi = llvm::cast(&m_exitBB->front()); + retPhi->addIncoming(Constant::get(_returnCode), m_builder.GetInsertBlock()); } void RuntimeManager::abort(llvm::Value* _jmpBuf) { - auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); - m_builder.CreateCall(longjmp, {_jmpBuf}); + auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); + m_builder.CreateCall(longjmp, {_jmpBuf}); } void RuntimeManager::resetReturnBuf() { - m_builder.CreateStore(m_builder.getInt64(0), m_returnBufSizePtr); + m_builder.CreateStore(m_builder.getInt64(0), m_returnBufSizePtr); } llvm::Value* RuntimeManager::getCallData() { - return m_dataElts[RuntimeData::CallData]; + return m_dataElts[RuntimeData::CallData]; } llvm::Value* RuntimeManager::getCode() { - // OPT Check what is faster - //return get(RuntimeData::Code); - if (!m_codePtr) - m_codePtr = m_builder.CreateGlobalStringPtr({reinterpret_cast(m_codeBegin), static_cast(m_codeEnd - m_codeBegin)}, "code"); - return m_codePtr; + // OPT Check what is faster + // return get(RuntimeData::Code); + if (!m_codePtr) + m_codePtr = + m_builder.CreateGlobalStringPtr({reinterpret_cast(m_codeBegin), + static_cast(m_codeEnd - m_codeBegin)}, + "code"); + return m_codePtr; } llvm::Value* RuntimeManager::getCodeSize() { - return Constant::get(m_codeEnd - m_codeBegin); + return Constant::get(m_codeEnd - m_codeBegin); } llvm::Value* RuntimeManager::getCallDataSize() { - auto value = m_dataElts[RuntimeData::CallDataSize]; - assert(value->getType() == Type::Size); - return m_builder.CreateZExt(value, Type::Word); + auto value = m_dataElts[RuntimeData::CallDataSize]; + assert(value->getType() == Type::Size); + return m_builder.CreateZExt(value, Type::Word); } llvm::Value* RuntimeManager::getGas() { - return m_builder.CreateLoad(getGasPtr(), "gas"); + return m_builder.CreateLoad(getGasPtr(), "gas"); } llvm::Value* RuntimeManager::getGasPtr() { - assert(getMainFunction()); - return m_gasPtr; + assert(getMainFunction()); + return m_gasPtr; } llvm::Value* RuntimeManager::getMem() { - assert(getMainFunction()); - return m_memPtr; + assert(getMainFunction()); + return m_memPtr; } void RuntimeManager::setGas(llvm::Value* _gas) { - assert(_gas->getType() == Type::Gas); - m_builder.CreateStore(_gas, getGasPtr()); + assert(_gas->getType() == Type::Gas); + m_builder.CreateStore(_gas, getGasPtr()); } -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/RuntimeManager.h b/libevmjit/RuntimeManager.h index 023df782..452e89ce 100644 --- a/libevmjit/RuntimeManager.h +++ b/libevmjit/RuntimeManager.h @@ -3,8 +3,8 @@ #include #include "CompilerHelper.h" -#include "Type.h" #include "Instruction.h" +#include "Type.h" namespace dev { @@ -14,86 +14,86 @@ namespace jit { using namespace evmjit; -class RuntimeManager: public CompilerHelper +class RuntimeManager : public CompilerHelper { public: - RuntimeManager(IRBuilder& _builder, code_iterator _codeBegin, code_iterator _codeEnd); + RuntimeManager(IRBuilder& _builder, code_iterator _codeBegin, code_iterator _codeEnd); - llvm::Value* getRuntimePtr(); - llvm::Value* getDataPtr(); - llvm::Value* getEnvPtr(); + llvm::Value* getRuntimePtr(); + llvm::Value* getDataPtr(); + llvm::Value* getEnvPtr(); - llvm::Value* getAddress(); - llvm::Value* getSender(); - llvm::Value* getValue(); - llvm::Value* getGas(); - llvm::Value* getGasPtr(); - llvm::Value* getCallData(); - llvm::Value* getCode(); - llvm::Value* getCodeSize(); - llvm::Value* getCallDataSize(); - llvm::Value* getDepth(); - llvm::Value* getJmpBuf() { return m_jmpBuf; } - void setGas(llvm::Value* _gas); + llvm::Value* getAddress(); + llvm::Value* getSender(); + llvm::Value* getValue(); + llvm::Value* getGas(); + llvm::Value* getGasPtr(); + llvm::Value* getCallData(); + llvm::Value* getCode(); + llvm::Value* getCodeSize(); + llvm::Value* getCallDataSize(); + llvm::Value* getDepth(); + llvm::Value* getJmpBuf() { return m_jmpBuf; } + void setGas(llvm::Value* _gas); - llvm::Value* getMem(); + llvm::Value* getMem(); - void registerReturnData(llvm::Value* _index, llvm::Value* _size); // TODO: Move to Memory. + void registerReturnData(llvm::Value* _index, llvm::Value* _size); // TODO: Move to Memory. - void exit(ReturnCode _returnCode); + void exit(ReturnCode _returnCode); - void abort(llvm::Value* _jmpBuf); + void abort(llvm::Value* _jmpBuf); - llvm::Value* getStackBase() const { return m_stackBase; } - llvm::Value* getStackSize() const { return m_stackSize; } + llvm::Value* getStackBase() const { return m_stackBase; } + llvm::Value* getStackSize() const { return m_stackSize; } - llvm::Value* getReturnBufDataPtr() const { return m_returnBufDataPtr; } - llvm::Value* getReturnBufSizePtr() const { return m_returnBufSizePtr; } + llvm::Value* getReturnBufDataPtr() const { return m_returnBufDataPtr; } + llvm::Value* getReturnBufSizePtr() const { return m_returnBufSizePtr; } - /// Reset RETURNDATA buffer (before calls). - /// This effectively only sets the buffer size to 0. - void resetReturnBuf(); + /// Reset RETURNDATA buffer (before calls). + /// This effectively only sets the buffer size to 0. + void resetReturnBuf(); - void setJmpBuf(llvm::Value* _jmpBuf) { m_jmpBuf = _jmpBuf; } - void setExitBB(llvm::BasicBlock* _bb) { m_exitBB = _bb; } + void setJmpBuf(llvm::Value* _jmpBuf) { m_jmpBuf = _jmpBuf; } + void setExitBB(llvm::BasicBlock* _bb) { m_exitBB = _bb; } - static llvm::StructType* getRuntimeType(); - static llvm::StructType* getRuntimeDataType(); - llvm::StructType* getTxContextType(); + static llvm::StructType* getRuntimeType(); + static llvm::StructType* getRuntimeDataType(); + llvm::StructType* getTxContextType(); - llvm::Value* getTxContextItem(unsigned _index); + llvm::Value* getTxContextItem(unsigned _index); - //TODO Move to schedule - static const size_t stackSizeLimit = 1024; + // TODO Move to schedule + static const size_t stackSizeLimit = 1024; private: - llvm::Value* getPtr(RuntimeData::Index _index); - void set(RuntimeData::Index _index, llvm::Value* _value); + llvm::Value* getPtr(RuntimeData::Index _index); + void set(RuntimeData::Index _index, llvm::Value* _value); - llvm::Value* m_jmpBuf = nullptr; - llvm::Value* m_dataPtr = nullptr; - llvm::Value* m_gasPtr = nullptr; - llvm::Value* m_memPtr = nullptr; - llvm::Value* m_envPtr = nullptr; - llvm::Value* m_returnBufDataPtr = nullptr; - llvm::Value* m_returnBufSizePtr = nullptr; + llvm::Value* m_jmpBuf = nullptr; + llvm::Value* m_dataPtr = nullptr; + llvm::Value* m_gasPtr = nullptr; + llvm::Value* m_memPtr = nullptr; + llvm::Value* m_envPtr = nullptr; + llvm::Value* m_returnBufDataPtr = nullptr; + llvm::Value* m_returnBufSizePtr = nullptr; - llvm::Value* m_txCtxLoaded = nullptr; - llvm::Value* m_txCtx = nullptr; - llvm::Function* m_loadTxCtxFn = nullptr; + llvm::Value* m_txCtxLoaded = nullptr; + llvm::Value* m_txCtx = nullptr; + llvm::Function* m_loadTxCtxFn = nullptr; - std::array m_dataElts; + std::array m_dataElts; - llvm::Value* m_stackBase = nullptr; - llvm::Value* m_stackSize = nullptr; + llvm::Value* m_stackBase = nullptr; + llvm::Value* m_stackSize = nullptr; - llvm::BasicBlock* m_exitBB = nullptr; + llvm::BasicBlock* m_exitBB = nullptr; - code_iterator m_codeBegin = {}; - code_iterator m_codeEnd = {}; - llvm::Value* m_codePtr = nullptr; + code_iterator m_codeBegin = {}; + code_iterator m_codeEnd = {}; + llvm::Value* m_codePtr = nullptr; }; -} -} -} +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Type.cpp b/libevmjit/Type.cpp index 6ac9a646..557fbc17 100644 --- a/libevmjit/Type.cpp +++ b/libevmjit/Type.cpp @@ -10,7 +10,6 @@ namespace eth { namespace jit { - llvm::IntegerType* Type::Word; llvm::PointerType* Type::WordPtr; llvm::IntegerType* Type::Bool; @@ -29,45 +28,45 @@ llvm::MDNode* Type::expectTrue; void Type::init(llvm::LLVMContext& _context) { - if (!Word) // Do init only once - { - Word = llvm::Type::getIntNTy(_context, 256); - WordPtr = Word->getPointerTo(); - Bool = llvm::Type::getInt1Ty(_context); - Size = llvm::Type::getInt64Ty(_context); - Gas = Size; - GasPtr = Gas->getPointerTo(); - Byte = llvm::Type::getInt8Ty(_context); - BytePtr = Byte->getPointerTo(); - Void = llvm::Type::getVoidTy(_context); - MainReturn = llvm::Type::getInt32Ty(_context); + if (!Word) // Do init only once + { + Word = llvm::Type::getIntNTy(_context, 256); + WordPtr = Word->getPointerTo(); + Bool = llvm::Type::getInt1Ty(_context); + Size = llvm::Type::getInt64Ty(_context); + Gas = Size; + GasPtr = Gas->getPointerTo(); + Byte = llvm::Type::getInt8Ty(_context); + BytePtr = Byte->getPointerTo(); + Void = llvm::Type::getVoidTy(_context); + MainReturn = llvm::Type::getInt32Ty(_context); - EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo(); - RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo(); - RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo(); + EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo(); + RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo(); + RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo(); - Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits::max()); + Constant::gasMax = + llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits::max()); - expectTrue = llvm::MDBuilder{_context}.createBranchWeights(1, 0); - } + expectTrue = llvm::MDBuilder{_context}.createBranchWeights(1, 0); + } } llvm::ConstantInt* Constant::get(int64_t _n) { - return llvm::ConstantInt::getSigned(Type::Word, _n); + return llvm::ConstantInt::getSigned(Type::Word, _n); } llvm::ConstantInt* Constant::get(llvm::APInt const& _n) { - return llvm::ConstantInt::get(Type::Word->getContext(), _n); + return llvm::ConstantInt::get(Type::Word->getContext(), _n); } llvm::ConstantInt* Constant::get(ReturnCode _returnCode) { - return llvm::ConstantInt::get(Type::MainReturn, static_cast(_returnCode)); -} - -} -} + return llvm::ConstantInt::get(Type::MainReturn, static_cast(_returnCode)); } +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Type.h b/libevmjit/Type.h index 42a9e25c..7a58251e 100644 --- a/libevmjit/Type.h +++ b/libevmjit/Type.h @@ -1,10 +1,10 @@ #pragma once +#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_start.h" -#include #include #include -#include "preprocessor/llvm_includes_end.h" +#include #include "JIT.h" @@ -18,44 +18,43 @@ using namespace evmjit; struct Type { - static llvm::IntegerType* Word; - static llvm::PointerType* WordPtr; + static llvm::IntegerType* Word; + static llvm::PointerType* WordPtr; - static llvm::IntegerType* Bool; - static llvm::IntegerType* Size; - static llvm::IntegerType* Gas; - static llvm::PointerType* GasPtr; + static llvm::IntegerType* Bool; + static llvm::IntegerType* Size; + static llvm::IntegerType* Gas; + static llvm::PointerType* GasPtr; - static llvm::IntegerType* Byte; - static llvm::PointerType* BytePtr; + static llvm::IntegerType* Byte; + static llvm::PointerType* BytePtr; - static llvm::Type* Void; + static llvm::Type* Void; - /// Main function return type - static llvm::IntegerType* MainReturn; + /// Main function return type + static llvm::IntegerType* MainReturn; - static llvm::PointerType* EnvPtr; - static llvm::PointerType* RuntimeDataPtr; - static llvm::PointerType* RuntimePtr; + static llvm::PointerType* EnvPtr; + static llvm::PointerType* RuntimeDataPtr; + static llvm::PointerType* RuntimePtr; - // TODO: Redesign static LLVM objects - static llvm::MDNode* expectTrue; + // TODO: Redesign static LLVM objects + static llvm::MDNode* expectTrue; - static void init(llvm::LLVMContext& _context); + static void init(llvm::LLVMContext& _context); }; struct Constant { - static llvm::ConstantInt* gasMax; + static llvm::ConstantInt* gasMax; - /// Returns word-size constant - static llvm::ConstantInt* get(int64_t _n); - static llvm::ConstantInt* get(llvm::APInt const& _n); + /// Returns word-size constant + static llvm::ConstantInt* get(int64_t _n); + static llvm::ConstantInt* get(llvm::APInt const& _n); - static llvm::ConstantInt* get(ReturnCode _returnCode); + static llvm::ConstantInt* get(ReturnCode _returnCode); }; -} -} -} - +} // namespace jit +} // namespace eth +} // namespace dev diff --git a/libevmjit/Utils.cpp b/libevmjit/Utils.cpp index ae2b7035..3d3fbd4a 100644 --- a/libevmjit/Utils.cpp +++ b/libevmjit/Utils.cpp @@ -10,16 +10,15 @@ namespace dev { namespace evmjit { - -#if !defined(NDEBUG) // Debug +#if !defined(NDEBUG) // Debug std::ostream& getLogStream(char const* _channel) { - static std::ostream nullStream{nullptr}; + static std::ostream nullStream{nullptr}; #if LLVM_DEBUG - return (llvm::DebugFlag && llvm::isCurrentDebugType(_channel)) ? std::cerr : nullStream; + return (llvm::DebugFlag && llvm::isCurrentDebugType(_channel)) ? std::cerr : nullStream; #else - return (void)_channel, nullStream; + return (void)_channel, nullStream; #endif } @@ -27,7 +26,6 @@ std::ostream& getLogStream(char const* _channel) namespace { - /** libkeccak-tiny * * A single-file implementation of SHA-3 and SHAKE. @@ -40,23 +38,16 @@ namespace /******** The Keccak-f[1600] permutation ********/ /*** Constants. ***/ -static const uint8_t rho[24] = \ - { 1, 3, 6, 10, 15, 21, - 28, 36, 45, 55, 2, 14, - 27, 41, 56, 8, 25, 43, - 62, 18, 39, 61, 20, 44}; -static const uint8_t pi[24] = \ - {10, 7, 11, 17, 18, 3, - 5, 16, 8, 21, 24, 4, - 15, 23, 19, 13, 12, 2, - 20, 14, 22, 9, 6, 1}; -static const uint64_t RC[24] = \ - {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, - 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, - 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, - 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, - 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL, - 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL}; +static const uint8_t rho[24] = { + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44}; +static const uint8_t pi[24] = { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1}; +static const uint64_t RC[24] = {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, + 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x8aULL, 0x88ULL, + 0x80008009ULL, 0x8000000aULL, 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, + 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, + 0x800000008000000aULL, 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, + 0x8000000080008008ULL}; /*** Helper macros to unroll the permutation. ***/ #define rol(x, s) (((x) << s) | ((x) >> (64 - s))) @@ -64,121 +55,110 @@ static const uint64_t RC[24] = \ #define REPEAT24(e) REPEAT6(e e e e) #define REPEAT5(e) e e e e e #define FOR5(v, s, e) \ - v = 0; \ - REPEAT5(e; v = decltype(v)(v + s);) + v = 0; \ + REPEAT5(e; v = decltype(v)(v + s);) /*** Keccak-f[1600] ***/ -static inline void keccakf(void* state) { - uint64_t* a = (uint64_t*)state; - uint64_t b[5] = {0}; - uint64_t t = 0; - uint8_t x, y; - - for (int i = 0; i < 24; i++) { - // Theta - FOR5(x, 1, - b[x] = 0; - FOR5(y, 5, - b[x] ^= a[x + y]; )) - FOR5(x, 1, - FOR5(y, 5, - a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) - // Rho and pi - t = a[1]; - x = 0; - REPEAT24(b[0] = a[pi[x]]; - a[pi[x]] = rol(t, rho[x]); - t = b[0]; - x++; ) - // Chi - FOR5(y, - 5, - FOR5(x, 1, - b[x] = a[y + x];) - FOR5(x, 1, - a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); )) - // Iota - a[0] ^= RC[i]; - } +static inline void keccakf(void* state) +{ + uint64_t* a = (uint64_t*)state; + uint64_t b[5] = {0}; + uint64_t t = 0; + uint8_t x, y; + + for (int i = 0; i < 24; i++) + { + // Theta + FOR5(x, 1, b[x] = 0; FOR5(y, 5, b[x] ^= a[x + y];)) + FOR5(x, 1, FOR5(y, 5, a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1);)) + // Rho and pi + t = a[1]; + x = 0; + REPEAT24(b[0] = a[pi[x]]; a[pi[x]] = rol(t, rho[x]); t = b[0]; x++;) + // Chi + FOR5(y, 5, + FOR5(x, 1, b[x] = a[y + x];) + FOR5(x, 1, a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]);)) + // Iota + a[0] ^= RC[i]; + } } /******** The FIPS202-defined functions. ********/ /*** Some helper macros. ***/ -#define _(S) do { S } while (0) -#define FOR(i, ST, L, S) \ - _(for (size_t i = 0; i < L; i += ST) { S; }) -#define mkapply_ds(NAME, S) \ - static inline void NAME(uint8_t* dst, \ - const uint8_t* src, \ - size_t len) { \ - FOR(i, 1, len, S); \ - } -#define mkapply_sd(NAME, S) \ - static inline void NAME(const uint8_t* src, \ - uint8_t* dst, \ - size_t len) { \ - FOR(i, 1, len, S); \ - } - -mkapply_ds(xorin, dst[i] ^= src[i]) // xorin -mkapply_sd(setout, dst[i] = src[i]) // setout +#define _(S) \ + do \ + { \ + S \ + } while (0) +#define FOR(i, ST, L, S) _(for (size_t i = 0; i < L; i += ST) { S; }) +#define mkapply_ds(NAME, S) \ + static inline void NAME(uint8_t* dst, const uint8_t* src, size_t len) { FOR(i, 1, len, S); } +#define mkapply_sd(NAME, S) \ + static inline void NAME(const uint8_t* src, uint8_t* dst, size_t len) { FOR(i, 1, len, S); } + +mkapply_ds(xorin, dst[i] ^= src[i]) // xorin + mkapply_sd(setout, dst[i] = src[i]) // setout #define P keccakf #define Plen 200 // Fold P*F over the full blocks of an input. #define foldP(I, L, F) \ - while (L >= rate) { \ - F(a, I, rate); \ - P(a); \ - I += rate; \ - L -= rate; \ - } - -/** The sponge-based hash construction. **/ -static inline int hash(uint8_t* out, size_t outlen, - const uint8_t* in, size_t inlen, - size_t rate, uint8_t delim) { - if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) { - return -1; - } - uint8_t a[Plen] = {0}; - // Absorb input. - foldP(in, inlen, xorin); - // Xor in the DS and pad frame. - a[inlen] ^= delim; - a[rate - 1] ^= 0x80; - // Xor in the last block. - xorin(a, in, inlen); - // Apply P - P(a); - // Squeeze output. - foldP(out, outlen, setout); - setout(a, out, outlen); - memset(a, 0, 200); - return 0; + while (L >= rate) \ + { \ + F(a, I, rate); \ + P(a); \ + I += rate; \ + L -= rate; \ + } + + /** The sponge-based hash construction. **/ + static inline int hash( + uint8_t* out, size_t outlen, const uint8_t* in, size_t inlen, size_t rate, uint8_t delim) +{ + if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) + { + return -1; + } + uint8_t a[Plen] = {0}; + // Absorb input. + foldP(in, inlen, xorin); + // Xor in the DS and pad frame. + a[inlen] ^= delim; + a[rate - 1] ^= 0x80; + // Xor in the last block. + xorin(a, in, inlen); + // Apply P + P(a); + // Squeeze output. + foldP(out, outlen, setout); + setout(a, out, outlen); + memset(a, 0, 200); + return 0; } /*** Helper macros to define SHA3 and SHAKE instances. ***/ -#define defkeccak(bits) \ - static int keccak_##bits(uint8_t* out, size_t outlen, \ - const uint8_t* in, size_t inlen) { \ - if (outlen > (bits/8)) { \ - return -1; \ - } \ - return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \ - } +#define defkeccak(bits) \ + static int keccak_##bits(uint8_t* out, size_t outlen, const uint8_t* in, size_t inlen) \ + { \ + if (outlen > (bits / 8)) \ + { \ + return -1; \ + } \ + return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \ + } defkeccak(256) -} +} // namespace void keccak(uint8_t const* _data, uint64_t _size, uint8_t* o_hash) { - keccak_256(o_hash, 32, _data, _size); + keccak_256(o_hash, 32, _data, _size); } -} -} +} // namespace evmjit +} // namespace dev diff --git a/libevmjit/Utils.h b/libevmjit/Utils.h index 862d45f7..5a0adb94 100644 --- a/libevmjit/Utils.h +++ b/libevmjit/Utils.h @@ -3,21 +3,22 @@ #include #include -namespace dev { -namespace evmjit { - -void keccak(uint8_t const *_data, uint64_t _size, uint8_t *o_hash); +namespace dev +{ +namespace evmjit +{ +void keccak(uint8_t const* _data, uint64_t _size, uint8_t* o_hash); // The same as assert, but expression is always evaluated and result returned #define CHECK(expr) (assert(expr), expr) -#if !defined(NDEBUG) // Debug +#if !defined(NDEBUG) // Debug -std::ostream &getLogStream(char const *_channel); +std::ostream& getLogStream(char const* _channel); #define DLOG(CHANNEL) ::dev::evmjit::getLogStream(#CHANNEL) -#else // Release +#else // Release struct Voider { @@ -28,5 +29,5 @@ struct Voider #endif -} -} +} // namespace evmjit +} // namespace dev diff --git a/libevmjit/preprocessor/llvm_includes_end.h b/libevmjit/preprocessor/llvm_includes_end.h index 643e0306..658cd5d5 100644 --- a/libevmjit/preprocessor/llvm_includes_end.h +++ b/libevmjit/preprocessor/llvm_includes_end.h @@ -1,3 +1,3 @@ #if defined(_MSC_VER) - #pragma warning(pop) +#pragma warning(pop) #endif diff --git a/libevmjit/preprocessor/llvm_includes_start.h b/libevmjit/preprocessor/llvm_includes_start.h index c951399b..0f580d57 100644 --- a/libevmjit/preprocessor/llvm_includes_start.h +++ b/libevmjit/preprocessor/llvm_includes_start.h @@ -1,4 +1,4 @@ #if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable: 4267 4244 4800 4624 4141 4291 4146) +#pragma warning(push) +#pragma warning(disable : 4267 4244 4800 4624 4141 4291 4146) #endif