diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..d003e0b --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,78 @@ +version: "{build}" + +image: Visual Studio 2015 +clone_folder: c:\dev\asmjit + +environment: + matrix: + - BUILD_TYPE: Debug + MINGW_PATH: C:\msys64\mingw64 + TOOLCHAIN: "MinGW Makefiles" + + - BUILD_TYPE: Release + MINGW_PATH: C:\msys64\mingw64 + TOOLCHAIN: "MinGW Makefiles" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 10 2010" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 10 2010" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 10 2010 Win64" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 10 2010 Win64" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 12 2013" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 12 2013" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 12 2013 Win64" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 12 2013 Win64" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 14 2015" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 14 2015" + + - BUILD_TYPE: Debug + TOOLCHAIN: "Visual Studio 14 2015 Win64" + + - BUILD_TYPE: Release + TOOLCHAIN: "Visual Studio 14 2015 Win64" + +install: + - if "%TOOLCHAIN%"=="MinGW Makefiles" set PATH=%PATH:C:\Program Files\Git\usr\bin;=% + - if "%TOOLCHAIN%"=="MinGW Makefiles" set PATH=%MINGW_PATH%\bin;%PATH% + +build_script: + - cd c:\dev\asmjit + - md build + - cd build + - if "%TOOLCHAIN%"=="MinGW Makefiles" ( + cmake .. -G"%TOOLCHAIN%" -DCMAKE_PREFIX_PATH="%MINGW_PATH%" -DCMAKE_BUILD_TYPE="%BUILD_TYPE%" -DASMJIT_BUILD_TEST=1 && + mingw32-make + ) + else ( + cmake .. -G"%TOOLCHAIN%" -DASMJIT_BUILD_TEST=1 && + msbuild /m /nologo /v:quiet /p:Configuration=%BUILD_TYPE% asmjit.sln + ) + +test_script: + - if "%TOOLCHAIN%"=="MinGW Makefiles" ( + cd c:\dev\asmjit\build + ) + else ( + cd c:\dev\asmjit\build\%BUILD_TYPE% + ) + - asmjit_test_unit.exe + - asmjit_test_x86_asm.exe + - asmjit_test_x86_cc.exe diff --git a/.gitignore b/.gitignore index 2bd624b..71badb6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ +.kdev4 +*.kdev4 +build build_* +tools/asmdb diff --git a/.travis.yml b/.travis.yml index b9928b3..fbd80b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,12 @@ language: cpp -os: - - linux - - osx +os: [linux, osx] +compiler: [gcc, clang] -compiler: - - clang - - gcc +addons: + apt: + packages: [cmake, gcc-multilib, g++-multilib, valgrind] + sources: [ubuntu-toolchain-r-test] env: matrix: @@ -20,20 +20,16 @@ matrix: - os: osx compiler: gcc -before_install: - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then - CMAKE_PACKAGE="cmake-3.3.2-Linux-x86_64" - && wget https://www.cmake.org/files/v3.3/${CMAKE_PACKAGE}.tar.gz --no-check-certificate - && sudo apt-get -qq update - && sudo apt-get -qq install gcc-multilib g++-multilib valgrind - && tar -xzf ${CMAKE_PACKAGE}.tar.gz - && sudo cp -fR ${CMAKE_PACKAGE}/* /usr - ; +install: + - | + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then + CMAKE_PACKAGE="https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz" + mkdir -p deps/cmake + wget --no-check-certificate --quiet -O - ${CMAKE_PACKAGE} | tar --strip-components=1 -xz -C deps/cmake + export PATH=${TRAVIS_BUILD_DIR}/deps/cmake/bin:${PATH} else - brew update - && brew unlink cmake - && brew install -v cmake - ; + brew update + brew outdated cmake || brew upgrade cmake fi before_script: @@ -49,7 +45,11 @@ script: - cd .. - ./build/asmjit_test_unit - - ./build/asmjit_test_x86 + - ./build/asmjit_test_opcode > /dev/null + - ./build/asmjit_test_x86_asm + - ./build/asmjit_test_x86_cc after_success: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then valgrind --leak-check=full --show-reachable=yes ./build/asmjit_test_unit; fi; + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then valgrind --leak-check=full --show-reachable=yes ./build/asmjit_test_x86_asm; fi; + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then valgrind --leak-check=full --show-reachable=yes ./build/asmjit_test_x86_cc; fi; diff --git a/BREAKING.md b/BREAKING.md index 03e1ec5..2ff5710 100644 --- a/BREAKING.md +++ b/BREAKING.md @@ -1,86 +1,75 @@ -2016-03-21 - -CpuInfo has been completely redesigned. It now supports multiple CPUs without having to inherit it to support a specific architecture. Also all CpuInfo-related constants have been moved to CpuInfo. - -Change: - -``` -const X86CpuInfo* cpu = X86CpuInfo::getHost(); -cpu->hasFeature(kX86CpuFeatureSSE4_1); -``` - -to - -``` -const CpuInfo& cpu = CpuInfo::getHost(); -cpu.hasFeature(CpuInfo::kX86FeatureSSE4_1); -``` - -The whole code-base now uses `noexcept` keyword to inform API users that these functions won't throw an exception. Moreover, the possibility to throw exception through `ErrorHandler` has been removed as it seems that nobody has ever used it. `Assembler::emit()` and friends are still not marked as `noexcept` in case this decision is taken back. If there is no complaint even `emit()` functions will be marked `noexcept` in the near future. - -2015-12-07 ----------- - -Compiler now attaches to Assembler. This change was required to create resource sharing where Assembler is the central part and Compiler is a "high-level" part that serializes to it. It's an incremental work to implement sections and to allow code generators to create executables and libraries. - -Also, Compiler has no longer Logger interface, it uses Assembler's one after it's attached to it. - -``` -JitRuntime runtime; -X86Compiler c(&runtime); - -// ... code generation ... - -void* p = c.make(); -``` - -to - -``` -JitRuntime runtime; -X86Assembler a(&runtime); -X86Compiler c(&a); - -// ... code generation ... - -c.finalize(); -void* p = a.make(); -``` - -All nodes were prefixed with HL, except for platform-specific nodes, change: - -``` -Node -> HLNode -FuncNode -> HLFunc -X86FuncNode -> X86Func -X86CallNode -> X86Call -``` - -`FuncConv` renamed to `CallConv` and is now part of a function prototype, change: - -``` -compiler.addFunc(kFuncConvHost, FuncBuilder0()); -``` - -to - -``` -compiler.addFunc(FuncBuilder0(kCallConvHost)); -``` - -Operand constructors that accept Assembler or Compiler are deprecated. Variables can now be created by using handy shortcuts like newInt32(), newIntPtr(), newXmmPd(), etc... Change: - -``` -X86Compiler c(...); -Label L(c); -X86GpVar x(c, kVarTypeIntPtr, "x"); -``` - -to - -``` -X86Compiler c(...); -Label L = c.newLabel(); -X86GpVar x = c.newIntPtr("x"); -``` - +2016-07-20 +---------- + + * Global `asmjit_cast<>` removed and introduced a more type-safe `asmjit::ptr_cast<>`, which can cast a function to `void*` (and vice-versa), but will refuse to cast a function to `void**`, for example. Just change `asmjit_cast` to `asmjit::ptr_cast` and everything should work as usual. As a consequence, the Runtime now contains a typesafe (templated) `add()` and `remove()` methods that accept a function type directly, no need to cast manually to `void*` and `void**`. If you use your own runtime rename your virtual methods from `add` to `_add` and from `release` to `_release` and enjoy the type-safe wrappers. + * Removed `Logger::Style` and `uint32_t style` parameter in Logging API. It was never used for anything so it was removed. + * There is a new `CodeEmitter` base class that defines assembler building blocks that are implemented by `Assembler` and `CodeBuilder`. `CodeCompiler` is now based on `CodeBuilder` and shares its instruction storage functionality. Most API haven't changed, just base classes and new functionality has been added. It's now possible to serialize code for further processing by using `CodeBuilder`. + * Renamed compile-time macro `ASMJIT_DISABLE_LOGGER` to `ASMJIT_DISABLE_LOGGING`. There is a new `Formatter` class which is also disabled with this option. + + * Operand API is mostly intact, omitting Var/Reg should fix most compile-time errors. There is now no difference between a register index and register id internally. If you ever used `reg.getRegIndex()` then use `reg.getId()` instead. Also renamed `isInitialized()` to `isValid()`. + * There are much more changes, but they are mostly internal and keeping most operand methods compatible. + * Added new functionality into `asmjit::x86` namespace related to operands. + * X86Xmm/X86Ymm/X86Zmm register operands now inherit from X86Vec. + * Register kind (was register class) is now part of `Reg` operand, you can get it by using `reg.getRegKind()`. + * Register class enum moved to `X86Reg`, `kX86RegClassGp` is now `X86Reg::kKindGp`. + * Register type enum moved to `X86Reg`, `kX86RegTypeXmm` is now `X86Reg::kRegXmm`. + * Register index enum moved to `X86Gp`, `kX86RegIndexAx` is now `X86Gp::kIdAx`. + * Segment index enum moved to `X86Seg`, `kX86SegFs` is now `X86Seg::kIdFs`. + * If you used `asmjit::noOperand` for any reason, change it to `Operand()`. + + * CodeBuilder and CodeCompiler now contain different prefix of their nodes to distinguish between them: + + * Rename `HLNode` to `CBNode` (CodeBuilder node). + * Rename all other `HL` to `CB`. + * Rename `X86FuncNode` to `CCFunc` (CodeCompiler function), no more arch specific prefixes here. + * Rename `X86CallNode` to `CCFuncCall` (CodeCompiler function-call), also, no more X86 prefix. + + * AsmJit now uses CodeHolder to hold code. You don't need `Runtime` anymore if you don't plan to execute the code or if you plan to relocate it yourself: + +```c++ +CodeHolder code; // Create CodeHolder (holds the code). +code.init(CodeInfo(ArchInfo::kIdX64)); // Initialize CodeHolder to hold X64 code. + +// Everything else as usual: +X86Assembler a(&code); // Create the emitter (Assembler, CodeBuilder, CodeCompiler). +``` + + * Initializing with JitRuntime involves using CodeHolder: + +```c++ +JitRuntime rt; // Create JitRuntime. + +CodeHolder code; // Create CodeHolder. +code.init(rt.getCodeInfo()); // Initialize CodeHolder to match the JitRuntime. + +X86Assembler a(&code); // Create the emitter (Assembler, CodeBuilder, CodeCompiler). +... // Generate some code. + +typedef void (*SomeFunc)(void); // Prototype of the function you generated. + +SomeFunc func; // Function pointer. +Error err = rt.add(&func, &code); // Add the generated function to the runtime. + +rt.remove(func); // Remove the generated function from the runtime. +``` + + * Merged virtual registers (known as variables or Vars) into registers themselves, making the interface simpler: + +```c++ +X86GpReg/X86GpVar merged to X86Gp +X86MmReg/X86MmVar merged to X86Mm +X86XmmReg/X86XmmVar merged to X86Xmm +X86YmmReg/X86YmmVar merged to X86Ymm +``` + + * Refactored instruction database, moved many enums related to instructions into `X86Inst`. Also some instructions were wrong (having wrong signature in Assembler and Compiler) and were fixed. + +```c++ +X86InstInfo renamed to X86Inst +kX86InstIdSomething renamed to X86Inst::kIdSomething +kX86InstOptionSomething renamed to X86Inst::kOptionSomething +kX86CondSomething renamed to X86Inst::kCondSomething +kX86CmpSomething renamed to X86Inst::kCmpSomething +kX86VCmpSomething renamed to X86Inst::kVCmpSomething +kX86PrefetchSomething renamed to X86Inst::kPrefetchSomething +``` diff --git a/CMakeLists.txt b/CMakeLists.txt index 37061c9..8c38965 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,150 +1,61 @@ cmake_minimum_required(VERSION 3.1) -include(CheckCXXCompilerFlag) + +# Don't create a project if it was already created by another CMakeLists.txt. +# This allows one library to embed another library without a project collision. +if(NOT CMAKE_PROJECT_NAME OR "${CMAKE_PROJECT_NAME}" STREQUAL "asmjit") + project(asmjit C CXX) +endif() # ============================================================================= # [AsmJit - Configuration] # ============================================================================= -# Embedding mode, asmjit will not create any targets (default FALSE). -# set(ASMJIT_EMBED FALSE) +set(ASMJIT_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE PATH "Location of 'asmjit'") -# Whether to build a static library (default FALSE). -# set(ASMJIT_STATIC FALSE) - -# Used for debugging asmjit itself (default FALSE). -# set(ASMJIT_TRACE FALSE) - -# Whether to build ARM32 backend (TRUE if building for ARM32). -# set(ASMJIT_BUILD_ARM32 FALSE) - -# Whether to build ARM64 backend (TRUE if building for ARM64). -# set(ASMJIT_BUILD_ARM64 FALSE) - -# Whether to build X86 backend (TRUE if building for X86). -# set(ASMJIT_BUILD_X86 FALSE) - -# Whether to build X64 backend (TRUE if building for X64). -# set(ASMJIT_BUILD_X64 FALSE) - -# Whether to build tests and samples (default FALSE). -# set(ASMJIT_BUILD_TEST FALSE) +set(ASMJIT_EMBED FALSE CACHE BOOLEAN "Embed 'asmjit' library (no targets)") +set(ASMJIT_STATIC FALSE CACHE BOOLEAN "Build 'asmjit' library as static") +set(ASMJIT_BUILD_ARM FALSE CACHE BOOLEAN "Build ARM32/ARM64 backends") +set(ASMJIT_BUILD_X86 FALSE CACHE BOOLEAN "Build X86/X64 backends") +set(ASMJIT_BUILD_TEST FALSE CACHE BOOLEAN "Build 'asmjit_test' applications") # ============================================================================= -# [AsmJit - Build / Embed] +# [AsmJit - Project] # ============================================================================= -# Do not create a project if this CMakeLists.txt is included from another -# project. This makes it easy to embed or create a static library. -if(NOT CMAKE_PROJECT_NAME OR "${CMAKE_PROJECT_NAME}" MATCHES "^asmjit$") - project(asmjit C CXX) - set(ASMJIT_SIGNATURE "Standalone") -else() - set(ASMJIT_SIGNATURE "Included") -endif() - -if(ASMJIT_EMBED) - set(ASMJIT_SIGNATURE "${ASMJIT_SIGNATURE} | Mode=Embed") - set(ASMJIT_STATIC TRUE) # Implies ASMJIT_STATIC. -elseif(ASMJIT_STATIC) - set(ASMJIT_SIGNATURE "${ASMJIT_SIGNATURE} | Mode=Static") -else() - set(ASMJIT_SIGNATURE "${ASMJIT_SIGNATURE} | Mode=Shared") -endif() - -if(ASMJIT_BUILD_TEST) - set(ASMJIT_SIGNATURE "${ASMJIT_SIGNATURE} | Test=On") -else() - set(ASMJIT_SIGNATURE "${ASMJIT_SIGNATURE} | Test=Off") -endif() - -if(NOT ASMJIT_DIR) - set(ASMJIT_DIR ${CMAKE_CURRENT_LIST_DIR}) -endif() - -message("-- [asmjit] ${ASMJIT_SIGNATURE}") -message("-- [asmjit] ASMJIT_DIR=${ASMJIT_DIR}") - -# ============================================================================= -# [NP-Utilities] -# ============================================================================= - -function(np_detect_options out) - set(out_array) - foreach(flag ${ARGN}) - check_cxx_compiler_flag("${flag}" ok) - if(ok) - list(APPEND out_array "${flag}") - endif() - unset(ok) - endforeach() - set(${out} "${out_array}" PARENT_SCOPE) -endfunction() - -# ============================================================================= -# [AsmJit - Flags / Deps] -# ============================================================================= - -set(ASMJIT_SOURCE_DIR "${ASMJIT_DIR}/src") # Asmjit source directory. -set(ASMJIT_INCLUDE_DIR "${ASMJIT_SOURCE_DIR}") # Asmjit include directory. - -set(ASMJIT_CFLAGS) # Asmjit CFLAGS / CXXFLAGS. -set(ASMJIT_DEPS) # Asmjit dependencies (list of libraries) for the linker. -set(ASMJIT_LIBS) # Asmjit dependencies with asmjit included, for consumers. - -# Internal, never use. -set(ASMJIT_D "-D") # Used to define a C/C++ preprocessor parameter (-D or /D). -set(ASMJIT_PRIVATE_CFLAGS) # Compiler flags independent of build type. -set(ASMJIT_PRIVATE_LFLAGS "") # Linker flags used by the library and tests. - -set(ASMJIT_PRIVATE_CFLAGS_DBG) # Compiler flags used only by debug build. -set(ASMJIT_PRIVATE_CFLAGS_REL) # Compiler flags used only by release build. +include("${ASMJIT_DIR}/CxxProject.cmake") +cxx_project(asmjit) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set(ASMJIT_D "/D") set(ASMJIT_PRIVATE_LFLAGS "/OPT:REF /OPT:ICF") list(APPEND ASMJIT_PRIVATE_CFLAGS /GF) list(APPEND ASMJIT_PRIVATE_CFLAGS_DBG /GS /GR-) list(APPEND ASMJIT_PRIVATE_CFLAGS_REL /Oi /Oy /GS- /GR-) + + # Enable multi-process compilation. if(NOT MSVC60 AND NOT MSVC70 AND NOT MSVC71) - list(APPEND ASMJIT_PRIVATE_CFLAGS /MP) # Enable multi-process compilation. + list(APPEND ASMJIT_PRIVATE_CFLAGS /MP) endif() endif() if("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(GNU|Clang)$") - # Keep only the first option detected. - np_detect_options(ASMJIT_CC_OPTIONS - "-std=c++14" - "-std=c++11" - "-std=c++0x") - if(ASMJIT_CC_OPTIONS) - list(GET ASMJIT_CC_OPTIONS 0 ASMJIT_CC_OPTIONS) - list(APPEND ASMJIT_PRIVATE_CFLAGS ${ASMJIT_CC_OPTIONS}) - endif() - - np_detect_options(ASMJIT_CC_OPTIONS - "-fno-exceptions" + cxx_detect_standard(ASMJIT_PRIVATE_CFLAGS) + cxx_detect_cflags(ASMJIT_PRIVATE_CFLAGS "-fno-tree-vectorize" - "-fvisibility=hidden") - - list(APPEND ASMJIT_PRIVATE_CFLAGS ${ASMJIT_CC_OPTIONS}) - list(APPEND ASMJIT_PRIVATE_CFLAGS_REL -fmerge-all-constants) - - unset(ASMJIT_CC_OPTIONS) -endif() - -if(ASMJIT_EMBED) - list(APPEND ASMJIT_PRIVATE_CFLAGS "${ASMJIT_D}ASMJIT_EMBED") -elseif(ASMJIT_STATIC) - list(APPEND ASMJIT_PRIVATE_CFLAGS "${ASMJIT_D}ASMJIT_STATIC") + "-fvisibility=hidden" + "-Winconsistent-missing-override") + cxx_detect_cflags(ASMJIT_PRIVATE_CFLAGS_REL + "-O2" # CMake by default uses -O3, which does nothing useful. + "-fno-keep-static-consts" + "-fmerge-all-constants") endif() if(ASMJIT_TRACE) - list(APPEND ASMJIT_PRIVATE_CFLAGS "${ASMJIT_D}ASMJIT_TRACE") + list(APPEND ASMJIT_PRIVATE_CFLAGS "${CXX_DEFINE}ASMJIT_TRACE") endif() if(WIN32) - list(APPEND ASMJIT_PRIVATE_CFLAGS "${ASMJIT_D}_UNICODE") + list(APPEND ASMJIT_PRIVATE_CFLAGS "${CXX_DEFINE}_UNICODE") else() list(APPEND ASMJIT_DEPS pthread) endif() @@ -158,72 +69,21 @@ if(NOT ASMJIT_EMBED) list(INSERT ASMJIT_LIBS 0 asmjit) endif() -if(ASMJIT_BUILD_ARM32) - List(APPEND ASMJIT_CFLAGS "${ASMJIT_D}ASMJIT_BUILD_ARM32") -endif() - -if(ASMJIT_BUILD_ARM64) - List(APPEND ASMJIT_CFLAGS "${ASMJIT_D}ASMJIT_BUILD_ARM64") -endif() - -if(ASMJIT_BUILD_X86) - List(APPEND ASMJIT_CFLAGS "${ASMJIT_D}ASMJIT_BUILD_X86") -endif() - -if(ASMJIT_BUILD_X64) - List(APPEND ASMJIT_CFLAGS "${ASMJIT_D}ASMJIT_BUILD_X64") -endif() - -set(ASMJIT_PRIVATE_CFLAGS_DBG ${ASMJIT_CFLAGS} ${ASMJIT_PRIVATE_CFLAGS} ${ASMJIT_PRIVATE_CFLAGS_DBG}) -set(ASMJIT_PRIVATE_CFLAGS_REL ${ASMJIT_CFLAGS} ${ASMJIT_PRIVATE_CFLAGS} ${ASMJIT_PRIVATE_CFLAGS_REL}) - -message("-- [asmjit] ASMJIT_DEPS=${ASMJIT_DEPS}") -message("-- [asmjit] ASMJIT_LIBS=${ASMJIT_LIBS}") -message("-- [asmjit] ASMJIT_CFLAGS=${ASMJIT_CFLAGS}") - -# ============================================================================= -# [AsmJit - Macros] -# ============================================================================= - -macro(asmjit_add_source _out_dst _src_dir) - set(_src_path "${ASMJIT_SOURCE_DIR}/${_src_dir}") - set(_src_list) - - foreach(_arg ${ARGN}) - set(_src_file "${_src_path}/${_arg}") - list(APPEND _src_list ${_src_file}) - endforeach() - - list(APPEND "${_out_dst}" ${_src_list}) - source_group(${_src_dir} FILES ${_src_list}) -endmacro() - -macro(asmjit_add_library _target _src _deps _cflags _cflags_dbg _cflags_rel) - if(NOT ASMJIT_STATIC) - add_library(${_target} SHARED ${_src}) - else() - add_library(${_target} STATIC ${_src}) +foreach(BUILD_OPTION + ASMJIT_BUILD_ARM + ASMJIT_BUILD_X86 + ASMJIT_DISABLE_BUILDER + ASMJIT_DISABLE_COMPILER + ASMJIT_DISABLE_TEXT + ASMJIT_DISABLE_LOGGING + ASMJIT_DISABLE_VALIDATION) + if(${BUILD_OPTION}) + List(APPEND ASMJIT_CFLAGS "${CXX_DEFINE}${BUILD_OPTION}") + List(APPEND ASMJIT_PRIVATE_CFLAGS "${CXX_DEFINE}${BUILD_OPTION}") endif() +endforeach() - target_link_libraries(${_target} ${_deps}) - set_target_properties(${_target} PROPERTIES LINK_FLAGS "${ASMJIT_PRIVATE_LFLAGS}") - - if(CMAKE_BUILD_TYPE) - if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - target_compile_options(${_target} PRIVATE ${_cflags} ${_cflags_dbg}) - else() - target_compile_options(${_target} PRIVATE ${_cflags} ${_cflags_rel}) - endif() - else() - target_compile_options(${_target} PRIVATE ${_cflags} - $<$:${_cflags_dbg}> - $<$>:${_cflags_rel}>) - endif() - - if(NOT ASMJIT_STATIC) - install(TARGETS ${_target} DESTINATION "lib${LIB_SUFFIX}") - endif() -endmacro() +cxx_project_info(asmjit) # ============================================================================= # [AsmJit - Source] @@ -231,48 +91,53 @@ endmacro() set(ASMJIT_SRC "") -asmjit_add_source(ASMJIT_SRC asmjit - apibegin.h - apiend.h - +cxx_add_source(asmjit ASMJIT_SRC asmjit asmjit.h + asmjit_apibegin.h + asmjit_apiend.h + asmjit_build.h base.h - build.h - host.h - arm.h x86.h ) -asmjit_add_source(ASMJIT_SRC asmjit/base +cxx_add_source(asmjit ASMJIT_SRC asmjit/base + arch.cpp + arch.h assembler.cpp assembler.h - compiler.cpp - compiler.h - compilercontext.cpp - compilercontext_p.h - compilerfunc.h + codebuilder.cpp + codebuilder.h + codecompiler.cpp + codecompiler.h + codeemitter.cpp + codeemitter.h + codeholder.cpp + codeholder.h constpool.cpp constpool.h - containers.cpp - containers.h cpuinfo.cpp cpuinfo.h + func.cpp + func.h globals.cpp globals.h - hlstream.cpp - hlstream.h - logger.cpp - logger.h + logging.cpp + logging.h + misc_p.h operand.cpp operand.h - podvector.cpp - podvector.h + osutils.cpp + osutils.h + regalloc.cpp + regalloc_p.h runtime.cpp runtime.h + simdtypes.h + string.cpp + string.h utils.cpp utils.h - vectypes.h vmem.cpp vmem.h zone.cpp @@ -280,7 +145,7 @@ asmjit_add_source(ASMJIT_SRC asmjit/base ) if(0) -asmjit_add_source(ASMJIT_SRC asmjit/arm +cxx_add_source(asmjit ASMJIT_SRC asmjit/arm armassembler.cpp armassembler.h arminst.cpp @@ -291,20 +156,29 @@ asmjit_add_source(ASMJIT_SRC asmjit/arm ) endif() -asmjit_add_source(ASMJIT_SRC asmjit/x86 +cxx_add_source(asmjit ASMJIT_SRC asmjit/x86 x86assembler.cpp x86assembler.h + x86builder.cpp + x86builder.h x86compiler.cpp x86compiler.h - x86compilercontext.cpp - x86compilercontext_p.h - x86compilerfunc.cpp - x86compilerfunc.h + x86emitter.h + x86globals.h + x86internal.cpp + x86internal_p.h x86inst.cpp x86inst.h + x86logging.cpp + x86logging_p.h + x86misc.h x86operand.cpp x86operand_regs.cpp x86operand.h + x86regalloc.cpp + x86regalloc_p.h + x86ssetoavxpass.cpp + x86ssetoavxpass_p.h ) # ============================================================================= @@ -313,13 +187,12 @@ asmjit_add_source(ASMJIT_SRC asmjit/x86 if(NOT ASMJIT_EMBED) # Add `asmjit` library. - asmjit_add_library(asmjit + cxx_add_library(asmjit asmjit "${ASMJIT_SRC}" "${ASMJIT_DEPS}" - "" + "${ASMJIT_PRIVATE_CFLAGS}" "${ASMJIT_PRIVATE_CFLAGS_DBG}" - "${ASMJIT_PRIVATE_CFLAGS_REL}" - ) + "${ASMJIT_PRIVATE_CFLAGS_REL}") foreach(_src_file ${ASMJIT_SRC}) get_filename_component(_src_dir ${_src_file} PATH) @@ -334,30 +207,16 @@ if(NOT ASMJIT_EMBED) # Add `asmjit` tests and samples. if(ASMJIT_BUILD_TEST) - set(ASMJIT_TEST_SRC "") - set(ASMJIT_TEST_CFLAGS ${ASMJIT_CFLAGS} ${ASMJIT_D}ASMJIT_TEST ${ASMJIT_D}ASMJIT_EMBED) - asmjit_add_source(ASMJIT_TEST_SRC test asmjit_test_unit.cpp broken.cpp broken.h) + cxx_add_source(asmjit ASMJIT_TEST_SRC ../test asmjit_test_unit.cpp broken.cpp broken.h) + cxx_add_executable(asmjit asmjit_test_unit + "${ASMJIT_SRC};${ASMJIT_TEST_SRC}" + "${ASMJIT_DEPS}" + "${ASMJIT_PRIVATE_CFLAGS};${CXX_DEFINE}ASMJIT_TEST;${CXX_DEFINE}ASMJIT_EMBED" + "${ASMJIT_PRIVATE_CFLAGS_DBG}" + "${ASMJIT_PRIVATE_CFLAGS_REL}") - add_executable(asmjit_test_unit ${ASMJIT_SRC} ${ASMJIT_TEST_SRC}) - target_link_libraries(asmjit_test_unit ${ASMJIT_DEPS}) - set_target_properties(asmjit_test_unit PROPERTIES LINK_FLAGS "${ASMJIT_PRIVATE_LFLAGS}") - - if(CMAKE_BUILD_TYPE) - if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - target_compile_options(asmjit_test_unit PRIVATE ${ASMJIT_TEST_CFLAGS} ${ASMJIT_PRIVATE_CFLAGS_DBG}) - else() - target_compile_options(asmjit_test_unit PRIVATE ${ASMJIT_TEST_CFLAGS} ${ASMJIT_PRIVATE_CFLAGS_REL}) - endif() - else() - target_compile_options(asmjit_test_unit PRIVATE ${ASMJIT_TEST_CFLAGS} - $<$:${ASMJIT_PRIVATE_CFLAGS_DBG}> - $<$>:${ASMJIT_PRIVATE_CFLAGS_REL}>) - endif() - - foreach(_target asmjit_bench_x86 asmjit_test_opcode asmjit_test_x86) - add_executable(${_target} "src/test/${_target}.cpp") - target_compile_options(${_target} PRIVATE ${ASMJIT_CFLAGS}) - target_link_libraries(${_target} ${ASMJIT_LIBS}) + foreach(_target asmjit_bench_x86 asmjit_test_opcode asmjit_test_x86_asm asmjit_test_x86_cc) + cxx_add_executable(asmjit ${_target} "test/${_target}.cpp" "${ASMJIT_LIBS}" "${ASMJIT_CFLAGS}" "" "") endforeach() endif() endif() diff --git a/CxxProject.cmake b/CxxProject.cmake new file mode 100644 index 0000000..b068a8a --- /dev/null +++ b/CxxProject.cmake @@ -0,0 +1,335 @@ +# CxxProject 1.0.0 +# ---------------- + +if (NOT __CXX_INCLUDED) + set(__CXX_INCLUDED TRUE) + include(CheckCXXCompilerFlag) + + # --------------------------------------------------------------------------- + # C++ COMPILER SUPPORT: + # + # * cxx_detect_cflags(out, ...) + # * cxx_detect_standard(out) + # --------------------------------------------------------------------------- + function(cxx_detect_cflags out) + set(out_array ${${out}}) + + foreach(flag ${ARGN}) + string(REGEX REPLACE "[-=:;/.]" "_" flag_signature "${flag}") + check_cxx_compiler_flag(${flag} "__CxxFlag_${flag_signature}") + if(${__CxxFlag_${flag_signature}}) + list(APPEND out_array "${flag}") + endif() + endforeach() + + set(${out} "${out_array}" PARENT_SCOPE) + endfunction() + + function(cxx_detect_standard out) + set(out_array) + cxx_detect_cflags(out_array "-std=c++14" "-std=c++11" "-std=c++0x") + + # Keep only the first flag detected, which keeps the highest version supported. + if(out_array) + list(GET out_array 0 out_array) + endif() + + set(out_array ${${out}} ${out_array}) + set(${out} "${out_array}" PARENT_SCOPE) + endfunction() + + function(cxx_print_cflags cflags_any cflags_dbg cflags_rel) + foreach(flag ${cflags_any}) + message(" ${flag}") + endforeach() + foreach(flag ${cflags_dbg}) + message(" ${flag} [DEBUG]") + endforeach() + foreach(flag ${cflags_rel}) + message(" ${flag} [RELEASE]") + endforeach() + endfunction() + + # ----------------------------------------------------------------------------- + # This part detects the c++ compiler and fills basic CXX_... variables to make + # integration with that compiler easier. It provides the most common flags in + # a cross-platform way. + # ----------------------------------------------------------------------------- + set(CXX_DEFINE "-D") # Define a preprocessor macro: "${CXX_DEFINE}VAR=1" + set(CXX_INCLUDE "-I") # Define an include directory: "${CXX_INCLUDE}PATH" + + set(CXX_CFLAGS_SSE "") # Compiler flags to build a file that uses SSE intrinsics. + set(CXX_CFLAGS_SSE2 "") # Compiler flags to build a file that uses SSE2 intrinsics. + set(CXX_CFLAGS_SSE3 "") # Compiler flags to build a file that uses SSE3 intrinsics. + set(CXX_CFLAGS_SSSE3 "") # Compiler flags to build a file that uses SSSE3 intrinsics. + set(CXX_CFLAGS_SSE4_1 "") # Compiler flags to build a file that uses SSE4.1 intrinsics. + set(CXX_CFLAGS_SSE4_2 "") # Compiler flags to build a file that uses SSE4.2 intrinsics. + set(CXX_CFLAGS_AVX "") # Compiler flags to build a file that uses AVX intrinsics. + set(CXX_CFLAGS_AVX2 "") # Compiler flags to build a file that uses AVX2 intrinsics. + + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set(CXX_DEFINE "/D") + set(CXX_INCLUDE "/I") + + if(CMAKE_CL_64) + # 64-bit MSVC compiler doesn't like /arch:SSE[2] as it's implicit. + list(APPEND CXX_CFLAGS_SSE "${CXX_DEFINE}__SSE__=1") + list(APPEND CXX_CFLAGS_SSE2 "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1") + else() + cxx_detect_cflags(CXX_CFLAGS_SSE "/arch:SSE") + if(CXX_CFLAGS_SSE) + list(APPEND CXX_CFLAGS_SSE "${CXX_DEFINE}__SSE__=1") + endif() + + cxx_detect_cflags(CXX_CFLAGS_SSE2 "/arch:SSE2") + if(CXX_CFLAGS_SSE2) + list(APPEND CXX_CFLAGS_SSE2 "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1") + endif() + endif() + + # MSVC doesn't provide any preprocessor definitions to detect SSE3+, + # these unify MSVC with definitions defined by Clang|GCC|Intel. + if(CXX_CFLAGS_SSE2) + list(APPEND CXX_CFLAGS_SSE3 "${CXX_CFLAGS_SSE2};${CXX_DEFINE}__SSE3__=1") + list(APPEND CXX_CFLAGS_SSSE3 "${CXX_CFLAGS_SSE3};${CXX_DEFINE}__SSSE3__=1") + list(APPEND CXX_CFLAGS_SSE4_1 "${CXX_CFLAGS_SSSE3};${CXX_DEFINE}__SSE4_1__=1") + list(APPEND CXX_CFLAGS_SSE4_2 "${CXX_CFLAGS_SSE4_1};${CXX_DEFINE}__SSE4_2__=1") + endif() + + # When using AVX and AVX2 MSVC does define '__AVX__' and '__AVX2__', respectively. + cxx_detect_cflags(CXX_CFLAGS_AVX "/arch:AVX") + cxx_detect_cflags(CXX_CFLAGS_AVX2 "/arch:AVX2") + + if(CXX_CFLAGS_AVX) + list(APPEND CXX_CFLAGS_AVX "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1;${CXX_DEFINE}__SSE3__=1;${CXX_DEFINE}__SSSE3__=1;${CXX_DEFINE}__SSE4_1__=1;${CXX_DEFINE}__SSE4_2__=1") + endif() + if(CXX_CFLAGS_AVX2) + list(APPEND CXX_CFLAGS_AVX2 "${CXX_DEFINE}__SSE__=1;${CXX_DEFINE}__SSE2__=1;${CXX_DEFINE}__SSE3__=1;${CXX_DEFINE}__SSSE3__=1;${CXX_DEFINE}__SSE4_1__=1;${CXX_DEFINE}__SSE4_2__=1") + endif() + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" AND WIN32) + # Intel on Windows uses CL syntax. + set(CXX_DEFINE "/D") + set(CXX_INCLUDE "/I") + + # Intel deprecated /arch:SSE, so it's implicit. In contrast to MSVC, Intel + # also provides /arch:SSE3+ options and uses the same definitions as GCC + # and Clang, so no magic needed here. + cxx_detect_cflags(CXX_CFLAGS_SSE2 "/arch:SSE2") + cxx_detect_cflags(CXX_CFLAGS_SSE3 "/arch:SSE3") + cxx_detect_cflags(CXX_CFLAGS_SSSE3 "/arch:SSSE3") + cxx_detect_cflags(CXX_CFLAGS_SSE4_1 "/arch:SSE4.1") + cxx_detect_cflags(CXX_CFLAGS_SSE4_2 "/arch:SSE4.2") + cxx_detect_cflags(CXX_CFLAGS_AVX "/arch:AVX") + cxx_detect_cflags(CXX_CFLAGS_AVX2 "/arch:AVX2") + else() + cxx_detect_cflags(CXX_CFLAGS_SSE "-msse") + cxx_detect_cflags(CXX_CFLAGS_SSE2 "-msse2") + cxx_detect_cflags(CXX_CFLAGS_SSE3 "-msse3") + cxx_detect_cflags(CXX_CFLAGS_SSSE3 "-mssse3") + cxx_detect_cflags(CXX_CFLAGS_SSE4_1 "-msse4.1") + cxx_detect_cflags(CXX_CFLAGS_SSE4_2 "-msse4.2") + cxx_detect_cflags(CXX_CFLAGS_AVX "-mavx") + cxx_detect_cflags(CXX_CFLAGS_AVX2 "-mavx2") + endif() + + # --------------------------------------------------------------------------- + # Function + # cxx_project(product) + # + # Create a master project or embed other project in a master project. + # --------------------------------------------------------------------------- + function(cxx_project product) + string(TOUPPER "${product}" PRODUCT) + + set(MODE_EMBED ${${PRODUCT}_EMBED}) + set(MODE_STATIC ${${PRODUCT}_STATIC}) + + # EMBED implies STATIC. + if(MODE_EMBED) + set(MODE_STATIC TRUE) + set(${PRODUCT}_STATIC TRUE PARENT_SCOPE) + endif() + + # Deduce source and include directories. By default CxxProject assumes that + # both source and include files are located at './src'. + set(SOURCE_DIR "${${PRODUCT}_SOURCE_DIR}") + set(INCLUDE_DIR "${${PRODUCT}_INCLUDE_DIR}") + + if(NOT SOURCE_DIR) + set(SOURCE_DIR "${${PRODUCT}_DIR}/src") + set(${PRODUCT}_SOURCE_DIR "${SOURCE_DIR}" PARENT_SCOPE) + endif() + + if(NOT INCLUDE_DIR) + set(INCLUDE_DIR "${SOURCE_DIR}") + set(${PRODUCT}_INCLUDE_DIR "${INCLUDE_DIR}" PARENT_SCOPE) + endif() + + set(DEPS "") # Dependencies (list of libraries) for the linker. + set(LIBS "") # Dependencies with project included, for consumers. + set(CFLAGS "") # Public compiler flags. + set(PRIVATE_CFLAGS "") # Private compiler flags independent of build type. + set(PRIVATE_CFLAGS_DBG "") # Private compiler flags used by debug builds. + set(PRIVATE_CFLAGS_REL "") # Private compiler flags used by release builds. + set(PRIVATE_LFLAGS "") # Private linker flags. + + if(MODE_EMBED) + list(APPEND CFLAGS "${CXX_DEFINE}${PRODUCT}_EMBED") + list(APPEND PRIVATE_CFLAGS "${CXX_DEFINE}${PRODUCT}_EMBED") + endif() + + if(MODE_STATIC) + list(APPEND CFLAGS "${CXX_DEFINE}${PRODUCT}_STATIC") + list(APPEND PRIVATE_CFLAGS "${CXX_DEFINE}${PRODUCT}_STATIC") + endif() + + # PUBLIC properties - usable by third parties. + set(${PRODUCT}_DEPS "${DEPS}" PARENT_SCOPE) + set(${PRODUCT}_LIBS "${LIBS}" PARENT_SCOPE) + set(${PRODUCT}_CFLAGS "${CFLAGS}" PARENT_SCOPE) + + # PRIVATE properties - only used during build. + set(${PRODUCT}_PRIVATE_CFLAGS "${PRIVATE_CFLAGS}" PARENT_SCOPE) + set(${PRODUCT}_PRIVATE_CFLAGS_DBG "${PRIVATE_CFLAGS_DBG}" PARENT_SCOPE) + set(${PRODUCT}_PRIVATE_CFLAGS_REL "${PRIVATE_CFLAGS_REL}" PARENT_SCOPE) + set(${PRODUCT}_PRIVATE_LFLAGS "${PRIVATE_LFLAGS}" PARENT_SCOPE) + endfunction() + + function(cxx_project_info product) + string(TOUPPER "${product}" PRODUCT) + + set(BUILD_MODE "") + set(BUILD_TEST "") + + if(${PRODUCT}_EMBED) + set(BUILD_MODE "Embed") + elseif(${PRODUCT}_STATIC) + set(BUILD_MODE "Static") + else() + set(BUILD_MODE "Shared") + endif() + + if(${PRODUCT}_BUILD_TEST) + set(BUILD_TEST "On") + else() + set(BUILD_TEST "Off") + endif() + + message("-- [${product}]") + message(" BuildMode=${BUILD_MODE}") + message(" BuildTest=${BUILD_TEST}") + message(" ${PRODUCT}_DIR=${${PRODUCT}_DIR}") + message(" ${PRODUCT}_DEPS=${${PRODUCT}_DEPS}") + message(" ${PRODUCT}_LIBS=${${PRODUCT}_LIBS}") + message(" ${PRODUCT}_CFLAGS=${${PRODUCT}_CFLAGS}") + message(" ${PRODUCT}_SOURCE_DIR=${${PRODUCT}_SOURCE_DIR}") + message(" ${PRODUCT}_INCLUDE_DIR=${${PRODUCT}_INCLUDE_DIR}") + message(" ${PRODUCT}_PRIVATE_CFLAGS=") + cxx_print_cflags( + "${${PRODUCT}_PRIVATE_CFLAGS}" + "${${PRODUCT}_PRIVATE_CFLAGS_DBG}" + "${${PRODUCT}_PRIVATE_CFLAGS_REL}") + endfunction() + + function(cxx_add_source product out src_dir) + string(TOUPPER "${product}" PRODUCT) + + set(src_path "${${PRODUCT}_SOURCE_DIR}/${src_dir}") + set(src_array) + + foreach(file ${ARGN}) + set(src_file "${src_path}/${file}") + set(src_cflags "") + + if(file MATCHES "\\.c|\\.cc|\\.cxx|\\.cpp|\\.m|\\.mm") + if(file MATCHES "_sse\\." AND NOT "${CXX_CFLAGS_SSE}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSE}) + endif() + if(file MATCHES "_sse2\\." AND NOT "${CXX_CFLAGS_SSE2}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSE2}) + endif() + if(file MATCHES "_sse3\\." AND NOT "${CXX_CFLAGS_SSE3}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSE3}) + endif() + if(file MATCHES "_ssse3\\." AND NOT "${CXX_CFLAGS_SSSE3}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSSE3}) + endif() + if(file MATCHES "_sse4_1\\." AND NOT "${CXX_CFLAGS_SSE4_1}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSE4_1}) + endif() + if(file MATCHES "_sse4_2\\." AND NOT "${CXX_CFLAGS_SSE4_2}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_SSE4_2}) + endif() + if(file MATCHES "_avx\\." AND NOT "${CXX_CFLAGS_AVX}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_AVX}) + endif() + if(file MATCHES "_avx2\\." AND NOT "${CXX_CFLAGS_AVX2}" STREQUAL "") + list(APPEND src_cflags ${CXX_CFLAGS_AVX2}) + endif() + + if(NOT "${src_cflags}" STREQUAL "") + set_source_files_properties(${src_file} PROPERTIES COMPILE_FLAGS ${src_cflags}) + endif() + endif() + list(APPEND src_array ${src_file}) + endforeach() + source_group(${src_dir} FILES ${src_array}) + + set(out_tmp ${${out}}) + list(APPEND out_tmp ${src_array}) + set("${out}" "${out_tmp}" PARENT_SCOPE) + endfunction() + + function(cxx_add_library product target src deps cflags cflags_dbg cflags_rel) + string(TOUPPER "${product}" PRODUCT) + + if(NOT ${PRODUCT}_STATIC) + add_library(${target} SHARED ${src}) + else() + add_library(${target} STATIC ${src}) + endif() + + target_link_libraries(${target} ${deps}) + if (NOT "${${PRODUCT}_PRIVATE_LFLAGS}" STREQUAL "") + set_target_properties(${target} PROPERTIES LINK_FLAGS "${${PRODUCT}_PRIVATE_LFLAGS}") + endif() + + if(CMAKE_BUILD_TYPE) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + target_compile_options(${target} PRIVATE ${cflags} ${cflags_dbg}) + else() + target_compile_options(${target} PRIVATE ${cflags} ${cflags_rel}) + endif() + else() + target_compile_options(${target} PRIVATE ${cflags} $<$:${cflags_dbg}> $<$>:${cflags_rel}>) + endif() + + if(NOT ${PRODUCT}_STATIC) + install(TARGETS ${target} DESTINATION "lib${LIB_SUFFIX}") + endif() + endfunction() + + function(cxx_add_executable product target src deps cflags cflags_dbg cflags_rel) + string(TOUPPER "${product}" PRODUCT) + add_executable(${target} ${src}) + + target_link_libraries(${target} ${deps}) + if (NOT "${${PRODUCT}_PRIVATE_LFLAGS}" STREQUAL "") + set_target_properties(${target} PROPERTIES LINK_FLAGS "${${PRODUCT}_PRIVATE_LFLAGS}") + endif() + + if(CMAKE_BUILD_TYPE) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + target_compile_options(${target} PRIVATE ${cflags} ${cflags_dbg}) + else() + target_compile_options(${target} PRIVATE ${cflags} ${cflags_rel}) + endif() + else() + target_compile_options(${target} PRIVATE ${cflags} $<$:${cflags_dbg}> $<$>:${cflags_rel}>) + endif() + + if(NOT ${PRODUCT}_STATIC) + install(TARGETS ${target} DESTINATION "lib${LIB_SUFFIX}") + endif() + endfunction() +endif() diff --git a/Doxyfile b/Doxyfile deleted file mode 100644 index 1b4e622..0000000 --- a/Doxyfile +++ /dev/null @@ -1,236 +0,0 @@ -# Doxyfile 1.8.7 - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -DOXYFILE_ENCODING = UTF-8 - -PROJECT_NAME = "AsmJit" -PROJECT_NUMBER = "1.1" -PROJECT_BRIEF = "Complete Remote and JIT Assembler for x86/x64" - -OUTPUT_DIRECTORY = . -CREATE_SUBDIRS = NO -ALLOW_UNICODE_NAMES = NO -OUTPUT_LANGUAGE = English - -FULL_PATH_NAMES = YES -STRIP_FROM_PATH = -STRIP_FROM_INC_PATH = -SHORT_NAMES = NO - -BRIEF_MEMBER_DESC = YES -REPEAT_BRIEF = YES -QT_AUTOBRIEF = NO -JAVADOC_AUTOBRIEF = YES -MULTILINE_CPP_IS_BRIEF = YES - -ALWAYS_DETAILED_SEC = NO -INLINE_INHERITED_MEMB = NO - -INHERIT_DOCS = YES -TAB_SIZE = 2 -MARKDOWN_SUPPORT = YES -AUTOLINK_SUPPORT = NO -IDL_PROPERTY_SUPPORT = NO -SEPARATE_MEMBER_PAGES = NO - -DISTRIBUTE_GROUP_DOC = NO -SUBGROUPING = YES - -INLINE_GROUPED_CLASSES = NO -INLINE_SIMPLE_STRUCTS = NO - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -EXTRACT_ALL = NO -EXTRACT_PRIVATE = NO -EXTRACT_PACKAGE = NO -EXTRACT_STATIC = NO -EXTRACT_LOCAL_CLASSES = NO - -HIDE_UNDOC_CLASSES = YES -HIDE_UNDOC_MEMBERS = NO -HIDE_FRIEND_COMPOUNDS = YES -HIDE_IN_BODY_DOCS = YES - -INTERNAL_DOCS = NO -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO -SHOW_INCLUDE_FILES = NO - -SHOW_GROUPED_MEMB_INC = NO -INLINE_INFO = YES -SORT_MEMBER_DOCS = NO -SORT_BRIEF_DOCS = NO -SORT_GROUP_NAMES = NO -SORT_BY_SCOPE_NAME = YES -STRICT_PROTO_MATCHING = NO - -GENERATE_TODOLIST = NO -GENERATE_TESTLIST = NO -GENERATE_BUGLIST = NO -GENERATE_DEPRECATEDLIST= NO - -MAX_INITIALIZER_LINES = 0 -SHOW_USED_FILES = NO -SHOW_FILES = NO -SHOW_NAMESPACES = NO - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -QUIET = YES -WARNINGS = YES -WARN_IF_UNDOCUMENTED = NO -WARN_IF_DOC_ERROR = YES -WARN_NO_PARAMDOC = NO -WARN_FORMAT = "$file:$line: $text" -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -INPUT = src/asmjit -INPUT_ENCODING = UTF-8 -RECURSIVE = YES -EXCLUDE = -USE_MDFILE_AS_MAINPAGE = README.md - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -SOURCE_BROWSER = NO -INLINE_SOURCES = NO -STRIP_CODE_COMMENTS = YES -SOURCE_TOOLTIPS = YES -VERBATIM_HEADERS = NO - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -ALPHABETICAL_INDEX = NO - -#--------------------------------------------------------------------------- -# Configuration options related to outputs -#--------------------------------------------------------------------------- - -GENERATE_HTML = YES -GENERATE_LATEX = NO -GENERATE_RTF = NO -GENERATE_MAN = NO - -GENERATE_XML = YES -XML_OUTPUT = build_xml -XML_PROGRAMLISTING = NO - -HTML_OUTPUT = build_doc -HTML_FILE_EXTENSION = .html - -LAYOUT_FILE = tools/doc-layout.xml -HTML_HEADER = tools/doc-header.html -HTML_FOOTER = tools/doc-footer.html -HTML_STYLESHEET = tools/doc-style.css -HTML_EXTRA_STYLESHEET = -HTML_EXTRA_FILES = - -HTML_COLORSTYLE_HUE = 220 -HTML_COLORSTYLE_SAT = 100 -HTML_COLORSTYLE_GAMMA = 80 -HTML_TIMESTAMP = NO - -HTML_DYNAMIC_SECTIONS = NO -HTML_INDEX_NUM_ENTRIES = 0 -SEARCHENGINE = NO - -#--------------------------------------------------------------------------- -# Configuration options related to the CHM output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it -# enables the Previous and Next buttons. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO -DISABLE_INDEX = NO -GENERATE_TREEVIEW = NO -ENUM_VALUES_PER_LINE = 0 -TREEVIEW_WIDTH = 250 -EXT_LINKS_IN_WINDOW = NO - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -ENABLE_PREPROCESSING = YES -MACRO_EXPANSION = YES -EXPAND_ONLY_PREDEF = NO -EXPAND_AS_DEFINED = -SKIP_FUNCTION_MACROS = YES - -PREDEFINED = ASMJIT_DOCGEN \ - ASMJIT_BUILD_X86 \ - ASMJIT_BUILD_X64 \ - ASMJIT_API - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -CLASS_DIAGRAMS = NO -CLASS_GRAPH = NO diff --git a/LICENSE.md b/LICENSE.md index 3660760..9f8aad2 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,5 +1,5 @@ AsmJit - Complete x86/x64 JIT and Remote Assembler for C++ -Copyright (c) 2008-2015, Petr Kobalicek +Copyright (c) 2008-2016, Petr Kobalicek This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/README.md b/README.md index e73984a..cf07cd9 100644 --- a/README.md +++ b/README.md @@ -3,544 +3,1721 @@ AsmJit Complete x86/x64 JIT and Remote Assembler for C++. - * [Official Repository (kobalicek/asmjit)](https://github.com/kobalicek/asmjit) - * [Official Blog (asmbits)] (https://asmbits.blogspot.com/ncr) - * [Official Chat (gitter)](https://gitter.im/kobalicek/asmjit) + * [Official Repository (asmjit/asmjit)](https://github.com/asmjit/asmjit) + * [Official Blog (asmbits)](https://asmbits.blogspot.com/ncr) + * [Official Chat (gitter)](https://gitter.im/asmjit/asmjit) * [Permissive ZLIB license](./LICENSE.md) -News ----- - -For all new users - please use asmjit:next branch instead of asmjit:master if you are starting with AsmJit and want to use a new API, which is not backward compatible with current master. Asmjit:next is still in development, but the main refactorization already happened, so if it breaks it will be minor changes. - Introduction ------------ -AsmJit is a complete JIT and remote assembler for C++ language. It can generate native code for x86 and x64 architectures and supports the whole x86/x64 instruction set - from legacy MMX to the newest AVX2. It has a type-safe API that allows C++ compiler to do semantic checks at compile-time even before the assembled code is generated and executed. +AsmJit is a complete JIT and remote assembler for C++ language. It can generate native code for x86 and x64 architectures and supports the whole x86/x64 instruction set - from legacy MMX to the newest AVX512. It has a type-safe API that allows C++ compiler to do semantic checks at compile-time even before the assembled code is generated and/or executed. -AsmJit is not a virtual machine (VM). It doesn't have functionality to implement VM out of the box; however, it can be be used as a JIT backend of your own VM. The usage of AsmJit is not limited at all; it's suitable for multimedia, VM backends, remote code generation, and many other tasks. +AsmJit, as the name implies, started as a project that provided JIT code-generation and execution. However, AsmJit evolved and it now contains features that are far beyond the scope of a simple JIT compilation. To keep the library small and lightweight the functionality not strictly related to JIT is provided by a sister project called [asmtk](https://github.com/asmjit/asmtk). -Features --------- - - * Complete x86/x64 instruction set - MMX, SSEx, AVX1/2, BMI, XOP, FMA3, and FMA4 (AVX-512 in progress) - * Low-level and high-level code generation concepts - * Built-in CPU features detection - * Virtual Memory management similar to malloc/free - * Powerful logging and error handling - * Small and embeddable, around 150-200kB compiled - * Zero dependencies, not even STL or RTTI - -Supported Environments ----------------------- - -### Operating Systems - - * BSDs (not tested regularly) - * Linux (tested by Travis-CI) - * Mac (tested by Travis-CI) - * Windows (tested manually) - -### C++ Compilers - - * Clang (tested by Travis-CI) - * CodeGear (including BorlandC++, not tested regularly) - * GCC (tested by Travis-CI) - * MinGW (tested manually) - * MSVC (tested manually, at least VS2003 is required) - * Other compilers require some testing and support in `asmjit/build.h` - -### Backends - - * ARM (work-in-progress) - * X86 (tested by Travis-CI) - * X64 (tested by Travis-CI) - -Project Organization --------------------- - - * `/` - Project root - * `src` - Source code - * `asmjit` - Source code and headers (always point include path in here) - * `base` - Generic API and interfaces, used by all backends - * `arm` - ARM/ARM64 specific API, used only by ARM and ARM64 backends - * `x86` - X86/X64 specific API, used only by X86 and X64 backends - * `test` - Unit and integration tests (don't embed in your project) - * `tools` - Tools used for configuring, documenting and generating files - -Code Generation Concepts ------------------------- - -AsmJit has two completely different code generation concepts. The difference is in how the code is generated. The first concept, also referred as a low level concept, is called `Assembler` and it's the same as writing RAW assembly by inserting instructions that use physical registers directly. In this case AsmJit does only instruction encoding, verification and final code relocation. - -The second concept, also referred as a high level concept, is called `Compiler`. Compiler lets you use virtually unlimited number of registers (it calls them variables), which significantly simplifies the code generation process. Compiler allocates these virtual registers to physical registers after the code generation is done. This requires some extra effort - Compiler has to generate information for each node (instruction, function declaration, function call, etc...) in the code, perform a variable liveness analysis and translate the code using variables to a code that uses only physical registers. - -In addition, Compiler understands functions and their calling conventions. It has been designed in a way that the code generated is always a function having a prototype like a real programming language. By having a function prototype the Compiler is able to insert prolog and epilog sequence to the function being generated and it's able to also generate a necessary code to call other function from your own code. - -There is no conclusion on which concept is better. `Assembler` brings full control and the best performance, while `Compiler` makes the code-generation more fun and more portable. - -Configuring & Building ----------------------- - -AsmJit is designed to be easy embeddable in any kind project. However, it has some compile-time flags that can be used to build a specific version of AsmJit including or omitting certain features. A typical way to build AsmJit is to use [cmake](http://www.cmake.org), but it's also possible to just include AsmJit source code in our project and build it with it optionally editing its `asmjit/config.h` file to turn on/off some specific features. The most easies way to include AsmJit in your project is to just copy AsmJit source somewhere into it and to define globally `ASMJIT_STATIC` macro. This way AsmJit can be just updated from time to time without any changes to it. Please do not include / compile AsmJit test files (`asmjit/test` directory) when embedding. - -### Build Type - - * `ASMJIT_EMBED` - Parameter that can be set to cmake to turn off building library, useful if you want to include asmjit in your project without building the library. `ASMJIT_EMBED` behaves identically as `ASMJIT_STATIC`. - * `ASMJIT_STATIC` - Define when building AsmJit as a static library. No symbols will be exported by AsmJit by default. - - * By default AsmJit build is configured as a shared library so none of `ASMJIT_EMBED` and `ASMJIT_STATIC` have to be defined explicitly. - -### Build Mode - - * `ASMJIT_DEBUG` - Define to always turn debugging on (regardless of build-mode). - * `ASMJIT_RELEASE` - Define to always turn debugging off (regardless of build-mode). - * `ASMJIT_TRACE` - Define to enable AsmJit tracing. Tracing is used to catch bugs in AsmJit and it has to be enabled explicitly. When AsmJit is compiled with `ASMJIT_TRACE` it uses `stdout` to log information related to AsmJit execution. This log can be helpful when examining liveness analysis, register allocation or any other part of AsmJit. - - * By default none of these is defined, AsmJit detects mode based on compile-time macros (useful when using IDE that has switches for Debug/Release/etc...). - -### Architectures - - * `ASMJIT_BUILD_ARM` - Build ARM backend. - * `ASMJIT_BUILD_ARM64` - Build ARM64 backend. - * `ASMJIT_BUILD_X86` - Build x86 backend. - * `ASMJIT_BUILD_X64` - Build x64 backend. - * `ASMJIT_BUILD_HOST` - Build host backend, if only `ASMJIT_BUILD_HOST` is used only the host architecture detected at compile-time will be included. - - * By default only `ASMJIT_BUILD_HOST` is defined. - -### Features - - * `ASMJIT_DISABLE_COMPILER` - Disable `Compiler` completely. Use this flag if you don't use Compiler and want a slimmer binary. - * `ASMJIT_DISABLE_LOGGER` - Disable `Logger` completely. Use this flag if you don't need `Logger` functionality and want slimmer binary. AsmJit compiled with or without `Logger` support is binary compatible (all classes that use Logger pointer will simply use `void*`), but the Logger interface and in general instruction dumps won't be available anymore. - * `ASMJIT_DISABLE_NAMES` - Disable everything that uses strings and that causes certain strings to be stored in the resulting binary. For example when this flag is enabled all instruction and error names (and related APIs) will not be available. This flag has to be disabled together with `ASMJIT_DISABLE_LOGGER`. - -Using AsmJit ------------- - -AsmJit library uses one global namespace called `asmjit`, which contains the basics. Architecture specific code is prefixed by the architecture and architecture specific registers and operand builders are in its own namespace. For example classes for both x86 and x64 code generation are prefixed by `X86`, enums by `kX86`, registers and operand builders are accessible through `x86` namespace. This design is very different from the initial version of AsmJit and it seems now as the most convenient one. - -### Runtime & Code-Generators - -AsmJit contains two classes that are required to generate a machine code. `Runtime` specifies where the code is generated and acts as a storage, while `CodeGen` specifies how the code is generated and acts as a machine code stream. All the examples here use `Compiler` code-generator to generate the code and `JitRuntime` to store and run it. - -### Instruction Operands - -Operand is a part of an instruction, which specifies the data the instruction will operate on. There are five types of operands in AsmJit: - - * `Reg` - Physical register, used only by `Assembler` - * `Var` - Virtual register, used only by `Compiler` - * `Mem` - Used to reference memory location - * `Label` - Used to reference a location in code - * `Imm` - Immediate value that is encoded with the instruction itself - -Base class for all operands is `Operand`. It contains interface that can be used by all types of operands only and it is typically passed by value, not as a pointer. The classes `Reg`, `Var`, `BaseMem`, `Label` and `Imm` all inherit `Operand` and provide an operand specific functionality. Architecture specific operands are prefixed by the architecture like `X86Reg` or `X86Mem`. Most of the architectures provide several types of registers, for example x86/x64 architecture has `X86GpReg`, `X86MmReg`, `X86FpReg`, `X86XmmReg` and `X86YmmReg` registers plus some extras including segment registers and `rip` (relative instruction pointer). - -When using a code-generator some operands have to be created explicitly by using its interface. For example labels are created by using `newLabel()` method of the code-generator and variables are created by using architecture specific methods like `newGpVar()`, `newMmVar()` or `newXmmVar()`. - -### Function Prototypes - -AsmJit needs to know the prototype of the function it will generate or call. AsmJit contains a mapping between a type and the register that will be used to represent it. To make life easier there is a function builder that does the mapping on the fly. Function builder is a template class that helps with creating a function prototype by using native C/C++ types that describe function arguments and return value. It translates C/C++ native types into AsmJit specific IDs and makes these IDs accessible to Compiler. - -### Putting It All Together - -Let's put all together and generate a first function that sums its two arguments and returns the result. At the end the generated function is called from a C++ code. +Minimal Example +--------------- ```c++ #include +#include using namespace asmjit; +// Signature of the generated function. +typedef int (*Func)(void); + int main(int argc, char* argv[]) { - // Create JitRuntime and X86 Assembler/Compiler. - JitRuntime runtime; - X86Assembler a(&runtime); - X86Compiler c(&a); + JitRuntime rt; // Runtime specialized for JIT code execution. - // Build function having two arguments and a return value of type 'int'. - // First type in function builder describes the return value. kCallConvHost - // tells the compiler to use the host calling convention. - c.addFunc(FuncBuilder2(kCallConvHost)); + CodeHolder code; // Holds code and relocation information. + code.init(rt.getCodeInfo()); // Initialize to the same arch as JIT runtime. - // Create 32-bit variables (virtual registers) and assign some names to - // them. Using variable names is not necessary, however, it can make - // debugging easier. - X86GpVar x = c.newInt32("x"); - X86GpVar y = c.newInt32("y"); + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + a.mov(x86::eax, 1); // Move one to 'eax' register. + a.ret(); // Return from function. + // ----> X86Assembler is no longer needed from here and can be destroyed <---- - // Tell asmjit to use these variables as function arguments. - c.setArg(0, x); - c.setArg(1, y); + Func fn; + Error err = rt.add(&fn, &code); // Add the generated code to the runtime. + if (err) return 1; // Handle a possible error returned by AsmJit. + // ----> CodeHolder is no longer needed from here and can be destroyed <---- - // x = x + y; - c.add(x, y); + int result = fn(); // Execute the generated code. + printf("%d\n", result); // Print the resulting "1". - // Tell asmjit to return `x`. - c.ret(x); - - // Finalize the current function. - c.endFunc(); - - // Now the Compiler contains the whole function, but the code is not yet - // generated. To tell the compiler to serialize the code to `Assembler` - // `c.finalize()` has to be called. After finalization the `Compiler` - // won't contain the code anymore and will be detached from the `Assembler`. - c.finalize(); - - // After finalization the code has been send to `Assembler`. It contains - // a handy method `make()`, which returns a pointer that points to the - // first byte of the generated code, which is the function entry in our - // case. - void* funcPtr = a.make(); - - // In order to run 'funcPtr' it has to be casted to the desired type. - // Typedef is a recommended and safe way to create a function-type. - typedef int (*FuncType)(int, int); - - // Using asmjit_cast is purely optional, it's basically a C-style cast - // that tries to make it visible that a function-type is returned. - FuncType func = asmjit_cast(funcPtr); - - // Finally, run it and do something with the result... - int z = func(1, 2); - printf("z=%d\n", z); // Outputs "z=3". - - // The function will remain in memory after Compiler and Assembler are - // destroyed. This is why the `JitRuntime` is used - it keeps track of - // the code generated. When `Runtime` is destroyed it also invalidates - // all code relocated by it (which is in our case also our `func`). So - // it's safe to just do nothing in our case, because destroying `Runtime` - // will free `func` as well, however, it's always better to release the - // generated code that is not needed anymore manually. - runtime.release((void*)func); + // All classes use RAII, all resources will be released before `main()` returns, + // the generated function can be, however, released explicitly if you intend to + // reuse or keep the runtime alive, which you should in a production-ready code. + rt.release(fn); return 0; } ``` -The code should be self explanatory, however there are some details to be clarified. - -The code above generates and calls a function of `kCallConvHost` calling convention. 32-bit architecture contains a wide range of function calling conventions that can be all used by a single program, so it's important to know which calling convention is used by your C/C++ compiler so you can call the function. However, most compilers should generate CDecl by default. In 64-bit mode there are only two calling conventions, one is specific for Windows (Win64 calling convention) and the other for Unix (AMD64 calling convention). The `kCallConvHost` is defined to be one of CDecl, Win64 or AMD64 depending on your architecture and operating system. - -Default integer size is platform specific, virtual types `kVarTypeIntPtr` and `kVarTypeUIntPtr` can be used to make the code more portable and they should be always used when a pointer type is needed. When no type is specified AsmJit always defaults to `kVarTypeIntPtr`. The code above works with integers where the default behavior has been overidden to 32-bits. Note it's always a good practice to specify the type of the variable used. Alternative form of creating a variable is `c.newGpVar(...)`, `c.newMmVar(...)`, `c.newXmmVar` and so on... - -The function starts with `c.addFunc()` and ends with `c.endFunc()`. It's not allowed to put code outside of the function; however, embedding data outside of the function body is allowed. - -### Using Labels - -Labels are essential for making jumps, function calls or to refer to a data that is embedded in the code section. Label has to be explicitly created by using `newLabel()` method of your code generator in order to be used. The following example executes a code that depends on the condition by using a `Label` and conditional jump instruction. If the first parameter is zero it returns `a + b`, otherwise `a - b`. - -```c++ -#include - -using namespace asmjit; - -int main(int argc, char* argv[]) { - JitRuntime runtime; - X86Assembler a(&runtime); - X86Compiler c(&a); - - // This function uses 3 arguments. - c.addFunc(FuncBuilder3(kCallConvHost)); - - // New variable 'op' added. - X86GpVar op = c.newInt32("op"); - X86GpVar x = c.newInt32("x"); - X86GpVar y = c.newInt32("y"); - - c.setArg(0, op); - c.setArg(1, x); - c.setArg(2, y); - - // Create labels. - Label L_Sub = c.newLabel(); - Label L_Skip = c.newLabel(); - - // If (op != 0) - // goto L_Sub; - c.test(op, op); - c.jne(L_Sub); - - // x = x + y; - // goto L_Skip; - c.add(x, y); - c.jmp(L_Skip); - - // L_Sub: - // x = x - y; - c.bind(L_Sub); - c.sub(x, y); - - // L_Skip: - c.bind(L_Skip); - - c.ret(x); - c.endFunc(); - c.finalize(); - - // The prototype of the generated function. - typedef int (*FuncType)(int, int, int); - FuncType func = asmjit_cast(a.make()); - - int res0 = func(0, 1, 2); - int res1 = func(1, 1, 2); - - printf("res0=%d\n", res0); // Outputs "res0=3". - printf("res1=%d\n", res1); // Outputs "res1=-1". - - runtime.release((void*)func); - return 0; -} -``` - -In this example conditional and unconditional jumps were used with labels together. Labels have to be created explicitely by `Compiler` by using a `Label L = c.newLabel()` form. Each label as an unique ID that identifies it, however it's not a string and there is no way to query for a `Label` instance that already exists at the moment. Label is like any other operand moved by value, so the copy of the label will still reference the same label and changing a copied label will not change the original label. - -Each label has to be bound to the location in the code by using `bind()`; however, it can be bound only once! Trying to bind the same label multiple times has undefined behavior - assertion failure is the best case. - -### Memory Addressing - -X86/X64 architectures have several memory addressing modes which can be used to combine base register, index register and a displacement. In addition, index register can be shifted by a constant from 1 to 3 that can help with addressing elements up to 8-byte long in an array. AsmJit supports all forms of memory addressing. Memory operand can be created by using `asmjit::X86Mem` or by using related non-member functions like `asmjit::x86::ptr` or `asmjit::x86::ptr_abs`. Use `ptr` to create a memory operand having a base register with optional index register and a displacement; use and `ptr_abs` to create a memory operand referring to an absolute address in memory (32-bit) and optionally having an index register. - -In the following example various memory addressing modes are used to demonstrate how to construct and use them. It creates a function that accepts an array and two indexes which specify which elements to sum and return. - -```c++ -#include - -using namespace asmjit; - -int main(int argc, char* argv[]) { - JitRuntime runtime; - X86Assembler a(&runtime); - X86Compiler c(&a); - - // Function returning 'int' accepting pointer and two indexes. - c.addFunc(FuncBuilder3(kCallConvHost)); - - X86GpVar p = c.newIntPtr("p"); - X86GpVar xIndex = c.newIntPtr("xIndex"); - X86GpVar yIndex = c.newIntPtr("yIndex"); - - c.setArg(0, p); - c.setArg(1, xIndex); - c.setArg(2, yIndex); - - X86GpVar x = c.newInt32("x"); - X86GpVar y = c.newInt32("y"); - - // Read `x` by using a memory operand having base register, index register - // and scale. Translates to `mov x, dword ptr [p + xIndex << 2]`. - c.mov(x, x86::ptr(p, xIndex, 2)); - - // Read `y` by using a memory operand having base register only. Variables - // `p` and `yIndex` are both modified. - - // Shift bIndex by 2 (exactly the same as multiplying by 4). - // And add scaled 'bIndex' to 'p' resulting in 'p = p + bIndex * 4'. - c.shl(yIndex, 2); - c.add(p, yIndex); - - // Read `y`. - c.mov(y, x86::ptr(p)); - - // x = x + y; - c.add(x, y); - - c.ret(x); - c.endFunc(); - c.finalize(); - - // The prototype of the generated function. - typedef int (*FuncType)(const int*, intptr_t, intptr_t); - FuncType func = asmjit_cast(a.make()); - - // Array passed to `func`. - static const int array[] = { 1, 2, 3, 5, 8, 13 }; - - int xVal = func(array, 1, 2); - int yVal = func(array, 3, 5); - - printf("xVal=%d\n", xVal); // Outputs "xVal=5". - printf("yVal=%d\n", yVal); // Outputs "yVal=18". - - runtime.release((void*)func); - return 0; -} -``` - -### Using Stack - -AsmJit uses stack automatically to spill variables if there is not enough registers to keep them all allocated. The stack frame is managed by `Compiler` that provides also an interface to allocate chunks of memory of user specified size and alignment. - -In the following example a stack of 256 bytes size is allocated, filled by bytes starting from 0 to 255 and then iterated again to sum all the values. - -```c++ -#include - -using namespace asmjit; - -int main(int argc, char* argv[]) { - JitRuntime runtime; - X86Assembler a(&runtime); - X86Compiler c(&a); - - // Function returning 'int' without any arguments. - c.addFunc(FuncBuilder0(kCallConvHost)); - - // Allocate 256 bytes on the stack aligned to 4 bytes. - X86Mem stack = c.newStack(256, 4); - - X86GpVar p = c.newIntPtr("p"); - X86GpVar i = c.newIntPtr("i"); - - // Load a stack address to `p`. This step is purely optional and shows - // that `lea` is useful to load a memory operands address (even absolute) - // to a general purpose register. - c.lea(p, stack); - - // Clear `i`. Notice that `xor_()` is used instead of `xor()` as it's keyword. - c.xor_(i, i); - - Label L1 = c.newLabel(); - Label L2 = c.newLabel(); - - // First loop, fill the stack allocated by a sequence of bytes from 0 to 255. - c.bind(L1); - - // Mov byte ptr[p + i], i. - // - // Any operand can be cloned and modified. By cloning `stack` and calling - // `setIndex()` we created a new memory operand based on stack having an - // index register assigned to it. - c.mov(stack.clone().setIndex(i), i.r8()); - - // if (++i < 256) - // goto L1; - c.inc(i); - c.cmp(i, 256); - c.jb(L1); - - // Second loop, sum all bytes stored in `stack`. - X86GpVar sum = c.newInt32("sum"); - X86GpVar val = c.newInt32("val"); - - c.xor_(i, i); - c.xor_(sum, sum); - - c.bind(L2); - - // Movzx val, byte ptr [stack + i] - c.movzx(val, stack.clone().setIndex(i).setSize(1)); - // sum += val; - c.add(sum, val); - - // if (++i < 256) - // goto L2; - c.inc(i); - c.cmp(i, 256); - c.jb(L2); - - c.ret(sum); - c.endFunc(); - c.finalize(); - - typedef int (*FuncType)(void); - FuncType func = asmjit_cast(a.make()); - - printf("sum=%d\n", func()); // Outputs "sum=32640". - - runtime.release((void*)func); - return 0; -} -``` - -### Using Constant Pool - -To be documented. +AsmJit Summary +-------------- + + * Complete x86/x64 instruction set - MMX, SSEx, BMIx, ADX, TBM, XOP, AVXx, FMAx, and AVX512. + * Assembler, CodeBuilder, and CodeCompiler emitters - each suitable for different tasks. + * Built-in CPU vendor and features detection. + * Advanced logging/formatting and robust error handling. + * Virtual memory management similar to malloc/free for JIT code-generation and execution. + * Lightweight and embeddable - 200-250kB compiled with all built-in features. + * Modularity - unneeded features can be disabled at compile-time to make the library smaller. + * Zero dependencies - no external libraries, no STL/RTTI - easy to embed and/or link statically. + * Doesn't use exceptions internally, but allows to attach a "throwable" error handler (your choice). Advanced Features ----------------- -AsmJit offers much more, but not everything can fit into the introduction. The following sections don't have complete examples, but contain hints that can be useful and can change a bit the way AsmJit is used. + * AsmJit contains a highly compressed instruction database: + * Instruction names - allows to convert instruction id to its name and vice versa. + * Instruction metadata - access (read|write|rw) of all operand combinations of all instructions. + * Instruction signatures - allows to strictly validate if an instruction (with all its operands) is valid. + * AsmJit allows to precisely control how instructions are encoded if there are multiple variations. + * AsmJit is highly dynamic, constructing operands at runtime is a common practice. + * Multiple emitters with the same interface - emit machine code directly or to a representation that can be processed afterwards. -### Logging and Error Handling +Important +--------- -Failures are common when working at machine level. AsmJit does already a good job with function overloading to prevent from emitting semantically incorrect instructions; however, AsmJit can't prevent from emitting code that is semantically correct, but contains bug(s). Logging has always been an important part of AsmJit's infrastructure and the output can be very valuable after something went wrong. +Breaking the official API is sometimes inevitable, what to do? + * Breaking changes are described in [BREAKING.md](./BREAKING.md) document. + * Visit our [Official Chat](https://gitter.im/asmjit/asmjit) if you need a quick help. + * See asmjit tests, they always compile and provide an implementation of a lot of use-cases: + * [asmjit_test_x86_asm.cpp](./test/asmjit_test_x86_cc.cpp) - Tests that use **X86Assembler** and **X86Builder**. + * [asmjit_test_x86_cc.cpp](./test/asmjit_test_x86_cc.cpp) - Tests that use **X86Compiler**. -AsmJit contains extensible logging interface defined by `Logger` class and implemented by `FileLogger` and `StringLogger`. `FileLogger` can log into a standard C-based `FILE*` stream while `StringLogger` logs to an internal buffer that can be used after the code generation is done. +TODOs: + * [ ] AsmJit added support for code sections, but only the first section (executable code) works atm. + * [ ] AsmJit supports AVX512, but {sae} and {er} are not handled properly yet. + * [ ] AsmJit next-wip branch implements a brand-new register allocator (and contains reworked CodeBuilder and CodeCompiler), but it's not complete yet. -Loggers can be assigned to any code generator and there is no restriction of assigning a single logger to multiple code generators, but this is not practical when running these in multiple threads. `FileLogger` is thread-safe since it uses plain C `FILE*` stream, but `StringLogger` is not! +Supported Environments +---------------------- -The following snippet describes how to log into `FILE*`: +### C++ Compilers: + + * Tested + * **Clang** - tested by Travis-CI. + * **GCC** - tested by Travis-CI. + * **MinGW** - tested by AppVeyor. + * **MSVC** - tested by AppVeyor. + * Maybe + * **CodeGear** - no maintainers. + * **Intel** - no maintainers. + * Other c++ compilers would require some testing and support in [asmjit_build.h](./src/asmjit/asmjit_build.h). + +### Operating Systems: + + * Tested + * **Linux** - tested by Travis-CI. + * **Mac** - tested by Travis-CI. + * **Windows** - tested by AppVeyor. + * Maybe + * **BSDs** - no maintainers. + * Other operating systems would require some testing and support in [asmjit_build.h](./src/asmjit/asmjit_build.h) and [osutils.cpp](./src/asmjit/base/osutils.cpp). + +### Backends: + + * **X86** - tested by both Travis-CI and AppVeyor. + * **X64** - tested by both Travis-CI and AppVeyor. + * **ARM** - work-in-progress (not public at the moment). + +Project Organization +-------------------- + + * **`/`** - Project root + * **src** - Source code + * **asmjit** - Source code and headers (always point include path in here) + * **base** - Backend independent API + * **arm** - ARM specific API, used only by ARM32 and ARM64 backends + * **x86** - X86 specific API, used only by X86 and X64 backends + * **test** - Unit and integration tests (don't embed in your project) + * **tools** - Tools used for configuring, documenting and generating files + +Configuring & Building +---------------------- + +AsmJit is designed to be easy embeddable in any project. However, it depends on some compile-time macros that can be used to build a specific version of AsmJit that includes or excludes certain features. A typical way of building AsmJit is to use [cmake](https://www.cmake.org), but it's also possible to just include AsmJit source code in your project and just build it. The easiest way to include AsmJit in your project is to just include **src** directory in your project and to define **ASMJIT_STATIC** or **ASMJIT_EMBED**. AsmJit can be just updated from time to time without any changes to this integration process. Do not embed AsmJit's [/test](./test) files in such case as these are used for testing. + +### Build Type: + + * **ASMJIT_DEBUG** - Define to always turn debugging on (regardless of compile-time options detected). + * **ASMJIT_RELEASE** - Define to always turn debugging off (regardless of compiler-time options detected). + +By default none of these is defined, AsmJit detects build-type based on compile-time macros and supports most IDE and compiler settings out of box. + +### Build Mode: + + * **ASMJIT_EMBED** - Define to embed AsmJit in another project. Embedding means that neither shared nor static library is created and AsmJit's source files and source files of the product that embeds AsmJit are part of the same target. This way of building AsmJit has certain advantages that are beyond this manual. **ASMJIT_EMBED** behaves similarly to **ASMJIT_STATIC** (no API exports). + * **ASMJIT_STATIC** - Define to build AsmJit as a static library. No symbols are exported in such case. + +By default AsmJit build is configured to be built as a shared library, thus note of **ASMJIT_EMBED** and **ASMJIT_STATIC** is defined. + +### Build Backends: + + * **ASMJIT_BUILD_ARM** - Build ARM32 and ARM64 backends (work-in-progress). + * **ASMJIT_BUILD_X86** - Build X86 and X64 backends. + * **ASMJIT_BUILD_HOST** - Build only the host backend (default). + +If none of **ASMJIT_BUILD_...** is defined AsmJit bails to **ASMJIT_BUILD_HOST**, which will detect the target architecture at compile-time. Each backend automatically supports 32-bit and 64-bit targets, so for example AsmJit with X86 support can generate both 32-bit and 64-bit code. + +### Disabling Features: + + * **ASMJIT_DISABLE_BUILDER** - Disables both **CodeBuilder** and **CodeCompiler** emitters (only **Assembler** will be available). Ideal for users that don't use **CodeBuilder** concept and want to create smaller AsmJit. + * **ASMJIT_DISABLE_COMPILER** - Disables **CodeCompiler** emitter. For users that use **CodeBuilder**, but not **CodeCompiler** + * **ASMJIT_DISABLE_LOGGING** - Disables logging (**Logger** and all classes that inherit it) and formatting features. + * **ASMJIT_DISABLE_TEXT** - Disables everything that uses text-representation and that causes certain strings to be stored in the resulting binary. For example when this flag is enabled all instruction and error names (and related APIs) will not be available. This flag has to be disabled together with **ASMJIT_DISABLE_LOGGING**. This option is suitable for deployment builds or builds that don't want to reveal the use of AsmJit. + * **ASMJIT_DISABLE_VALIDATION** - Disables instruction validation. Saves around 5kB of space when used. + +NOTE: Please don't disable any features if you plan to build AsmJit as a shared library that will be used by multiple projects that you don't control (for example asmjit in a Linux distribution). The possibility to disable certain features exists mainly for static builds of AsmJit. + +Using AsmJit +------------ + +AsmJit library uses one global namespace called `asmjit` that provides the whole functionality. Architecture specific code is prefixed by the architecture name and architecture specific registers and operand builders have their own namespace. For example API targeting both X86 and X64 architectures is prefixed with `X86` and registers & operand builders are accessible through `x86` namespace. This design is very different from the initial version of AsmJit and it seems now as the most convenient one. + +### CodeHolder & CodeEmitter + +AsmJit provides two classes that are used together for code generation: + + * **CodeHolder** - Provides functionality to hold generated code and stores all necessary information about code sections, labels, symbols, and possible relocations. + * **CodeEmitter** - Provides functionality to emit code into **CodeHolder**. **CodeEmitter** is abstract and provides just basic building blocks that are then implemented by **Assembler**, **CodeBuilder**, and **CodeCompiler**. + +Code emitters: + + * **Assembler** - Emitter designed to emit machine code directly. + * **CodeBuilder** - Emitter designed to emit code into a representation that can be processed. It stores the whole code in a double linked list consisting of nodes (**CBNode** aka code-builder node). There are nodes that represent instructions (**CBInst**), labels (**CBLabel**), and other building blocks (**CBAlign**, **CBData**, ...). Some nodes are used as markers (**CBSentinel**) and comments (**CBComment**). + * **CodeCompiler** - High-level code emitter that uses virtual registers and contains high-level function building features. **CodeCompiler** is based on **CodeBuilder**, but extends its functionality and introduces new node types starting with CC (**CCFunc**, **CCFuncExit**, **CCFuncCall**). CodeCompiler is the simplest way to start with AsmJit as it abstracts many details required to generate a function in asm language. + +### Runtime + +AsmJit's **Runtime** is designed for execution and/or linking. The **Runtime** itself is abstract and defines only how to **add()** and **release()** code held by **CodeHolder**. **CodeHolder** holds machine code and relocation entries, but should be seen as a temporary object only - after the code in **CodeHolder** is ready, it should be passed to **Runtime** or relocated manually. Users interested in inspecting the generated machine-code (instead of executing or linking) can keep it in **CodeHodler** and process it manually of course. + +The only **Runtime** implementation provided directly by AsmJit is called **JitRuntime**, which is suitable for storing and executing dynamically generated code. **JitRuntime** is used in most AsmJit examples as it makes the code management easy. It allows to add and release dynamically generated functions, so it's suitable for JIT code generators that want to keep many functions alive, and release functions which are no longer needed. + +### Instructions & Operands + +Instructions specify operations performed by the CPU, and operands specify the operation's input(s) and output(s). Each AsmJit's instruction has it's own unique id (**X86Inst::Id** for example) and platform specific code emitters always provide a type safe intrinsic (or multiple overloads) to emit such instruction. There are two ways of emitting an instruction: + + * Using emitter.**instName**(operands...) - A type-safe way provided by platform specific emitters - for example **X86Assembler** provides `mov(X86Gp, X86Gp)`. + * Using emitter.emit(**instId**, operands...) - Allows to emit an instruction in a dynamic way - you just need to know instruction's id and provide its operands. + +AsmJit's operands all inherit from a base class called **Operand** and then specialize its type to: + + * **None** (not used or uninitialized operand). + * **Register** (**Reg**) - Describes either physical or virtual register. Physical registers have id that matches the target's machine id directly, whereas virtual registers must be allocated into physical registers by a register allocator pass. Each **Reg** provides: + * **Register Type** - Unique id that describes each possible register provided by the target architecture - for example X86 backend provides **X86Reg::RegType**, which defines all variations of general purpose registers (GPB-LO, GPB-HI, GPW, GPD, and GPQ) and all types of other registers like K, MM, BND, XMM, YMM, and ZMM. + * **Register Kind** - Groups multiple register types under a single kind - for example all general-purpose registers (of all sizes) on X86 are **X86Reg::kKindGp**, all SIMD registers (XMM, YMM, ZMM) are **X86Reg::kKindVec**, etc. + * **Register Size** - Contains the size of the register in bytes. If the size depends on the mode (32-bit vs 64-bit) then generally the higher size is used (for example RIP register has size 8 by default). + * **Register ID** - Contains physical or virtual id of the register. + * **Memory Address** (**Mem**) - Used to reference a memory location. Each **Mem** provides: + * **Base Register** - A base register id (physical or virtual). + * **Index Register** - An index register id (physical or virtual). + * **Offset** - Displacement or absolute address to be referenced (32-bit if base register is used and 64-bit if base register is not used). + * **Flags** that can describe various architecture dependent information (like scale and segment-override on X86). + * **Immediate Value** (**Imm**) - Immediate values are usually part of instructions (encoded within the instruction itself) or data. + * **Label** - used to reference a location in code or data. Labels must be created by the **CodeEmitter** or by **CodeHolder**. Each label has its unique id per **CodeHolder** instance. + +AsmJit allows to construct operands dynamically, to store them, and to query a complete information about them at run-time. Operands are small (always 16 bytes per **Operand**) and should be always copied if you intend to store them (don't create operands by using **new** keyword, it's not recommended). Operands are safe to be **memcpy()ed** and **memset()ed** if you need to work with arrays of operands. + +Small example of manipulating and using operands: ```c++ -// Create logger logging to `stdout`. Logger life-time should always be -// greater than a life-time of the code generator. Alternatively the -// logger can be reset before it's destroyed. -FileLogger logger(stdout); +using namespace asmjit; -// Create runtime and assembler and attach a logger to the assembler. -JitRuntime runtime; -X86Assembler a(&runtime); -a.setLogger(&logger); +X86Gp getDstRegByValue() { return x86::ecx; } -// ... Generate the code ... +void usingOperandsExample(X86Assembler& a) { + // Create some operands. + X86Gp dst = getDstRegByValue(); // Get `ecx` register returned by a function. + X86Gp src = x86::rax; // Get `rax` register directly from the provided `x86` namespace. + X86Gp idx = x86::gpq(10); // Construct `r10` dynamically. + X86Mem m = x86::ptr(src, idx); // Construct [src + idx] memory address - referencing [rax + r10]. + + // Examine `m`: + m.getIndexType(); // Returns `X86Reg::kRegGpq`. + m.getIndexId(); // Returns 10 (`r10`). + + // Reconstruct `idx` stored in mem: + X86Gp idx_2 = X86Gp::fromTypeAndId(m.getIndexType(), m.getIndexId()); + idx == idx_2; // True, `idx` and idx_2` are identical. + + Operand op = m; // Possible. + op.isMem(); // True (can be casted to Mem and X86Mem). + + m == op; // True, `op` is just a copy of `m`. + static_cast(op).addOffset(1); // Static cast is fine and valid here. + m == op; // False, `op` now points to [rax + r10 + 1], which is not [rax + r10]. + + // Emitting 'mov' + a.mov(dst, m); // Type-safe way. + a.mov(dst, op); // Not possible, `mov` doesn't provide `X86Reg, Operand` overload. + + a.emit(X86Inst::kIdMov, dst, m); // Type-unsafe, but possible. + a.emit(X86Inst::kIdMov, dst, op); // Also possible, `emit()` is typeless and can be used dynamically. +} ``` -The following snippet describes how to log into a string: +Some operands have to be created explicitly by `CodeEmitter`. For example labels must be created by `newLabel()` before they are used. + +### Assembler Example + +X86Assembler is a code emitter that emits machine code into a CodeBuffer directly. It's capable of targeting both 32-bit and 64-bit instruction sets and it's possible to target both instruction sets within the same code-base. The following example shows how to generate a function that works in both 32-bit and 64-bit modes, and how to use JitRuntime, CodeHolder, and X86Assembler together. + +The example handles 3 calling conventions manually just to show how it could be done, however, AsmJit contains utilities that can be used to create function prologs and epilogs automatically, but these concepts will be explained later. ```c++ -StringLogger logger; +using namespace asmjit; -JitRuntime runtime; -X86Assembler a(&runtime); -a.setLogger(&logger); +// Signature of the generated function. +typedef int (*SumFunc)(const int* arr, size_t count); -// ... Generate the code ... +int main(int argc, char* argv[]) { + assert(sizeof(void*) == 8 && + "This example requires 64-bit environment."); -printf("Logger Content:\n%s", logger.getString()); + JitRuntime rt; // Create a runtime specialized for JIT. -// You can use `logger.clearString()` if the intend is to reuse the logger. + CodeHolder code; // Create a CodeHolder. + code.init(rt.getCodeInfo()); // Initialize it to be compatible with `rt`. + + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + // Decide between 32-bit CDECL, WIN64, and SysV64 calling conventions: + // 32-BIT - passed all arguments by stack. + // WIN64 - passes first 4 arguments by RCX, RDX, R8, and R9. + // UNIX64 - passes first 6 arguments by RDI, RSI, RCX, RDX, R8, and R9. + X86Gp arr, cnt; + X86Gp sum = x86::eax; // Use EAX as 'sum' as it's a return register. + + if (ASMJIT_ARCH_64BIT) { + bool isWinOS = static_cast(ASMJIT_OS_WINDOWS); + arr = isWinOS ? x86::rcx : x86::rdi; // First argument (array ptr). + cnt = isWinOS ? x86::rdx : x86::rsi; // Second argument (number of elements) + } + else { + arr = x86::edx; // Use EDX to hold the array pointer. + cnt = x86::ecx; // Use ECX to hold the counter. + a.mov(arr, x86::ptr(x86::esp, 4)); // Fetch first argument from [ESP + 4]. + a.mov(cnt, x86::ptr(x86::esp, 8)); // Fetch second argument from [ESP + 8]. + } + + Label Loop = a.newLabel(); // To construct the loop, we need some labels. + Label Exit = a.newLabel(); + + a.xor_(sum, sum); // Clear 'sum' register (shorter than 'mov'). + a.test(cnt, cnt); // Border case: + a.jz(Exit); // If 'cnt' is zero jump to 'Exit' now. + + a.bind(Loop); // Start of a loop iteration. + a.add(sum, x86::dword_ptr(arr)); // Add int at [arr] to 'sum'. + a.add(arr, 4); // Increment 'arr' pointer. + a.dec(cnt); // Decrease 'cnt'. + a.jnz(Loop); // If not zero jump to 'Loop'. + + a.bind(Exit); // Exit to handle the border case. + a.ret(); // Return from function ('sum' == 'eax'). + // ----> X86Assembler is no longer needed from here and can be destroyed <---- + + SumFunc fn; + Error err = rt.add(&fn, &code); // Add the generated code to the runtime. + + if (err) return 1; // Handle a possible error returned by AsmJit. + // ----> CodeHolder is no longer needed from here and can be destroyed <---- + + static const int array[6] = { 4, 8, 15, 16, 23, 42 }; + + int result = fn(array, 6); // Execute the generated code. + printf("%d\n", result); // Print sum of array (108). + + rt.release(fn); // Remove the function from the runtime. + return 0; +} ``` -Logger can be configured to show more information by using `logger.addOptions()` method. The following options are available: +The example should be self-explanatory. It shows how to work with labels, how to use operands, and how to emit instructions that can use different registers based on runtime selection. It implements 32-bit CDECL, WIN64, and SysV64 caling conventions and will work on most X86 environments. - * `Logger::kOptionBinaryForm` - Log also binary sequence for each instruction generated. - * `Logger::kOptionHexImmediate` - Log immediate values as hexadecimal. - * `Logger::kOptionHexDisplacement` - Log memory displacements as hexadecimal. +### More About Memory Addresses -TODO: Liveness analysis and instruction scheduling options. +X86 provides a complex memory addressing model that allows to encode addresses having a BASE register, INDEX register with a possible scale (left shift), and displacement (called offset in AsmJit). Memory address can also specify memory segment (segment-override in X86 terminology) and some instructions (gather / scatter) require INDEX to be a VECTOR register instead of a general-purpose register. AsmJit allows to encode and work with all forms of addresses mentioned and implemented by X86. It also allows to construct a 64-bit memory address, which is only allowed in one form of 'mov' instruction. + +```c++ +// Memory operand construction is provided by x86 namespace. +using namespace asmjit; +using namespace asmjit::x86; // Easier to access x86 regs. + +// BASE + OFFSET. +X86Mem a = ptr(rax); // a = [rax] +X86Mem b = ptr(rax, 15) // b = [rax + 15] + +// BASE + INDEX + SCALE - Scale is in BITS as used by X86! +X86Mem c = ptr(rax, rbx) // c = [rax + rbx] +X86Mem d = ptr(rax, rbx, 2) // d = [rax + rbx << 2] +X86Mem e = ptr(rax, rbx, 2, 15) // e = [rax + rbx << 2 + 15] + +// BASE + VM (Vector Index) (encoded as MOD+VSIB). +X86Mem f = ptr(rax, xmm1) // f = [rax + xmm1] +X86Mem g = ptr(rax, xmm1, 2) // g = [rax + xmm1 << 2] +X86Mem h = ptr(rax, xmm1, 2, 15) // h = [rax + xmm1 << 2 + 15] + +// WITHOUT BASE: +uint64_t ADDR = (uint64_t)0x1234; +X86Mem i = ptr(ADDR); // i = [0x1234] +X86Mem j = ptr(ADDR, rbx); // j = [0x1234 + rbx] +X86Mem k = ptr(ADDR, rbx, 2); // k = [0x1234 + rbx << 2] + +// LABEL - Will be encoded as RIP (64-bit) or absolute address (32-bit). +Label L = ...; +X86Mem m = ptr(L); // m = [L] +X86Mem n = ptr(L, rbx); // n = [L + rbx] +X86Mem o = ptr(L, rbx, 2); // o = [L + rbx << 2] +X86Mem p = ptr(L, rbx, 2, 15); // p = [L + rbx << 2 + 15] + +// RIP - 64-bit only (RIP can't use INDEX). +X86Mem q = ptr(rip, 24); // q = [rip + 24] +``` + +Memory operands can optionally contain memory size. This is required by instructions where the memory size cannot be deduced from other operands, like `inc` and `dec`: + +```c++ +X86Mem a = x86::dword_ptr(rax, rbx); // dword ptr [rax + rbx]. +X86Mem b = x86::qword_ptr(rdx, rsi, 0, 1);// qword ptr [rdx + rsi << 0 + 1]. +``` + +Memory operands provide API that can be used to work with them: + +```c++ +X86Mem mem = x86::dword_ptr(rax, 12); // dword ptr [rax + 12]. + +mem.hasBase(); // true. +mem.hasIndex(); // false. +mem.getSize(); // 4. +mem.getOffset(); // 12. + +mem.setSize(0); // Sets the size to 0 (makes it sizeless). +mem.addOffset(-1); // Adds -1 to the offset and makes it 11. +mem.setOffset(0); // Sets the offset to 0. +mem.setBase(rcx); // Changes BASE to RCX. +mem.setIndex(rax); // Changes INDEX to RAX. +mem.hasIndex(); // true. + +// ... +``` + +Making changes to memory operand is very comfortable when emitting loads and stores: + +```c++ +using namespace asmjit; + +X86Assembler a(...); // Your initialized X86Assembler. +X86Mem m = x86::ptr(eax); // Construct [eax] memory operand. + +// One way of emitting bunch of loads is to use `mem.adjusted()`. It returns +// a new memory operand and keeps the source operand unchanged. +a.movaps(x86::xmm0, m); // No adjustment needed to load [eax]. +a.movaps(x86::xmm1, m.adjusted(16)); // Loads [eax + 16]. +a.movaps(x86::xmm2, m.adjusted(32)); // Loads [eax + 32]. +a.movaps(x86::xmm3, m.adjusted(48)); // Loads [eax + 48]. + +// ... do something with xmm0-3 ... + +// Another way of adjusting memory is to change the operand in-place. If you +// want to keep the original operand you can simply clone it. +X86Mem mx = m.clone(); +a.movaps(mx, x86::xmm0); mx.addOffset(16);// Stores [eax] (and adds 16 to mx). +a.movaps(mx, x86::xmm1); mx.addOffset(16);// Stores [eax + 16] (and adds 16 to mx). +a.movaps(mx, x86::xmm2); mx.addOffset(16);// Stores [eax + 32] (and adds 16 to mx). +a.movaps(mx, x86::xmm3); // Stores [eax + 48]. +``` + +You can explore the possibilities by taking a look at [base/operand.h](./src/asmjit/base/operand.h) and [x86/x86operand.h](./src/asmjit/x86/x86operand.h). Always use `X86Mem` when targeting X86 as it extends the base `Mem` operand with features provided only by X86. + +### More About CodeInfo + +In the first complete example the `CodeInfo` is retrieved from `JitRuntime`. It's logical as `JitRuntime` will always return a `CodeInfo` that is compatible with the runtime environment. For example if your application runs in 64-bit mode the `CodeInfo` will use `ArchInfo::kTypeX64` architecture in contrast to `ArchInfo::kTypeX86`, which will be used in 32-bit mode. AsmJit also allows to setup `CodeInfo` manually, and to select a different architecture when needed. So let's do something else this time, let's always generate a 32-bit code and print it's binary representation. To do that, we create our own `CodeInfo` and initialize it to `ArchInfo::kTypeX86` architecture. CodeInfo will populate all basic fields just based on the architecture we provide, so it's super-easy: + +```c++ +using namespace asmjit; + +int main(int argc, char* argv[]) { + using namespace asmjit::x86; // Easier access to x86/x64 registers. + + CodeHolder code; // Create a CodeHolder. + code.init(CodeInfo(ArchInfo::kTypeX86));// Initialize it for a 32-bit X86 target. + + // Generate a 32-bit function that sums 4 floats and looks like: + // void func(float* dst, const float* a, const float* b) + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + a.mov(eax, dword_ptr(esp, 4)); // Load the destination pointer. + a.mov(ecx, dword_ptr(esp, 8)); // Load the first source pointer. + a.mov(edx, dword_ptr(esp, 12)); // Load the second source pointer. + + a.movups(xmm0, ptr(ecx)); // Load 4 floats from [ecx] to XMM0. + a.movups(xmm1, ptr(edx)); // Load 4 floats from [edx] to XMM1. + a.addps(xmm0, xmm1); // Add 4 floats in XMM1 to XMM0. + a.movups(ptr(eax), xmm0); // Store the result to [eax]. + a.ret(); // Return from function. + + // Now we have two options if we want to do something with the code hold + // by CodeHolder. In order to use it we must first sync X86Assembler with + // the CodeHolder as it doesn't do it for every instruction it generates for + // performance reasons. The options are: + // + // 1. Detach X86Assembler from CodeHolder (will automatically sync). + // 2. Sync explicitly, allows to use X86Assembler again if needed. + // + // NOTE: AsmJit always syncs internally when CodeHolder needs to access these + // buffers and knows that there is an Assembler attached, so you have to sync + // explicitly only if you bypass CodeHolder and intend to do something on your + // own. + code.sync(); // So let's sync, it's easy. + + // We have no Runtime this time, it's on us what we do with the code. + // CodeHolder stores code in SectionEntry, which embeds CodeSection + // and CodeBuffer structures. We are interested in section's CodeBuffer only. + // + // NOTE: The first section is always '.text', so it's safe to just use 0 index. + CodeBuffer& buf = code.getSectionEntry(0)->buffer; + + // Print the machine-code generated or do something more interesting with it? + // 8B4424048B4C24048B5424040F28010F58010F2900C3 + for (size_t i = 0; i < buf.length; i++) + printf("%02X", buf.data[i]); + + return 0; +} +``` + +### Explicit Code Relocation + +CodeInfo contains much more information than just the target architecture. It can be configured to specify a base-address (or a virtual base-address in a linker terminology), which could be static (useful when you know the location of the target's machine code) or dynamic. AsmJit assumes dynamic base-address by default and relocates the code held by `CodeHolder` to a user-provided address on-demand. To be able to relocate to a user-provided address it needs to store some information about relocations, which is represented by `CodeHolder::RelocEntry`. Relocation entries are only required if you call external functions from the generated code that cannot be encoded by using a 32-bit displacement (X64 architecture doesn't provide 64-bit encodable displacement) and when a label referenced in one section is bound in another, but this is not really a JIT case and it's more related to AOT (ahead-of-time) compilation. + +Next example shows how to use a built-in virtual memory manager `VMemMgr` instead of using `JitRuntime` (just in case you want to use your own memory management) and how to relocate the generated code into your own memory block - you can use your own virtual memory allocator if you need that, but that's OS specific and it's already provided by AsmJit, so we will use what AsmJit offers instead of rolling our own here. + +The following code is similar to the previous one, but implements a function working in both 32-bit and 64-bit environments: + +```c++ +using namespace asmjit; + +typedef void (*SumIntsFunc)(int* dst, const int* a, const int* b); + +int main(int argc, char* argv[]) { + CodeHolder code; // Create a CodeHolder. + code.init(CodeInfo(ArchInfo::kTypeHost));// Initialize it for the host architecture. + + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + // Generate a function runnable in both 32-bit and 64-bit architectures: + bool isX86 = static_cast(ASMJIT_ARCH_X86); + bool isWin = static_cast(ASMJIT_OS_WINDOWS); + + // Signature: 'void func(int* dst, const int* a, const int* b)'. + X86Gp dst; + X86Gp src_a; + X86Gp src_b; + + // Handle the difference between 32-bit and 64-bit calling convention. + // (arguments passed through stack vs. arguments passed by registers). + if (isX86) { + dst = x86::eax; + src_a = x86::ecx; + src_b = x86::edx; + a.mov(dst , dword_ptr(x86::esp, 4)); // Load the destination pointer. + a.mov(src_a, dword_ptr(x86::esp, 8)); // Load the first source pointer. + a.mov(src_b, dword_ptr(x86::esp, 12));// Load the second source pointer. + } + else { + dst = isWin ? x86::rcx : x86::rdi; // First argument (destination pointer). + src_a = isWin ? x86::rdx : x86::rsi; // Second argument (source 'a' pointer). + src_b = isWin ? x86::r8 : x86::rdx; // Third argument (source 'b' pointer). + } + + a.movdqu(x86::xmm0, x86::ptr(src_a)); // Load 4 ints from [src_a] to XMM0. + a.movdqu(x86::xmm1, x86::ptr(src_b)); // Load 4 ints from [src_b] to XMM1. + a.paddd(x86::xmm0, x86::xmm1); // Add 4 ints in XMM1 to XMM0. + a.movdqu(x86::ptr(dst), x86::xmm0); // Store the result to [dst]. + a.ret(); // Return from function. + + // After the code was generated it can be relocated manually to any memory + // location, however, we need to know it's size before we perform memory + // allocation. CodeHolder's `getCodeSize()` returns the worst estimated + // code-size (the biggest possible) in case that relocations are not + // possible without trampolines (in that case some extra code at the end + // of the current code buffer is generated during relocation). + size_t size = code.getCodeSize(); + + // Instead of rolling our own virtual memory allocator we can use the one + // AsmJit uses. It's decoupled so you don't need to use Runtime for that. + VMemMgr vm; + + void* p = vm.alloc(size); // Allocate a virtual memory (executable). + if (!p) return 0; // Handle a possible out-of-memory case. + + size_t realSize = code.relocate(p); // Relocate & store the output in 'p'. + + // Execute the generated function. + int inA[4] = { 4, 3, 2, 1 }; + int inB[4] = { 1, 5, 2, 8 }; + int out[4]; + + // This code uses AsmJit's ptr_as_func<> to cast between void* and SumIntsFunc. + ptr_as_func(p)(result, arr_a, arr_b); + + // Prints {5 8 4 9} + printf("{%d %d %d %d}\n", out[0], out[1], out[2], out[3]); + + // Release 'p' is it's no longer needed. It will be destroyed with 'vm' + // instance anyway, but it's a good practice to release it explicitly + // when you know that the function will not be needed anymore. + vm.release(p); + + return 0; +} +``` + +Configure the CodeInfo by calling `CodeInfo::setBaseAddress()` to initialize it to a user-provided base-address before passing it to `CodeHolder`: + +```c++ +// Configure CodeInfo. +CodeInfo ci(...); +ci.setBaseAddress(uint64_t(0x1234)); + +// Then initialize CodeHolder with it. +CodeHolder code; +code.init(ci); + +// ... after you emit the machine code it will be relocated to the base address +// provided and stored in the pointer passed to `CodeHolder::relocate()`. +``` + +TODO: Maybe `CodeHolder::relocate()` is not the best name? + +### Using Native Registers - zax, zbx, zcx, ... + +AsmJit's X86 code emitters always provide functions to construct machine-size registers depending on the target. This feature is for people that want to write code targeting both 32-bit and 64-bit at the same time. In AsmJit terminology these registers are named **zax**, **zcx**, **zdx**, **zbx**, **zsp**, **zbp**, **zsi**, and **zdi** (they are defined in this exact order by X86). They are accessible through `X86Assembler`, `X86Builder`, and `X86Compiler`. The following example illustrates how to use this feature: + +```c++ +using namespace asmjit; + +typedef int (*Func)(void); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Create a runtime specialized for JIT. + + CodeHolder code; // Create a CodeHolder. + code.init(rt.getCodeInfo()); // Initialize it to be compatible with `rt`. + + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + // Let's get these registers from X86Assembler. + X86Gp zbp = a.zbp(); + X86Gp zsp = a.zsp(); + + int stackSize = 32; + + // Function prolog. + a.push(zbp); + a.mov(zbp, zsp); + a.sub(zsp, stackSize); + + // ... emit some code (this just sets return value to zero) ... + a.xor_(x86::eax, x86::eax); + + // Function epilog and return. + a.mov(zsp, zbp); + a.pop(zbp); + a.ret(); + + // To make the example complete let's call it. + Func fn; + Error err = rt.add(&fn, &code); // Add the generated code to the runtime. + if (err) return 1; // Handle a possible error returned by AsmJit. + + int result = fn(); // Execute the generated code. + printf("%d\n", result); // Print the resulting "0". + + rt.release(fn); // Remove the function from the runtime. + return 0; +} +``` + +The example just returns `0`, but the function generated contains a standard prolog and epilog sequence and the function itself reserves 32 bytes of local stack. The advantage is clear - a single code-base can handle multiple targets easily. If you want to create a register of native size dynamically by specifying its id it's also possible: + +```c++ +void example(X86Assembler& a) { + X86Gp zax = a.gpz(X86Gp::kIdAx); + X86Gp zbx = a.gpz(X86Gp::kIdBx); + X86Gp zcx = a.gpz(X86Gp::kIdCx); + X86Gp zdx = a.gpz(X86Gp::kIdDx); + + // You can also change register's id easily. + X86Gp zsp = zax; + zsp.setId(4); // or X86Gp::kIdSp. +} +``` + +Cloning existing registers and chaning their IDs is fine in AsmJit; and this technique is used internally in many places. + +### Using Assembler as Code-Patcher + +This is an advanced topic that is sometimes unavoidable. AsmJit by default appends machine-code it generates into a `CodeBuffer`, however, it also allows to set the offset in `CodeBuffer` explicitly and to overwrite its content. This technique is extremely dangerous for asm beginners as X86 instructions have variable length (see below), so you should in general only patch code to change instruction's offset or some basic other details you didn't know about the first time you emitted it. A typical scenario that requires code-patching is when you start emitting function and you don't know how much stack you want to reserve for it. + +Before we go further it's important to introduce instruction options, because they can help with code-patching (and not only patching, but that will be explained in AVX-512 section): + + * Many general-purpose instructions (especially arithmetic ones) on X86 have multiple encodings - in AsmJit this is usually called 'short form' and 'long form'. + * AsmJit always tries to use 'short form' as it makes the resulting machine-code smaller, which is always good - this decision is used by majority of assemblers out there. + * AsmJit allows to override the default decision by using `short_()` and `long_()` instruction options to force short or long form, respectively. The most useful is `long_()` as it basically forces AsmJit to always emit the long form. The `short_()` is not that useful as it's automatic (except jumps to non-bound labels). Note the underscore after each function name as it avoids collision with built-in C++ types. + +To illustrate what short form and long form means in binary let's assume we want to emit `add esp, 16` instruction, which has two possible binary encodings: + + * `83C410` - This is a short form aka `short add esp, 16` - You can see opcode byte (0x8C), MOD/RM byte (0xC4) and an 8-bit immediate value representing `16`. + * `81C410000000` - This is a long form aka `long add esp, 16` - You can see a different opcode byte (0x81), the same Mod/RM byte (0xC4) and a 32-bit immediate in little-endian representing `16`. + +If you generate an instruction in a short form and then patch it in a long form or vice-versa then something really bad will happen when you try to execute such code. The following example illustrates how to patch the code properly (it just extends the previous example): + +```c++ +using namespace asmjit; + +typedef int (*Func)(void); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Create a runtime specialized for JIT. + + CodeHolder code; // Create a CodeHolder. + code.init(rt.getCodeInfo()); // Initialize it to be compatible with `rt`. + + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + // Let's get these registers from X86Assembler. + X86Gp zbp = a.zbp(); + X86Gp zsp = a.zsp(); + + // Function prolog. + a.push(zbp); + a.mov(zbp, zsp); + + // This is where we are gonna patch the code later, so let's get the offset + // (the current location) from the beginning of the code-buffer. + size_t patchOffset = a.getOffset(); + // Let's just emit 'sub zsp, 0' for now, but don't forget to use LONG form. + a.long_().sub(zsp, 0); + + // ... emit some code (this just sets return value to zero) ... + a.xor_(x86::eax, x86::eax); + + // Function epilog and return. + a.mov(zsp, zbp); + a.pop(zbp); + a.ret(); + + // Now we know how much stack size we want to reserve. I have chosen 128 + // bytes on purpose as it's encodable only in long form that we have used. + + int stackSize = 128; // Number of bytes to reserve on the stack. + a.setOffset(patchOffset); // Move the current cursor to `patchOffset`. + a.long_().sub(zsp, stackSize); // Patch the code; don't forget to use LONG form. + + // Now the code is ready to be called + Func fn; + Error err = rt.add(&fn, &code); // Add the generated code to the runtime. + if (err) return 1; // Handle a possible error returned by AsmJit. + + int result = fn(); // Execute the generated code. + printf("%d\n", result); // Print the resulting "0". + + rt.release(fn); // Remove the function from the runtime. + return 0; +} +``` + +If you run the example it would just work. As an experiment you can try removing `long_()` form to see what happens when wrong code is generated. + +### Code Patching and REX Prefix + +In 64-bit mode there is one more thing to worry about when patching code - REX prefix. It's a single byte prefix designed to address registers with ids from 9 to 15 and to override the default width of operation from 32 to 64 bits. AsmJit, like other assemblers, only emits REX prefix when it's necessary. If the patched code only changes the immediate value as shown in the previous example then there is nothing to worry about as it doesn't change the logic behind emitting REX prefix, however, if the patched code changes register id or overrides the operation width then it's important to take care of REX prefix as well. + +AsmJit contains another instruction option that controls (forces) REX prefix - `rex()`. If you use it the instruction emitted will always use REX prefix even when it's encodable without it. The following list contains some instructions and their binary representations to illustrate when it's emitted: + + * `__83C410` - `add esp, 16` - 32-bit operation in 64-bit mode doesn't require REX prefix. + * `4083C410` - `rex add esp, 16` - 32-bit operation in 64-bit mode with forced REX prefix (0x40). + * `4883C410` - `add rsp, 16` - 64-bit operation in 64-bit mode requires REX prefix (0x48). + * `4183C410` - `add r12d, 16` - 32-bit operation in 64-bit mode using R12D requires REX prefix (0x41). + * `4983C410` - `add r12, 16` - 64-bit operation in 64-bit mode using R12 requires REX prefix (0x49). + +### Generic Function API + +So far all examples shown above handled creating function prologs and epilogs manually. While it's possible to do it that way it's much better to automate such process as function calling conventions vary across architectures and also across operating systems. + +AsmJit contains a functionality that can be used to define function signatures and to calculate automatically optimal frame layout that can be used directly by a prolog and epilog inserter. This feature was exclusive to AsmJit's CodeCompiler for a very long time, but was abstracted out and is now available for all users regardless of CodeEmitter they use. The design of handling functions prologs and epilogs allows generally two use cases: + + * Calculate function layout before the function is generated - this is the only way if you use pure `Assembler` emitter and shown in the next example. + * Calculate function layout after the function is generated - this way is generally used by `CodeBuilder` and `CodeCompiler` (will be described together with `X86Compiler`). + +The following concepts are used to describe and create functions in AsmJit: + + * **CallConv** - Describes a calling convention - this class contains instructions to assign registers and stack addresses to function arguments and return value(s), but doesn't specify any function signature. Calling conventions are architecture and OS dependent. + + * **TypeId** - TypeId is an 8-bit value that describes a platform independent type. It provides abstractions for most common types like `int8_t`, `uint32_t`, `uintptr_t`, `float`, `double`, and all possible vector types to match ISAs up to AVX512. **TypeId** was introduced originally to be used with **CodeCompiler**, but is now used by **FuncSignature** as well. + + * **FuncSignature** - Describes a function signature, for example `int func(int, int)`. **FuncSignature** contains a function calling convention id, return value type, and function arguments. The signature itself is platform independent and uses **TypeId** to describe types of function arguments and its return value(s). + + * **FuncDetail** - Architecture and ABI dependent information that describes **CallConv** and expanded **FuncSignature**. Each function argument and return value is represented as **FuncDetail::Value** that contains the original **TypeId** enriched by additional information that specifies if the value is passed/returned by register (and which register) or by stack. Each value also contains some other metadata that provide additional information required to handle it properly (for example if a vector value is passed indirectly by a pointer as required by WIN64 calling convention, etc...). + + * **FuncArgsMapper** - A helper that can be used to define where each function argument is expected to be. It's architecture and ABI dependent mapping from function arguments described by CallConv and FuncDetail into registers specified by the user. + + * **FuncFrameInfo** - Contains information about a function-frame. Holds callout-stack size and alignment (i.e. stack used to call functions), stack-frame size and alignment (the stack required by the function itself), and various attributes that describe how prolog and epilog should be constructed. FuncFrameInfo doesn't know anything about function arguments or returns, it should be seen as a class that describes minimum requirements of the function frame and its attributes before the final `FuncFrameLayout` is calculated. + + * **FuncFrameLayout** - Contains the final function layout that can be passed to `FuncUtils::emitProlog()` and `FuncUtils::emitEpilog()`. The content of this class should always be calculated by AsmJit by calling `FuncFrameLayout::init(const FuncDetail& detail, const FuncFrameInfo& ffi)`. + +It's a lot of concepts where each represents one step in the function layout calculation. In addition, the whole machinery can also be used to create function calls, instead of function prologs and epilogs. The next example shows how AsmJit can be used to create functions for both 32-bit and 64-bit targets and various calling conventions: + +```c++ +using namespace asmjit; + +typedef void (*SumIntsFunc)(int* dst, const int* a, const int* b); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Create JIT Runtime. + + CodeHolder code; // Create a CodeHolder. + code.init(rt.getCodeInfo()); // Initialize it to match `rt`. + X86Assembler a(&code); // Create and attach X86Assembler to `code`. + + // Decide which registers will be mapped to function arguments. Try changing + // registers of `dst`, `src_a`, and `src_b` and see what happens in function's + // prolog and epilog. + X86Gp dst = a.zax(); + X86Gp src_a = a.zcx(); + X86Gp src_b = a.zdx(); + + X86Xmm vec0 = x86::xmm0; + X86Xmm vec1 = x86::xmm1; + + // Create and initialize `FuncDetail` and `FuncFrameInfo`. Both are + // needed to create a function and they hold different kind of data. + FuncDetail func; + func.init(FuncSignature3(CallConv::kIdHost)); + + FuncFrameInfo ffi; + ffi.setDirtyRegs(X86Reg::kKindVec, // Make XMM0 and XMM1 dirty. VEC kind + Utils::mask(0, 1)); // describes XMM|YMM|ZMM registers. + + FuncArgsMapper args(&func); // Create function arguments mapper. + args.assignAll(dst, src_a, src_b); // Assign our registers to arguments. + args.updateFrameInfo(ffi); // Reflect our args in FuncFrameInfo. + + FuncFrameLayout layout; // Create the FuncFrameLayout, which + layout.init(func, ffi); // contains metadata of prolog/epilog. + + FuncUtils::emitProlog(&a, layout); // Emit function prolog. + FuncUtils::allocArgs(&a, layout, args); // Allocate arguments to registers. + a.movdqu(vec0, x86::ptr(src_a)); // Load 4 ints from [src_a] to XMM0. + a.movdqu(vec1, x86::ptr(src_b)); // Load 4 ints from [src_b] to XMM1. + a.paddd(vec0, vec1); // Add 4 ints in XMM1 to XMM0. + a.movdqu(x86::ptr(dst), vec0); // Store the result to [dst]. + FuncUtils::emitEpilog(&a, layout); // Emit function epilog and return. + + SumIntsFunc fn; + Error err = rt.add(&fn, &code); // Add the generated code to the runtime. + if (err) return 1; // Handle a possible error case. + + // Execute the generated function. + int inA[4] = { 4, 3, 2, 1 }; + int inB[4] = { 1, 5, 2, 8 }; + int out[4]; + fn(out, inA, inB); + + // Prints {5 8 4 9} + printf("{%d %d %d %d}\n", out[0], out[1], out[2], out[3]); + + rt.release(fn); // Remove the function from the runtime. + return 0; +} +``` + +CodeBuilder +----------- + +Both **CodeBuilder** and **CodeCompiler** are emitters that emit everything to a representation that allows further processing. The code stored in such representation is completely safe to be patched, simplified, reordered, obfuscated, removed, injected, analyzed, and 'think-of-anything-else'. Each instruction (or label, directive, ...) is stored as **CBNode** (Code-Builder Node) and contains all the necessary information to emit machine code out of it later. + +There is a difference between **CodeBuilder** and **CodeCompiler**: + + * **CodeBuilder** (low-level): + * Maximum compatibility with **Assembler**, easy to switch from **Assembler** to **CodeBuilder** and vice versa. + * Doesn't generate machine code directly, allows to serialize to **Assembler** when the whole code is ready to be encoded. + + * **CodeCompiler** (high-level): + * Virtual registers - allows to use unlimited number of virtual registers which are allocated into physical registers by a built-in register allocator. + * Function nodes - allows to create functions by specifying their signatures and assigning virtual registers to function arguments and return value(s). + * Function calls - allows to call other functions within the generated code by using the same interface that is used to create functions. + +There are multiple node types used by both **CodeBuilder** and **CodeCompiler**: + + * Basic nodes: + * **CBNode** - Base class for all nodes. + * **CBInst** - Instruction node. + * **CBAlign** - Alignment directive (.align). + * **CBLabel** - Label (location where to bound it). + + * Data nodes: + * **CBData** - Data embedded into the code. + * **CBConstPool** - Constant pool data. + * **CBLabelData** - Label address embedded as data. + + * Informative nodes: + * **CBComment** - Contains a comment string, doesn't affect code generation. + * **CBSentinel** - A marker that can be used to remember certain position, doesn't affect code generation. + + * **CodeCompiler** nodes: + * **CCFunc** - Start of a function. + * **CCFuncExit** - Return from a function. + * **CCFuncCall** - Function call. + +NOTE: All nodes that have **CB** prefix are used by both **CodeBuilder** and **CodeCompiler**. Nodes that have **CC** prefix are exclusive to **CodeCompiler** and are usually lowered to **CBNodes** by a **CodeBuilder** specific pass or treated as one of **CB** nodes; for example **CCFunc** inherits **CBLabel** so it's treated as **CBLabel** by **CodeBuilder** and as **CCFunc** by **CodeCompiler**. + +### Using CodeBuilder + +**CodeBuilder** was designed to be used as an **Assembler** replacement in case that post-processing of the generated code is required. The code can be modified during or after code generation. The post processing can be done manually or through **Pass** (Code-Builder Pass) object. **CodeBuilder** stores the emitted code as a double-linked list, which allows O(1) insertion and removal. + +The code representation used by **CodeBuilder** is compatible with everything AsmJit provides. Each instruction is stored as **CBInst**, which contains instruction id, options, and operands. Each instruction emitted will create a new **CBInst** instance and add it to the current cursor in the double-linked list of nodes. Since the instruction stream used by **CodeBuilder** can be manipulated, we can rewrite the **SumInts** example into the following: + +```c++ +using namespace asmjit; + +typedef void (*SumIntsFunc)(int* dst, const int* a, const int* b); + +// Small helper function to print the current content of `cb`. +static void dumpCode(CodeBuilder& cb, const char* phase) { + StringBuilder sb; + cb.dump(sb); + printf("%s:\n%s\n", phase, sb.getData()); +} + +int main(int argc, char* argv[]) { + JitRuntime rt; // Create JIT Runtime. + + CodeHolder code; // Create a CodeHolder. + code.init(rt.getCodeInfo()); // Initialize it to match `rt`. + X86Builder cb(&code); // Create and attach X86Builder to `code`. + + // Decide which registers will be mapped to function arguments. Try changing + // registers of `dst`, `src_a`, and `src_b` and see what happens in function's + // prolog and epilog. + X86Gp dst = cb.zax(); + X86Gp src_a = cb.zcx(); + X86Gp src_b = cb.zdx(); + + X86Xmm vec0 = x86::xmm0; + X86Xmm vec1 = x86::xmm1; + + // Create and initialize `FuncDetail`. + FuncDetail func; + func.init(FuncSignature3(CallConv::kIdHost)); + + // Remember prolog insertion point. + CBNode* prologInsertionPoint = cb.getCursor(); + + // Emit function body: + cb.movdqu(vec0, x86::ptr(src_a)); // Load 4 ints from [src_a] to XMM0. + cb.movdqu(vec1, x86::ptr(src_b)); // Load 4 ints from [src_b] to XMM1. + cb.paddd(vec0, vec1); // Add 4 ints in XMM1 to XMM0. + cb.movdqu(x86::ptr(dst), vec0); // Store the result to [dst]. + + // Remember epilog insertion point. + CBNode* epilogInsertionPoint = cb.getCursor(); + + // Let's see what we have now. + dumpCode(cb, "Raw Function"); + + // Now, after we emitted the function body, we can insert the prolog, arguments + // allocation, and epilog. This is not possible with using pure X86Assembler. + FuncFrameInfo ffi; + ffi.setDirtyRegs(X86Reg::kKindVec, // Make XMM0 and XMM1 dirty. VEC kind + Utils::mask(0, 1)); // describes XMM|YMM|ZMM registers. + + FuncArgsMapper args(&func); // Create function arguments mapper. + args.assignAll(dst, src_a, src_b); // Assign our registers to arguments. + args.updateFrameInfo(ffi); // Reflect our args in FuncFrameInfo. + + FuncFrameLayout layout; // Create the FuncFrameLayout, which + layout.init(func, ffi); // contains metadata of prolog/epilog. + + // Insert function prolog and allocate arguments to registers. + cb.setCursor(prologInsertionPoint); + FuncUtils::emitProlog(&cb, layout); + FuncUtils::allocArgs(&cb, layout, args); + + // Insert function epilog. + cb.setCursor(epilogInsertionPoint); + FuncUtils::emitEpilog(&cb, layout); + + // Let's see how the function prolog and epilog looks. + dumpCode(cb, "Prolog & Epilog"); + + // IMPORTANT: CodeBuilder requires `finalize()` to be called to serialize + // the code to the Assembler (it automatically creates one if not attached). + cb.finalize(); + + SumIntsFunc fn; + Error err = rt.add(&fn, &code); // Add the generated code to the runtime. + if (err) return 1; // Handle a possible error case. + + // Execute the generated function. + int inA[4] = { 4, 3, 2, 1 }; + int inB[4] = { 1, 5, 2, 8 }; + int out[4]; + fn(out, inA, inB); + + // Prints {5 8 4 9} + printf("{%d %d %d %d}\n", out[0], out[1], out[2], out[3]); + + rt.release(fn); // Remove the function from the runtime. + return 0; +} +``` + +When the example is executed it should output the following (this one using AMD64-SystemV ABI): + +``` +Raw Function: +movdqu xmm0, [rcx] +movdqu xmm1, [rdx] +paddd xmm0, xmm1 +movdqu [rax], xmm0 + +Prolog & Epilog: +mov rax, rdi +mov rcx, rsi +movdqu xmm0, [rcx] +movdqu xmm1, [rdx] +paddd xmm0, xmm1 +movdqu [rax], xmm0 +ret + +{5 8 4 9} +``` + +The number of use-cases of **X86Builder** is not limited and highly depends on your creativity and experience. The previous example can be easily improved to collect all dirty registers inside the function programmatically and to pass them to `ffi.setDirtyRegs()`: + +```c++ +using namespace asmjit; + +// NOTE: This function doesn't cover all possible instructions. It ignores +// instructions that write to implicit registers that are not part of the +// operand list. It also counts read-only registers. Real implementation +// would be a bit more complicated, but still relatively easy. +static void collectDirtyRegs(const CBNode* first, const CBNode* last, uint32_t regMask[X86Reg::kKindCount]) { + const CBNode* node = first; + while (node) { + if (node->actsAsInst()) { + const CBInst* inst = node->as(); + const Operand* opArray = inst->getOpArray(); + + for (uint32_t i = 0, opCount = inst->getOpCount(); i < opCount; i++) { + const Operand& op = opArray[i]; + if (op.isReg()) { + const X86Reg& reg = op.as(); + regMask[reg.getKind()] |= 1U << reg.getId(); + } + } + } + + if (node == last) break; + node = node->getNext(); + } +} + +static void setDirtyRegsOfFFI(const X86Builder& cb, FuncFrameInfo& ffi) { + uint32_t regMask[X86Reg::kKindCount] = { 0 }; + collectDirtyRegs(cb.getFirstNode(), cb.getLastNode(), regMask); + + // X86/X64 ABIs only require to save GP/XMM registers: + ffi.setDirtyRegs(X86Reg::kKindGp, regMask[X86Reg::kKindGp]); + ffi.setDirtyRegs(X86Reg::kKindVec, regMask[X86Reg::kKindVec]); +} +``` + +### Using X86Assembler or X86Builder through X86Emitter + +Even when **Assembler** and **CodeBuilder** implement the same interface defined by **CodeEmitter** their platform dependent variants (**X86Assembler** and **X86Builder**, respective) cannot be interchanged or casted to each other by using C++'s `static_cast<>`. The main reason is the inheritance graph of these classes is different and cast-incompatible, as illustrated in the following graph: + +``` + +--------------+ +=======================+ + +----------------------->| X86Emitter |<--+--# X86EmitterImplicitT<> #<--+ + | +--------------+ | +=======================+ | + | (abstract) | (mixin) | + | +--------------+ +~~~~~~~~~~~~~~+ | | + +-->| Assembler |---->| X86Assembler |<--+ | + | +--------------+ +~~~~~~~~~~~~~~+ | | + | (abstract) (final) | | ++===============+ | +--------------+ +~~~~~~~~~~~~~~+ | | +# CodeEmitter #--+-->| CodeBuilder |--+->| X86Builder |<--+ | ++===============+ +--------------+ | +~~~~~~~~~~~~~~+ | + (abstract) (abstract) | (final) | + +---------------------+ | + | | + | +--------------+ +~~~~~~~~~~~~~~+ +=======================+ | + +-->| CodeCompiler |---->| X86Compiler |<-----# X86EmitterExplicitT<> #---+ + +--------------+ +~~~~~~~~~~~~~~+ +=======================+ + (abstract) (final) (mixin) +``` + +The graph basically shows that it's not possible to cast **X86Assembler** to **X86Builder** and vice versa. However, since both **X86Assembler** and **X86Builder** share the same interface defined by both **CodeEmitter** and **X86EmmiterImplicitT** a class called **X86Emitter** was introduced to make it possible to write a function that can emit to both **X86Assembler** and **X86Builder**. Note that **X86Emitter** cannot be created, it's abstract and has private constructors and destructors; it was only designed to be casted to and used as an interface. + +Each X86 emitter implements a member function called **asEmitter()**, which casts the instance to the **X86Emitter**, as illustrated on the next example: + +```c++ +using namespace asmjit; + +static void emitSomething(X86Emitter* e) { + e->mov(x86::eax, x86::ebx); +} + +static void assemble(CodeHolder& code, bool useAsm) { + if (useAsm) { + X86Assembler a(&code); + emitSomething(a.asEmitter()); + } + else { + X86Builder cb(&code); + emitSomething(cb.asEmitter()); + + // IMPORTANT: CodeBuilder requires `finalize()` to be called to serialize + // the code to the Assembler (it automatically creates one if not attached). + cb.finalize(); + } +} +``` + +The example above shows how to create a function that can emit code to either **X86Assembler** or **X86Builder** through **X86Emitter**, which provides emitter-neutral functionality. **X86Emitter**, however, doesn't provide any emitter **X86Assembler** or **X86Builder** specific functionality like **setCursor()**. + +CodeCompiler +------------ + +**CodeCompiler** is a high-level code emitter that provides virtual registers and automatically handles function calling conventions. It's still architecture dependent, but makes the code generation much easier by offering a built-in register allocator and function builder. Functions are essential; the first-step to generate some code is to define the signature of the function you want to generate (before generating the function body). Function arguments and return value(s) are handled by assigning virtual registers to them. Similarly, function calls are handled the same way. + +**CodeCompiler** also makes the use of passes (introduced by **CodeBuilder**) and automatically adds an architecture-dependent register allocator pass to the list of passes when attached to **CodeHolder**. + +### Compiler Basics + +The first **CodeCompiler** example shows how to generate a function that simply returns an integer value. It's an analogy to the very first example: + +```c++ +#include +#include + +using namespace asmjit; + +// Signature of the generated function. +typedef int (*Func)(void); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Runtime specialized for JIT code execution. + + CodeHolder code; // Holds code and relocation information. + code.init(rt.getCodeInfo()); // Initialize to the same arch as JIT runtime. + + X86Compiler cc(&code); // Create and attach X86Compiler to `code`. + cc.addFunc(FuncSignature0()); // Begin a function of `int fn(void)` signature. + + X86Gp vReg = cc.newGpd(); // Create a 32-bit general purpose register. + cc.mov(vReg, 1); // Move one to our virtual register `vReg`. + cc.ret(vReg); // Return `vReg` from the function. + + cc.endFunc(); // End of the function body. + cc.finalize(); // Translate and assemble the whole `cc` content. + // ----> X86Compiler is no longer needed from here and can be destroyed <---- + + Func fn; + Error err = rt.add(&fn, &code); // Add the generated code to the runtime. + if (err) return 1; // Handle a possible error returned by AsmJit. + // ----> CodeHolder is no longer needed from here and can be destroyed <---- + + int result = fn(); // Execute the generated code. + printf("%d\n", result); // Print the resulting "1". + + rt.release(fn); // RAII, but let's make it explicit. + return 0; +} +``` + +The **addFunc()** and **endFunc()** methods define the body of the function. Both functions must be called per function, but the body doesn't have to be generated in sequence. An example of generating two functions will be shown later. The next example shows more complicated code that contain a loop and generates a **memcpy32()** function: + +```c++ +#include +#include + +using namespace asmjit; + +// Signature of the generated function. +typedef void (*MemCpy32)(uint32_t* dst, const uint32_t* src, size_t count); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Runtime specialized for JIT code execution. + + CodeHolder code; // Holds code and relocation information. + code.init(rt.getCodeInfo()); // Initialize to the same arch as JIT runtime. + + X86Compiler cc(&code); // Create and attach X86Compiler to `code`. + cc.addFunc( // Begin the function of the following signature: + FuncSignature3()); // 3rd argument - size_t (machine reg-size). + + Label L_Loop = cc.newLabel(); // Start of the loop. + Label L_Exit = cc.newLabel(); // Used to exit early. + + X86Gp dst = cc.newIntPtr("dst"); // Create `dst` register (destination pointer). + X86Gp src = cc.newIntPtr("src"); // Create `src` register (source pointer). + X86Gp cnt = cc.newUIntPtr("cnt"); // Create `cnt` register (loop counter). + + cc.setArg(0, dst); // Assign `dst` argument. + cc.setArg(1, src); // Assign `src` argument. + cc.setArg(2, cnt); // Assign `cnt` argument. + + cc.test(cnt, cnt); // Early exit if length is zero. + cc.jz(L_Exit); + + cc.bind(L_Loop); // Bind the beginning of the loop here. + + X86Gp tmp = cc.newInt32("tmp"); // Copy a single dword (4 bytes). + cc.mov(tmp, x86::dword_ptr(src)); // Load DWORD from [src] address. + cc.mov(x86::dword_ptr(dst), tmp); // Store DWORD to [dst] address. + + cc.add(src, 4); // Increment `src`. + cc.add(dst, 4); // Increment `dst`. + + cc.dec(cnt); // Loop until `cnt` is non-zero. + cc.jnz(L_Loop); + + cc.bind(L_Exit); // Label used by early exit. + cc.endFunc(); // End of the function body. + + cc.finalize(); // Translate and assemble the whole `cc` content. + // ----> X86Compiler is no longer needed from here and can be destroyed <---- + + MemCpy32 memcpy32; + Error err = rt.add(&memcpy32, &code); // Add the generated code to the runtime. + if (err) return 1; // Handle a possible error returned by AsmJit. + // ----> CodeHolder is no longer needed from here and can be destroyed <---- + + // Test the generated code. + uint32_t src[6] = { 1, 2, 3, 5, 8, 13 }; + uint32_t dst[6]; + memcpy32(dst, src, 6); + + for (uint32_t i = 0; i < 6; i++) + printf("%d\n", dst[i]); + + rt.release(memcpy32); // RAII, but let's make it explicit. + return 0; +} +``` + +### Recursive Functions + +It's possible to create more functions by using the same `X86Compiler` instance and make links between them. In such case it's important to keep the pointer to the `CCFunc` node. The first example creates a simple Fibonacci function that calls itself recursively: + +```c++ +#include +#include + +using namespace asmjit; + +// Signature of the generated function. +typedef uint32_t (*Fibonacci)(uint32_t x); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Runtime specialized for JIT code execution. + + CodeHolder code; // Holds code and relocation information. + code.init(rt.getCodeInfo()); // Initialize to the same arch as JIT runtime. + + X86Compiler cc(&code); // Create and attach X86Compiler to `code`. + CCFunc* func = cc.addFunc( // Begin of the Fibonacci function, `addFunc()` + FuncSignature1()); // Returns a pointer to the `CCFunc` node. + + Label L_Exit = cc.newLabel() // Exit label. + X86Gp x = cc.newU32(); // Function `x` argument. + X86Gp y = cc.newU32(); // Temporary. + + cc.setArg(0, x); + + cc.cmp(x, 3); // Return `x` if less than 3. + cc.jb(L_Exit); + + cc.mov(y, x); // Make copy of the original `x`. + cc.dec(x); // Decrease `x`. + + CCFuncCall* call = cc.call( // Function call: + func->getLabel(), // Function address or Label. + FuncSignature1()); // Function signature. + + call->setArg(0, x); // Assign `x` as the first argument and + call->setRet(0, x); // assign `x` as a return value as well. + + cc.add(x, y); // Combine the return value with `y`. + + cc.bind(L_Exit); + cc.ret(x); // Return `x`. + cc.endFunc(); // End of the function body. + + cc.finalize(); // Translate and assemble the whole `cc` content. + // ----> X86Compiler is no longer needed from here and can be destroyed <---- + + Fibonacci fib; + Error err = rt.add(&fib, &code); // Add the generated code to the runtime. + if (err) return 1; // Handle a possible error returned by AsmJit. + // ----> CodeHolder is no longer needed from here and can be destroyed <---- + + printf("Fib(%u) -> %u\n, 8, fib(8)); // Test the generated code. + + rt.release(fib); // RAII, but let's make it explicit. + return 0; +} +``` + +### Stack Management + +**CodeCompiler** manages function's stack-frame, which is used by the register allocator to spill virtual registers. It also provides an interface to allocate user-defined block of the stack, which can be used as a temporary storage by the generated function. In the following example a stack of 256 bytes size is allocated, filled by bytes starting from 0 to 255 and then iterated again to sum all the values. + +```c++ +#include +#include + +using namespace asmjit; + +// Signature of the generated function. +typedef int (*Func)(void); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Runtime specialized for JIT code execution. + + CodeHolder code; // Holds code and relocation information. + code.init(rt.getCodeInfo()); // Initialize to the same arch as JIT runtime. + + X86Compiler cc(&code); // Create and attach X86Compiler to `code`. + cc.addFunc(FuncSignature0()); // Create a function that returns 'int'. + + X86Gp p = cc.newIntPtr("p"); + X86Gp i = cc.newIntPtr("i"); + + X86Mem stack = c.newStack(256, 4); // Allocate 256 bytes on the stack aligned to 4 bytes. + X86Mem stackIdx(stack); // Copy of `stack` with `i` added. + stackIdx.setIndex(i); // stackIdx <- stack[i]. + stackIdx.setSize(1); // stackIdx <- byte ptr stack[i]. + + // Load a stack address to `p`. This step is purely optional and shows + // that `lea` is useful to load a memory operands address (even absolute) + // to a general purpose register. + cc.lea(p, stack); + + // Clear `i` (`xor` as it's C++ keyword, hence `xor_` is used instead). + cc.xor_(i, i); + + Label L1 = cc.newLabel(); + Label L2 = cc.newLabel(); + + cc.bind(L1); // First loop, fill the stack. + cc.mov(stackIdx, i.r8()); // stack[i] = uint8_t(i). + + cc.inc(i); // i++; + cc.cmp(i, 256); // if (i < 256) + cc.jb(L1); // goto L1; + + // Second loop, sum all bytes stored in `stack`. + X86Gp sum = cc.newI32("sum"); + X86Gp val = cc.newI32("val"); + + cc.xor_(i, i); + cc.xor_(sum, sum); + + cc.bind(L2); + + cc.movzx(val, stackIdx); // val = uint32_t(stack[i]); + cc.add(sum, val); // sum += val; + + cc.inc(i); // i++; + cc.cmp(i, 256); // if (i < 256) + cc.jb(L2); // goto L2; + + cc.ret(sum); // Return the `sum` of all values. + cc.endFunc(); // End of the function body. + + cc.finalize(); // Translate and assemble the whole `cc` content. + // ----> X86Compiler is no longer needed from here and can be destroyed <---- + + Func fib; + Error err = rt.add(&fib, &code); // Add the generated code to the runtime. + if (err) return 1; // Handle a possible error returned by AsmJit. + // ----> CodeHolder is no longer needed from here and can be destroyed <---- + + printf("Func() -> %d\n, func()); // Test the generated code. + + rt.release(fib); // RAII, but let's make it explicit. + return 0; +} +``` + +### Constant Pool + +**CodeCompiler** provides two constant pools for a general purpose code generation - local and global. Local constant pool is related to a single **CCFunc** node and is generally flushed after the function body, and global constant pool is flushed at the end of the generated code by **CodeCompiler::finalize()**. + +```c++ +#include + +using namespace asmjit; + +static void exampleUseOfConstPool(X86Compiler& cc) { + cc.addFunc(FuncSignature0()); + + X86Gp v0 = cc.newGpd("v0"); + X86Gp v1 = cc.newGpd("v1"); + + X86Mem c0 = cc.newInt32Const(kConstScopeLocal, 200); + X86Mem c1 = cc.newInt32Const(kConstScopeLocal, 33); + + cc.mov(v0, c0); + cc.mov(v1, c1); + cc.add(v0, v1); + + cc.ret(v0); + cc.endFunc(); +} +``` ### Code Injection -Code injection was one of key concepts of Compiler from the beginning. Compiler records all emitted instructions in a double-linked list which can be manipulated before `make()` is called. Any call to Compiler that adds instruction, function or anything else in fact manipulates this list by inserting nodes into it. +Both **CodeBuilder** and **CodeCompiler** emitters store their nodes in a double-linked list, which makes it easy to manipulate during the code generation or after it. Each node is always emitted next to the current **cursor** and the cursor is changed to that newly emitted node. Cursor can be explicitly retrieved and assigned by **getCursor()** and **setCursor()**, respectively. -To manipulate the current cursor use Compiler's `getCursor()` and `setCursor()` methods. The following snippet demonstrates the proper way of code injection. +The following example shows how to inject code at the beginning of the function by providing an **XmmConstInjector** helper class. ```c++ -X86Compiler c(...); +#include +#include +#include -X86GpVar x = c.newInt32("x"); -X86GpVar y = c.newInt32("y"); +using namespace asmjit; -ASNode* here = c.getCursor(); -c.mov(y, 2); +// Simple injector that injects `movaps()` to the beginning of the function. +class XmmConstInjector { +public: + struct Slot { + X86Xmm reg; + Data128 value; + }; -// Now, `here` can be used to inject something before `mov y, 2`. To inject -// something it's always good to remember the current cursor so it can be set -// back after the injecting is done. When `setCursor()` is called it returns -// the old cursor to be remembered. -ASNode* prev = c.setCursor(here); -c.mov(x, 1); -c.setCursor(prev); + XmmConstInjector(X86Compiler* cc) + : _cc(cc), + _injectTarget(cc->getCursor()) {} + + X86Xmm xmmOf(const Data128& value) { + // First reuse the register if it already holds the given `value`. + for (std::vector::const_iterator it(_slots.begin()); it != _slots.end(); ++it) { + const Slot& slot = *it; + if (::memcmp(&slot.value, &value, sizeof(Data128)) == 0) + return slot.reg; + } + + // Create a new register / value pair and store in `_slots`. + X86Xmm reg = _cc->newXmm("const%u", static_cast(_slots.size())); + + Slot newSlot; + newSlot.value = value; + newSlot.reg = reg; + _slots.push_back(newSlot); + + // Create the constant and inject it after the injectTarget. + X86Mem mem = _cc->newConst(kConstScopeGlobal, &value, 16); + CBNode* saved = _cc->setCursor(_injectTarget); + + _cc->movaps(reg, mem); + // Make sure we inject next load after the load we just emitted. + _injectTarget = _cc->getCursor(); + + // Restore the original cursor, so the code emitting can continue from where it was. + _cc->setCursor(saved); + return reg; + } + + X86Compiler* _cc; + CBNode* _injectTarget; + std::vector _slots; +}; + +// Signature of the generated function. +typedef void (*Func)(uint16_t*); + +int main(int argc, char* argv[]) { + JitRuntime rt; // Runtime specialized for JIT code execution. + + FileLogger logger(stdout); + + CodeHolder code; // Holds code and relocation information. + code.init(rt.getCodeInfo()); // Initialize to the same arch as JIT runtime. + code.setLogger(&logger); + + X86Compiler cc(&code); // Create and attach X86Compiler to `code`. + cc.addFunc( + FuncSignature1()); // Create a function that accepts `uint16_t[]'. + + X86Gp p = cc.newIntPtr("p"); // Create and Assign the function argument `p`. + cc.setArg(0, p); + + XmmConstInjector injector(&cc); // The injector will inject the code |here|. + + X86Xmm x = cc.newXmm("x"); + cc.movups(x, x86::ptr(p)); // Load 16 bytes from `[p]` to `x`. + + // Now use injector to add constants to the constant pool and to inject their loads. + Data128 data0 = Data128::fromU16(0x80); + Data128 data1 = Data128::fromU16(0x13); + + cc.paddw(x, injector.xmmOf(data0)); // x.u16 = x.u16 + 0x80. + cc.pmullw(x, injector.xmmOf(data1)); // x.u16 = x.u16 * 0x13. + cc.movups(x86::ptr(p), x); // Write back to `[p]`. + + cc.endFunc(); // End of the function body. + cc.finalize(); // Translate and assemble the whole `cc` content. + // ----> X86Compiler is no longer needed from here and can be destroyed <---- + + Func func; + Error err = rt.add(&func, &code); // Add the generated code to the runtime. + if (err) return 1; // Handle a possible error returned by AsmJit. + // ----> CodeHolder is no longer needed from here and can be destroyed <---- + + // Test the generated function + uint16_t vec[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + func(vec); + + for (uint32_t i = 0; i < 8; i++) + printf("%u ", vec[i]); + printf("\n"); + + rt.release(func); // RAII, but let's make it explicit. + return 0; +} ``` -The resulting code would look like: +The code generated would look similar to: +```x86asm +L0: +movaps xmm0, oword [L2] ; movaps const0, oword [L2] +movaps xmm1, oword [L2+16] ; movaps const1, oword [L2+16] +movups xmm2, [rdi] ; movups x, [p] +paddw xmm2, xmm0 ; paddw x, const0 +pmullw xmm2, xmm1 ; pmullw x, const1 +movups [rdi], xmm2 ; movups [p], x +L1: +ret +.align 16 +L2: +.data 80008000800080008000800080008000 +.data 13001300130013001300130013001300 ``` -c.mov(x, 1); -c.mov(y, 2); + +There are many other applications of code injection, usually it's used to lazy-add some initialization code and such, but the application is practically unlimited. + +Advanced Features +----------------- + +### Logging + +Failures are common, especially when working at machine-code level. AsmJit does already a good job with function overloading to prevent from emitting semantically incorrect instructions, but it can't prevent from emitting code that is semantically correct, but contains bugs. Logging has always been an important part of AsmJit's infrastructure and looking at logs could become handy when your code doesn't work as expected. + +AsmJit's **Logger** provides the following: + * Defines basic logging interface used by AsmJit, + * Allows to reimplement its `Error _log(const char* str, size_t len)` function. + * **FileLogger** implements logging into a C `FILE*` stream. + * **StringLogger** implements logging into AsmJit's `StringBuilder`. + +**Logger** also contains useful options that control the output and what should be logged: + + * **Logger::kOptionBinaryForm** - Output also binary representation of each instruction. + * **Logger::kOptionImmExtended** - Output meaning of some immediate values. + * **Logger::kOptionHexImmediate** - Display all immediates in hexadecimal. + * **Logger::kOptionHexDisplacement** - Display all offsets in hexadecimal. + +**Logger** is typically attached to **CodeHolder** and all attached code emitters automatically use it: + +```c++ +#include +#include + +using namespace asmjit; + +int main(int argc, char* argv[]) { + JitRuntime rt; // Runtime specialized for JIT code execution. + FileLogger logger(stdout); // Logger should always survive the CodeHolder. + + CodeHolder code; // Holds code and relocation information. + code.init(rt.getCodeInfo()); // Initialize to the same arch as JIT runtime. + code.setLogger(&logger); // Attach the `logger` to `code` holder. + + // ... code as usual, everything you emit will be logged to `stdout` ... + + return 0; +} ``` +### Error Handling + +AsmJit uses error codes to represent and return errors. Every function where error can occur returns **Error**. Exceptions are never thrown by AsmJit even in extreme conditions like out-of-memory. Errors should never be ignored, however, checking errors after each asmjit API call would simply be overcomplicate the whole code generation. To handle these errors AsmJit provides **ErrorHandler**, which contains **handleError()**: + + `virtual bool handleError(Error err, const char* message, CodeEmitter* origin) = 0;` + +That can be overridden by AsmJit users and do the following: + + * 1. Return `true` or `false` from `handleError()`. If `true` is returned it means that error was handled and AsmJit can continue execution. The error code still be propagated to the caller, but won't put the origin into an error state (it won't set last-error). However, `false` reports to AsmJit that the error cannot be handled - in such case it stores the error, which can retrieved later by `getLastError()`. Returning `false` is the default behavior when no error handler is provided. To put the assembler into a non-error state again the `resetLastError()` must be called. + * 2. Throw an exception. AsmJit doesn't use exceptions and is completely exception-safe, but you can throw exception from the error handler if this way is easier / preferred by you. Throwing an exception acts virtually as returning `true` - AsmJit won't store the error. + * 3. Use plain old C's `setjmp()` and `longjmp()`. Asmjit always puts `Assembler` and `Compiler` to a consistent state before calling the `handleError()` so `longjmp()` can be used without issues to cancel the code-generation if an error occurred. + +**ErrorHandler** is simply attached to **CodeHolder** and will be used by every emitter attached to it. The first example uses error handler that just prints the error, but lets AsmJit continue: + +```c++ +// Error handling #1: +#include + +#include + +// Error handler that just prints the error and lets AsmJit ignore it. +class PrintErrorHandler : public asmjit::ErrorHandler { +public: + // Return `true` to set last error to `err`, return `false` to do nothing. + bool handleError(asmjit::Error err, const char* message, asmjit::CodeEmitter* origin) override { + fprintf(stderr, "ERROR: %s\n", message); + return false; + } +}; + +int main(int argc, char* argv[]) { + using namespace asmjit; + + JitRuntime rt; + PrintErrorHandler eh; + + CodeHolder code; + code.init(rt.getCodeInfo()); + code.setErrorHandler(&eh); + + // Try to emit instruction that doesn't exist. + X86Assembler a(&code); + a.emit(X86Inst::kIdMov, x86::xmm0, x86::xmm1); + + return 0; +} +``` + +If error happens during instruction emitting / encoding the assembler behaves transactionally - the output buffer won't advance if encoding failed, thus either a fully encoded instruction or nothing is emitted. The error handling shown above is useful, but it's still not the best way of dealing with errors in AsmJit. The following example shows how to use exception handling to handle errors in a more C++ way: + +```c++ +// Error handling #2: +#include + +#include +#include +#include + +// Error handler that throws a user-defined `AsmJitException`. +class AsmJitException : public std::exception { +public: + AsmJitException(asmjit::Error err, const char* message) noexcept + : error(err), + message(message) {} + + const char* what() const noexcept override { return message.c_str(); } + + asmjit::Error error; + std::string message; +}; + +class ThrowErrorHandler : public asmjit::ErrorHandler { +public: + // Throw is possible, functions that use ErrorHandler are never 'noexcept'. + bool handleError(asmjit::Error err, const char* message, asmjit::CodeEmitter* origin) override { + throw AsmJitException(err, message); + } +}; + +int main(int argc, char* argv[]) { + using namespace asmjit; + + JitRuntime rt; + ThrowErrorHandler eh; + + CodeHolder code; + code.init(rt.getCodeInfo()); + code.setErrorHandler(&eh); + + // Try to emit instruction that doesn't exist. + try { + X86Assembler a(&code); + a.emit(X86Inst::kIdMov, x86::xmm0, x86::xmm1); + } + catch (const AsmJitException& ex) { + printf("EXCEPTION THROWN: %s\n", ex.what()); + } + + return 0; +} +``` + +If C++ exceptions are not what you like or your project turns off them completely there is still a way of reducing the error handling to a minimum by using a standard `setjmp/longjmp` approach. AsmJit is exception-safe and cleans up everything before calling the **ErrorHandler**, so any approach is safe. You can simply jump from the error handler without causing any side-effects or memory leaks. The following example demonstrates how it could be done: + +```c++ +// Error handling #3: +#include + +#include +#include + +class LongJmpErrorHandler : public asmjit::ErrorHandler { +public: + inline LongJmpErrorHandler() : err(asmjit::kErrorOk) {} + + virtual bool handleError(asmjit::Error err, const char* message, asmjit::CodeEmitter* origin) { + this->err = err; + longjmp(state, 1); + } + + jmp_buf state; + asmjit::Error err; +}; + +int main(int argc, char* argv[]) { + using namespace asmjit; + + JitRuntime rt; + LongJmpErrorHandler eh; + + CodeHolder code; + code.init(rt.getCodeInfo()); + code.setErrorHandler(&eh); + + // Try to emit instruction that doesn't exist. + X86Assembler a(&code); + if (!setjmp(eh.state)) { + a.emit(X86Inst::kIdMov, x86::xmm0, x86::xmm1); + } + else { + Error err = eh.err; + printf("ASMJIT ERROR: 0x%08X [%s]\n", err, DebugUtils::errorAsString(err)); + } + + return 0; +} +``` + +### TODO + +...More documentation... + Support ------- diff --git a/src/asmjit/apibegin.h b/src/asmjit/apibegin.h deleted file mode 100644 index ccb1157..0000000 --- a/src/asmjit/apibegin.h +++ /dev/null @@ -1,76 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Dependencies] -#if !defined(_ASMJIT_BUILD_H) -#include "./build.h" -#endif // !_ASMJIT_BUILD_H - -// [Guard] -#if !defined(ASMJIT_API_SCOPE) -# define ASMJIT_API_SCOPE -#else -# error "[asmjit] Api-Scope is already active, previous scope not closed by apiend.h?" -#endif // ASMJIT_API_SCOPE - -// [NoExcept] -#if !ASMJIT_CC_HAS_NOEXCEPT && !defined(noexcept) -# define noexcept ASMJIT_NOEXCEPT -# define ASMJIT_UNDEF_NOEXCEPT -#endif // !ASMJIT_CC_HAS_NOEXCEPT && !noexcept - -// [NullPtr] -#if !ASMJIT_CC_HAS_NULLPTR && !defined(nullptr) -# define nullptr NULL -# define ASMJIT_UNDEF_NULLPTR -#endif // !ASMJIT_CC_HAS_NULLPTR && !nullptr - -// [Override] -#if !ASMJIT_CC_HAS_OVERRIDE && !defined(override) -# define override -# define ASMJIT_UNDEF_OVERRIDE -#endif // !ASMJIT_CC_HAS_OVERRIDE && !override - -// [CLang] -#if ASMJIT_CC_CLANG -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunnamed-type-template-args" -#endif // ASMJIT_CC_CLANG - -// [GCC] -#if ASMJIT_CC_GCC -# pragma GCC diagnostic push -# pragma GCC diagnostic warning "-Winline" -#endif // ASMJIT_CC_GCC - -// [MSC] -#if ASMJIT_CC_MSC - -# pragma warning(push) -# pragma warning(disable: 4127) // conditional expression is constant -# pragma warning(disable: 4201) // nameless struct/union -# pragma warning(disable: 4244) // '+=' : conversion from 'int' to 'x', possible - // loss of data -# pragma warning(disable: 4251) // struct needs to have dll-interface to be used - // by clients of struct ... -# pragma warning(disable: 4275) // non dll-interface struct ... used as base for - // dll-interface struct -# pragma warning(disable: 4355) // this used in base member initializer list -# pragma warning(disable: 4480) // specifying underlying type for enum -# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' - -// TODO: Check if these defines are needed and for which version of MSC. There are -// news about these as they are part of C99. -# if !defined(vsnprintf) -# define ASMJIT_UNDEF_VSNPRINTF -# define vsnprintf _vsnprintf -# endif // !vsnprintf -# if !defined(snprintf) -# define ASMJIT_UNDEF_SNPRINTF -# define snprintf _snprintf -# endif // !snprintf - -#endif // ASMJIT_CC_MSC diff --git a/src/asmjit/apiend.h b/src/asmjit/apiend.h deleted file mode 100644 index 39979d9..0000000 --- a/src/asmjit/apiend.h +++ /dev/null @@ -1,53 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Guard] -#if defined(ASMJIT_API_SCOPE) -# undef ASMJIT_API_SCOPE -#else -# error "[asmjit] Api-Scope not active, forgot to include apibegin.h?" -#endif // ASMJIT_API_SCOPE - -// [NoExcept] -#if defined(ASMJIT_UNDEF_NOEXCEPT) -# undef noexcept -# undef ASMJIT_UNDEF_NOEXCEPT -#endif // ASMJIT_UNDEF_NOEXCEPT - -// [NullPtr] -#if defined(ASMJIT_UNDEF_NULLPTR) -# undef nullptr -# undef ASMJIT_UNDEF_NULLPTR -#endif // ASMJIT_UNDEF_NULLPTR - -// [Override] -#if defined(ASMJIT_UNDEF_OVERRIDE) -# undef override -# undef ASMJIT_UNDEF_OVERRIDE -#endif // ASMJIT_UNDEF_OVERRIDE - -// [CLang] -#if ASMJIT_CC_CLANG -# pragma clang diagnostic pop -#endif // ASMJIT_CC_CLANG - -// [GCC] -#if ASMJIT_CC_GCC -# pragma GCC diagnostic pop -#endif // ASMJIT_CC_GCC - -// [MSC] -#if ASMJIT_CC_MSC -# pragma warning(pop) -# if defined(ASMJIT_UNDEF_VSNPRINTF) -# undef vsnprintf -# undef ASMJIT_UNDEF_VSNPRINTF -# endif // ASMJIT_UNDEF_VSNPRINTF -# if defined(ASMJIT_UNDEF_SNPRINTF) -# undef snprintf -# undef ASMJIT_UNDEF_SNPRINTF -# endif // ASMJIT_UNDEF_SNPRINTF -#endif // ASMJIT_CC_MSC diff --git a/src/asmjit/arm.h b/src/asmjit/arm.h index 271c7f6..0a916d9 100644 --- a/src/asmjit/arm.h +++ b/src/asmjit/arm.h @@ -12,6 +12,7 @@ #include "./base.h" #include "./arm/armassembler.h" +#include "./arm/armbuilder.h" #include "./arm/armcompiler.h" #include "./arm/arminst.h" #include "./arm/armoperand.h" diff --git a/src/asmjit/asmjit.h b/src/asmjit/asmjit.h index b94f788..ead90f0 100644 --- a/src/asmjit/asmjit.h +++ b/src/asmjit/asmjit.h @@ -16,345 +16,32 @@ //! //! AsmJit - Complete x86/x64 JIT and Remote Assembler for C++. //! -//! A complete JIT and remote assembler for C++ language. It can generate native -//! code for x86 and x64 architectures and supports the whole x86/x64 instruction -//! set - from legacy MMX to the newest AVX2. It has a type-safe API that allows -//! C++ compiler to do semantic checks at compile-time even before the assembled -//! code is generated and executed. -//! -//! AsmJit is not a virtual machine (VM). It doesn't have functionality to -//! implement VM out of the box; however, it can be be used as a JIT backend -//! of your own VM. The usage of AsmJit is not limited at all; it's suitable -//! for multimedia, VM backends, remote code generation, and many other tasks. -//! -//! \section AsmJit_Main_Concepts Code Generation Concepts -//! -//! AsmJit has two completely different code generation concepts. The difference -//! is in how the code is generated. The first concept, also referred as a low -//! level concept, is called `Assembler` and it's the same as writing RAW -//! assembly by inserting instructions that use physical registers directly. In -//! this case AsmJit does only instruction encoding, verification and final code -//! relocation. -//! -//! The second concept, also referred as a high level concept, is called -//! `Compiler`. Compiler lets you use virtually unlimited number of registers -//! (it calls them variables), which significantly simplifies the code generation -//! process. Compiler allocates these virtual registers to physical registers -//! after the code generation is done. This requires some extra effort - Compiler -//! has to generate information for each node (instruction, function declaration, -//! function call, etc...) in the code, perform a variable liveness analysis and -//! translate the code using variables to a code that uses only physical registers. -//! -//! In addition, Compiler understands functions and their calling conventions. -//! It has been designed in a way that the code generated is always a function -//! having a prototype like a real programming language. By having a function -//! prototype the Compiler is able to insert prolog and epilog sequence to the -//! function being generated and it's able to also generate a necessary code -//! to call other function from your own code. -//! -//! There is no conclusion on which concept is better. `Assembler` brings full -//! control and the best performance, while `Compiler` makes the code-generation -//! more fun and more portable. -//! -//! \section AsmJit_Main_Sections Documentation Sections -//! -//! AsmJit documentation is structured into the following sections: -//! - \ref asmjit_base "Base" - Base API (architecture independent). -//! - \ref asmjit_x86 "X86/X64" - X86/X64 API. -//! -//! \section AsmJit_Main_HomePage AsmJit Homepage -//! -//! - https://github.com/kobalicek/asmjit - -// ============================================================================ -// [asmjit_base] -// ============================================================================ +//! Introduction provided by the project page at https://github.com/asmjit/asmjit. //! \defgroup asmjit_base AsmJit Base API (architecture independent) //! -//! \brief Base API. -//! -//! Base API contains all classes that are platform and architecture independent. -//! -//! Code-Generation and Operands -//! ---------------------------- -//! -//! List of the most useful code-generation and operand classes: -//! - \ref asmjit::Assembler - Low-level code-generation. -//! - \ref asmjit::ExternalTool - An external tool that can serialize to `Assembler`: -//! - \ref asmjit::Compiler - High-level code-generation. -//! - \ref asmjit::Runtime - Describes where the code is stored and how it's executed: -//! - \ref asmjit::HostRuntime - Runtime that runs on the host machine: -//! - \ref asmjit::JitRuntime - Runtime designed for JIT code generation and execution. -//! - \ref asmjit::StaticRuntime - Runtime for code that starts at a specific address. -//! - \ref asmjit::Stream - Stream is a list of \ref HLNode objects stored as a double -//! linked list: -//! - \ref asmjit::HLNode - Base node interface: -//! - \ref asmjit::HLInst - Instruction node. -//! - \ref asmjit::HLData - Data node. -//! - \ref asmjit::HLAlign - Align directive node. -//! - \ref asmjit::HLLabel - Label node. -//! - \ref asmjit::HLComment - Comment node. -//! - \ref asmjit::HLSentinel - Sentinel node. -//! - \ref asmjit::HLHint - Instruction node. -//! - \ref asmjit::HLFunc - Function declaration node. -//! - \ref asmjit::HLRet - Function return node. -//! - \ref asmjit::HLCall - Function call node. -//! - \ref asmjit::HLCallArg - Function call argument node. -//! - \ref asmjit::Operand - base class for all operands: -//! - \ref asmjit::Reg - Register operand (`Assembler` only). -//! - \ref asmjit::Var - Variable operand (`Compiler` only). -//! - \ref asmjit::Mem - Memory operand. -//! - \ref asmjit::Imm - Immediate operand. -//! - \ref asmjit::Label - Label operand. -//! -//! The following snippet shows how to setup a basic JIT code generation: -//! -//! ~~~ -//! using namespace asmjit; -//! -//! int main(int argc, char* argv[]) { -//! // JIT runtime is designed for JIT code generation and execution. -//! JitRuntime runtime; -//! -//! // Assembler instance requires to know the runtime to function. -//! X86Assembler a(&runtime); -//! -//! // Compiler (if you indend to use it) requires an assembler instance. -//! X86Compiler c(&a); -//! -//! return 0; -//! } -//! ~~~ -//! -//! Logging and Error Handling -//! -------------------------- -//! -//! AsmJit contains a robust interface that can be used to log the generated code -//! and to handle possible errors. Base logging interface is provided by \ref -//! Logger, which is abstract and can be used as a base for your own logger. -//! AsmJit also implements some trivial logging concepts out of the box to -//! simplify the development. \ref FileLogger logs into a C `FILE*` stream and -//! \ref StringLogger concatenates all log messages into a single string. -//! -//! The following snippet shows how to setup a basic logger and error handler: -//! -//! ~~~ -//! using namespace asmjit; -//! -//! struct MyErrorHandler : public ErrorHandler { -//! virtual bool handleError(Error code, const char* message, void* origin) { -//! printf("Error 0x%0.8X: %s\n", code, message); -//! -//! // True - error handled and code generation can continue. -//! // False - error not handled, code generation should stop. -//! return false; -//! } -//! } -//! -//! int main(int argc, char* argv[]) { -//! JitRuntime runtime; -//! FileLogger logger(stderr); -//! MyErrorHandler eh; -//! -//! X86Assembler a(&runtime); -//! a.setLogger(&logger); -//! a.setErrorHandler(&eh); -//! -//! ... -//! -//! return 0; -//! } -//! ~~~ -//! -//! AsmJit also contains an \ref ErrorHandler, which is an abstract class that -//! can be used to implement your own error handling. It can be associated with -//! \ref Assembler and used to report all errors. It's a very convenient way to -//! be aware of any error that happens during the code generation without making -//! the error handling complicated. -//! -//! List of the most useful logging and error handling classes: -//! - \ref asmjit::Logger - abstract logging interface: -//! - \ref asmjit::FileLogger - A logger that logs to `FILE*`. -//! - \ref asmjit::StringLogger - A logger that concatenates to a single string. -//! - \ref asmjit::ErrorHandler - Easy way to handle \ref Assembler and \ref -//! Compiler -//! errors. -//! -//! Zone Memory Allocator -//! --------------------- -//! -//! Zone memory allocator is an incremental memory allocator that can be used -//! to allocate data of short life-time. It has much better performance -//! characteristics than all other allocators, because the only thing it can do -//! is to increment a pointer and return its previous address. See \ref Zone -//! for more details. -//! -//! The whole AsmJit library is based on zone memory allocation for performance -//! reasons. It has many other benefits, but the performance was the main one -//! when designing the library. -//! -//! POD Containers -//! -------------- -//! -//! POD containers are used by AsmJit to manage its own data structures. The -//! following classes can be used by AsmJit consumers: -//! -//! - \ref asmjit::BitArray - A fixed bit-array that is used internally. -//! - \ref asmjit::PodVector - A simple array-like container for storing -//! POD data. -//! - \ref asmjit::PodList - A single linked list. -//! - \ref asmjit::StringBuilder - A string builder that can append strings -//! and integers. -//! -//! Utility Functions -//! ----------------- -//! -//! Utility functions are implementated static class \ref Utils. There are -//! utilities for bit manipulation and bit counting, utilities to get an -//! integer minimum / maximum and various other helpers required to perform -//! alignment checks and binary casting from float to integer and vice versa. -//! -//! String utilities are also implemented by a static class \ref Utils. They -//! are mostly used by AsmJit internals and not really important to end users. -//! -//! SIMD Utilities -//! -------------- -//! -//! SIMD code generation often requires to embed constants after each function -//! or at the end of the whole code block. AsmJit contains `Vec64`, `Vec128` -//! and `Vec256` classes that can be used to prepare data useful when generating -//! SIMD code. -//! -//! X86/X64 code generators contain member functions `dmm`, `dxmm`, and `dymm`, -//! which can be used to embed 64-bit, 128-bit and 256-bit data structures into -//! the machine code. - -// ============================================================================ -// [asmjit_x86] -// ============================================================================ +//! \brief Backend Neutral API. //! \defgroup asmjit_x86 AsmJit X86/X64 API //! -//! \brief X86/X64 API +//! \brief X86/X64 Backend API. + +//! \defgroup asmjit_arm AsmJit ARM32/ARM64 API //! -//! X86/X64 Code Generation -//! ----------------------- -//! -//! X86/X64 code generation is realized throught: -//! - \ref X86Assembler - low-level code generation. -//! - \ref X86Compiler - high-level code generation. -//! -//! X86/X64 Registers -//! ----------------- -//! -//! There are static objects that represents X86 and X64 registers. They can -//! be used directly (like `eax`, `mm`, `xmm`, ...) or created through -//! these functions: -//! -//! - `asmjit::x86::gpb_lo()` - Get an 8-bit low GPB register. -//! - `asmjit::x86::gpb_hi()` - Get an 8-bit high GPB register. -//! - `asmjit::x86::gpw()` - Get a 16-bit GPW register. -//! - `asmjit::x86::gpd()` - Get a 32-bit GPD register. -//! - `asmjit::x86::gpq()` - Get a 64-bit GPQ Gp register. -//! - `asmjit::x86::gpz()` - Get a 32-bit or 64-bit GPD/GPQ register. -//! - `asmjit::x86::fp()` - Get a 80-bit FPU register. -//! - `asmjit::x86::mm()` - Get a 64-bit MMX register. -//! - `asmjit::x86::xmm()` - Get a 128-bit XMM register. -//! - `asmjit::x86::ymm()` - Get a 256-bit YMM register. -//! - `asmjit::x86::amm()` - Get a 512-bit ZMM register. -//! -//! X86/X64 Addressing -//! ------------------ -//! -//! X86 and x64 architectures contains several addressing modes and most ones -//! are possible with AsmJit library. Memory represents are represented by -//! `BaseMem` class. These functions are used to make operands that represents -//! memory addresses: -//! -//! - `asmjit::x86::ptr()` - Address size not specified. -//! - `asmjit::x86::byte_ptr()` - 1 byte. -//! - `asmjit::x86::word_ptr()` - 2 bytes (GPW size). -//! - `asmjit::x86::dword_ptr()` - 4 bytes (GPD size). -//! - `asmjit::x86::qword_ptr()` - 8 bytes (GPQ/MMX size). -//! - `asmjit::x86::tword_ptr()` - 10 bytes (FPU size). -//! - `asmjit::x86::dqword_ptr()` - 16 bytes (XMM size). -//! - `asmjit::x86::yword_ptr()` - 32 bytes (YMM size). -//! - `asmjit::x86::zword_ptr()` - 64 bytes (ZMM size). -//! -//! Most useful function to make pointer should be `asmjit::x86::ptr()`. It -//! creates a pointer to the target with an unspecified size. Unspecified size -//! works in all intrinsics where are used registers (this means that size is -//! specified by register operand or by instruction itself). For example -//! `asmjit::x86::ptr()` can't be used with `Assembler::inc()` instruction. In -//! this case the size must be specified and it's also reason to differentiate -//! between pointer sizes. -//! -//! X86 and X86 support simple address forms like `[base + displacement]` and -//! also complex address forms like `[base + index * scale + displacement]`. -//! -//! X86/X64 Immediates -//! ------------------ -//! -//! Immediate values are constants thats passed directly after instruction -//! opcode. To create such value use `asmjit::imm()` or `asmjit::imm_u()` -//! methods to create a signed or unsigned immediate value. -//! -//! X86/X64 CPU Information -//! ----------------------- -//! -//! The CPUID instruction can be used to get an exhaustive information about -//! the host X86/X64 processor. AsmJit contains utilities that can get the most -//! important information related to the features supported by the CPU and the -//! host operating system, in addition to host processor name and number of -//! cores. Class `CpuInfo` provides generic information about a host or target -//! processor and contains also a specific X86/X64 information. -//! -//! By default AsmJit queries the CPU information after the library is loaded -//! and the queried information is reused by all instances of `JitRuntime`. -//! The global instance of `CpuInfo` can't be changed, because it will affect -//! the code generation of all `Runtime`s. If there is a need to have a -//! specific CPU information which contains modified features or processor -//! vendor it's possible by creating a new instance of the `CpuInfo` and setting -//! up its members. -//! -//! Cpu detection is important when generating a JIT code that may or may not -//! use certain CPU features. For example there used to be a SSE/SSE2 detection -//! in the past and today there is often AVX/AVX2 detection. -//! -//! The example below shows how to detect a SSE4.1 instruction set: -//! -//! ~~~ -//! using namespace asmjit; -//! -//! const CpuInfo& cpuInfo = CpuInfo::getHost(); -//! -//! if (cpuInfo.hasFeature(CpuInfo::kX86FeatureSSE4_1)) { -//! // Processor has SSE4.1. -//! } -//! else if (cpuInfo.hasFeature(CpuInfo::kX86FeatureSSE2)) { -//! // Processor doesn't have SSE4.1, but has SSE2. -//! } -//! else { -//! // Processor is archaic; it's a wonder AsmJit works here! -//! } -//! ~~~ +//! \brief ARM32/ARM64 Backend API. // [Dependencies] #include "./base.h" -// [ARM/ARM64] -#if defined(ASMJIT_BUILD_ARM32) || defined(ASMJIT_BUILD_ARM64) -#include "./arm.h" -#endif // ASMJIT_BUILD_ARM32 || ASMJIT_BUILD_ARM64 - // [X86/X64] -#if defined(ASMJIT_BUILD_X86) || defined(ASMJIT_BUILD_X64) +#if defined(ASMJIT_BUILD_X86) #include "./x86.h" -#endif // ASMJIT_BUILD_X86 || ASMJIT_BUILD_X64 +#endif // ASMJIT_BUILD_X86 -// [Host] -#include "./host.h" +// [ARM32/ARM64] +#if defined(ASMJIT_BUILD_ARM) +#include "./arm.h" +#endif // ASMJIT_BUILD_ARM // [Guard] #endif // _ASMJIT_ASMJIT_H diff --git a/src/asmjit/asmjit_apibegin.h b/src/asmjit/asmjit_apibegin.h new file mode 100644 index 0000000..58d16db --- /dev/null +++ b/src/asmjit/asmjit_apibegin.h @@ -0,0 +1,117 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Dependencies] +#if !defined(_ASMJIT_BUILD_H) +# include "./build.h" +#endif // !_ASMJIT_BUILD_H + +// [Guard] +#if !defined(ASMJIT_API_SCOPE) +# define ASMJIT_API_SCOPE +#else +# error "[asmjit] api-scope is already active, previous scope not closed by asmjit_apiend.h?" +#endif // ASMJIT_API_SCOPE + +// ============================================================================ +// [C++ Support] +// ============================================================================ + +// [NoExcept] +#if !ASMJIT_CC_HAS_NOEXCEPT && !defined(noexcept) +# define noexcept ASMJIT_NOEXCEPT +# define ASMJIT_UNDEF_NOEXCEPT +#endif // !ASMJIT_CC_HAS_NOEXCEPT && !noexcept + +// [NullPtr] +#if !ASMJIT_CC_HAS_NULLPTR && !defined(nullptr) +# define nullptr NULL +# define ASMJIT_UNDEF_NULLPTR +#endif // !ASMJIT_CC_HAS_NULLPTR && !nullptr + +// [Override] +#if !ASMJIT_CC_HAS_OVERRIDE && !defined(override) +# define override +# define ASMJIT_UNDEF_OVERRIDE +#endif // !ASMJIT_CC_HAS_OVERRIDE && !override + +// ============================================================================ +// [Compiler Support] +// ============================================================================ + +// [Clang] +#if ASMJIT_CC_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wc++11-extensions" +# pragma clang diagnostic ignored "-Wconstant-logical-operand" +# pragma clang diagnostic ignored "-Wunnamed-type-template-args" +#endif // ASMJIT_CC_CLANG + +// [GCC] +#if ASMJIT_CC_GCC +# pragma GCC diagnostic push +#endif // ASMJIT_CC_GCC + +// [MSC] +#if ASMJIT_CC_MSC +# pragma warning(push) +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4201) // nameless struct/union +# pragma warning(disable: 4244) // '+=' : conversion from 'int' to 'x', possible loss of data +# pragma warning(disable: 4251) // struct needs to have dll-interface to be used by clients of struct ... +# pragma warning(disable: 4275) // non dll-interface struct ... used as base for dll-interface struct +# pragma warning(disable: 4355) // this used in base member initializer list +# pragma warning(disable: 4480) // specifying underlying type for enum +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' +# if _MSC_VER < 1900 +# if !defined(vsnprintf) +# define ASMJIT_UNDEF_VSNPRINTF +# define vsnprintf _vsnprintf +# endif // !vsnprintf +# if !defined(snprintf) +# define ASMJIT_UNDEF_SNPRINTF +# define snprintf _snprintf +# endif // !snprintf +# endif +#endif // ASMJIT_CC_MSC + +// ============================================================================ +// [Custom Macros] +// ============================================================================ + +// [ASMJIT_NON...] +#if ASMJIT_CC_HAS_DELETE_FUNCTION +#define ASMJIT_NONCONSTRUCTIBLE(...) \ +private: \ + __VA_ARGS__() = delete; \ + __VA_ARGS__(const __VA_ARGS__& other) = delete; \ + __VA_ARGS__& operator=(const __VA_ARGS__& other) = delete; \ +public: +#define ASMJIT_NONCOPYABLE(...) \ +private: \ + __VA_ARGS__(const __VA_ARGS__& other) = delete; \ + __VA_ARGS__& operator=(const __VA_ARGS__& other) = delete; \ +public: +#else +#define ASMJIT_NONCONSTRUCTIBLE(...) \ +private: \ + inline __VA_ARGS__(); \ + inline __VA_ARGS__(const __VA_ARGS__& other); \ + inline __VA_ARGS__& operator=(const __VA_ARGS__& other); \ +public: +#define ASMJIT_NONCOPYABLE(...) \ +private: \ + inline __VA_ARGS__(const __VA_ARGS__& other); \ + inline __VA_ARGS__& operator=(const __VA_ARGS__& other); \ +public: +#endif // ASMJIT_CC_HAS_DELETE_FUNCTION + +// [ASMJIT_ENUM] +#if defined(_MSC_VER) && _MSC_VER >= 1400 +# define ASMJIT_ENUM(NAME) enum NAME : uint32_t +#else +# define ASMJIT_ENUM(NAME) enum NAME +#endif diff --git a/src/asmjit/asmjit_apiend.h b/src/asmjit/asmjit_apiend.h new file mode 100644 index 0000000..a51630b --- /dev/null +++ b/src/asmjit/asmjit_apiend.h @@ -0,0 +1,74 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#if defined(ASMJIT_API_SCOPE) +# undef ASMJIT_API_SCOPE +#else +# error "[asmjit] api-scope not active, forgot to include asmjit_apibegin.h?" +#endif // ASMJIT_API_SCOPE + +// ============================================================================ +// [C++ Support] +// ============================================================================ + +// [NoExcept] +#if defined(ASMJIT_UNDEF_NOEXCEPT) +# undef noexcept +# undef ASMJIT_UNDEF_NOEXCEPT +#endif // ASMJIT_UNDEF_NOEXCEPT + +// [NullPtr] +#if defined(ASMJIT_UNDEF_NULLPTR) +# undef nullptr +# undef ASMJIT_UNDEF_NULLPTR +#endif // ASMJIT_UNDEF_NULLPTR + +// [Override] +#if defined(ASMJIT_UNDEF_OVERRIDE) +# undef override +# undef ASMJIT_UNDEF_OVERRIDE +#endif // ASMJIT_UNDEF_OVERRIDE + +// ============================================================================ +// [Compiler Support] +// ============================================================================ + +// [Clang] +#if ASMJIT_CC_CLANG +# pragma clang diagnostic pop +#endif // ASMJIT_CC_CLANG + +// [GCC] +#if ASMJIT_CC_GCC +# pragma GCC diagnostic pop +#endif // ASMJIT_CC_GCC + +// [MSC] +#if ASMJIT_CC_MSC +# pragma warning(pop) +# if _MSC_VER < 1900 +# if defined(ASMJIT_UNDEF_VSNPRINTF) +# undef vsnprintf +# undef ASMJIT_UNDEF_VSNPRINTF +# endif // ASMJIT_UNDEF_VSNPRINTF +# if defined(ASMJIT_UNDEF_SNPRINTF) +# undef snprintf +# undef ASMJIT_UNDEF_SNPRINTF +# endif // ASMJIT_UNDEF_SNPRINTF +# endif +#endif // ASMJIT_CC_MSC + +// ============================================================================ +// [Custom Macros] +// ============================================================================ + +// [ASMJIT_NON...] +#undef ASMJIT_NONCONSTRUCTIBLE +#undef ASMJIT_NONCOPYABLE + +// [ASMJIT_ENUM] +#undef ASMJIT_ENUM diff --git a/src/asmjit/build.h b/src/asmjit/asmjit_build.h similarity index 75% rename from src/asmjit/build.h rename to src/asmjit/asmjit_build.h index 00a0be7..2a80f80 100644 --- a/src/asmjit/build.h +++ b/src/asmjit/asmjit_build.h @@ -13,7 +13,7 @@ // ============================================================================ // AsmJit is by default compiled only for a host processor for the purpose of -// JIT code generation. Both Assembler and Compiler code generators are compiled +// JIT code generation. Both Assembler and CodeCompiler emitters are compiled // by default. Preprocessor macros can be used to change the default behavior. // External Config File @@ -48,7 +48,6 @@ // // #define ASMJIT_DEBUG // Define to enable debug-mode. // #define ASMJIT_RELEASE // Define to enable release-mode. -// #define ASMJIT_TRACE // Define to enable tracing. // AsmJit Build Backends // --------------------- @@ -56,33 +55,36 @@ // These definitions control which backends to compile. If none of these is // defined AsmJit will use host architecture by default (for JIT code generation). // -// #define ASMJIT_BUILD_X86 // Define to enable x86 instruction set (32-bit). -// #define ASMJIT_BUILD_X64 // Define to enable x64 instruction set (64-bit). +// #define ASMJIT_BUILD_X86 // Define to enable X86 and X64 code-generation. +// #define ASMJIT_BUILD_ARM // Define to enable ARM32 and ARM64 code-generation. // #define ASMJIT_BUILD_HOST // Define to enable host instruction set. // AsmJit Build Features // --------------------- // // Flags can be defined to disable standard features. These are handy especially -// when building asmjit statically and some features are not needed or unwanted -// (like Compiler). +// when building AsmJit statically and some features are not needed or unwanted +// (like CodeCompiler). // // AsmJit features are enabled by default. -// #define ASMJIT_DISABLE_COMPILER // Disable Compiler (completely). -// #define ASMJIT_DISABLE_LOGGER // Disable Logger (completely). +// #define ASMJIT_DISABLE_COMPILER // Disable CodeCompiler (completely). +// #define ASMJIT_DISABLE_LOGGING // Disable logging and formatting (completely). // #define ASMJIT_DISABLE_TEXT // Disable everything that contains text // // representation (instructions, errors, ...). +// #define ASMJIT_DISABLE_VALIDATION // Disable Validation (completely). // Prevent compile-time errors caused by misconfiguration. -#if defined(ASMJIT_DISABLE_TEXT) && !defined(ASMJIT_DISABLE_LOGGER) -# error "[asmjit] ASMJIT_DISABLE_TEXT requires ASMJIT_DISABLE_LOGGER to be defined." -#endif // ASMJIT_DISABLE_TEXT && !ASMJIT_DISABLE_LOGGER +#if defined(ASMJIT_DISABLE_TEXT) && !defined(ASMJIT_DISABLE_LOGGING) +# error "[asmjit] ASMJIT_DISABLE_TEXT requires ASMJIT_DISABLE_LOGGING to be defined." +#endif // ASMJIT_DISABLE_TEXT && !ASMJIT_DISABLE_LOGGING // Detect ASMJIT_DEBUG and ASMJIT_RELEASE if not forced from outside. -#if !defined(ASMJIT_DEBUG) && !defined(ASMJIT_RELEASE) && !defined(NDEBUG) -# define ASMJIT_DEBUG -#else -# define ASMJIT_RELEASE +#if !defined(ASMJIT_DEBUG) && !defined(ASMJIT_RELEASE) +# if !defined(NDEBUG) +# define ASMJIT_DEBUG +# else +# define ASMJIT_RELEASE +# endif #endif // ASMJIT_EMBED implies ASMJIT_STATIC. @@ -305,26 +307,34 @@ // [@CC{@] // \def ASMJIT_CC_CLANG -// True if the detected C++ compiler is CLANG (contains normalized CLANG version). +// Non-zero if the detected C++ compiler is CLANG (contains normalized CLANG version). // // \def ASMJIT_CC_CODEGEAR -// True if the detected C++ compiler is CODEGEAR or BORLAND (version not normalized). +// Non-zero if the detected C++ compiler is CODEGEAR or BORLAND (version not normalized). +// +// \def ASMJIT_CC_INTEL +// Non-zero if the detected C++ compiler is INTEL (version not normalized). // // \def ASMJIT_CC_GCC -// True if the detected C++ compiler is GCC (contains normalized GCC version). +// Non-zero if the detected C++ compiler is GCC (contains normalized GCC version). // // \def ASMJIT_CC_MSC -// True if the detected C++ compiler is MSC (contains normalized MSC version). +// Non-zero if the detected C++ compiler is MSC (contains normalized MSC version). // // \def ASMJIT_CC_MINGW -// Defined to 32 or 64 in case this is a MINGW, otherwise 0. +// Non-zero if the detected C++ compiler is MINGW32 (set to 32) or MINGW64 (set to 64). -#define ASMJIT_CC_CLANG 0 +#define ASMJIT_CC_CLANG 0 #define ASMJIT_CC_CODEGEAR 0 -#define ASMJIT_CC_GCC 0 -#define ASMJIT_CC_MSC 0 +#define ASMJIT_CC_GCC 0 +#define ASMJIT_CC_INTEL 0 +#define ASMJIT_CC_MSC 0 -#if defined(__CODEGEARC__) +// Intel masquerades as GCC, so check for it first. +#if defined(__INTEL_COMPILER) +# undef ASMJIT_CC_INTEL +# define ASMJIT_CC_INTEL __INTEL_COMPILER +#elif defined(__CODEGEARC__) # undef ASMJIT_CC_CODEGEAR # define ASMJIT_CC_CODEGEAR (__CODEGEARC__) #elif defined(__BORLANDC__) @@ -347,12 +357,27 @@ # error "[asmjit] Unable to detect the C/C++ compiler." #endif -#if ASMJIT_CC_GCC && defined(__GXX_EXPERIMENTAL_CXX0X__) -# define ASMJIT_CC_GCC_CXX0X 1 -#else -# define ASMJIT_CC_GCC_CXX0X 0 +#if ASMJIT_CC_INTEL && (defined(__GNUC__) || defined(__clang__)) +# define ASMJIT_CC_INTEL_COMPAT_MODE 1 +# else +# define ASMJIT_CC_INTEL_COMPAT_MODE 0 #endif +#define ASMJIT_CC_CODEGEAR_EQ(x, y) (ASMJIT_CC_CODEGEAR == (((x) << 8) + (y))) +#define ASMJIT_CC_CODEGEAR_GE(x, y) (ASMJIT_CC_CODEGEAR >= (((x) << 8) + (y))) + +#define ASMJIT_CC_CLANG_EQ(x, y, z) (ASMJIT_CC_CLANG == ((x) * 10000000 + (y) * 100000 + (z))) +#define ASMJIT_CC_CLANG_GE(x, y, z) (ASMJIT_CC_CLANG >= ((x) * 10000000 + (y) * 100000 + (z))) + +#define ASMJIT_CC_GCC_EQ(x, y, z) (ASMJIT_CC_GCC == ((x) * 10000000 + (y) * 100000 + (z))) +#define ASMJIT_CC_GCC_GE(x, y, z) (ASMJIT_CC_GCC >= ((x) * 10000000 + (y) * 100000 + (z))) + +#define ASMJIT_CC_INTEL_EQ(x, y) (ASMJIT_CC_INTEL == (((x) * 100) + (y))) +#define ASMJIT_CC_INTEL_GE(x, y) (ASMJIT_CC_INTEL >= (((x) * 100) + (y))) + +#define ASMJIT_CC_MSC_EQ(x, y, z) (ASMJIT_CC_MSC == ((x) * 10000000 + (y) * 100000 + (z))) +#define ASMJIT_CC_MSC_GE(x, y, z) (ASMJIT_CC_MSC >= ((x) * 10000000 + (y) * 100000 + (z))) + #if defined(__MINGW64__) # define ASMJIT_CC_MINGW 64 #elif defined(__MINGW32__) @@ -361,55 +386,35 @@ # define ASMJIT_CC_MINGW 0 #endif -#define ASMJIT_CC_CODEGEAR_EQ(x, y, z) (ASMJIT_CC_CODEGEAR == (x << 8) + y) -#define ASMJIT_CC_CODEGEAR_GE(x, y, z) (ASMJIT_CC_CODEGEAR >= (x << 8) + y) +#if defined(__cplusplus) +# if __cplusplus >= 201103L +# define ASMJIT_CC_CXX_VERSION __cplusplus +# elif defined(__GXX_EXPERIMENTAL_CXX0X__) || ASMJIT_CC_MSC_GE(18, 0, 0) || ASMJIT_CC_INTEL_GE(14, 0) +# define ASMJIT_CC_CXX_VERSION 201103L +# else +# define ASMJIT_CC_CXX_VERSION 199711L +# endif +#endif -#define ASMJIT_CC_CLANG_EQ(x, y, z) (ASMJIT_CC_CLANG == x * 10000000 + y * 100000 + z) -#define ASMJIT_CC_CLANG_GE(x, y, z) (ASMJIT_CC_CLANG >= x * 10000000 + y * 100000 + z) - -#define ASMJIT_CC_GCC_EQ(x, y, z) (ASMJIT_CC_GCC == x * 10000000 + y * 100000 + z) -#define ASMJIT_CC_GCC_GE(x, y, z) (ASMJIT_CC_GCC >= x * 10000000 + y * 100000 + z) - -#define ASMJIT_CC_MSC_EQ(x, y, z) (ASMJIT_CC_MSC == x * 10000000 + y * 100000 + z) -#define ASMJIT_CC_MSC_GE(x, y, z) (ASMJIT_CC_MSC >= x * 10000000 + y * 100000 + z) +#if !defined(ASMJIT_CC_CXX_VERSION) +# define ASMJIT_CC_CXX_VERSION 0 +#endif // [@CC}@] // [@CC_FEATURES{@] -// \def ASMJIT_CC_HAS_NATIVE_CHAR -// True if the C++ compiler treats char as a native type. -// -// \def ASMJIT_CC_HAS_NATIVE_WCHAR_T -// True if the C++ compiler treats wchar_t as a native type. -// -// \def ASMJIT_CC_HAS_NATIVE_CHAR16_T -// True if the C++ compiler treats char16_t as a native type. -// -// \def ASMJIT_CC_HAS_NATIVE_CHAR32_T -// True if the C++ compiler treats char32_t as a native type. -// -// \def ASMJIT_CC_HAS_OVERRIDE -// True if the C++ compiler supports override keyword. -// -// \def ASMJIT_CC_HAS_NOEXCEPT -// True if the C++ compiler supports noexcept keyword. - #if ASMJIT_CC_CLANG # define ASMJIT_CC_HAS_ATTRIBUTE (1) -# define ASMJIT_CC_HAS_BUILTIN (1) -# define ASMJIT_CC_HAS_DECLSPEC (0) - -# define ASMJIT_CC_HAS_ALIGNAS (__has_extension(__cxx_alignas__)) -# define ASMJIT_CC_HAS_ALIGNOF (__has_extension(__cxx_alignof__)) -# define ASMJIT_CC_HAS_ASSUME (0) -# define ASMJIT_CC_HAS_ASSUME_ALIGNED (0) # define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (__has_attribute(__aligned__)) # define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (__has_attribute(__always_inline__)) # define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (__has_attribute(__noinline__)) # define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (__has_attribute(__noreturn__)) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (__has_attribute(__optimize__)) # define ASMJIT_CC_HAS_BUILTIN_ASSUME (__has_builtin(__builtin_assume)) # define ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED (__has_builtin(__builtin_assume_aligned)) # define ASMJIT_CC_HAS_BUILTIN_EXPECT (__has_builtin(__builtin_expect)) # define ASMJIT_CC_HAS_BUILTIN_UNREACHABLE (__has_builtin(__builtin_unreachable)) +# define ASMJIT_CC_HAS_ALIGNAS (__has_extension(__cxx_alignas__)) +# define ASMJIT_CC_HAS_ALIGNOF (__has_extension(__cxx_alignof__)) # define ASMJIT_CC_HAS_CONSTEXPR (__has_extension(__cxx_constexpr__)) # define ASMJIT_CC_HAS_DECLTYPE (__has_extension(__cxx_decltype__)) # define ASMJIT_CC_HAS_DEFAULT_FUNCTION (__has_extension(__cxx_defaulted_functions__)) @@ -418,30 +423,25 @@ # define ASMJIT_CC_HAS_INITIALIZER_LIST (__has_extension(__cxx_generalized_initializers__)) # define ASMJIT_CC_HAS_LAMBDA (__has_extension(__cxx_lambdas__)) # define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) # define ASMJIT_CC_HAS_NATIVE_CHAR16_T (__has_extension(__cxx_unicode_literals__)) # define ASMJIT_CC_HAS_NATIVE_CHAR32_T (__has_extension(__cxx_unicode_literals__)) -# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) # define ASMJIT_CC_HAS_NOEXCEPT (__has_extension(__cxx_noexcept__)) # define ASMJIT_CC_HAS_NULLPTR (__has_extension(__cxx_nullptr__)) # define ASMJIT_CC_HAS_OVERRIDE (__has_extension(__cxx_override_control__)) # define ASMJIT_CC_HAS_RVALUE (__has_extension(__cxx_rvalue_references__)) # define ASMJIT_CC_HAS_STATIC_ASSERT (__has_extension(__cxx_static_assert__)) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (__has_extension(__cxx_variadic_templates__)) #endif #if ASMJIT_CC_CODEGEAR -# define ASMJIT_CC_HAS_ATTRIBUTE (0) -# define ASMJIT_CC_HAS_BUILTIN (0) -# define ASMJIT_CC_HAS_DECLSPEC (1) - -# define ASMJIT_CC_HAS_ALIGNAS (0) -# define ASMJIT_CC_HAS_ALIGNOF (0) -# define ASMJIT_CC_HAS_ASSUME (0) -# define ASMJIT_CC_HAS_ASSUME_ALIGNED (0) -# define ASMJIT_CC_HAS_CONSTEXPR (0) # define ASMJIT_CC_HAS_DECLSPEC_ALIGN (ASMJIT_CC_CODEGEAR >= 0x0610) # define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (0) # define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (0) # define ASMJIT_CC_HAS_DECLSPEC_NORETURN (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_ALIGNAS (0) +# define ASMJIT_CC_HAS_ALIGNOF (0) +# define ASMJIT_CC_HAS_CONSTEXPR (0) # define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_CODEGEAR >= 0x0610) # define ASMJIT_CC_HAS_DEFAULT_FUNCTION (0) # define ASMJIT_CC_HAS_DELETE_FUNCTION (0) @@ -449,65 +449,94 @@ # define ASMJIT_CC_HAS_INITIALIZER_LIST (0) # define ASMJIT_CC_HAS_LAMBDA (0) # define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) # define ASMJIT_CC_HAS_NATIVE_CHAR16_T (0) # define ASMJIT_CC_HAS_NATIVE_CHAR32_T (0) -# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) # define ASMJIT_CC_HAS_NOEXCEPT (0) # define ASMJIT_CC_HAS_NULLPTR (0) # define ASMJIT_CC_HAS_OVERRIDE (0) # define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_CODEGEAR >= 0x0610) # define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_CODEGEAR >= 0x0610) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (0) #endif #if ASMJIT_CC_GCC # define ASMJIT_CC_HAS_ATTRIBUTE (1) -# define ASMJIT_CC_HAS_BUILTIN (1) -# define ASMJIT_CC_HAS_DECLSPEC (0) - -# define ASMJIT_CC_HAS_ALIGNAS (ASMJIT_CC_GCC_GE(4, 8, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_ALIGNOF (ASMJIT_CC_GCC_GE(4, 8, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_ASSUME (0) -# define ASMJIT_CC_HAS_ASSUME_ALIGNED (0) # define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (ASMJIT_CC_GCC_GE(2, 7, 0)) # define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (ASMJIT_CC_GCC_GE(4, 4, 0) && !ASMJIT_CC_MINGW) # define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (ASMJIT_CC_GCC_GE(3, 4, 0) && !ASMJIT_CC_MINGW) # define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (ASMJIT_CC_GCC_GE(2, 5, 0)) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (ASMJIT_CC_GCC_GE(4, 4, 0)) # define ASMJIT_CC_HAS_BUILTIN_ASSUME (0) # define ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED (ASMJIT_CC_GCC_GE(4, 7, 0)) # define ASMJIT_CC_HAS_BUILTIN_EXPECT (1) -# define ASMJIT_CC_HAS_BUILTIN_UNREACHABLE (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_CONSTEXPR (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_DELETE_FUNCTION (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_FINAL (ASMJIT_CC_GCC_GE(4, 7, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_INITIALIZER_LIST (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_LAMBDA (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_GCC_CXX0X) +# define ASMJIT_CC_HAS_BUILTIN_UNREACHABLE (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_ALIGNAS (ASMJIT_CC_GCC_GE(4, 8, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_ALIGNOF (ASMJIT_CC_GCC_GE(4, 8, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_CONSTEXPR (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_FINAL (ASMJIT_CC_GCC_GE(4, 7, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (ASMJIT_CC_GCC_GE(4, 4, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_LAMBDA (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) # define ASMJIT_CC_HAS_NATIVE_CHAR (1) -# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_GCC_CXX0X) # define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) -# define ASMJIT_CC_HAS_NOEXCEPT (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_NULLPTR (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_OVERRIDE (ASMJIT_CC_GCC_GE(4, 7, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_GCC_CXX0X) -# define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_GCC_CXX0X) +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (ASMJIT_CC_GCC_GE(4, 5, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_NOEXCEPT (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_NULLPTR (ASMJIT_CC_GCC_GE(4, 6, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_OVERRIDE (ASMJIT_CC_GCC_GE(4, 7, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (ASMJIT_CC_GCC_GE(4, 3, 0) && ASMJIT_CC_CXX_VERSION >= 201103L) +#endif + +#if ASMJIT_CC_INTEL +# define ASMJIT_CC_HAS_ATTRIBUTE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_BUILTIN_EXPECT (ASMJIT_CC_INTEL_COMPAT_MODE) +# define ASMJIT_CC_HAS_DECLSPEC_ALIGN (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_DECLSPEC_NORETURN (ASMJIT_CC_INTEL_COMPAT_MODE == 0) +# define ASMJIT_CC_HAS_ASSUME (1) +# define ASMJIT_CC_HAS_ASSUME_ALIGNED (1) +# define ASMJIT_CC_HAS_ALIGNAS (ASMJIT_CC_INTEL >= 1500) +# define ASMJIT_CC_HAS_ALIGNOF (ASMJIT_CC_INTEL >= 1500) +# define ASMJIT_CC_HAS_CONSTEXPR (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_DEFAULT_FUNCTION (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_DELETE_FUNCTION (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_FINAL (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_INITIALIZER_LIST (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_LAMBDA (ASMJIT_CC_INTEL >= 1200) +# define ASMJIT_CC_HAS_NATIVE_CHAR (1) +# define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (ASMJIT_CC_INTEL >= 1400 || (ASMJIT_CC_INTEL_COMPAT_MODE > 0 && ASMJIT_CC_INTEL >= 1206)) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (ASMJIT_CC_INTEL >= 1400 || (ASMJIT_CC_INTEL_COMPAT_MODE > 0 && ASMJIT_CC_INTEL >= 1206)) +# define ASMJIT_CC_HAS_NOEXCEPT (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_NULLPTR (ASMJIT_CC_INTEL >= 1206) +# define ASMJIT_CC_HAS_OVERRIDE (ASMJIT_CC_INTEL >= 1400) +# define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_INTEL >= 1110) +# define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_INTEL >= 1110) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (ASMJIT_CC_INTEL >= 1206) #endif #if ASMJIT_CC_MSC -# define ASMJIT_CC_HAS_ATTRIBUTE (0) -# define ASMJIT_CC_HAS_BUILTIN (0) -# define ASMJIT_CC_HAS_DECLSPEC (1) - -# define ASMJIT_CC_HAS_ALIGNAS (ASMJIT_CC_MSC_GE(19, 0, 0)) -# define ASMJIT_CC_HAS_ALIGNOF (ASMJIT_CC_MSC_GE(19, 0, 0)) -# define ASMJIT_CC_HAS_ASSUME (1) -# define ASMJIT_CC_HAS_ASSUME_ALIGNED (0) -# define ASMJIT_CC_HAS_CONSTEXPR (ASMJIT_CC_MSC_GE(19, 0, 0)) # define ASMJIT_CC_HAS_DECLSPEC_ALIGN (1) # define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (1) # define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (1) # define ASMJIT_CC_HAS_DECLSPEC_NORETURN (1) +# define ASMJIT_CC_HAS_ASSUME (1) +# define ASMJIT_CC_HAS_ASSUME_ALIGNED (0) +# define ASMJIT_CC_HAS_ALIGNAS (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_ALIGNOF (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_CONSTEXPR (ASMJIT_CC_MSC_GE(19, 0, 0)) # define ASMJIT_CC_HAS_DECLTYPE (ASMJIT_CC_MSC_GE(16, 0, 0)) # define ASMJIT_CC_HAS_DEFAULT_FUNCTION (ASMJIT_CC_MSC_GE(18, 0, 0)) # define ASMJIT_CC_HAS_DELETE_FUNCTION (ASMJIT_CC_MSC_GE(18, 0, 0)) @@ -515,38 +544,74 @@ # define ASMJIT_CC_HAS_INITIALIZER_LIST (ASMJIT_CC_MSC_GE(18, 0, 0)) # define ASMJIT_CC_HAS_LAMBDA (ASMJIT_CC_MSC_GE(16, 0, 0)) # define ASMJIT_CC_HAS_NATIVE_CHAR (1) -# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (ASMJIT_CC_MSC_GE(19, 0, 0)) -# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (ASMJIT_CC_MSC_GE(19, 0, 0)) # if defined(_NATIVE_WCHAR_T_DEFINED) # define ASMJIT_CC_HAS_NATIVE_WCHAR_T (1) # else # define ASMJIT_CC_HAS_NATIVE_WCHAR_T (0) # endif +# define ASMJIT_CC_HAS_NATIVE_CHAR16_T (ASMJIT_CC_MSC_GE(19, 0, 0)) +# define ASMJIT_CC_HAS_NATIVE_CHAR32_T (ASMJIT_CC_MSC_GE(19, 0, 0)) # define ASMJIT_CC_HAS_NOEXCEPT (ASMJIT_CC_MSC_GE(19, 0, 0)) # define ASMJIT_CC_HAS_NULLPTR (ASMJIT_CC_MSC_GE(16, 0, 0)) # define ASMJIT_CC_HAS_OVERRIDE (ASMJIT_CC_MSC_GE(14, 0, 0)) # define ASMJIT_CC_HAS_RVALUE (ASMJIT_CC_MSC_GE(16, 0, 0)) # define ASMJIT_CC_HAS_STATIC_ASSERT (ASMJIT_CC_MSC_GE(16, 0, 0)) +# define ASMJIT_CC_HAS_VARIADIC_TEMPLATES (ASMJIT_CC_MSC_GE(18, 0, 0)) #endif -#if !ASMJIT_CC_HAS_ATTRIBUTE +// Fixup some vendor specific keywords. +#if !defined(ASMJIT_CC_HAS_ASSUME) +# define ASMJIT_CC_HAS_ASSUME (0) +#endif +#if !defined(ASMJIT_CC_HAS_ASSUME_ALIGNED) +# define ASMJIT_CC_HAS_ASSUME_ALIGNED (0) +#endif + +// Fixup compilers that don't support '__attribute__'. +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE) +# define ASMJIT_CC_HAS_ATTRIBUTE (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED) # define ASMJIT_CC_HAS_ATTRIBUTE_ALIGNED (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE) # define ASMJIT_CC_HAS_ATTRIBUTE_ALWAYS_INLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE) # define ASMJIT_CC_HAS_ATTRIBUTE_NOINLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_NORETURN) # define ASMJIT_CC_HAS_ATTRIBUTE_NORETURN (0) #endif +#if !defined(ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE) +# define ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE (0) +#endif -#if !ASMJIT_CC_HAS_BUILTIN +// Fixup compilers that don't support '__builtin?'. +#if !defined(ASMJIT_CC_HAS_BUILTIN_ASSUME) # define ASMJIT_CC_HAS_BUILTIN_ASSUME (0) +#endif +#if !defined(ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED) # define ASMJIT_CC_HAS_BUILTIN_ASSUME_ALIGNED (0) +#endif +#if !defined(ASMJIT_CC_HAS_BUILTIN_EXPECT) # define ASMJIT_CC_HAS_BUILTIN_EXPECT (0) +#endif +#if !defined(ASMJIT_CC_HAS_BUILTIN_UNREACHABLE) # define ASMJIT_CC_HAS_BUILTIN_UNREACHABLE (0) #endif -#if !ASMJIT_CC_HAS_DECLSPEC +// Fixup compilers that don't support 'declspec'. +#if !defined(ASMJIT_CC_HAS_DECLSPEC_ALIGN) # define ASMJIT_CC_HAS_DECLSPEC_ALIGN (0) +#endif +#if !defined(ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE) # define ASMJIT_CC_HAS_DECLSPEC_FORCEINLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_DECLSPEC_NOINLINE) # define ASMJIT_CC_HAS_DECLSPEC_NOINLINE (0) +#endif +#if !defined(ASMJIT_CC_HAS_DECLSPEC_NORETURN) # define ASMJIT_CC_HAS_DECLSPEC_NORETURN (0) #endif // [@CC_FEATURES}@] @@ -685,7 +750,7 @@ // [@CC_REGPARM{@] // \def ASMJIT_REGPARM(n) // A custom calling convention which passes n arguments in registers. -#if ASMJIT_ARCH_X86 && (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) +#if ASMJIT_ARCH_X86 && ASMJIT_CC_HAS_ATTRIBUTE # define ASMJIT_REGPARM(n) __attribute__((__regparm__(n))) #else # define ASMJIT_REGPARM(n) @@ -742,7 +807,7 @@ // // \def ASMJIT_UNLIKELY(exp) // Expression exp is likely to be false. -#if ASMJIT_HAS_BUILTIN_EXPECT +#if ASMJIT_CC_HAS_BUILTIN_EXPECT # define ASMJIT_LIKELY(exp) __builtin_expect(!!(exp), 1) # define ASMJIT_UNLIKELY(exp) __builtin_expect(!!(exp), 0) #else @@ -810,13 +875,9 @@ typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; # endif # endif -# define ASMJIT_INT64_C(x) (x##i64) -# define ASMJIT_UINT64_C(x) (x##ui64) #else # include # include -# define ASMJIT_INT64_C(x) (x##ll) -# define ASMJIT_UINT64_C(x) (x##ull) #endif // [@STDTYPES}@] @@ -824,12 +885,14 @@ typedef unsigned __int64 uint64_t; // [asmjit::Build - Dependencies] // ============================================================================ -#include #include #include #include #include +#include +#include + #if ASMJIT_OS_POSIX # include #endif // ASMJIT_OS_POSIX @@ -840,88 +903,46 @@ typedef unsigned __int64 uint64_t; // Build host architecture if no architecture is selected. #if !defined(ASMJIT_BUILD_HOST) && \ - !defined(ASMJIT_BUILD_X86) && \ - !defined(ASMJIT_BUILD_X64) + !defined(ASMJIT_BUILD_X86) && \ + !defined(ASMJIT_BUILD_ARM) # define ASMJIT_BUILD_HOST #endif -// Autodetect host architecture if enabled. +// Detect host architecture if building only for host. #if defined(ASMJIT_BUILD_HOST) -# if ASMJIT_ARCH_X86 && !defined(ASMJIT_BUILD_X86) +# if (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) && !defined(ASMJIT_BUILD_X86) # define ASMJIT_BUILD_X86 -# endif // ASMJIT_ARCH_X86 && !ASMJIT_BUILD_X86 -# if ASMJIT_ARCH_X64 && !defined(ASMJIT_BUILD_X64) -# define ASMJIT_BUILD_X64 -# endif // ASMJIT_ARCH_X64 && !ASMJIT_BUILD_X64 +# endif // ASMJIT_ARCH_X86 #endif // ASMJIT_BUILD_HOST -#if defined(_MSC_VER) && _MSC_VER >= 1400 -# define ASMJIT_ENUM(name) enum name : uint32_t +#if ASMJIT_CC_MSC +# define ASMJIT_UINT64_C(x) x##ui64 #else -# define ASMJIT_ENUM(name) enum name +# define ASMJIT_UINT64_C(x) x##ull #endif #if ASMJIT_ARCH_LE -# define _ASMJIT_ARCH_INDEX(total, index) (index) +# define ASMJIT_PACK32_4x8(A, B, C, D) ((A) + ((B) << 8) + ((C) << 16) + ((D) << 24)) #else -# define _ASMJIT_ARCH_INDEX(total, index) ((total) - 1 - (index)) +# define ASMJIT_PACK32_4x8(A, B, C, D) ((D) + ((C) << 8) + ((B) << 16) + ((A) << 24)) #endif -#if !defined(ASMJIT_ALLOC) && !defined(ASMJIT_REALLOC) && !defined(ASMJIT_FREE) -# define ASMJIT_ALLOC(size) ::malloc(size) -# define ASMJIT_REALLOC(ptr, size) ::realloc(ptr, size) -# define ASMJIT_FREE(ptr) ::free(ptr) -#else -# if !defined(ASMJIT_ALLOC) || !defined(ASMJIT_REALLOC) || !defined(ASMJIT_FREE) -# error "[asmjit] You must provide ASMJIT_ALLOC, ASMJIT_REALLOC and ASMJIT_FREE." -# endif -#endif // !ASMJIT_ALLOC && !ASMJIT_REALLOC && !ASMJIT_FREE - -#define ASMJIT_NO_COPY(...) \ -private: \ - ASMJIT_INLINE __VA_ARGS__(const __VA_ARGS__& other) ASMJIT_NOEXCEPT; \ - ASMJIT_INLINE __VA_ARGS__& operator=(const __VA_ARGS__& other) ASMJIT_NOEXCEPT; \ -public: - -// ============================================================================ -// [asmjit::Build - Relative Path] -// ============================================================================ - -namespace asmjit { -namespace DebugUtils { - -// Workaround that is used to convert an absolute path to a relative one at -// a C macro level, used by asserts and tracing. This workaround is needed -// as some build systems always convert the source code files to use absolute -// paths. Please note that if absolute paths are used this doesn't remove them -// from the compiled binary and can be still considered a security risk. -enum { - kSourceRelativePathOffset = int(sizeof(__FILE__) - sizeof("asmjit/build.h")) -}; - -// ASMJIT_TRACE is only used by sources and private headers. It's safe to make -// it unavailable outside of AsmJit. +// Internal macros that are only used when building AsmJit itself. #if defined(ASMJIT_EXPORTS) -static inline int disabledTrace(...) { return 0; } -# if defined(ASMJIT_TRACE) -# define ASMJIT_TSEC(section) section -# define ASMJIT_TLOG ::printf +# if !defined(ASMJIT_DEBUG) && ASMJIT_CC_HAS_ATTRIBUTE_OPTIMIZE +# define ASMJIT_FAVOR_SIZE __attribute__((__optimize__("Os"))) # else -# define ASMJIT_TSEC(section) ASMJIT_NOP -# define ASMJIT_TLOG 0 && ::asmjit::DebugUtils::disabledTrace -# endif // ASMJIT_TRACE +# define ASMJIT_FAVOR_SIZE +# endif #endif // ASMJIT_EXPORTS -} // DebugUtils namespace -} // asmjit namespace - // ============================================================================ // [asmjit::Build - Test] // ============================================================================ // Include a unit testing package if this is a `asmjit_test` build. #if defined(ASMJIT_TEST) -# include "../test/broken.h" +# include "../../test/broken.h" #endif // ASMJIT_TEST // [Guard] diff --git a/src/asmjit/base.h b/src/asmjit/base.h index 7d8661e..8d4a916 100644 --- a/src/asmjit/base.h +++ b/src/asmjit/base.h @@ -9,27 +9,25 @@ #define _ASMJIT_BASE_H // [Dependencies] -#include "./build.h" - +#include "./base/arch.h" #include "./base/assembler.h" +#include "./base/codebuilder.h" +#include "./base/codecompiler.h" +#include "./base/codeemitter.h" +#include "./base/codeholder.h" #include "./base/constpool.h" -#include "./base/containers.h" #include "./base/cpuinfo.h" +#include "./base/func.h" #include "./base/globals.h" -#include "./base/logger.h" +#include "./base/logging.h" #include "./base/operand.h" -#include "./base/podvector.h" +#include "./base/osutils.h" #include "./base/runtime.h" +#include "./base/simdtypes.h" +#include "./base/string.h" #include "./base/utils.h" -#include "./base/vectypes.h" #include "./base/vmem.h" #include "./base/zone.h" -#if !defined(ASMJIT_DISABLE_COMPILER) -#include "./base/compiler.h" -#include "./base/compilerfunc.h" -#include "./base/hlstream.h" -#endif // !ASMJIT_DISABLE_COMPILER - // [Guard] #endif // _ASMJIT_BASE_H diff --git a/src/asmjit/base/arch.cpp b/src/asmjit/base/arch.cpp new file mode 100644 index 0000000..2e849c6 --- /dev/null +++ b/src/asmjit/base/arch.cpp @@ -0,0 +1,161 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/arch.h" + +#if defined(ASMJIT_BUILD_X86) +#include "../x86/x86operand.h" +#endif // ASMJIT_BUILD_X86 + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::ArchInfo] +// ============================================================================ + +static const uint32_t archInfoTable[] = { + // <-------------+---------------------+-----------------------+-------+ + // | Type | SubType | GPInfo| + // <-------------+---------------------+-----------------------+-------+ + ASMJIT_PACK32_4x8(ArchInfo::kTypeNone , ArchInfo::kSubTypeNone, 0, 0), + ASMJIT_PACK32_4x8(ArchInfo::kTypeX86 , ArchInfo::kSubTypeNone, 4, 8), + ASMJIT_PACK32_4x8(ArchInfo::kTypeX64 , ArchInfo::kSubTypeNone, 8, 16), + ASMJIT_PACK32_4x8(ArchInfo::kTypeX32 , ArchInfo::kSubTypeNone, 8, 16), + ASMJIT_PACK32_4x8(ArchInfo::kTypeA32 , ArchInfo::kSubTypeNone, 4, 16), + ASMJIT_PACK32_4x8(ArchInfo::kTypeA64 , ArchInfo::kSubTypeNone, 8, 32) +}; + +ASMJIT_FAVOR_SIZE void ArchInfo::init(uint32_t type, uint32_t subType) noexcept { + uint32_t index = type < ASMJIT_ARRAY_SIZE(archInfoTable) ? type : uint32_t(0); + + // Make sure the `archInfoTable` array is correctly indexed. + _signature = archInfoTable[index]; + ASMJIT_ASSERT(_type == index); + + // Even if the architecture is not known we setup its type and sub-type, + // however, such architecture is not really useful. + _type = type; + _subType = subType; +} + +// ============================================================================ +// [asmjit::ArchUtils] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegInfo(uint32_t archType, uint32_t& typeIdInOut, RegInfo& regInfo) noexcept { + uint32_t typeId = typeIdInOut; + + // Zero the signature so it's clear in case that typeId is not invalid. + regInfo._signature = 0; + +#if defined(ASMJIT_BUILD_X86) + if (ArchInfo::isX86Family(archType)) { + // Passed RegType instead of TypeId? + if (typeId <= Reg::kRegMax) + typeId = x86OpData.archRegs.regTypeToTypeId[typeId]; + + if (ASMJIT_UNLIKELY(!TypeId::isValid(typeId))) + return DebugUtils::errored(kErrorInvalidTypeId); + + // First normalize architecture dependent types. + if (TypeId::isAbstract(typeId)) { + if (typeId == TypeId::kIntPtr) + typeId = (archType == ArchInfo::kTypeX86) ? TypeId::kI32 : TypeId::kI64; + else + typeId = (archType == ArchInfo::kTypeX86) ? TypeId::kU32 : TypeId::kU64; + } + + // Type size helps to construct all kinds of registers. If the size is zero + // then the TypeId is invalid. + uint32_t size = TypeId::sizeOf(typeId); + if (ASMJIT_UNLIKELY(!size)) + return DebugUtils::errored(kErrorInvalidTypeId); + + if (ASMJIT_UNLIKELY(typeId == TypeId::kF80)) + return DebugUtils::errored(kErrorInvalidUseOfF80); + + uint32_t regType = 0; + + switch (typeId) { + case TypeId::kI8: + case TypeId::kU8: + regType = X86Reg::kRegGpbLo; + break; + + case TypeId::kI16: + case TypeId::kU16: + regType = X86Reg::kRegGpw; + break; + + case TypeId::kI32: + case TypeId::kU32: + regType = X86Reg::kRegGpd; + break; + + case TypeId::kI64: + case TypeId::kU64: + if (archType == ArchInfo::kTypeX86) + return DebugUtils::errored(kErrorInvalidUseOfGpq); + + regType = X86Reg::kRegGpq; + break; + + // F32 and F64 are always promoted to use vector registers. + case TypeId::kF32: + typeId = TypeId::kF32x1; + regType = X86Reg::kRegXmm; + break; + + case TypeId::kF64: + typeId = TypeId::kF64x1; + regType = X86Reg::kRegXmm; + break; + + // Mask registers {k}. + case TypeId::kMask8: + case TypeId::kMask16: + case TypeId::kMask32: + case TypeId::kMask64: + regType = X86Reg::kRegK; + break; + + // MMX registers. + case TypeId::kMmx32: + case TypeId::kMmx64: + regType = X86Reg::kRegMm; + break; + + // XMM|YMM|ZMM registers. + default: + if (size <= 16) + regType = X86Reg::kRegXmm; + else if (size == 32) + regType = X86Reg::kRegYmm; + else + regType = X86Reg::kRegZmm; + break; + } + + typeIdInOut = typeId; + regInfo._signature = x86OpData.archRegs.regInfo[regType].getSignature(); + return kErrorOk; + } +#endif // ASMJIT_BUILD_X86 + + return DebugUtils::errored(kErrorInvalidArch); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/arch.h b/src/asmjit/base/arch.h new file mode 100644 index 0000000..e03c6af --- /dev/null +++ b/src/asmjit/base/arch.h @@ -0,0 +1,199 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_ARCH_H +#define _ASMJIT_BASE_ARCH_H + +// [Dependencies] +#include "../base/globals.h" +#include "../base/operand.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::ArchInfo] +// ============================================================================ + +class ArchInfo { +public: + //! Architecture type. + ASMJIT_ENUM(Type) { + kTypeNone = 0, //!< No/Unknown architecture. + + // X86 architectures. + kTypeX86 = 1, //!< X86 architecture (32-bit). + kTypeX64 = 2, //!< X64 architecture (64-bit) (AMD64). + kTypeX32 = 3, //!< X32 architecture (DEAD-END). + + // ARM architectures. + kTypeA32 = 4, //!< ARM 32-bit architecture (AArch32/ARM/THUMB). + kTypeA64 = 5, //!< ARM 64-bit architecture (AArch64). + + //! Architecture detected at compile-time (architecture of the host). + kTypeHost = ASMJIT_ARCH_X86 ? kTypeX86 : + ASMJIT_ARCH_X64 ? kTypeX64 : + ASMJIT_ARCH_ARM32 ? kTypeA32 : + ASMJIT_ARCH_ARM64 ? kTypeA64 : kTypeNone + }; + + //! Architecture sub-type or execution mode. + ASMJIT_ENUM(SubType) { + kSubTypeNone = 0, //!< Default mode (or no specific mode). + + // X86 sub-types. + kSubTypeX86_AVX = 1, //!< Code generation uses AVX by default (VEC instructions). + kSubTypeX86_AVX2 = 2, //!< Code generation uses AVX2 by default (VEC instructions). + kSubTypeX86_AVX512 = 3, //!< Code generation uses AVX-512F by default (+32 vector regs). + kSubTypeX86_AVX512VL = 4, //!< Code generation uses AVX-512F-VL by default (+VL extensions). + + // ARM sub-types. + kSubTypeA32_Thumb = 8, //!< THUMB|THUMB2 sub-type (only ARM in 32-bit mode). + +#if (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) && defined(__AVX512VL__) + kSubTypeHost = kSubTypeX86_AVX512VL +#elif (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) && defined(__AVX512F__) + kSubTypeHost = kSubTypeX86_AVX512 +#elif (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) && defined(__AVX2__) + kSubTypeHost = kSubTypeX86_AVX2 +#elif (ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64) && defined(__AVX__) + kSubTypeHost = kSubTypeX86_AVX +#elif (ASMJIT_ARCH_ARM32) && (defined(_M_ARMT) || defined(__thumb__) || defined(__thumb2__)) + kSubTypeHost = kSubTypeA32_Thumb +#else + kSubTypeHost = 0 +#endif + }; + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE bool isX86Family(uint32_t archType) noexcept { return archType >= kTypeX86 && archType <= kTypeX32; } + static ASMJIT_INLINE bool isArmFamily(uint32_t archType) noexcept { return archType >= kTypeA32 && archType <= kTypeA64; } + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ArchInfo() noexcept : _signature(0) {} + ASMJIT_INLINE ArchInfo(const ArchInfo& other) noexcept : _signature(other._signature) {} + explicit ASMJIT_INLINE ArchInfo(uint32_t type, uint32_t subType = kSubTypeNone) noexcept { init(type, subType); } + + ASMJIT_INLINE static ArchInfo host() noexcept { return ArchInfo(kTypeHost, kSubTypeHost); } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { return _type != kTypeNone; } + + ASMJIT_API void init(uint32_t type, uint32_t subType = kSubTypeNone) noexcept; + ASMJIT_INLINE void reset() noexcept { _signature = 0; } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get if the architecture is 32-bit. + ASMJIT_INLINE bool is32Bit() const noexcept { return _gpSize == 4; } + //! Get if the architecture is 64-bit. + ASMJIT_INLINE bool is64Bit() const noexcept { return _gpSize == 8; } + + //! Get architecture type, see \ref Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + + //! Get architecture sub-type, see \ref SubType. + //! + //! X86 & X64 + //! --------- + //! + //! Architecture subtype describe the highest instruction-set level that can + //! be used. + //! + //! ARM32 + //! ----- + //! + //! Architecture mode means the instruction encoding to be used when generating + //! machine code, thus mode can be used to force generation of THUMB and THUMB2 + //! encoding or regular ARM encoding. + //! + //! ARM64 + //! ----- + //! + //! No meaning yet. + ASMJIT_INLINE uint32_t getSubType() const noexcept { return _subType; } + + //! Get if the architecture is X86, X64, or X32. + ASMJIT_INLINE bool isX86Family() const noexcept { return isX86Family(_type); } + //! Get if the architecture is ARM32 or ARM64. + ASMJIT_INLINE bool isArmFamily() const noexcept { return isArmFamily(_type); } + + //! Get a size of a general-purpose register. + ASMJIT_INLINE uint32_t getGpSize() const noexcept { return _gpSize; } + //! Get number of general-purpose registers. + ASMJIT_INLINE uint32_t getGpCount() const noexcept { return _gpCount; } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE const ArchInfo& operator=(const ArchInfo& other) noexcept { _signature = other._signature; return *this; } + ASMJIT_INLINE bool operator==(const ArchInfo& other) const noexcept { return _signature == other._signature; } + ASMJIT_INLINE bool operator!=(const ArchInfo& other) const noexcept { return _signature != other._signature; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + union { + struct { + uint8_t _type; //!< Architecture type. + uint8_t _subType; //!< Architecture sub-type. + uint8_t _gpSize; //!< Default size of a general purpose register. + uint8_t _gpCount; //!< Count of all general purpose registers. + }; + uint32_t _signature; //!< Architecture signature (32-bit int). + }; +}; + +// ============================================================================ +// [asmjit::ArchRegs] +// ============================================================================ + +//! Information about all architecture registers. +struct ArchRegs { + //! Register information and signatures indexed by \ref Reg::Type. + RegInfo regInfo[Reg::kRegMax + 1]; + //! Count (maximum) of registers per \ref Reg::Type. + uint8_t regCount[Reg::kRegMax + 1]; + //! Converts RegType to TypeId, see \ref TypeId::Id. + uint8_t regTypeToTypeId[Reg::kRegMax + 1]; +}; + +// ============================================================================ +// [asmjit::ArchUtils] +// ============================================================================ + +struct ArchUtils { + ASMJIT_API static Error typeIdToRegInfo(uint32_t archType, uint32_t& typeIdInOut, RegInfo& regInfo) noexcept; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_ARCH_H diff --git a/src/asmjit/base/assembler.cpp b/src/asmjit/base/assembler.cpp index 9162ec9..014885a 100644 --- a/src/asmjit/base/assembler.cpp +++ b/src/asmjit/base/assembler.cpp @@ -9,495 +9,379 @@ // [Dependencies] #include "../base/assembler.h" +#include "../base/constpool.h" #include "../base/utils.h" #include "../base/vmem.h" -#include // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { -// ============================================================================ -// [asmjit::ErrorHandler] -// ============================================================================ - -ErrorHandler::ErrorHandler() noexcept {} -ErrorHandler::~ErrorHandler() noexcept {} - -ErrorHandler* ErrorHandler::addRef() const noexcept { - return const_cast(this); -} -void ErrorHandler::release() noexcept {} - -// ============================================================================ -// [asmjit::ExternalTool] -// ============================================================================ - -ExternalTool::ExternalTool() noexcept - : _assembler(nullptr), - _exId(0), - _arch(kArchNone), - _regSize(0), - _finalized(false), - _reserved(0), - _lastError(kErrorNotInitialized) {} -ExternalTool::~ExternalTool() noexcept {} - -Error ExternalTool::setLastError(Error error, const char* message) noexcept { - // Special case, reset the last error the error is `kErrorOk`. - if (error == kErrorOk) { - _lastError = kErrorOk; - return kErrorOk; - } - - // Don't do anything if the code-generator doesn't have associated assembler. - Assembler* assembler = getAssembler(); - if (assembler == nullptr) - return error; - - if (message == nullptr) - message = DebugUtils::errorAsString(error); - - // Logging is skipped if the error is handled by `ErrorHandler. - ErrorHandler* eh = assembler->getErrorHandler(); - ASMJIT_TLOG("[ERROR (ExternalTool)] %s (0x%0.8u) %s\n", message, - static_cast(error), - !eh ? "(Possibly unhandled?)" : ""); - - if (eh != nullptr && eh->handleError(error, message, this)) - return error; - -#if !defined(ASMJIT_DISABLE_LOGGER) - Logger* logger = assembler->getLogger(); - if (logger != nullptr) - logger->logFormat(Logger::kStyleComment, - "*** ERROR (ExternalTool): %s (0x%0.8u).\n", message, - static_cast(error)); -#endif // !ASMJIT_DISABLE_LOGGER - - // The handler->handleError() function may throw an exception or longjmp() - // to terminate the execution of `setLastError()`. This is the reason why - // we have delayed changing the `_error` member until now. - _lastError = error; - return error; -} - // ============================================================================ // [asmjit::Assembler - Construction / Destruction] // ============================================================================ -Assembler::Assembler(Runtime* runtime) noexcept - : _runtime(runtime), - _logger(nullptr), - _errorHandler(nullptr), - _arch(kArchNone), - _regSize(0), - _reserved(0), - _asmOptions(0), - _instOptions(0), - _lastError(runtime ? kErrorOk : kErrorNotInitialized), - _exIdGenerator(0), - _exCountAttached(0), - _zoneAllocator(8192 - Zone::kZoneOverhead), - _buffer(nullptr), - _end(nullptr), - _cursor(nullptr), - _trampolinesSize(0), - _comment(nullptr), - _unusedLinks(nullptr), - _labels(), - _relocations() {} +Assembler::Assembler() noexcept + : CodeEmitter(kTypeAssembler), + _section(nullptr), + _bufferData(nullptr), + _bufferEnd(nullptr), + _bufferPtr(nullptr) {} Assembler::~Assembler() noexcept { - reset(true); - - if (_errorHandler != nullptr) - _errorHandler->release(); + if (_code) sync(); } // ============================================================================ -// [asmjit::Assembler - Reset] +// [asmjit::Assembler - Events] // ============================================================================ -void Assembler::reset(bool releaseMemory) noexcept { - _asmOptions = 0; - _instOptions = 0; - _lastError = kErrorOk; - _exIdGenerator = 0; - _exCountAttached = 0; +Error Assembler::onAttach(CodeHolder* code) noexcept { + // Attach to the end of the .text section. + _section = code->_sections[0]; + uint8_t* p = _section->_buffer._data; - _zoneAllocator.reset(releaseMemory); + _bufferData = p; + _bufferEnd = p + _section->_buffer._capacity; + _bufferPtr = p + _section->_buffer._length; + return Base::onAttach(code); +} - if (releaseMemory && _buffer != nullptr) { - ASMJIT_FREE(_buffer); - _buffer = nullptr; - _end = nullptr; - } - - _cursor = _buffer; - _trampolinesSize = 0; - - _comment = nullptr; - _unusedLinks = nullptr; - - _sections.reset(releaseMemory); - _labels.reset(releaseMemory); - _relocations.reset(releaseMemory); +Error Assembler::onDetach(CodeHolder* code) noexcept { + _section = nullptr; + _bufferData = nullptr; + _bufferEnd = nullptr; + _bufferPtr = nullptr; + return Base::onDetach(code); } // ============================================================================ -// [asmjit::Assembler - Logging & Error Handling] +// [asmjit::Assembler - Sync] // ============================================================================ -Error Assembler::setLastError(Error error, const char* message) noexcept { - // Special case, reset the last error the error is `kErrorOk`. - if (error == kErrorOk) { - _lastError = kErrorOk; - return kErrorOk; - } +void Assembler::sync() noexcept { + ASMJIT_ASSERT(_code != nullptr); // Only called by CodeHolder, so we must be attached. + ASMJIT_ASSERT(_section != nullptr); // One section must always be active, no matter what. + ASMJIT_ASSERT(_bufferData == _section->_buffer._data); // `_bufferStart` is a shortcut to `_section->buffer.data`. - if (message == nullptr) - message = DebugUtils::errorAsString(error); - - // Logging is skipped if the error is handled by `ErrorHandler`. - ErrorHandler* eh = _errorHandler; - ASMJIT_TLOG("[ERROR (Assembler)] %s (0x%0.8u) %s\n", message, - static_cast(error), - !eh ? "(Possibly unhandled?)" : ""); - - if (eh != nullptr && eh->handleError(error, message, this)) - return error; - -#if !defined(ASMJIT_DISABLE_LOGGER) - Logger* logger = _logger; - if (logger != nullptr) - logger->logFormat(Logger::kStyleComment, - "*** ERROR (Assembler): %s (0x%0.8u).\n", message, - static_cast(error)); -#endif // !ASMJIT_DISABLE_LOGGER - - // The handler->handleError() function may throw an exception or longjmp() - // to terminate the execution of `setLastError()`. This is the reason why - // we have delayed changing the `_error` member until now. - _lastError = error; - return error; + // Update only if the current offset is greater than the section length. + size_t offset = (size_t)(_bufferPtr - _bufferData); + if (_section->getBuffer().getLength() < offset) + _section->_buffer._length = offset; } -Error Assembler::setErrorHandler(ErrorHandler* handler) noexcept { - ErrorHandler* oldHandler = _errorHandler; +// ============================================================================ +// [asmjit::Assembler - Code-Buffer] +// ============================================================================ - if (oldHandler != nullptr) - oldHandler->release(); +Error Assembler::setOffset(size_t offset) { + if (_lastError) return _lastError; - if (handler != nullptr) - handler = handler->addRef(); + size_t length = std::max(_section->getBuffer().getLength(), getOffset()); + if (ASMJIT_UNLIKELY(offset > length)) + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); - _errorHandler = handler; + // If the `Assembler` generated any code the `_bufferPtr` may be higher than + // the section length stored in `CodeHolder` as it doesn't update it each + // time it generates machine code. This is the same as calling `sync()`. + if (_section->_buffer._length < length) + _section->_buffer._length = length; + + _bufferPtr = _bufferData + offset; return kErrorOk; } // ============================================================================ -// [asmjit::Assembler - Buffer] +// [asmjit::Assembler - Comment] // ============================================================================ -Error Assembler::_grow(size_t n) noexcept { - size_t capacity = getCapacity(); - size_t after = getOffset() + n; +Error Assembler::comment(const char* s, size_t len) { + if (_lastError) return _lastError; - // Overflow. - if (n > IntTraits::maxValue() - capacity) - return setLastError(kErrorNoHeapMemory); - - // Grow is called when allocation is needed, so it shouldn't happen, but on - // the other hand it is simple to catch and it's not an error. - if (after <= capacity) +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) { + Logger* logger = _code->getLogger(); + logger->log(s, len); + logger->log("\n", 1); return kErrorOk; - - if (capacity < kMemAllocOverhead) - capacity = kMemAllocOverhead; - else - capacity += kMemAllocOverhead; - - do { - size_t oldCapacity = capacity; - - if (capacity < kMemAllocGrowMax) - capacity *= 2; - else - capacity += kMemAllocGrowMax; - - // Overflow. - if (oldCapacity > capacity) - return setLastError(kErrorNoHeapMemory); - } while (capacity - kMemAllocOverhead < after); - - capacity -= kMemAllocOverhead; - return _reserve(capacity); -} - -Error Assembler::_reserve(size_t n) noexcept { - size_t capacity = getCapacity(); - if (n <= capacity) - return kErrorOk; - - uint8_t* newBuffer; - if (_buffer == nullptr) - newBuffer = static_cast(ASMJIT_ALLOC(n)); - else - newBuffer = static_cast(ASMJIT_REALLOC(_buffer, n)); - - if (newBuffer == nullptr) - return setLastError(kErrorNoHeapMemory); - - size_t offset = getOffset(); - - _buffer = newBuffer; - _end = _buffer + n; - _cursor = newBuffer + offset; + } +#else + ASMJIT_UNUSED(s); + ASMJIT_UNUSED(len); +#endif return kErrorOk; } // ============================================================================ -// [asmjit::Assembler - Label] +// [asmjit::Assembler - Building Blocks] // ============================================================================ -Error Assembler::_newLabelId() noexcept { - LabelData* data = _zoneAllocator.allocT(); - - data->offset = -1; - data->links = nullptr; - data->exId = 0; - data->exData = nullptr; - - uint32_t id = OperandUtil::makeLabelId(static_cast(_labels.getLength())); - Error error = _labels.append(data); - - if (error != kErrorOk) { - setLastError(kErrorNoHeapMemory); - return kInvalidValue; +Label Assembler::newLabel() { + uint32_t id = 0; + if (!_lastError) { + ASMJIT_ASSERT(_code != nullptr); + Error err = _code->newLabelId(id); + if (ASMJIT_UNLIKELY(err)) setLastError(err); } - - return id; + return Label(id); } -LabelLink* Assembler::_newLabelLink() noexcept { - LabelLink* link = _unusedLinks; - - if (link) { - _unusedLinks = link->prev; +Label Assembler::newNamedLabel(const char* name, size_t nameLength, uint32_t type, uint32_t parentId) { + uint32_t id = 0; + if (!_lastError) { + ASMJIT_ASSERT(_code != nullptr); + Error err = _code->newNamedLabelId(id, name, nameLength, type, parentId); + if (ASMJIT_UNLIKELY(err)) setLastError(err); } - else { - link = _zoneAllocator.allocT(); - if (link == nullptr) - return nullptr; - } - - link->prev = nullptr; - link->offset = 0; - link->displacement = 0; - link->relocId = -1; - - return link; + return Label(id); } -Error Assembler::bind(const Label& label) noexcept { - // Get label data based on label id. - uint32_t index = label.getId(); - LabelData* data = getLabelData(index); +Error Assembler::bind(const Label& label) { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + LabelEntry* le = _code->getLabelEntry(label); + if (ASMJIT_UNLIKELY(!le)) + return setLastError(DebugUtils::errored(kErrorInvalidLabel)); // Label can be bound only once. - if (data->offset != -1) - return setLastError(kErrorLabelAlreadyBound); + if (ASMJIT_UNLIKELY(le->isBound())) + return setLastError(DebugUtils::errored(kErrorLabelAlreadyBound)); -#if !defined(ASMJIT_DISABLE_LOGGER) - if (_logger) { +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) { StringBuilderTmp<256> sb; - sb.setFormat("L%u:", index); + sb.setFormat("L%u:", Operand::unpackId(label.getId())); size_t binSize = 0; - if (!_logger->hasOption(Logger::kOptionBinaryForm)) - binSize = kInvalidIndex; + if (!_code->_logger->hasOption(Logger::kOptionBinaryForm)) + binSize = Globals::kInvalidIndex; - LogUtil::formatLine(sb, nullptr, binSize, 0, 0, _comment); - _logger->logString(Logger::kStyleLabel, sb.getData(), sb.getLength()); + Logging::formatLine(sb, nullptr, binSize, 0, 0, getInlineComment()); + _code->_logger->log(sb.getData(), sb.getLength()); } -#endif // !ASMJIT_DISABLE_LOGGER +#endif // !ASMJIT_DISABLE_LOGGING - Error error = kErrorOk; + Error err = kErrorOk; size_t pos = getOffset(); - LabelLink* link = data->links; + LabelLink* link = le->_links; LabelLink* prev = nullptr; while (link) { intptr_t offset = link->offset; + uint32_t relocId = link->relocId; - if (link->relocId != -1) { - // Handle RelocData - We have to update RelocData information instead of - // patching the displacement in LabelData. - _relocations[link->relocId].data += static_cast(pos); + if (relocId != RelocEntry::kInvalidId) { + // Adjust relocation data. + RelocEntry* re = _code->_relocations[relocId]; + re->_data += static_cast(pos); } else { // Not using relocId, this means that we are overwriting a real - // displacement in the binary stream. + // displacement in the CodeBuffer. int32_t patchedValue = static_cast( - static_cast(pos) - offset + link->displacement); + static_cast(pos) - offset + link->rel); // Size of the value we are going to patch. Only BYTE/DWORD is allowed. - uint32_t size = readU8At(offset); - ASMJIT_ASSERT(size == 1 || size == 4); - - if (size == 4) { - writeI32At(offset, patchedValue); - } - else { - ASMJIT_ASSERT(size == 1); - if (Utils::isInt8(patchedValue)) - writeU8At(offset, static_cast(patchedValue) & 0xFF); - else - error = kErrorIllegalDisplacement; - } + uint32_t size = _bufferData[offset]; + if (size == 4) + Utils::writeI32u(_bufferData + offset, static_cast(patchedValue)); + else if (size == 1 && Utils::isInt8(patchedValue)) + _bufferData[offset] = static_cast(patchedValue & 0xFF); + else + err = DebugUtils::errored(kErrorInvalidDisplacement); } prev = link->prev; + _code->_unresolvedLabelsCount--; + _code->_baseHeap.release(link, sizeof(LabelLink)); + link = prev; } - // Chain unused links. - link = data->links; - if (link) { - if (prev == nullptr) - prev = link; + // Set as bound. + le->_sectionId = _section->getId(); + le->_offset = pos; + le->_links = nullptr; + resetInlineComment(); - prev->prev = _unusedLinks; - _unusedLinks = link; - } - - // Set as bound (offset is zero or greater and no links). - data->offset = pos; - data->links = nullptr; - - if (error != kErrorOk) - return setLastError(error); - - _comment = nullptr; - return error; -} - -// ============================================================================ -// [asmjit::Assembler - Embed] -// ============================================================================ - -Error Assembler::embed(const void* data, uint32_t size) noexcept { - if (getRemainingSpace() < size) { - Error error = _grow(size); - if (error != kErrorOk) - return setLastError(error); - } - - uint8_t* cursor = getCursor(); - ::memcpy(cursor, data, size); - setCursor(cursor + size); - -#if !defined(ASMJIT_DISABLE_LOGGER) - if (_logger) - _logger->logBinary(Logger::kStyleData, data, size); -#endif // !ASMJIT_DISABLE_LOGGER + if (err != kErrorOk) + return setLastError(err); return kErrorOk; } -// ============================================================================ -// [asmjit::Assembler - Reloc] -// ============================================================================ +Error Assembler::embed(const void* data, uint32_t size) { + if (_lastError) return _lastError; -size_t Assembler::relocCode(void* dst, Ptr baseAddress) const noexcept { - if (baseAddress == kNoBaseAddress) - baseAddress = static_cast((uintptr_t)dst); - return _relocCode(dst, baseAddress); + if (getRemainingSpace() < size) { + Error err = _code->growBuffer(&_section->_buffer, size); + if (ASMJIT_UNLIKELY(err != kErrorOk)) return setLastError(err); + } + + ::memcpy(_bufferPtr, data, size); + _bufferPtr += size; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + _code->_logger->logBinary(data, size); +#endif // !ASMJIT_DISABLE_LOGGING + + return kErrorOk; +} + +Error Assembler::embedLabel(const Label& label) { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + RelocEntry* re; + LabelEntry* le = _code->getLabelEntry(label); + + if (ASMJIT_UNLIKELY(!le)) + return setLastError(DebugUtils::errored(kErrorInvalidLabel)); + + Error err; + uint32_t gpSize = getGpSize(); + + if (getRemainingSpace() < gpSize) { + err = _code->growBuffer(&_section->_buffer, gpSize); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + } + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + _code->_logger->logf(gpSize == 4 ? ".dd L%u\n" : ".dq L%u\n", Operand::unpackId(label.getId())); +#endif // !ASMJIT_DISABLE_LOGGING + + err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, gpSize); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + re->_sourceSectionId = _section->getId(); + re->_sourceOffset = static_cast(getOffset()); + + if (le->isBound()) { + re->_targetSectionId = le->getSectionId(); + re->_data = static_cast(static_cast(le->getOffset())); + } + else { + LabelLink* link = _code->newLabelLink(le, _section->getId(), getOffset(), 0); + if (ASMJIT_UNLIKELY(!link)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + link->relocId = re->getId(); + } + + // Emit dummy DWORD/QWORD depending on the address size. + ::memset(_bufferPtr, 0, gpSize); + _bufferPtr += gpSize; + + return kErrorOk; +} + +Error Assembler::embedConstPool(const Label& label, const ConstPool& pool) { + if (_lastError) return _lastError; + + if (!isLabelValid(label)) + return DebugUtils::errored(kErrorInvalidLabel); + + ASMJIT_PROPAGATE(align(kAlignData, static_cast(pool.getAlignment()))); + ASMJIT_PROPAGATE(bind(label)); + + size_t size = pool.getSize(); + if (getRemainingSpace() < size) { + Error err = _code->growBuffer(&_section->_buffer, size); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + } + + uint8_t* p = _bufferPtr; + pool.fill(p); + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + _code->_logger->logBinary(p, size); +#endif // !ASMJIT_DISABLE_LOGGING + + _bufferPtr += size; + return kErrorOk; } // ============================================================================ -// [asmjit::Assembler - Make] +// [asmjit::Assembler - Emit-Helpers] // ============================================================================ -void* Assembler::make() noexcept { - // Do nothing on error condition or if no instruction has been emitted. - if (_lastError != kErrorOk || getCodeSize() == 0) - return nullptr; +#if !defined(ASMJIT_DISABLE_LOGGING) +void Assembler::_emitLog( + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, + uint32_t relSize, uint32_t imLen, uint8_t* afterCursor) { - void* p; - Error error = _runtime->add(&p, this); + Logger* logger = _code->getLogger(); + ASMJIT_ASSERT(logger != nullptr); + ASMJIT_ASSERT(options & CodeEmitter::kOptionLoggingEnabled); - if (error != kErrorOk) - setLastError(error); + StringBuilderTmp<256> sb; + uint32_t logOptions = logger->getOptions(); - return p; + uint8_t* beforeCursor = _bufferPtr; + intptr_t emittedSize = (intptr_t)(afterCursor - beforeCursor); + + sb.appendString(logger->getIndentation()); + + Operand_ opArray[6]; + opArray[0].copyFrom(o0); + opArray[1].copyFrom(o1); + opArray[2].copyFrom(o2); + opArray[3].copyFrom(o3); + opArray[4].copyFrom(_op4); + opArray[5].copyFrom(_op5); + if (!(options & CodeEmitter::kOptionOp4)) opArray[4].reset(); + if (!(options & CodeEmitter::kOptionOp5)) opArray[5].reset(); + + Logging::formatInstruction( + sb, logOptions, + this, getArchType(), + instId, options, _opExtra, opArray, 6); + + if ((logOptions & Logger::kOptionBinaryForm) != 0) + Logging::formatLine(sb, _bufferPtr, emittedSize, relSize, imLen, getInlineComment()); + else + Logging::formatLine(sb, nullptr, Globals::kInvalidIndex, 0, 0, getInlineComment()); + + logger->log(sb.getData(), sb.getLength()); } -// ============================================================================ -// [asmjit::Assembler - Emit (Helpers)] -// ============================================================================ +Error Assembler::_emitFailed( + Error err, + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { -#define NA noOperand + StringBuilderTmp<256> sb; + sb.appendString(DebugUtils::errorAsString(err)); + sb.appendString(": "); -Error Assembler::emit(uint32_t code) { - return _emit(code, NA, NA, NA, NA); + Operand_ opArray[6]; + opArray[0].copyFrom(o0); + opArray[1].copyFrom(o1); + opArray[2].copyFrom(o2); + opArray[3].copyFrom(o3); + opArray[4].copyFrom(_op4); + opArray[5].copyFrom(_op5); + + if (!(options & CodeEmitter::kOptionOp4)) opArray[4].reset(); + if (!(options & CodeEmitter::kOptionOp5)) opArray[5].reset(); + + Logging::formatInstruction( + sb, 0, + this, getArchType(), + instId, options, _opExtra, opArray, 6); + + resetOptions(); + resetInlineComment(); + return setLastError(err, sb.getData()); } - -Error Assembler::emit(uint32_t code, const Operand& o0) { - return _emit(code, o0, NA, NA, NA); -} - -Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1) { - return _emit(code, o0, o1, NA, NA); -} - -Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2) { - return _emit(code, o0, o1, o2, NA); -} - -Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, const Operand& o3) { - return _emit(code, o0, o1, o2, o3); -} - -Error Assembler::emit(uint32_t code, int o0) { - return _emit(code, Imm(o0), NA, NA, NA); -} - -Error Assembler::emit(uint32_t code, const Operand& o0, int o1) { - return _emit(code, o0, Imm(o1), NA, NA); -} - -Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, int o2) { - return _emit(code, o0, o1, Imm(o2), NA); -} - -Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, int o3) { - return _emit(code, o0, o1, o2, Imm(o3)); -} - -Error Assembler::emit(uint32_t code, int64_t o0) { - return _emit(code, Imm(o0), NA, NA, NA); -} - -Error Assembler::emit(uint32_t code, const Operand& o0, int64_t o1) { - return _emit(code, o0, Imm(o1), NA, NA); -} - -Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, int64_t o2) { - return _emit(code, o0, o1, Imm(o2), NA); -} - -Error Assembler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, int64_t o3) { - return _emit(code, o0, o1, o2, Imm(o3)); -} - -#undef NA +#endif } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/assembler.h b/src/asmjit/base/assembler.h index 822de3c..5dee5b4 100644 --- a/src/asmjit/base/assembler.h +++ b/src/asmjit/base/assembler.h @@ -9,393 +9,19 @@ #define _ASMJIT_BASE_ASSEMBLER_H // [Dependencies] -#include "../base/containers.h" -#include "../base/logger.h" +#include "../base/codeemitter.h" +#include "../base/codeholder.h" #include "../base/operand.h" -#include "../base/podvector.h" -#include "../base/runtime.h" -#include "../base/zone.h" +#include "../base/simdtypes.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { //! \addtogroup asmjit_base //! \{ -// ============================================================================ -// [asmjit::InstId] -// ============================================================================ - -//! Instruction codes (stub). -ASMJIT_ENUM(InstId) { - //! No instruction. - kInstIdNone = 0 -}; - -// ============================================================================ -// [asmjit::InstOptions] -// ============================================================================ - -//! Instruction options. -ASMJIT_ENUM(InstOptions) { - //! No instruction options. - kInstOptionNone = 0x00000000, - - //! Emit short form of the instruction (X86/X64 only). - //! - //! X86/X64 Specific - //! ---------------- - //! - //! Short form is mostly related to jmp and jcc instructions, but can be used - //! by other instructions supporting 8-bit or 32-bit immediates. This option - //! can be dangerous if the short jmp/jcc is required, but not encodable due - //! to a large displacement, in such case an error is reported. - kInstOptionShortForm = 0x00000001, - - //! Emit long form of the instruction (X86/X64 only). - //! - //! X86/X64 Specific - //! ---------------- - //! - //! Long form is mostly related to jmp and jcc instructions, but like the - //! `kInstOptionShortForm` option it can be used by other instructions - //! supporting both 8-bit and 32-bit immediates. - kInstOptionLongForm = 0x00000002, - - //! Condition is likely to be taken. - //! - //! X86/X64 Specific - //! ---------------- - //! - //! This option has no effect at the moment. Intel stopped supporting - //! conditional hints after P4 and AMD has never supported them. - kInstOptionTaken = 0x00000004, - - //! Condition is unlikely to be taken. - //! - //! X86/X64 Specific - //! ---------------- - //! - //! This option has no effect at the moment. Intel stopped supporting - //! conditional hints after P4 and AMD has never supported them. - kInstOptionNotTaken = 0x00000008, - - //! Don't follow the jump (Compiler only). - //! - //! Prevents following the jump during compilation. - kInstOptionUnfollow = 0x00000010, - - //! Overwrite the destination operand (Compiler only). - //! - //! Hint that is important for variable liveness analysis. It tells the - //! compiler that the destination operand will be overwritten now or by - //! adjacent instructions. Compiler knows when a variable is overwritten by - //! a single instruction, for example you don't have to mark "movaps" or - //! "pxor x, x" instructions, however, if a pair of instructions is used, - //! and the first of them doesn't completely overwrite the content of the - //! destination, then the compiler fails to mark that variable as dead in. - //! - //! X86/X64 Specific - //! ---------------- - //! - //! - All instructions that always overwrite at least the size of the - //! register that the variable uses, for example "mov", "movq", "movaps" - //! don't need the overwrite modifier to be used - conversion, shuffle, - //! and other miscellaneous instructions included. - //! - //! - All instructions that clear the destination register if all operands - //! are the same, for example "xor x, x", "pcmpeqb", etc... - //! - //! - Consecutive instructions that partially overwrite the variable until - //! there is no old content require the `overwrite()` to be used. Some - //! examples (not always the best use cases thought): - //! - //! - `movlps xmm0, ?` followed by `movhps xmm0, ?` and vice versa - //! - `movlpd xmm0, ?` followed by `movhpd xmm0, ?` and vice versa - //! - `mov al, ?` followed by `and ax, 0xFF` - //! - `mov al, ?` followed by `mov ah, al` - //! - `pinsrq xmm0, ?, 0` followed by `pinsrq xmm0, ?, 1` - //! - //! - If allocated variable is used temporarily for scalar operations. For - //! example if you allocate a full vector like `X86Compiler::newXmm()` - //! and then use that vector for scalar operations you should use - //! `overwrite()` directive: - //! - //! - `sqrtss x, y` - only LO element of `x` is changed, if you don't use - //! HI elements, use `X86Compiler.overwrite().sqrtss(x, y)`. - kInstOptionOverwrite = 0x00000020 -}; - -// ============================================================================ -// [asmjit::AlignMode] -// ============================================================================ - -//! Code aligning mode. -ASMJIT_ENUM(AlignMode) { - //! Align by emitting a sequence that can be executed (code). - kAlignCode = 0, - //! Align by emitting a sequence that shouldn't be executed (data). - kAlignData = 1, - //! Align by emitting a sequence of zeros. - kAlignZero = 2 -}; - -// ============================================================================ -// [asmjit::RelocMode] -// ============================================================================ - -//! Relocation mode. -ASMJIT_ENUM(RelocMode) { - //! Relocate an absolute address to an absolute address. - kRelocAbsToAbs = 0, - //! Relocate a relative address to an absolute address. - kRelocRelToAbs = 1, - //! Relocate an absolute address to a relative address. - kRelocAbsToRel = 2, - //! Relocate an absolute address to a relative address or use trampoline. - kRelocTrampoline = 3 -}; - -// ============================================================================ -// [asmjit::LabelLink] -// ============================================================================ - -//! \internal -//! -//! Data structure used to link labels. -struct LabelLink { - //! Previous link. - LabelLink* prev; - //! Offset. - intptr_t offset; - //! Inlined displacement. - intptr_t displacement; - //! RelocId in case the link has to be absolute after relocated. - intptr_t relocId; -}; - -// ============================================================================ -// [asmjit::LabelData] -// ============================================================================ - -//! \internal -//! -//! Label data. -struct LabelData { - //! Label offset. - intptr_t offset; - //! Label links chain. - LabelLink* links; - - //! External tool ID, if linked to any. - uint64_t exId; - //! Pointer to a data that `ExternalTool` associated with the label. - void* exData; -}; - -// ============================================================================ -// [asmjit::RelocData] -// ============================================================================ - -//! \internal -//! -//! Code relocation data (relative vs. absolute addresses). -//! -//! X86/X64 Specific -//! ---------------- -//! -//! X86 architecture uses 32-bit absolute addressing model by memory operands, -//! but 64-bit mode uses relative addressing model (RIP + displacement). In -//! code we are always using relative addressing model for referencing labels -//! and embedded data. In 32-bit mode we must patch all references to absolute -//! address before we can call generated function. -struct RelocData { - //! Type of relocation. - uint32_t type; - //! Size of relocation (4 or 8 bytes). - uint32_t size; - - //! Offset from the initial code address. - Ptr from; - //! Relative displacement from the initial code address or from the absolute address. - Ptr data; -}; - -// ============================================================================ -// [asmjit::ErrorHandler] -// ============================================================================ - -//! Error handler. -//! -//! Error handler can be used to override the default behavior of `Assembler` -//! error handling and propagation. See `handleError()` on how to override it. -//! -//! Please note that `addRef` and `release` functions are used, but there is -//! no reference counting implemented by default, reimplement to change the -//! default behavior. -class ASMJIT_VIRTAPI ErrorHandler { - public: - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `ErrorHandler` instance. - ASMJIT_API ErrorHandler() noexcept; - //! Destroy the `ErrorHandler` instance. - ASMJIT_API virtual ~ErrorHandler() noexcept; - - // -------------------------------------------------------------------------- - // [AddRef / Release] - // -------------------------------------------------------------------------- - - //! Reference this error handler. - //! - //! NOTE: This member function is provided for convenience. The default - //! implementation does nothing. If you are working in environment where - //! multiple `ErrorHandler` instances are used by a different code generators - //! you may provide your own functionality for reference counting. In that - //! case `addRef()` and `release()` functions should be overridden. - ASMJIT_API virtual ErrorHandler* addRef() const noexcept; - - //! Release this error handler. - //! - //! NOTE: This member function is provided for convenience. See `addRef()` - //! for more detailed information related to reference counting. - ASMJIT_API virtual void release() noexcept; - - // -------------------------------------------------------------------------- - // [Handle Error] - // -------------------------------------------------------------------------- - - //! Error handler (pure). - //! - //! Error handler is called after an error happened. An error can happen in - //! many places, but error handler is mostly used by `Assembler` to report - //! anything a fatal problem. There are multiple ways how the error handler - //! can be used: - //! - //! 1. Returning `true` or `false` from `handleError()`. If `true` is - //! returned it means that error was reported and AsmJit can continue - //! with code-generation. However, `false` reports to AsmJit that the - //! error cannot be handled, in such case it stores the error in - //! `Assembler` and puts it into an error state. The error is accessible - //! through `Assembler::getLastError(). Returning `false` is default when - //! no error handler is used. - //! - //! 2. AsmJit doesn't use exception handling so your error should also not - //! throw an exception, however, it's possible to use plain old C's - //! `setjmp()` and `longjmp()`. Asmjit always puts `Assembler` and - //! `Compiler` to a consistent state before calling the `handleError()`, - //! so you can use `longjmp()` to leave the code-generation if an error - //! happened. - virtual bool handleError(Error code, const char* message, void* origin) noexcept = 0; -}; - -// ============================================================================ -// [asmjit::ExternalTool] -// ============================================================================ - -//! An external tool (i.e. `Stream` or `Compiler`) that can serialize to `Assembler` -class ASMJIT_VIRTAPI ExternalTool { - public: - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - ASMJIT_API ExternalTool() noexcept; - ASMJIT_API virtual ~ExternalTool() noexcept; - - // -------------------------------------------------------------------------- - // [Attach / Reset] - // -------------------------------------------------------------------------- - - //! \internal - //! - //! Called to attach this code generator to the `assembler`. - virtual Error attach(Assembler* assembler) noexcept = 0; - - //! Reset the code-generator (also detaches if attached). - virtual void reset(bool releaseMemory) noexcept = 0; - - // -------------------------------------------------------------------------- - // [Finalize] - // -------------------------------------------------------------------------- - - //! Finalize the code-generation. - //! - //! The finalization has two passes: - //! - serializes code to the attached assembler. - //! - resets the `ExternalTool` (detaching from the `Assembler as well) so - //! it can be reused or destroyed). - virtual Error finalize() noexcept = 0; - - // -------------------------------------------------------------------------- - // [Runtime / Assembler] - // -------------------------------------------------------------------------- - - //! Get the `Runtime` instance that is associated with the code-generator. - ASMJIT_INLINE Runtime* getRuntime() const noexcept { return _runtime; } - //! Get the `Assembler` instance that is associated with the code-generator. - ASMJIT_INLINE Assembler* getAssembler() const noexcept { return _assembler; } - - // -------------------------------------------------------------------------- - // [Architecture] - // -------------------------------------------------------------------------- - - //! Get the target architecture. - ASMJIT_INLINE uint32_t getArch() const noexcept { return _arch; } - //! Get the default register size - 4 or 8 bytes, depends on the target. - ASMJIT_INLINE uint32_t getRegSize() const noexcept { return _regSize; } - - // -------------------------------------------------------------------------- - // [Error Handling] - // -------------------------------------------------------------------------- - - //! Get the last error code. - ASMJIT_INLINE Error getLastError() const noexcept { return _lastError; } - //! Set the last error code and propagate it through the error handler. - ASMJIT_API Error setLastError(Error error, const char* message = nullptr) noexcept; - //! Clear the last error code. - ASMJIT_INLINE void resetLastError() noexcept { _lastError = kErrorOk; } - - // -------------------------------------------------------------------------- - // [ID] - // -------------------------------------------------------------------------- - - //! Get the tool ID, provided by `Assembler` when attached to it. - ASMJIT_INLINE uint64_t getExId() const noexcept { return _exId; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Associated runtime. - Runtime* _runtime; - //! Associated assembler. - Assembler* _assembler; - - //! `ExternalTool` ID, provided by `Assembler`. - //! - //! If multiple high-evel code generators are associated with a single - //! assembler the `_exId` member can be used to distinguish between them and - //! to provide a mechanism to check whether the high-level code generator is - //! accessing the resource it really owns. - uint64_t _exId; - - //! Target's architecture ID. - uint8_t _arch; - //! Target's architecture GP register size in bytes (4 or 8). - uint8_t _regSize; - //! The code generator has been finalized. - uint8_t _finalized; - //! \internal - uint8_t _reserved; - //! Last error code. - uint32_t _lastError; -}; - // ============================================================================ // [asmjit::Assembler] // ============================================================================ @@ -405,593 +31,104 @@ class ASMJIT_VIRTAPI ExternalTool { //! This class implements a base interface that is used by architecture //! specific assemblers. //! -//! \sa Compiler. -class ASMJIT_VIRTAPI Assembler { - public: - ASMJIT_NO_COPY(Assembler) - - // -------------------------------------------------------------------------- - // [Options] - // -------------------------------------------------------------------------- - - //! Assembler options. - ASMJIT_ENUM(Options) { - //! Emit optimized code-alignment sequences (`Assembler` and `Compiler`). - //! - //! Default `true`. - //! - //! X86/X64 Specific - //! ---------------- - //! - //! Default align sequence used by X86/X64 architecture is one-byte 0x90 - //! opcode that is mostly shown by disassemblers as nop. However there are - //! more optimized align sequences for 2-11 bytes that may execute faster. - //! If this feature is enabled asmjit will generate specialized sequences - //! for alignment between 1 to 11 bytes. Also when `X86Compiler` is used, - //! it can add REX prefixes into the code to make some instructions greater - //! so no alignment sequence is needed. - kOptionOptimizedAlign = 0, - - //! Emit jump-prediction hints (`Assembler` and `Compiler`). - //! - //! Default `false`. - //! - //! X86/X64 Specific - //! ---------------- - //! - //! Jump prediction is usually based on the direction of the jump. If the - //! jump is backward it is usually predicted as taken; and if the jump is - //! forward it is usually predicted as not-taken. The reason is that loops - //! generally use backward jumps and conditions usually use forward jumps. - //! However this behavior can be overridden by using instruction prefixes. - //! If this option is enabled these hints will be emitted. - //! - //! This feature is disabled by default, because the only processor that - //! used to take into consideration prediction hints was P4. Newer processors - //! implement heuristics for branch prediction that ignores any static hints. - kOptionPredictedJumps = 1 - }; - - // -------------------------------------------------------------------------- - // [Buffer] - // -------------------------------------------------------------------------- - - //! Code or data buffer. - struct Buffer { - //! Code data. - uint8_t* data; - //! Total length of `data` in bytes. - size_t capacity; - //! Number of bytes of `data` used. - size_t length; - //! Current offset (assembler's cursor) in bytes. - size_t offset; - }; - - // -------------------------------------------------------------------------- - // [Section] - // -------------------------------------------------------------------------- - - //! Code or data section. - struct Section { - //! Section id. - uint32_t id; - //! Section flags. - uint32_t flags; - //! Section name (limited to 35 characters, PE allows max 8 chars). - char name[36]; - //! Section alignment requirements (0 if no requirements). - uint32_t alignment; - //! Section content. - Buffer content; - }; +//! \sa CodeCompiler. +class ASMJIT_VIRTAPI Assembler : public CodeEmitter { +public: + ASMJIT_NONCOPYABLE(Assembler) + typedef CodeEmitter Base; // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- //! Create a new `Assembler` instance. - ASMJIT_API Assembler(Runtime* runtime) noexcept; + ASMJIT_API Assembler() noexcept; //! Destroy the `Assembler` instance. ASMJIT_API virtual ~Assembler() noexcept; // -------------------------------------------------------------------------- - // [Reset] + // [Events] // -------------------------------------------------------------------------- - //! Reset the assembler. - //! - //! If `releaseMemory` is true all buffers will be released to the system. - ASMJIT_API void reset(bool releaseMemory = false) noexcept; - - // -------------------------------------------------------------------------- - // [Runtime] - // -------------------------------------------------------------------------- - - //! Get the runtime associated with the assembler. - //! - //! NOTE: Runtime is persistent across `reset()` calls. - ASMJIT_INLINE Runtime* getRuntime() const noexcept { return _runtime; } - - // -------------------------------------------------------------------------- - // [Architecture] - // -------------------------------------------------------------------------- - - //! Get the target architecture. - ASMJIT_INLINE uint32_t getArch() const noexcept { return _arch; } - //! Get the default register size - 4 or 8 bytes, depends on the target. - ASMJIT_INLINE uint32_t getRegSize() const noexcept { return _regSize; } - - // -------------------------------------------------------------------------- - // [Logging] - // -------------------------------------------------------------------------- - -#if !defined(ASMJIT_DISABLE_LOGGER) - //! Get whether the assembler has a logger. - ASMJIT_INLINE bool hasLogger() const noexcept { return _logger != nullptr; } - //! Get the logger. - ASMJIT_INLINE Logger* getLogger() const noexcept { return _logger; } - //! Set the logger to `logger`. - ASMJIT_INLINE void setLogger(Logger* logger) noexcept { _logger = logger; } -#endif // !ASMJIT_DISABLE_LOGGER - - // -------------------------------------------------------------------------- - // [Error Handling] - // -------------------------------------------------------------------------- - - //! Get the error handler. - ASMJIT_INLINE ErrorHandler* getErrorHandler() const noexcept { return _errorHandler; } - //! Set the error handler. - ASMJIT_API Error setErrorHandler(ErrorHandler* handler) noexcept; - //! Clear the error handler. - ASMJIT_INLINE Error resetErrorHandler() noexcept { return setErrorHandler(nullptr); } - - //! Get the last error code. - ASMJIT_INLINE Error getLastError() const noexcept { return _lastError; } - //! Set the last error code and propagate it through the error handler. - ASMJIT_API Error setLastError(Error error, const char* message = nullptr) noexcept; - //! Clear the last error code. - ASMJIT_INLINE void resetLastError() noexcept { _lastError = kErrorOk; } - - // -------------------------------------------------------------------------- - // [Serializers] - // -------------------------------------------------------------------------- - - //! \internal - //! - //! Called after the code generator `cg` has been attached to the assembler. - ASMJIT_INLINE void _attached(ExternalTool* exTool) noexcept { - exTool->_runtime = getRuntime(); - exTool->_assembler = this; - exTool->_exId = _nextExId(); - _exCountAttached++; - } - - //! \internal - //! - //! Called after the code generator `cg` has been detached from the assembler. - ASMJIT_INLINE void _detached(ExternalTool* exTool) noexcept { - exTool->_runtime = nullptr; - exTool->_assembler = nullptr; - exTool->_exId = 0; - _exCountAttached--; - } - - //! \internal - //! - //! Return a new code-gen ID (always greater than zero). - ASMJIT_INLINE uint64_t _nextExId() noexcept { - ASMJIT_ASSERT(_exIdGenerator != ASMJIT_UINT64_C(0xFFFFFFFFFFFFFFFF)); - return ++_exIdGenerator; - } - - // -------------------------------------------------------------------------- - // [Assembler Options] - // -------------------------------------------------------------------------- - - //! Get global assembler options. - ASMJIT_INLINE uint32_t getAsmOptions() const noexcept { - return _asmOptions; - } - //! Get whether the global assembler `option` is turned on. - ASMJIT_INLINE bool hasAsmOption(uint32_t option) const noexcept { - return (_asmOptions & option) != 0; - } - //! Turn on global assembler `options`. - ASMJIT_INLINE void addAsmOptions(uint32_t options) noexcept { - _asmOptions |= options; - } - //! Turn off global assembler `options`. - ASMJIT_INLINE void clearAsmOptions(uint32_t options) noexcept { - _asmOptions &= ~options; - } - - // -------------------------------------------------------------------------- - // [Instruction Options] - // -------------------------------------------------------------------------- - - //! Get options of the next instruction. - ASMJIT_INLINE uint32_t getInstOptions() const noexcept { - return _instOptions; - } - //! Set options of the next instruction. - ASMJIT_INLINE void setInstOptions(uint32_t instOptions) noexcept { - _instOptions = instOptions; - } - //! Get options of the next instruction and reset them. - ASMJIT_INLINE uint32_t getInstOptionsAndReset() noexcept { - uint32_t instOptions = _instOptions; - _instOptions = 0; - return instOptions; - }; + ASMJIT_API Error onAttach(CodeHolder* code) noexcept override; + ASMJIT_API Error onDetach(CodeHolder* code) noexcept override; // -------------------------------------------------------------------------- // [Code-Buffer] // -------------------------------------------------------------------------- - //! Grow the code-buffer. + //! Called by \ref CodeHolder::sync(). + ASMJIT_API virtual void sync() noexcept; + + //! Get the capacity of the current CodeBuffer. + ASMJIT_INLINE size_t getBufferCapacity() const noexcept { return (size_t)(_bufferEnd - _bufferData); } + //! Get the number of remaining bytes in the current CodeBuffer. + ASMJIT_INLINE size_t getRemainingSpace() const noexcept { return (size_t)(_bufferEnd - _bufferPtr); } + + //! Get the current position in the CodeBuffer. + ASMJIT_INLINE size_t getOffset() const noexcept { return (size_t)(_bufferPtr - _bufferData); } + //! Set the current position in the CodeBuffer to `offset`. //! - //! The internal code-buffer will grow at least by `n` bytes so `n` bytes can - //! be added to it. If `n` is zero or `getOffset() + n` is not greater than - //! the current capacity of the code-buffer this function does nothing. - ASMJIT_API Error _grow(size_t n) noexcept; - //! Reserve the code-buffer to at least `n` bytes. - ASMJIT_API Error _reserve(size_t n) noexcept; + //! NOTE: The `offset` cannot be outside of the buffer length (even if it's + //! within buffer's capacity). + ASMJIT_API Error setOffset(size_t offset); - //! Get capacity of the code-buffer. - ASMJIT_INLINE size_t getCapacity() const noexcept { - return (size_t)(_end - _buffer); - } - //! Get the number of remaining bytes in code-buffer. - ASMJIT_INLINE size_t getRemainingSpace() const noexcept { - return (size_t)(_end - _cursor); - } - - //! Get current offset in buffer, same as `getOffset() + getTramplineSize()`. - ASMJIT_INLINE size_t getCodeSize() const noexcept { - return getOffset() + getTrampolinesSize(); - } - - //! Get size of all possible trampolines. - //! - //! Trampolines are needed to successfuly generate relative jumps to absolute - //! addresses. This value is only non-zero if jmp of call instructions were - //! used with immediate operand (this means jumping or calling an absolute - //! address directly). - ASMJIT_INLINE size_t getTrampolinesSize() const noexcept { return _trampolinesSize; } - - //! Get code-buffer. - ASMJIT_INLINE uint8_t* getBuffer() const noexcept { return _buffer; } - //! Get the end of the code-buffer (points to the first byte that is invalid). - ASMJIT_INLINE uint8_t* getEnd() const noexcept { return _end; } - - //! Get the current position in the code-buffer. - ASMJIT_INLINE uint8_t* getCursor() const noexcept { return _cursor; } - //! Set the current position in the buffer. - ASMJIT_INLINE void setCursor(uint8_t* cursor) noexcept { - ASMJIT_ASSERT(cursor >= _buffer && cursor <= _end); - _cursor = cursor; - } - - //! Get the current offset in the buffer. - ASMJIT_INLINE size_t getOffset() const noexcept { return (size_t)(_cursor - _buffer); } - //! Set the current offset in the buffer to `offset` and return the previous value. - ASMJIT_INLINE size_t setOffset(size_t offset) noexcept { - ASMJIT_ASSERT(offset < getCapacity()); - - size_t oldOffset = (size_t)(_cursor - _buffer); - _cursor = _buffer + offset; - return oldOffset; - } - - //! Read `int8_t` at index `pos`. - ASMJIT_INLINE int32_t readI8At(size_t pos) const noexcept { - ASMJIT_ASSERT(pos + 1 <= (size_t)(_end - _buffer)); - return Utils::readI8(_buffer + pos); - } - - //! Read `uint8_t` at index `pos`. - ASMJIT_INLINE uint32_t readU8At(size_t pos) const noexcept { - ASMJIT_ASSERT(pos + 1 <= (size_t)(_end - _buffer)); - return Utils::readU8(_buffer + pos); - } - - //! Read `int16_t` at index `pos`. - ASMJIT_INLINE int32_t readI16At(size_t pos) const noexcept { - ASMJIT_ASSERT(pos + 2 <= (size_t)(_end - _buffer)); - return Utils::readI16u(_buffer + pos); - } - - //! Read `uint16_t` at index `pos`. - ASMJIT_INLINE uint32_t readU16At(size_t pos) const noexcept { - ASMJIT_ASSERT(pos + 2 <= (size_t)(_end - _buffer)); - return Utils::readU16u(_buffer + pos); - } - - //! Read `int32_t` at index `pos`. - ASMJIT_INLINE int32_t readI32At(size_t pos) const noexcept { - ASMJIT_ASSERT(pos + 4 <= (size_t)(_end - _buffer)); - return Utils::readI32u(_buffer + pos); - } - - //! Read `uint32_t` at index `pos`. - ASMJIT_INLINE uint32_t readU32At(size_t pos) const noexcept { - ASMJIT_ASSERT(pos + 4 <= (size_t)(_end - _buffer)); - return Utils::readU32u(_buffer + pos); - } - - //! Read `uint64_t` at index `pos`. - ASMJIT_INLINE int64_t readI64At(size_t pos) const noexcept { - ASMJIT_ASSERT(pos + 8 <= (size_t)(_end - _buffer)); - return Utils::readI64u(_buffer + pos); - } - - //! Read `uint64_t` at index `pos`. - ASMJIT_INLINE uint64_t readU64At(size_t pos) const noexcept { - ASMJIT_ASSERT(pos + 8 <= (size_t)(_end - _buffer)); - return Utils::readU64u(_buffer + pos); - } - - //! Write `int8_t` at index `pos`. - ASMJIT_INLINE void writeI8At(size_t pos, int32_t x) noexcept { - ASMJIT_ASSERT(pos + 1 <= (size_t)(_end - _buffer)); - Utils::writeI8(_buffer + pos, x); - } - - //! Write `uint8_t` at index `pos`. - ASMJIT_INLINE void writeU8At(size_t pos, uint32_t x) noexcept { - ASMJIT_ASSERT(pos + 1 <= (size_t)(_end - _buffer)); - Utils::writeU8(_buffer + pos, x); - } - - //! Write `int8_t` at index `pos`. - ASMJIT_INLINE void writeI16At(size_t pos, int32_t x) noexcept { - ASMJIT_ASSERT(pos + 2 <= (size_t)(_end - _buffer)); - Utils::writeI16u(_buffer + pos, x); - } - - //! Write `uint8_t` at index `pos`. - ASMJIT_INLINE void writeU16At(size_t pos, uint32_t x) noexcept { - ASMJIT_ASSERT(pos + 2 <= (size_t)(_end - _buffer)); - Utils::writeU16u(_buffer + pos, x); - } - - //! Write `int32_t` at index `pos`. - ASMJIT_INLINE void writeI32At(size_t pos, int32_t x) noexcept { - ASMJIT_ASSERT(pos + 4 <= (size_t)(_end - _buffer)); - Utils::writeI32u(_buffer + pos, x); - } - - //! Write `uint32_t` at index `pos`. - ASMJIT_INLINE void writeU32At(size_t pos, uint32_t x) noexcept { - ASMJIT_ASSERT(pos + 4 <= (size_t)(_end - _buffer)); - Utils::writeU32u(_buffer + pos, x); - } - - //! Write `int64_t` at index `pos`. - ASMJIT_INLINE void writeI64At(size_t pos, int64_t x) noexcept { - ASMJIT_ASSERT(pos + 8 <= (size_t)(_end - _buffer)); - Utils::writeI64u(_buffer + pos, x); - } - - //! Write `uint64_t` at index `pos`. - ASMJIT_INLINE void writeU64At(size_t pos, uint64_t x) noexcept { - ASMJIT_ASSERT(pos + 8 <= (size_t)(_end - _buffer)); - Utils::writeU64u(_buffer + pos, x); - } + //! Get start of the CodeBuffer of the current section. + ASMJIT_INLINE uint8_t* getBufferData() const noexcept { return _bufferData; } + //! Get end (first invalid byte) of the current section. + ASMJIT_INLINE uint8_t* getBufferEnd() const noexcept { return _bufferEnd; } + //! Get pointer in the CodeBuffer of the current section. + ASMJIT_INLINE uint8_t* getBufferPtr() const noexcept { return _bufferPtr; } // -------------------------------------------------------------------------- - // [Embed] + // [Code-Generation] // -------------------------------------------------------------------------- - //! Embed raw data into the code-buffer. - ASMJIT_API virtual Error embed(const void* data, uint32_t size) noexcept; + ASMJIT_API Label newLabel() override; + ASMJIT_API Label newNamedLabel( + const char* name, + size_t nameLength = Globals::kInvalidIndex, + uint32_t type = Label::kTypeGlobal, + uint32_t parentId = 0) override; + ASMJIT_API Error bind(const Label& label) override; + ASMJIT_API Error embed(const void* data, uint32_t size) override; + ASMJIT_API Error embedLabel(const Label& label) override; + ASMJIT_API Error embedConstPool(const Label& label, const ConstPool& pool) override; + ASMJIT_API Error comment(const char* s, size_t len = Globals::kInvalidIndex) override; // -------------------------------------------------------------------------- - // [Align] + // [Emit-Helpers] // -------------------------------------------------------------------------- - //! Align target buffer to the `offset` specified. - //! - //! The sequence that is used to fill the gap between the aligned location - //! and the current depends on `alignMode`, see \ref AlignMode. - virtual Error align(uint32_t alignMode, uint32_t offset) noexcept = 0; +protected: +#if !defined(ASMJIT_DISABLE_LOGGING) + void _emitLog( + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, + uint32_t relSize, uint32_t imLen, uint8_t* afterCursor); - // -------------------------------------------------------------------------- - // [Label] - // -------------------------------------------------------------------------- + Error _emitFailed( + Error err, + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3); +#else + ASMJIT_INLINE _emitFailed( + uint32_t err, + uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { - //! Get number of labels created. - ASMJIT_INLINE size_t getLabelsCount() const noexcept { - return _labels.getLength(); + resetOptions(); + resetInlineComment(); + return setLastError(err); } - - //! Get whether the `label` is valid (i.e. registered). - ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept { - return isLabelValid(label.getId()); - } - //! Get whether the label `id` is valid (i.e. registered). - ASMJIT_INLINE bool isLabelValid(uint32_t id) const noexcept { - return static_cast(id) < _labels.getLength(); - } - - //! Get whether the `label` is bound. - //! - //! NOTE: It's an error to pass label that is not valid. Check the validity - //! of the label by using `isLabelValid()` method before the bound check if - //! you are not sure about its validity, otherwise you may hit an assertion - //! failure in debug mode, and undefined behavior in release mode. - ASMJIT_INLINE bool isLabelBound(const Label& label) const noexcept { - return isLabelBound(label.getId()); - } - //! \overload - ASMJIT_INLINE bool isLabelBound(uint32_t id) const noexcept { - ASMJIT_ASSERT(isLabelValid(id)); - return _labels[id]->offset != -1; - } - - //! Get a `label` offset or -1 if the label is not yet bound. - ASMJIT_INLINE intptr_t getLabelOffset(const Label& label) const noexcept { - return getLabelOffset(label.getId()); - } - //! \overload - ASMJIT_INLINE intptr_t getLabelOffset(uint32_t id) const noexcept { - ASMJIT_ASSERT(isLabelValid(id)); - return _labels[id]->offset; - } - - //! Get `LabelData` by `label`. - ASMJIT_INLINE LabelData* getLabelData(const Label& label) const noexcept { - return getLabelData(label.getId()); - } - //! \overload - ASMJIT_INLINE LabelData* getLabelData(uint32_t id) const noexcept { - ASMJIT_ASSERT(isLabelValid(id)); - return const_cast(_labels[id]); - } - - //! \internal - //! - //! Create a new label and return its ID. - ASMJIT_API uint32_t _newLabelId() noexcept; - - //! \internal - //! - //! New LabelLink instance. - ASMJIT_API LabelLink* _newLabelLink() noexcept; - - //! Create and return a new `Label`. - ASMJIT_INLINE Label newLabel() noexcept { return Label(_newLabelId()); } - - //! Bind the `label` to the current offset. - //! - //! NOTE: Label can be bound only once! - ASMJIT_API virtual Error bind(const Label& label) noexcept; - - // -------------------------------------------------------------------------- - // [Reloc] - // -------------------------------------------------------------------------- - - //! Relocate the code to `baseAddress` and copy it to `dst`. - //! - //! \param dst Contains the location where the relocated code should be - //! copied. The pointer can be address returned by virtual memory allocator - //! or any other address that has sufficient space. - //! - //! \param baseAddress Base address used for relocation. The `JitRuntime` - //! always sets the `baseAddress` address to be the same as `dst`, but other - //! runtimes, for example `StaticRuntime`, do not have to follow this rule. - //! - //! \retval The number bytes actually used. If the code generator reserved - //! space for possible trampolines, but didn't use it, the number of bytes - //! used can actually be less than the expected worst case. Virtual memory - //! allocator can shrink the memory allocated first time. - //! - //! A given buffer will be overwritten, to get the number of bytes required, - //! use `getCodeSize()`. - ASMJIT_API size_t relocCode(void* dst, Ptr baseAddress = kNoBaseAddress) const noexcept; - - //! \internal - //! - //! Reloc code. - virtual size_t _relocCode(void* dst, Ptr baseAddress) const noexcept = 0; - - // -------------------------------------------------------------------------- - // [Make] - // -------------------------------------------------------------------------- - - ASMJIT_API virtual void* make() noexcept; - - // -------------------------------------------------------------------------- - // [Emit] - // -------------------------------------------------------------------------- - - //! Emit an instruction (virtual). - virtual Error _emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, const Operand& o3) = 0; - - //! Emit an instruction. - ASMJIT_API Error emit(uint32_t code); - //! \overload - ASMJIT_API Error emit(uint32_t code, const Operand& o0); - //! \overload - ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1); - //! \overload - ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2); - //! \overload - ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, const Operand& o3); - - //! Emit an instruction that has an immediate operand. - ASMJIT_API Error emit(uint32_t code, int o0); - //! \overload - ASMJIT_API Error emit(uint32_t code, const Operand& o0, int o1); - //! \overload - ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1, int o2); - //! \overload - ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, int o3); - - //! \overload - ASMJIT_API Error emit(uint32_t code, int64_t o0); - //! \overload - ASMJIT_API Error emit(uint32_t code, const Operand& o0, int64_t o1); - //! \overload - ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1, int64_t o2); - //! \overload - ASMJIT_API Error emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, int64_t o3); +#endif // -------------------------------------------------------------------------- // [Members] // -------------------------------------------------------------------------- - //! Associated runtime. - Runtime* _runtime; - //! Associated logger. - Logger* _logger; - //! Associated error handler, triggered by \ref setLastError(). - ErrorHandler* _errorHandler; - - //! Target architecture ID. - uint8_t _arch; - //! Target architecture GP register size in bytes (4 or 8). - uint8_t _regSize; - //! \internal - uint16_t _reserved; - - //! Assembler options, used by \ref getAsmOptions() and \ref hasAsmOption(). - uint32_t _asmOptions; - //! Instruction options, affect the next instruction that will be emitted. - uint32_t _instOptions; - //! Last error code. - uint32_t _lastError; - - //! External tool ID generator. - uint64_t _exIdGenerator; - //! Count of external tools currently attached. - size_t _exCountAttached; - - //! General purpose zone allocator. - Zone _zoneAllocator; - - //! Start of the code-buffer of the current section. - uint8_t* _buffer; - //! End of the code-buffer of the current section (points to the first invalid byte). - uint8_t* _end; - //! The current position in `_buffer` of the current section. - uint8_t* _cursor; - - //! Size of all possible trampolines. - uint32_t _trampolinesSize; - - //! Inline comment that will be logged by the next instruction and set to nullptr. - const char* _comment; - //! Unused `LabelLink` structures pool. - LabelLink* _unusedLinks; - - //! Assembler sections. - PodVectorTmp _sections; - //! Assembler labels. - PodVectorTmp _labels; - //! Table of relocations. - PodVector _relocations; +public: + SectionEntry* _section; //!< Current section where the assembling happens. + uint8_t* _bufferData; //!< Start of the CodeBuffer of the current section. + uint8_t* _bufferEnd; //!< End (first invalid byte) of the current section. + uint8_t* _bufferPtr; //!< Pointer in the CodeBuffer of the current section. }; //! \} @@ -999,7 +136,7 @@ class ASMJIT_VIRTAPI Assembler { } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" // [Guard] #endif // _ASMJIT_BASE_ASSEMBLER_H diff --git a/src/asmjit/base/codebuilder.cpp b/src/asmjit/base/codebuilder.cpp new file mode 100644 index 0000000..2ce970c --- /dev/null +++ b/src/asmjit/base/codebuilder.cpp @@ -0,0 +1,605 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Guard] +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_BUILDER) + +// [Dependencies] +#include "../base/codebuilder.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::CodeBuilder - Construction / Destruction] +// ============================================================================ + +CodeBuilder::CodeBuilder() noexcept + : CodeEmitter(kTypeBuilder), + _cbBaseZone(32768 - Zone::kZoneOverhead), + _cbDataZone(16384 - Zone::kZoneOverhead), + _cbPassZone(32768 - Zone::kZoneOverhead), + _cbHeap(&_cbBaseZone), + _cbPasses(), + _cbLabels(), + _position(0), + _nodeFlags(0), + _firstNode(nullptr), + _lastNode(nullptr), + _cursor(nullptr) {} +CodeBuilder::~CodeBuilder() noexcept {} + +// ============================================================================ +// [asmjit::CodeBuilder - Events] +// ============================================================================ + +Error CodeBuilder::onAttach(CodeHolder* code) noexcept { + return Base::onAttach(code); +} + +Error CodeBuilder::onDetach(CodeHolder* code) noexcept { + _cbPasses.reset(); + _cbLabels.reset(); + _cbHeap.reset(&_cbBaseZone); + + _cbBaseZone.reset(false); + _cbDataZone.reset(false); + _cbPassZone.reset(false); + + _position = 0; + _nodeFlags = 0; + + _firstNode = nullptr; + _lastNode = nullptr; + _cursor = nullptr; + + return Base::onDetach(code); +} + +// ============================================================================ +// [asmjit::CodeBuilder - Node-Factory] +// ============================================================================ + +Error CodeBuilder::getCBLabel(CBLabel** pOut, uint32_t id) noexcept { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + size_t index = Operand::unpackId(id); + if (ASMJIT_UNLIKELY(index >= _code->getLabelsCount())) + return DebugUtils::errored(kErrorInvalidLabel); + + if (index >= _cbLabels.getLength()) + ASMJIT_PROPAGATE(_cbLabels.resize(&_cbHeap, index + 1)); + + CBLabel* node = _cbLabels[index]; + if (!node) { + node = newNodeT(id); + if (ASMJIT_UNLIKELY(!node)) + return DebugUtils::errored(kErrorNoHeapMemory); + _cbLabels[index] = node; + } + + *pOut = node; + return kErrorOk; +} + +Error CodeBuilder::registerLabelNode(CBLabel* node) noexcept { + if (_lastError) return _lastError; + ASMJIT_ASSERT(_code != nullptr); + + // Don't call setLastError() from here, we are noexcept and we are called + // by `newLabelNode()` and `newFuncNode()`, which are noexcept as well. + uint32_t id; + ASMJIT_PROPAGATE(_code->newLabelId(id)); + size_t index = Operand::unpackId(id); + + // We just added one label so it must be true. + ASMJIT_ASSERT(_cbLabels.getLength() < index + 1); + ASMJIT_PROPAGATE(_cbLabels.resize(&_cbHeap, index + 1)); + + _cbLabels[index] = node; + node->_id = id; + return kErrorOk; +} + +CBLabel* CodeBuilder::newLabelNode() noexcept { + CBLabel* node = newNodeT(); + if (!node || registerLabelNode(node) != kErrorOk) + return nullptr; + return node; +} + +CBAlign* CodeBuilder::newAlignNode(uint32_t mode, uint32_t alignment) noexcept { + return newNodeT(mode, alignment); +} + +CBData* CodeBuilder::newDataNode(const void* data, uint32_t size) noexcept { + if (size > CBData::kInlineBufferSize) { + void* cloned = _cbDataZone.alloc(size); + if (!cloned) return nullptr; + + if (data) ::memcpy(cloned, data, size); + data = cloned; + } + + return newNodeT(const_cast(data), size); +} + +CBConstPool* CodeBuilder::newConstPool() noexcept { + CBConstPool* node = newNodeT(); + if (!node || registerLabelNode(node) != kErrorOk) + return nullptr; + return node; +} + +CBComment* CodeBuilder::newCommentNode(const char* s, size_t len) noexcept { + if (s) { + if (len == Globals::kInvalidIndex) len = ::strlen(s); + if (len > 0) { + s = static_cast(_cbDataZone.dup(s, len, true)); + if (!s) return nullptr; + } + } + + return newNodeT(s); +} + +// ============================================================================ +// [asmjit::CodeBuilder - Code-Emitter] +// ============================================================================ + +Label CodeBuilder::newLabel() { + uint32_t id = kInvalidValue; + + if (!_lastError) { + CBLabel* node = newNodeT(id); + if (ASMJIT_UNLIKELY(!node)) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + else { + Error err = registerLabelNode(node); + if (ASMJIT_UNLIKELY(err)) + setLastError(err); + else + id = node->getId(); + } + } + + return Label(id); +} + +Label CodeBuilder::newNamedLabel(const char* name, size_t nameLength, uint32_t type, uint32_t parentId) { + uint32_t id = kInvalidValue; + + if (!_lastError) { + CBLabel* node = newNodeT(id); + if (ASMJIT_UNLIKELY(!node)) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + else { + Error err = _code->newNamedLabelId(id, name, nameLength, type, parentId); + if (ASMJIT_UNLIKELY(err)) + setLastError(err); + else + id = node->getId(); + } + } + + return Label(id); +} + +Error CodeBuilder::bind(const Label& label) { + if (_lastError) return _lastError; + + CBLabel* node; + Error err = getCBLabel(&node, label); + if (ASMJIT_UNLIKELY(err)) + return setLastError(err); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::align(uint32_t mode, uint32_t alignment) { + if (_lastError) return _lastError; + + CBAlign* node = newAlignNode(mode, alignment); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::embed(const void* data, uint32_t size) { + if (_lastError) return _lastError; + + CBData* node = newDataNode(data, size); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::embedLabel(const Label& label) { + if (_lastError) return _lastError; + + CBLabelData* node = newNodeT(label.getId()); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::embedConstPool(const Label& label, const ConstPool& pool) { + if (_lastError) return _lastError; + + if (!isLabelValid(label)) + return setLastError(DebugUtils::errored(kErrorInvalidLabel)); + + ASMJIT_PROPAGATE(align(kAlignData, static_cast(pool.getAlignment()))); + ASMJIT_PROPAGATE(bind(label)); + + CBData* node = newDataNode(nullptr, static_cast(pool.getSize())); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + pool.fill(node->getData()); + addNode(node); + return kErrorOk; +} + +Error CodeBuilder::comment(const char* s, size_t len) { + if (_lastError) return _lastError; + + CBComment* node = newCommentNode(s, len); + if (ASMJIT_UNLIKELY(!node)) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeBuilder - Node-Management] +// ============================================================================ + +CBNode* CodeBuilder::addNode(CBNode* node) noexcept { + ASMJIT_ASSERT(node); + ASMJIT_ASSERT(node->_prev == nullptr); + ASMJIT_ASSERT(node->_next == nullptr); + + if (!_cursor) { + if (!_firstNode) { + _firstNode = node; + _lastNode = node; + } + else { + node->_next = _firstNode; + _firstNode->_prev = node; + _firstNode = node; + } + } + else { + CBNode* prev = _cursor; + CBNode* next = _cursor->_next; + + node->_prev = prev; + node->_next = next; + + prev->_next = node; + if (next) + next->_prev = node; + else + _lastNode = node; + } + + _cursor = node; + return node; +} + +CBNode* CodeBuilder::addAfter(CBNode* node, CBNode* ref) noexcept { + ASMJIT_ASSERT(node); + ASMJIT_ASSERT(ref); + + ASMJIT_ASSERT(node->_prev == nullptr); + ASMJIT_ASSERT(node->_next == nullptr); + + CBNode* prev = ref; + CBNode* next = ref->_next; + + node->_prev = prev; + node->_next = next; + + prev->_next = node; + if (next) + next->_prev = node; + else + _lastNode = node; + + return node; +} + +CBNode* CodeBuilder::addBefore(CBNode* node, CBNode* ref) noexcept { + ASMJIT_ASSERT(node != nullptr); + ASMJIT_ASSERT(node->_prev == nullptr); + ASMJIT_ASSERT(node->_next == nullptr); + ASMJIT_ASSERT(ref != nullptr); + + CBNode* prev = ref->_prev; + CBNode* next = ref; + + node->_prev = prev; + node->_next = next; + + next->_prev = node; + if (prev) + prev->_next = node; + else + _firstNode = node; + + return node; +} + +static ASMJIT_INLINE void CodeBuilder_nodeRemoved(CodeBuilder* self, CBNode* node_) noexcept { + if (node_->isJmpOrJcc()) { + CBJump* node = static_cast(node_); + CBLabel* label = node->getTarget(); + + if (label) { + // Disconnect. + CBJump** pPrev = &label->_from; + for (;;) { + ASMJIT_ASSERT(*pPrev != nullptr); + + CBJump* current = *pPrev; + if (!current) break; + + if (current == node) { + *pPrev = node->_jumpNext; + break; + } + + pPrev = ¤t->_jumpNext; + } + + label->subNumRefs(); + } + } +} + +CBNode* CodeBuilder::removeNode(CBNode* node) noexcept { + CBNode* prev = node->_prev; + CBNode* next = node->_next; + + if (_firstNode == node) + _firstNode = next; + else + prev->_next = next; + + if (_lastNode == node) + _lastNode = prev; + else + next->_prev = prev; + + node->_prev = nullptr; + node->_next = nullptr; + + if (_cursor == node) + _cursor = prev; + CodeBuilder_nodeRemoved(this, node); + + return node; +} + +void CodeBuilder::removeNodes(CBNode* first, CBNode* last) noexcept { + if (first == last) { + removeNode(first); + return; + } + + CBNode* prev = first->_prev; + CBNode* next = last->_next; + + if (_firstNode == first) + _firstNode = next; + else + prev->_next = next; + + if (_lastNode == last) + _lastNode = prev; + else + next->_prev = prev; + + CBNode* node = first; + for (;;) { + CBNode* next = node->getNext(); + ASMJIT_ASSERT(next != nullptr); + + node->_prev = nullptr; + node->_next = nullptr; + + if (_cursor == node) + _cursor = prev; + CodeBuilder_nodeRemoved(this, node); + + if (node == last) + break; + node = next; + } +} + +CBNode* CodeBuilder::setCursor(CBNode* node) noexcept { + CBNode* old = _cursor; + _cursor = node; + return old; +} + +// ============================================================================ +// [asmjit::CodeBuilder - Passes] +// ============================================================================ + +ASMJIT_FAVOR_SIZE CBPass* CodeBuilder::getPassByName(const char* name) const noexcept { + for (size_t i = 0, len = _cbPasses.getLength(); i < len; i++) { + CBPass* pass = _cbPasses[i]; + if (::strcmp(pass->getName(), name) == 0) + return pass; + } + + return nullptr; +} + +ASMJIT_FAVOR_SIZE Error CodeBuilder::addPass(CBPass* pass) noexcept { + if (ASMJIT_UNLIKELY(pass == nullptr)) { + // Since this is directly called by `addPassT()` we treat `null` argument + // as out-of-memory condition. Otherwise it would be API misuse. + return DebugUtils::errored(kErrorNoHeapMemory); + } + else if (ASMJIT_UNLIKELY(pass->_cb)) { + // Kind of weird, but okay... + if (pass->_cb == this) + return kErrorOk; + return DebugUtils::errored(kErrorInvalidState); + } + + ASMJIT_PROPAGATE(_cbPasses.append(&_cbHeap, pass)); + pass->_cb = this; + return kErrorOk; +} + +ASMJIT_FAVOR_SIZE Error CodeBuilder::deletePass(CBPass* pass) noexcept { + if (ASMJIT_UNLIKELY(pass == nullptr)) + return DebugUtils::errored(kErrorInvalidArgument); + + if (pass->_cb != nullptr) { + if (pass->_cb != this) + return DebugUtils::errored(kErrorInvalidState); + + size_t index = _cbPasses.indexOf(pass); + ASMJIT_ASSERT(index != Globals::kInvalidIndex); + + pass->_cb = nullptr; + _cbPasses.removeAt(index); + } + + pass->~CBPass(); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeBuilder - Serialization] +// ============================================================================ + +Error CodeBuilder::serialize(CodeEmitter* dst) { + Error err = kErrorOk; + CBNode* node_ = getFirstNode(); + + do { + dst->setInlineComment(node_->getInlineComment()); + + switch (node_->getType()) { + case CBNode::kNodeAlign: { + CBAlign* node = static_cast(node_); + err = dst->align(node->getMode(), node->getAlignment()); + break; + } + + case CBNode::kNodeData: { + CBData* node = static_cast(node_); + err = dst->embed(node->getData(), node->getSize()); + break; + } + + case CBNode::kNodeFunc: + case CBNode::kNodeLabel: { + CBLabel* node = static_cast(node_); + err = dst->bind(node->getLabel()); + break; + } + + case CBNode::kNodeLabelData: { + CBLabelData* node = static_cast(node_); + err = dst->embedLabel(node->getLabel()); + break; + } + + case CBNode::kNodeConstPool: { + CBConstPool* node = static_cast(node_); + err = dst->embedConstPool(node->getLabel(), node->getConstPool()); + break; + } + + case CBNode::kNodeInst: + case CBNode::kNodeFuncCall: { + CBInst* node = static_cast(node_); + + uint32_t instId = node->getInstId(); + uint32_t options = node->getOptions(); + + const Operand* opArray = node->getOpArray(); + uint32_t opCount = node->getOpCount(); + + const Operand_* o0 = &dst->_none; + const Operand_* o1 = &dst->_none; + const Operand_* o2 = &dst->_none; + const Operand_* o3 = &dst->_none; + + switch (opCount) { + case 6: dst->_op5 = opArray[5]; options |= CodeEmitter::kOptionOp5; ASMJIT_FALLTHROUGH; + case 5: dst->_op4 = opArray[4]; options |= CodeEmitter::kOptionOp4; ASMJIT_FALLTHROUGH; + case 4: o3 = &opArray[3]; ASMJIT_FALLTHROUGH; + case 3: o2 = &opArray[2]; ASMJIT_FALLTHROUGH; + case 2: o1 = &opArray[1]; ASMJIT_FALLTHROUGH; + case 1: o0 = &opArray[0]; ASMJIT_FALLTHROUGH; + case 0: break; + } + + dst->setOptions(options); + err = dst->_emit(instId, *o0, *o1, *o2, *o3); + break; + } + + case CBNode::kNodeComment: { + CBComment* node = static_cast(node_); + err = dst->comment(node->getInlineComment()); + break; + } + + default: + break; + } + + if (err) break; + node_ = node_->getNext(); + } while (node_); + + return err; +} + +// ============================================================================ +// [asmjit::CBPass] +// ============================================================================ + +CBPass::CBPass(const char* name) noexcept + : _cb(nullptr), + _name(name) {} +CBPass::~CBPass() noexcept {} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_BUILDER diff --git a/src/asmjit/base/codebuilder.h b/src/asmjit/base/codebuilder.h new file mode 100644 index 0000000..365070a --- /dev/null +++ b/src/asmjit/base/codebuilder.h @@ -0,0 +1,915 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODEBUILDER_H +#define _ASMJIT_BASE_CODEBUILDER_H + +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_BUILDER) + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/codeholder.h" +#include "../base/constpool.h" +#include "../base/operand.h" +#include "../base/utils.h" +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class CBNode; +class CBPass; + +class CBAlign; +class CBComment; +class CBConstPool; +class CBData; +class CBInst; +class CBJump; +class CBLabel; +class CBLabelData; +class CBSentinel; + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::CodeBuilder] +// ============================================================================ + +class ASMJIT_VIRTAPI CodeBuilder : public CodeEmitter { +public: + ASMJIT_NONCOPYABLE(CodeBuilder) + typedef CodeEmitter Base; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CodeBuilder` instance. + ASMJIT_API CodeBuilder() noexcept; + //! Destroy the `CodeBuilder` instance. + ASMJIT_API virtual ~CodeBuilder() noexcept; + + // -------------------------------------------------------------------------- + // [Events] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error onAttach(CodeHolder* code) noexcept override; + ASMJIT_API virtual Error onDetach(CodeHolder* code) noexcept override; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get a vector of CBPass objects that will be executed by `process()`. + ASMJIT_INLINE const ZoneVector& getPasses() const noexcept { return _cbPasses; } + + //! Get a vector of CBLabel nodes. + //! + //! NOTE: If a label of some index is not associated with `CodeBuilder` it + //! would be null, so always check for nulls if you iterate over the vector. + ASMJIT_INLINE const ZoneVector& getLabels() const noexcept { return _cbLabels; } + + //! Get the first node. + ASMJIT_INLINE CBNode* getFirstNode() const noexcept { return _firstNode; } + //! Get the last node. + ASMJIT_INLINE CBNode* getLastNode() const noexcept { return _lastNode; } + + // -------------------------------------------------------------------------- + // [Node-Management] + // -------------------------------------------------------------------------- + + //! \internal + template + ASMJIT_INLINE T* newNodeT() noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this); } + + //! \internal + template + ASMJIT_INLINE T* newNodeT(P0 p0) noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this, p0); } + + //! \internal + template + ASMJIT_INLINE T* newNodeT(P0 p0, P1 p1) noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this, p0, p1); } + + //! \internal + template + ASMJIT_INLINE T* newNodeT(P0 p0, P1 p1, P2 p2) noexcept { return new(_cbHeap.alloc(sizeof(T))) T(this, p0, p1, p2); } + + ASMJIT_API Error registerLabelNode(CBLabel* node) noexcept; + //! Get `CBLabel` by `id`. + ASMJIT_API Error getCBLabel(CBLabel** pOut, uint32_t id) noexcept; + //! Get `CBLabel` by `label`. + ASMJIT_INLINE Error getCBLabel(CBLabel** pOut, const Label& label) noexcept { return getCBLabel(pOut, label.getId()); } + + //! Create a new \ref CBLabel node. + ASMJIT_API CBLabel* newLabelNode() noexcept; + //! Create a new \ref CBAlign node. + ASMJIT_API CBAlign* newAlignNode(uint32_t mode, uint32_t alignment) noexcept; + //! Create a new \ref CBData node. + ASMJIT_API CBData* newDataNode(const void* data, uint32_t size) noexcept; + //! Create a new \ref CBConstPool node. + ASMJIT_API CBConstPool* newConstPool() noexcept; + //! Create a new \ref CBComment node. + ASMJIT_API CBComment* newCommentNode(const char* s, size_t len) noexcept; + + // -------------------------------------------------------------------------- + // [Code-Emitter] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Label newLabel() override; + ASMJIT_API virtual Label newNamedLabel(const char* name, size_t nameLength = Globals::kInvalidIndex, uint32_t type = Label::kTypeGlobal, uint32_t parentId = kInvalidValue) override; + ASMJIT_API virtual Error bind(const Label& label) override; + ASMJIT_API virtual Error align(uint32_t mode, uint32_t alignment) override; + ASMJIT_API virtual Error embed(const void* data, uint32_t size) override; + ASMJIT_API virtual Error embedLabel(const Label& label) override; + ASMJIT_API virtual Error embedConstPool(const Label& label, const ConstPool& pool) override; + ASMJIT_API virtual Error comment(const char* s, size_t len = Globals::kInvalidIndex) override; + + // -------------------------------------------------------------------------- + // [Node-Management] + // -------------------------------------------------------------------------- + + //! Add `node` after the current and set current to `node`. + ASMJIT_API CBNode* addNode(CBNode* node) noexcept; + //! Insert `node` after `ref`. + ASMJIT_API CBNode* addAfter(CBNode* node, CBNode* ref) noexcept; + //! Insert `node` before `ref`. + ASMJIT_API CBNode* addBefore(CBNode* node, CBNode* ref) noexcept; + //! Remove `node`. + ASMJIT_API CBNode* removeNode(CBNode* node) noexcept; + //! Remove multiple nodes. + ASMJIT_API void removeNodes(CBNode* first, CBNode* last) noexcept; + + //! Get current node. + //! + //! \note If this method returns null it means that nothing has been + //! emitted yet. + ASMJIT_INLINE CBNode* getCursor() const noexcept { return _cursor; } + //! Set the current node without returning the previous node. + ASMJIT_INLINE void _setCursor(CBNode* node) noexcept { _cursor = node; } + //! Set the current node to `node` and return the previous one. + ASMJIT_API CBNode* setCursor(CBNode* node) noexcept; + + // -------------------------------------------------------------------------- + // [Passes] + // -------------------------------------------------------------------------- + + template + ASMJIT_INLINE T* newPassT() noexcept { return new(_cbBaseZone.alloc(sizeof(T))) T(); } + template + ASMJIT_INLINE T* newPassT(P0 p0) noexcept { return new(_cbBaseZone.alloc(sizeof(T))) T(p0); } + template + ASMJIT_INLINE T* newPassT(P0 p0, P1 p1) noexcept { return new(_cbBaseZone.alloc(sizeof(T))) T(p0, p1); } + + template + ASMJIT_INLINE Error addPassT() noexcept { return addPass(newPassT()); } + template + ASMJIT_INLINE Error addPassT(P0 p0) noexcept { return addPass(newPassT(p0)); } + template + ASMJIT_INLINE Error addPassT(P0 p0, P1 p1) noexcept { return addPass(newPassT(p0, p1)); } + + //! Get a `CBPass` by name. + ASMJIT_API CBPass* getPassByName(const char* name) const noexcept; + //! Add `pass` to the list of passes. + ASMJIT_API Error addPass(CBPass* pass) noexcept; + //! Remove `pass` from the list of passes and delete it. + ASMJIT_API Error deletePass(CBPass* pass) noexcept; + + // -------------------------------------------------------------------------- + // [Serialization] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error serialize(CodeEmitter* dst); + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Zone _cbBaseZone; //!< Base zone used to allocate nodes and `CBPass`. + Zone _cbDataZone; //!< Data zone used to allocate data and names. + Zone _cbPassZone; //!< Zone passed to `CBPass::process()`. + ZoneHeap _cbHeap; //!< ZoneHeap that uses `_cbBaseZone`. + + ZoneVector _cbPasses; //!< Array of `CBPass` objects. + ZoneVector _cbLabels; //!< Maps label indexes to `CBLabel` nodes. + + CBNode* _firstNode; //!< First node of the current section. + CBNode* _lastNode; //!< Last node of the current section. + CBNode* _cursor; //!< Current node (cursor). + + uint32_t _position; //!< Flow-id assigned to each new node. + uint32_t _nodeFlags; //!< Flags assigned to each new node. +}; + +// ============================================================================ +// [asmjit::CBPass] +// ============================================================================ + +//! `CodeBuilder` pass used to code transformations, analysis, and lowering. +class ASMJIT_VIRTAPI CBPass { +public: + ASMJIT_NONCOPYABLE(CBPass); + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_API CBPass(const char* name) noexcept; + ASMJIT_API virtual ~CBPass() noexcept; + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + //! Process the code stored in CodeBuffer `cb`. + //! + //! This is the only function that is called by the `CodeBuilder` to process + //! the code. It passes the CodeBuilder itself (`cb`) and also a zone memory + //! allocator `zone`, which will be reset after the `process()` returns. The + //! allocator should be used for all allocations as it's fast and everything + //! it allocates will be released at once when `process()` returns. + virtual Error process(Zone* zone) noexcept = 0; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE const CodeBuilder* cb() const noexcept { return _cb; } + ASMJIT_INLINE const char* getName() const noexcept { return _name; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CodeBuilder* _cb; //!< CodeBuilder this pass is assigned to. + const char* _name; //!< Name of the pass. +}; + +// ============================================================================ +// [asmjit::CBNode] +// ============================================================================ + +//! Node (CodeBuilder). +//! +//! Every node represents a building-block used by \ref CodeBuilder. It can be +//! instruction, data, label, comment, directive, or any other high-level +//! representation that can be transformed to the building blocks mentioned. +//! Every class that inherits \ref CodeBuilder can define its own nodes that it +//! can lower to basic nodes. +class CBNode { +public: + ASMJIT_NONCOPYABLE(CBNode) + + // -------------------------------------------------------------------------- + // [Type] + // -------------------------------------------------------------------------- + + //! Type of \ref CBNode. + ASMJIT_ENUM(NodeType) { + kNodeNone = 0, //!< Invalid node (internal, don't use). + + // [CodeBuilder] + kNodeInst = 1, //!< Node is \ref CBInst or \ref CBJump. + kNodeData = 2, //!< Node is \ref CBData. + kNodeAlign = 3, //!< Node is \ref CBAlign. + kNodeLabel = 4, //!< Node is \ref CBLabel. + kNodeLabelData = 5, //!< Node is \ref CBLabelData. + kNodeConstPool = 6, //!< Node is \ref CBConstPool. + kNodeComment = 7, //!< Node is \ref CBComment. + kNodeSentinel = 8, //!< Node is \ref CBSentinel. + + // [CodeCompiler] + kNodeFunc = 16, //!< Node is \ref CCFunc (considered as \ref CBLabel by \ref CodeBuilder). + kNodeFuncExit = 17, //!< Node is \ref CCFuncRet. + kNodeFuncCall = 18, //!< Node is \ref CCFuncCall. + kNodePushArg = 19, //!< Node is \ref CCPushArg. + kNodeHint = 20, //!< Node is \ref CCHint. + + // [UserDefined] + kNodeUser = 32 //!< First id of a user-defined node. + }; + + // -------------------------------------------------------------------------- + // [Flags] + // -------------------------------------------------------------------------- + + ASMJIT_ENUM(Flags) { + //! The node has been translated by the CodeCompiler. + kFlagIsTranslated = 0x0001, + //! If the node can be safely removed (has no effect). + kFlagIsRemovable = 0x0004, + //! If the node is informative only and can be safely removed. + kFlagIsInformative = 0x0008, + + //! If the `CBInst` is a jump. + kFlagIsJmp = 0x0010, + //! If the `CBInst` is a conditional jump. + kFlagIsJcc = 0x0020, + + //! If the `CBInst` is an unconditional jump or conditional jump that is + //! likely to be taken. + kFlagIsTaken = 0x0040, + + //! If the `CBNode` will return from a function. + //! + //! This flag is used by both `CBSentinel` and `CCFuncRet`. + kFlagIsRet = 0x0080, + + //! Whether the instruction is special. + kFlagIsSpecial = 0x0100, + + //! Whether the instruction is an FPU instruction. + kFlagIsFp = 0x0200 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new \ref CBNode - always use \ref CodeBuilder to allocate nodes. + ASMJIT_INLINE CBNode(CodeBuilder* cb, uint32_t type) noexcept { + _prev = nullptr; + _next = nullptr; + _type = static_cast(type); + _opCount = 0; + _flags = static_cast(cb->_nodeFlags); + _position = cb->_position; + _inlineComment = nullptr; + _passData = nullptr; + } + //! Destroy the `CBNode` instance (NEVER CALLED). + ASMJIT_INLINE ~CBNode() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + template + ASMJIT_INLINE T* as() noexcept { return static_cast(this); } + template + ASMJIT_INLINE const T* as() const noexcept { return static_cast(this); } + + //! Get previous node in the compiler stream. + ASMJIT_INLINE CBNode* getPrev() const noexcept { return _prev; } + //! Get next node in the compiler stream. + ASMJIT_INLINE CBNode* getNext() const noexcept { return _next; } + + //! Get the node type, see \ref Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + //! Get the node flags. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + + //! Get whether the instruction has flag `flag`. + ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (static_cast(_flags) & flag) != 0; } + //! Set node flags to `flags`. + ASMJIT_INLINE void setFlags(uint32_t flags) noexcept { _flags = static_cast(flags); } + //! Add instruction `flags`. + ASMJIT_INLINE void orFlags(uint32_t flags) noexcept { _flags |= static_cast(flags); } + //! And instruction `flags`. + ASMJIT_INLINE void andFlags(uint32_t flags) noexcept { _flags &= static_cast(flags); } + //! Clear instruction `flags`. + ASMJIT_INLINE void andNotFlags(uint32_t flags) noexcept { _flags &= ~static_cast(flags); } + + //! Get whether the node has been translated. + ASMJIT_INLINE bool isTranslated() const noexcept { return hasFlag(kFlagIsTranslated); } + + //! Get whether the node is removable if it's in unreachable code block. + ASMJIT_INLINE bool isRemovable() const noexcept { return hasFlag(kFlagIsRemovable); } + //! Get whether the node is informative only (comment, hint). + ASMJIT_INLINE bool isInformative() const noexcept { return hasFlag(kFlagIsInformative); } + + //! Whether the node is `CBLabel`. + ASMJIT_INLINE bool isLabel() const noexcept { return _type == kNodeLabel; } + //! Whether the `CBInst` node is an unconditional jump. + ASMJIT_INLINE bool isJmp() const noexcept { return hasFlag(kFlagIsJmp); } + //! Whether the `CBInst` node is a conditional jump. + ASMJIT_INLINE bool isJcc() const noexcept { return hasFlag(kFlagIsJcc); } + //! Whether the `CBInst` node is a conditional/unconditional jump. + ASMJIT_INLINE bool isJmpOrJcc() const noexcept { return hasFlag(kFlagIsJmp | kFlagIsJcc); } + //! Whether the `CBInst` node is a return. + ASMJIT_INLINE bool isRet() const noexcept { return hasFlag(kFlagIsRet); } + + //! Get whether the node is `CBInst` and the instruction is special. + ASMJIT_INLINE bool isSpecial() const noexcept { return hasFlag(kFlagIsSpecial); } + //! Get whether the node is `CBInst` and the instruction uses x87-FPU. + ASMJIT_INLINE bool isFp() const noexcept { return hasFlag(kFlagIsFp); } + + ASMJIT_INLINE bool hasPosition() const noexcept { return _position != 0; } + //! Get flow index. + ASMJIT_INLINE uint32_t getPosition() const noexcept { return _position; } + //! Set flow index. + ASMJIT_INLINE void setPosition(uint32_t position) noexcept { _position = position; } + + //! Get if the node has an inline comment. + ASMJIT_INLINE bool hasInlineComment() const noexcept { return _inlineComment != nullptr; } + //! Get an inline comment string. + ASMJIT_INLINE const char* getInlineComment() const noexcept { return _inlineComment; } + //! Set an inline comment string to `s`. + ASMJIT_INLINE void setInlineComment(const char* s) noexcept { _inlineComment = s; } + //! Set an inline comment string to null. + ASMJIT_INLINE void resetInlineComment() noexcept { _inlineComment = nullptr; } + + //! Get if the node has associated work-data. + ASMJIT_INLINE bool hasPassData() const noexcept { return _passData != nullptr; } + //! Get work-data - data used during processing & transformations. + template + ASMJIT_INLINE T* getPassData() const noexcept { return (T*)_passData; } + //! Set work-data to `data`. + template + ASMJIT_INLINE void setPassData(T* data) noexcept { _passData = (void*)data; } + //! Reset work-data to null. + ASMJIT_INLINE void resetPassData() noexcept { _passData = nullptr; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CBNode* _prev; //!< Previous node. + CBNode* _next; //!< Next node. + + uint8_t _type; //!< Node type, see \ref NodeType. + uint8_t _opCount; //!< Count of operands or zero. + uint16_t _flags; //!< Flags, different meaning for every type of the node. + uint32_t _position; //!< Flow index. + + const char* _inlineComment; //!< Inline comment or null if not used. + void* _passData; //!< Data used exclusively by the current `CBPass`. +}; + +// ============================================================================ +// [asmjit::CBInst] +// ============================================================================ + +//! Instruction (CodeBuilder). +//! +//! Wraps an instruction with its options and operands. +class CBInst : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBInst) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBInst` instance. + ASMJIT_INLINE CBInst(CodeBuilder* cb, uint32_t instId, uint32_t options, Operand* opArray, uint32_t opCount) noexcept + : CBNode(cb, kNodeInst) { + + orFlags(kFlagIsRemovable); + _instId = static_cast(instId); + _reserved = 0; + _options = options; + + _opCount = static_cast(opCount); + _opArray = opArray; + + _updateMemOp(); + } + + //! Destroy the `CBInst` instance (NEVER CALLED). + ASMJIT_INLINE ~CBInst() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the instruction id, see \ref X86Inst::Id. + ASMJIT_INLINE uint32_t getInstId() const noexcept { return _instId; } + //! Set the instruction id to `instId`. + //! + //! NOTE: Please do not modify instruction code if you don't know what you + //! are doing. Incorrect instruction code and/or operands can cause random + //! errors in production builds and will most probably trigger assertion + //! failures in debug builds. + ASMJIT_INLINE void setInstId(uint32_t instId) noexcept { _instId = static_cast(instId); } + + //! Whether the instruction is either a jump or a conditional jump likely to + //! be taken. + ASMJIT_INLINE bool isTaken() const noexcept { return hasFlag(kFlagIsTaken); } + + //! Get emit options. + ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; } + //! Set emit options. + ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _options = options; } + //! Add emit options. + ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; } + //! Mask emit options. + ASMJIT_INLINE void andOptions(uint32_t options) noexcept { _options &= options; } + //! Clear emit options. + ASMJIT_INLINE void delOptions(uint32_t options) noexcept { _options &= ~options; } + + //! Get op-mask operand (used to represent AVX-512 op-mask selector). + ASMJIT_INLINE Operand& getOpExtra() noexcept { return _opExtra; } + //! \overload + ASMJIT_INLINE const Operand& getOpExtra() const noexcept { return _opExtra; } + //1 Set op-mask operand. + ASMJIT_INLINE void setOpExtra(const Operand& opExtra) noexcept { _opExtra = opExtra; } + + //! Get operands count. + ASMJIT_INLINE uint32_t getOpCount() const noexcept { return _opCount; } + //! Get operands list. + ASMJIT_INLINE Operand* getOpArray() noexcept { return _opArray; } + //! \overload + ASMJIT_INLINE const Operand* getOpArray() const noexcept { return _opArray; } + + //! Get whether the instruction contains a memory operand. + ASMJIT_INLINE bool hasMemOp() const noexcept { return _memOpIndex != 0xFF; } + //! Get memory operand. + //! + //! NOTE: Can only be called if the instruction has such operand, + //! see `hasMemOp()`. + ASMJIT_INLINE Mem* getMemOp() const noexcept { + ASMJIT_ASSERT(hasMemOp()); + return static_cast(&_opArray[_memOpIndex]); + } + //! \overload + template + ASMJIT_INLINE T* getMemOp() const noexcept { + ASMJIT_ASSERT(hasMemOp()); + return static_cast(&_opArray[_memOpIndex]); + } + + //! Set memory operand index, `0xFF` means no memory operand. + ASMJIT_INLINE void setMemOpIndex(uint32_t index) noexcept { _memOpIndex = static_cast(index); } + //! Reset memory operand index to `0xFF` (no operand). + ASMJIT_INLINE void resetMemOpIndex() noexcept { _memOpIndex = 0xFF; } + + // -------------------------------------------------------------------------- + // [Utils] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void _updateMemOp() noexcept { + Operand* opArray = getOpArray(); + uint32_t opCount = getOpCount(); + + uint32_t i; + for (i = 0; i < opCount; i++) + if (opArray[i].isMem()) + goto Update; + i = 0xFF; + +Update: + setMemOpIndex(i); + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint16_t _instId; //!< Instruction id (architecture dependent). + uint8_t _memOpIndex; //!< \internal + uint8_t _reserved; //!< \internal + uint32_t _options; //!< Instruction options. + Operand _opExtra; //!< Extra operand (op-mask {k} on AVX-512). + Operand* _opArray; //!< Instruction operands. +}; + +// ============================================================================ +// [asmjit::CBInstEx] +// ============================================================================ + +struct CBInstEx : public CBInst { + Operand _op4; + Operand _op5; + Operand _opExtra; +}; + +// ============================================================================ +// [asmjit::CBJump] +// ============================================================================ + +//! Asm jump (conditional or direct). +//! +//! Extension of `CBInst` node, which stores more information about the jump. +class CBJump : public CBInst { +public: + ASMJIT_NONCOPYABLE(CBJump) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CBJump(CodeBuilder* cb, uint32_t instId, uint32_t options, Operand* opArray, uint32_t opCount) noexcept + : CBInst(cb, instId, options, opArray, opCount), + _target(nullptr), + _jumpNext(nullptr) {} + ASMJIT_INLINE ~CBJump() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CBLabel* getTarget() const noexcept { return _target; } + ASMJIT_INLINE CBJump* getJumpNext() const noexcept { return _jumpNext; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CBLabel* _target; //!< Target node. + CBJump* _jumpNext; //!< Next jump to the same target in a single linked-list. +}; + +// ============================================================================ +// [asmjit::CBData] +// ============================================================================ + +//! Asm data (CodeBuilder). +//! +//! Wraps `.data` directive. The node contains data that will be placed at the +//! node's position in the assembler stream. The data is considered to be RAW; +//! no analysis nor byte-order conversion is performed on RAW data. +class CBData : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBData) + enum { kInlineBufferSize = static_cast(64 - sizeof(CBNode) - 4) }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBData` instance. + ASMJIT_INLINE CBData(CodeBuilder* cb, void* data, uint32_t size) noexcept : CBNode(cb, kNodeData) { + if (size <= kInlineBufferSize) { + if (data) ::memcpy(_buf, data, size); + } + else { + _externalPtr = static_cast(data); + } + _size = size; + } + + //! Destroy the `CBData` instance (NEVER CALLED). + ASMJIT_INLINE ~CBData() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get size of the data. + uint32_t getSize() const noexcept { return _size; } + //! Get pointer to the data. + uint8_t* getData() const noexcept { return _size <= kInlineBufferSize ? const_cast(_buf) : _externalPtr; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + union { + struct { + uint8_t _buf[kInlineBufferSize]; //!< Embedded data buffer. + uint32_t _size; //!< Size of the data. + }; + struct { + uint8_t* _externalPtr; //!< Pointer to external data. + }; + }; +}; + +// ============================================================================ +// [asmjit::CBAlign] +// ============================================================================ + +//! Align directive (CodeBuilder). +//! +//! Wraps `.align` directive. +class CBAlign : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBAlign) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBAlign` instance. + ASMJIT_INLINE CBAlign(CodeBuilder* cb, uint32_t mode, uint32_t alignment) noexcept + : CBNode(cb, kNodeAlign), + _mode(mode), + _alignment(alignment) {} + //! Destroy the `CBAlign` instance (NEVER CALLED). + ASMJIT_INLINE ~CBAlign() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get align mode. + ASMJIT_INLINE uint32_t getMode() const noexcept { return _mode; } + //! Set align mode. + ASMJIT_INLINE void setMode(uint32_t mode) noexcept { _mode = mode; } + + //! Get align offset in bytes. + ASMJIT_INLINE uint32_t getAlignment() const noexcept { return _alignment; } + //! Set align offset in bytes to `offset`. + ASMJIT_INLINE void setAlignment(uint32_t alignment) noexcept { _alignment = alignment; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _mode; //!< Align mode, see \ref AlignMode. + uint32_t _alignment; //!< Alignment (in bytes). +}; + +// ============================================================================ +// [asmjit::CBLabel] +// ============================================================================ + +//! Label (CodeBuilder). +class CBLabel : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBLabel) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBLabel` instance. + ASMJIT_INLINE CBLabel(CodeBuilder* cb, uint32_t id = kInvalidValue) noexcept + : CBNode(cb, kNodeLabel), + _id(id), + _numRefs(0), + _from(nullptr) {} + //! Destroy the `CBLabel` instance (NEVER CALLED). + ASMJIT_INLINE ~CBLabel() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the label id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Get the label as `Label` operand. + ASMJIT_INLINE Label getLabel() const noexcept { return Label(_id); } + + //! Get first jmp instruction. + ASMJIT_INLINE CBJump* getFrom() const noexcept { return _from; } + + //! Get number of jumps to this target. + ASMJIT_INLINE uint32_t getNumRefs() const noexcept { return _numRefs; } + //! Set number of jumps to this target. + ASMJIT_INLINE void setNumRefs(uint32_t i) noexcept { _numRefs = i; } + + //! Add number of jumps to this target. + ASMJIT_INLINE void addNumRefs(uint32_t i = 1) noexcept { _numRefs += i; } + //! Subtract number of jumps to this target. + ASMJIT_INLINE void subNumRefs(uint32_t i = 1) noexcept { _numRefs -= i; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; //!< Label id. + uint32_t _numRefs; //!< Count of jumps here. + CBJump* _from; //!< Linked-list of nodes that can jump here. +}; + +// ============================================================================ +// [asmjit::CBLabelData] +// ============================================================================ + +class CBLabelData : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBLabelData) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBLabelData` instance. + ASMJIT_INLINE CBLabelData(CodeBuilder* cb, uint32_t id = kInvalidValue) noexcept + : CBNode(cb, kNodeLabelData), + _id(id) {} + + //! Destroy the `CBLabelData` instance (NEVER CALLED). + ASMJIT_INLINE ~CBLabelData() noexcept {} + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + //! Get the label id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Get the label as `Label` operand. + ASMJIT_INLINE Label getLabel() const noexcept { return Label(_id); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; +}; + +// ============================================================================ +// [asmjit::CBConstPool] +// ============================================================================ + +class CBConstPool : public CBLabel { +public: + ASMJIT_NONCOPYABLE(CBConstPool) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBConstPool` instance. + ASMJIT_INLINE CBConstPool(CodeBuilder* cb, uint32_t id = kInvalidValue) noexcept + : CBLabel(cb, id), + _constPool(&cb->_cbBaseZone) { _type = kNodeConstPool; } + + //! Destroy the `CBConstPool` instance (NEVER CALLED). + ASMJIT_INLINE ~CBConstPool() noexcept {} + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ConstPool& getConstPool() noexcept { return _constPool; } + ASMJIT_INLINE const ConstPool& getConstPool() const noexcept { return _constPool; } + + //! Get whether the constant-pool is empty. + ASMJIT_INLINE bool isEmpty() const noexcept { return _constPool.isEmpty(); } + //! Get the size of the constant-pool in bytes. + ASMJIT_INLINE size_t getSize() const noexcept { return _constPool.getSize(); } + //! Get minimum alignment. + ASMJIT_INLINE size_t getAlignment() const noexcept { return _constPool.getAlignment(); } + + //! See \ref ConstPool::add(). + ASMJIT_INLINE Error add(const void* data, size_t size, size_t& dstOffset) noexcept { + return _constPool.add(data, size, dstOffset); + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ConstPool _constPool; +}; + +// ============================================================================ +// [asmjit::CBComment] +// ============================================================================ + +//! Comment (CodeBuilder). +class CBComment : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBComment) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBComment` instance. + ASMJIT_INLINE CBComment(CodeBuilder* cb, const char* comment) noexcept : CBNode(cb, kNodeComment) { + orFlags(kFlagIsRemovable | kFlagIsInformative); + _inlineComment = comment; + } + + //! Destroy the `CBComment` instance (NEVER CALLED). + ASMJIT_INLINE ~CBComment() noexcept {} +}; + +// ============================================================================ +// [asmjit::CBSentinel] +// ============================================================================ + +//! Sentinel (CodeBuilder). +//! +//! Sentinel is a marker that is completely ignored by the code builder. It's +//! used to remember a position in a code as it never gets removed by any pass. +class CBSentinel : public CBNode { +public: + ASMJIT_NONCOPYABLE(CBSentinel) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CBSentinel` instance. + ASMJIT_INLINE CBSentinel(CodeBuilder* cb) noexcept : CBNode(cb, kNodeSentinel) {} + //! Destroy the `CBSentinel` instance (NEVER CALLED). + ASMJIT_INLINE ~CBSentinel() noexcept {} +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_BUILDER +#endif // _ASMJIT_BASE_CODEBUILDER_H diff --git a/src/asmjit/base/codecompiler.cpp b/src/asmjit/base/codecompiler.cpp new file mode 100644 index 0000000..15bf9dd --- /dev/null +++ b/src/asmjit/base/codecompiler.cpp @@ -0,0 +1,571 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Guard] +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/codecompiler.h" +#include "../base/cpuinfo.h" +#include "../base/logging.h" +#include "../base/regalloc_p.h" +#include "../base/utils.h" +#include + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [Constants] +// ============================================================================ + +static const char noName[1] = { '\0' }; + +// ============================================================================ +// [asmjit::CCFuncCall - Arg / Ret] +// ============================================================================ + +bool CCFuncCall::_setArg(uint32_t i, const Operand_& op) noexcept { + if ((i & ~kFuncArgHi) >= _funcDetail.getArgCount()) + return false; + + _args[i] = op; + return true; +} + +bool CCFuncCall::_setRet(uint32_t i, const Operand_& op) noexcept { + if (i >= 2) + return false; + + _ret[i] = op; + return true; +} + +// ============================================================================ +// [asmjit::CodeCompiler - Construction / Destruction] +// ============================================================================ + +CodeCompiler::CodeCompiler() noexcept + : CodeBuilder(), + _func(nullptr), + _vRegZone(4096 - Zone::kZoneOverhead), + _vRegArray(), + _localConstPool(nullptr), + _globalConstPool(nullptr) { + + _type = kTypeCompiler; +} +CodeCompiler::~CodeCompiler() noexcept {} + +// ============================================================================ +// [asmjit::CodeCompiler - Events] +// ============================================================================ + +Error CodeCompiler::onAttach(CodeHolder* code) noexcept { + return Base::onAttach(code); +} + +Error CodeCompiler::onDetach(CodeHolder* code) noexcept { + _func = nullptr; + + _localConstPool = nullptr; + _globalConstPool = nullptr; + + _vRegArray.reset(); + _vRegZone.reset(false); + + return Base::onDetach(code); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Node-Factory] +// ============================================================================ + +CCHint* CodeCompiler::newHintNode(Reg& r, uint32_t hint, uint32_t value) noexcept { + if (!r.isVirtReg()) return nullptr; + + VirtReg* vr = getVirtReg(r); + return newNodeT(vr, hint, value); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Func] +// ============================================================================ + +CCFunc* CodeCompiler::newFunc(const FuncSignature& sign) noexcept { + Error err; + + CCFunc* func = newNodeT(); + if (!func) goto _NoMemory; + + err = registerLabelNode(func); + if (ASMJIT_UNLIKELY(err)) { + // TODO: Calls setLastError, maybe rethink noexcept? + setLastError(err); + return nullptr; + } + + // Create helper nodes. + func->_end = newNodeT(); + func->_exitNode = newLabelNode(); + if (!func->_exitNode || !func->_end) goto _NoMemory; + + // Function prototype. + err = func->getDetail().init(sign); + if (err != kErrorOk) { + setLastError(err); + return nullptr; + } + + // Override the natural stack alignment of the calling convention to what's + // specified by CodeInfo. + func->_funcDetail._callConv.setNaturalStackAlignment(_codeInfo.getStackAlignment()); + + // Allocate space for function arguments. + func->_args = nullptr; + if (func->getArgCount() != 0) { + func->_args = _cbHeap.allocT(func->getArgCount() * sizeof(VirtReg*)); + if (!func->_args) goto _NoMemory; + + ::memset(func->_args, 0, func->getArgCount() * sizeof(VirtReg*)); + } + + return func; + +_NoMemory: + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; +} + +CCFunc* CodeCompiler::addFunc(CCFunc* func) { + ASMJIT_ASSERT(_func == nullptr); + _func = func; + + addNode(func); // Function node. + CBNode* cursor = getCursor(); // {CURSOR}. + addNode(func->getExitNode()); // Function exit label. + addNode(func->getEnd()); // Function end marker. + + _setCursor(cursor); + return func; +} + +CCFunc* CodeCompiler::addFunc(const FuncSignature& sign) { + CCFunc* func = newFunc(sign); + + if (!func) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; + } + + return addFunc(func); +} + +CBSentinel* CodeCompiler::endFunc() { + CCFunc* func = getFunc(); + if (!func) { + // TODO: + return nullptr; + } + + // Add the local constant pool at the end of the function (if exist). + setCursor(func->getExitNode()); + + if (_localConstPool) { + addNode(_localConstPool); + _localConstPool = nullptr; + } + + // Mark as finished. + func->_isFinished = true; + _func = nullptr; + + setCursor(func->getEnd()); + return func->getEnd(); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Ret] +// ============================================================================ + +CCFuncRet* CodeCompiler::newRet(const Operand_& o0, const Operand_& o1) noexcept { + CCFuncRet* node = newNodeT(o0, o1); + if (!node) { + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; + } + return node; +} + +CCFuncRet* CodeCompiler::addRet(const Operand_& o0, const Operand_& o1) noexcept { + CCFuncRet* node = newRet(o0, o1); + if (!node) return nullptr; + return static_cast(addNode(node)); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Call] +// ============================================================================ + +CCFuncCall* CodeCompiler::newCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept { + Error err; + uint32_t nArgs; + + CCFuncCall* node = _cbHeap.allocT(sizeof(CCFuncCall) + sizeof(Operand)); + Operand* opArray = reinterpret_cast(reinterpret_cast(node) + sizeof(CCFuncCall)); + + if (ASMJIT_UNLIKELY(!node)) + goto _NoMemory; + + opArray[0].copyFrom(o0); + new (node) CCFuncCall(this, instId, 0, opArray, 1); + + if ((err = node->getDetail().init(sign)) != kErrorOk) { + setLastError(err); + return nullptr; + } + + // If there are no arguments skip the allocation. + if ((nArgs = sign.getArgCount()) == 0) + return node; + + node->_args = static_cast(_cbHeap.alloc(nArgs * sizeof(Operand))); + if (!node->_args) goto _NoMemory; + + ::memset(node->_args, 0, nArgs * sizeof(Operand)); + return node; + +_NoMemory: + setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; +} + +CCFuncCall* CodeCompiler::addCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept { + CCFuncCall* node = newCall(instId, o0, sign); + if (!node) return nullptr; + return static_cast(addNode(node)); +} + +// ============================================================================ +// [asmjit::CodeCompiler - Vars] +// ============================================================================ + +Error CodeCompiler::setArg(uint32_t argIndex, const Reg& r) { + CCFunc* func = getFunc(); + + if (!func) + return setLastError(DebugUtils::errored(kErrorInvalidState)); + + if (!isVirtRegValid(r)) + return setLastError(DebugUtils::errored(kErrorInvalidVirtId)); + + VirtReg* vr = getVirtReg(r); + func->setArg(argIndex, vr); + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeCompiler - Hint] +// ============================================================================ + +Error CodeCompiler::_hint(Reg& r, uint32_t hint, uint32_t value) { + if (!r.isVirtReg()) return kErrorOk; + + CCHint* node = newHintNode(r, hint, value); + if (!node) return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + addNode(node); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeCompiler - Vars] +// ============================================================================ + +VirtReg* CodeCompiler::newVirtReg(uint32_t typeId, uint32_t signature, const char* name) noexcept { + size_t index = _vRegArray.getLength(); + if (ASMJIT_UNLIKELY(index > Operand::kPackedIdCount)) + return nullptr; + + VirtReg* vreg; + if (_vRegArray.willGrow(&_cbHeap, 1) != kErrorOk || !(vreg = _vRegZone.allocZeroedT())) + return nullptr; + + vreg->_id = Operand::packId(static_cast(index)); + vreg->_regInfo._signature = signature; + vreg->_name = noName; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (name && name[0] != '\0') + vreg->_name = static_cast(_cbDataZone.dup(name, ::strlen(name), true)); +#endif // !ASMJIT_DISABLE_LOGGING + + vreg->_size = TypeId::sizeOf(typeId); + vreg->_typeId = typeId; + vreg->_alignment = static_cast(std::min(vreg->_size, 64)); + vreg->_priority = 10; + + // The following are only used by `RAPass`. + vreg->_raId = kInvalidValue; + vreg->_state = VirtReg::kStateNone; + vreg->_physId = Globals::kInvalidRegId; + + _vRegArray.appendUnsafe(vreg); + return vreg; +} + +Error CodeCompiler::_newReg(Reg& out, uint32_t typeId, const char* name) { + RegInfo regInfo; + + Error err = ArchUtils::typeIdToRegInfo(getArchType(), typeId, regInfo); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + VirtReg* vReg = newVirtReg(typeId, regInfo.getSignature(), name); + if (ASMJIT_UNLIKELY(!vReg)) { + out.reset(); + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + + out._initReg(regInfo.getSignature(), vReg->getId()); + return kErrorOk; +} + +Error CodeCompiler::_newReg(Reg& out, uint32_t typeId, const char* nameFmt, va_list ap) { + StringBuilderTmp<256> sb; + sb.appendFormatVA(nameFmt, ap); + return _newReg(out, typeId, sb.getData()); +} + +Error CodeCompiler::_newReg(Reg& out, const Reg& ref, const char* name) { + RegInfo regInfo; + uint32_t typeId; + + if (isVirtRegValid(ref)) { + VirtReg* vRef = getVirtReg(ref); + typeId = vRef->getTypeId(); + + // NOTE: It's possible to cast one register type to another if it's the + // same register kind. However, VirtReg always contains the TypeId that + // was used to create the register. This means that in some cases we may + // end up having different size of `ref` and `vRef`. In such case we + // adjust the TypeId to match the `ref` register type instead of the + // original register type, which should be the expected behavior. + uint32_t typeSize = TypeId::sizeOf(typeId); + uint32_t refSize = ref.getSize(); + + if (typeSize != refSize) { + if (TypeId::isInt(typeId)) { + // GP register - change TypeId to match `ref`, but keep sign of `vRef`. + switch (refSize) { + case 1: typeId = TypeId::kI8 | (typeId & 1); break; + case 2: typeId = TypeId::kI16 | (typeId & 1); break; + case 4: typeId = TypeId::kI32 | (typeId & 1); break; + case 8: typeId = TypeId::kI64 | (typeId & 1); break; + default: typeId = TypeId::kVoid; break; + } + } + else if (TypeId::isMmx(typeId)) { + // MMX register - always use 64-bit. + typeId = TypeId::kMmx64; + } + else if (TypeId::isMask(typeId)) { + // Mask register - change TypeId to match `ref` size. + switch (refSize) { + case 1: typeId = TypeId::kMask8; break; + case 2: typeId = TypeId::kMask16; break; + case 4: typeId = TypeId::kMask32; break; + case 8: typeId = TypeId::kMask64; break; + default: typeId = TypeId::kVoid; break; + } + } + else { + // VEC register - change TypeId to match `ref` size, keep vector metadata. + uint32_t elementTypeId = TypeId::elementOf(typeId); + + switch (refSize) { + case 16: typeId = TypeId::_kVec128Start + (elementTypeId - TypeId::kI8); break; + case 32: typeId = TypeId::_kVec256Start + (elementTypeId - TypeId::kI8); break; + case 64: typeId = TypeId::_kVec512Start + (elementTypeId - TypeId::kI8); break; + default: typeId = TypeId::kVoid; break; + } + } + + if (typeId == TypeId::kVoid) + return setLastError(DebugUtils::errored(kErrorInvalidState)); + } + } + else { + typeId = ref.getType(); + } + + Error err = ArchUtils::typeIdToRegInfo(getArchType(), typeId, regInfo); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + VirtReg* vReg = newVirtReg(typeId, regInfo.getSignature(), name); + if (ASMJIT_UNLIKELY(!vReg)) { + out.reset(); + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + + out._initReg(regInfo.getSignature(), vReg->getId()); + return kErrorOk; +} + +Error CodeCompiler::_newReg(Reg& out, const Reg& ref, const char* nameFmt, va_list ap) { + StringBuilderTmp<256> sb; + sb.appendFormatVA(nameFmt, ap); + return _newReg(out, ref, sb.getData()); +} + +Error CodeCompiler::_newStack(Mem& out, uint32_t size, uint32_t alignment, const char* name) { + if (size == 0) + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + if (alignment == 0) alignment = 1; + if (!Utils::isPowerOf2(alignment)) + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + if (alignment > 64) alignment = 64; + + VirtReg* vReg = newVirtReg(0, 0, name); + if (ASMJIT_UNLIKELY(!vReg)) { + out.reset(); + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + } + + vReg->_size = size; + vReg->_isStack = true; + vReg->_alignment = static_cast(alignment); + + // Set the memory operand to GPD/GPQ and its id to VirtReg. + out = Mem(Init, _nativeGpReg.getType(), vReg->getId(), Reg::kRegNone, kInvalidValue, 0, 0, Mem::kSignatureMemRegHomeFlag); + return kErrorOk; +} + +Error CodeCompiler::_newConst(Mem& out, uint32_t scope, const void* data, size_t size) { + CBConstPool** pPool; + if (scope == kConstScopeLocal) + pPool = &_localConstPool; + else if (scope == kConstScopeGlobal) + pPool = &_globalConstPool; + else + return setLastError(DebugUtils::errored(kErrorInvalidArgument)); + + if (!*pPool && !(*pPool = newConstPool())) + return setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + + CBConstPool* pool = *pPool; + size_t off; + + Error err = pool->add(data, size, off); + if (ASMJIT_UNLIKELY(err)) return setLastError(err); + + out = Mem(Init, + Label::kLabelTag, // Base type. + pool->getId(), // Base id. + 0, // Index type. + kInvalidValue, // Index id. + static_cast(off), // Offset. + static_cast(size), // Size. + 0); // Flags. + return kErrorOk; +} + +Error CodeCompiler::alloc(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintAlloc, kInvalidValue); +} + +Error CodeCompiler::alloc(Reg& reg, uint32_t physId) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintAlloc, physId); +} + +Error CodeCompiler::alloc(Reg& reg, const Reg& physReg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintAlloc, physReg.getId()); +} + +Error CodeCompiler::save(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintSave, kInvalidValue); +} + +Error CodeCompiler::spill(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintSpill, kInvalidValue); +} + +Error CodeCompiler::unuse(Reg& reg) { + if (!reg.isVirtReg()) return kErrorOk; + return _hint(reg, CCHint::kHintUnuse, kInvalidValue); +} + +uint32_t CodeCompiler::getPriority(Reg& reg) const { + if (!reg.isVirtReg()) return 0; + return getVirtRegById(reg.getId())->getPriority(); +} + +void CodeCompiler::setPriority(Reg& reg, uint32_t priority) { + if (!reg.isVirtReg()) return; + if (priority > 255) priority = 255; + + VirtReg* vreg = getVirtRegById(reg.getId()); + if (vreg) vreg->_priority = static_cast(priority); +} + +bool CodeCompiler::getSaveOnUnuse(Reg& reg) const { + if (!reg.isVirtReg()) return false; + + VirtReg* vreg = getVirtRegById(reg.getId()); + return static_cast(vreg->_saveOnUnuse); +} + +void CodeCompiler::setSaveOnUnuse(Reg& reg, bool value) { + if (!reg.isVirtReg()) return; + + VirtReg* vreg = getVirtRegById(reg.getId()); + if (!vreg) return; + + vreg->_saveOnUnuse = value; +} + +void CodeCompiler::rename(Reg& reg, const char* fmt, ...) { + if (!reg.isVirtReg()) return; + + VirtReg* vreg = getVirtRegById(reg.getId()); + if (!vreg) return; + + vreg->_name = noName; + if (fmt && fmt[0] != '\0') { + char buf[64]; + + va_list ap; + va_start(ap, fmt); + + vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap); + buf[ASMJIT_ARRAY_SIZE(buf) - 1] = '\0'; + + vreg->_name = static_cast(_cbDataZone.dup(buf, ::strlen(buf), true)); + va_end(ap); + } +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER diff --git a/src/asmjit/base/codecompiler.h b/src/asmjit/base/codecompiler.h new file mode 100644 index 0000000..e149f2b --- /dev/null +++ b/src/asmjit/base/codecompiler.h @@ -0,0 +1,738 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODECOMPILER_H +#define _ASMJIT_BASE_CODECOMPILER_H + +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/codebuilder.h" +#include "../base/constpool.h" +#include "../base/func.h" +#include "../base/operand.h" +#include "../base/utils.h" +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +struct VirtReg; +struct TiedReg; +struct RAState; +struct RACell; + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::ConstScope] +// ============================================================================ + +//! Scope of the constant. +ASMJIT_ENUM(ConstScope) { + //! Local constant, always embedded right after the current function. + kConstScopeLocal = 0, + //! Global constant, embedded at the end of the currently compiled code. + kConstScopeGlobal = 1 +}; + +// ============================================================================ +// [asmjit::VirtReg] +// ============================================================================ + +//! Virtual register data (CodeCompiler). +struct VirtReg { + //! A state of a virtual register (used during register allocation). + ASMJIT_ENUM(State) { + kStateNone = 0, //!< Not allocated, not used. + kStateReg = 1, //!< Allocated in register. + kStateMem = 2 //!< Allocated in memory or spilled. + }; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the virtual-register id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Get virtual-register's name. + ASMJIT_INLINE const char* getName() const noexcept { return _name; } + + //! Get a physical register type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _regInfo.getType(); } + //! Get a physical register kind. + ASMJIT_INLINE uint32_t getKind() const noexcept { return _regInfo.getKind(); } + //! Get a physical register size. + ASMJIT_INLINE uint32_t getRegSize() const noexcept { return _regInfo.getSize(); } + //! Get a register signature of this virtual register. + ASMJIT_INLINE uint32_t getSignature() const noexcept { return _regInfo.getSignature(); } + + //! Get a register's type-id, see \ref TypeId. + ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _typeId; } + + //! Get virtual-register's size. + ASMJIT_INLINE uint32_t getSize() const noexcept { return _size; } + //! Get virtual-register's alignment. + ASMJIT_INLINE uint32_t getAlignment() const noexcept { return _alignment; } + + //! Get the virtual-register priority, used by compiler to decide which variable to spill. + ASMJIT_INLINE uint32_t getPriority() const noexcept { return _priority; } + //! Set the virtual-register priority. + ASMJIT_INLINE void setPriority(uint32_t priority) noexcept { + ASMJIT_ASSERT(priority <= 0xFF); + _priority = static_cast(priority); + } + + //! Get variable state, only used by `RAPass`. + ASMJIT_INLINE uint32_t getState() const noexcept { return _state; } + //! Set variable state, only used by `RAPass`. + ASMJIT_INLINE void setState(uint32_t state) { + ASMJIT_ASSERT(state <= 0xFF); + _state = static_cast(state); + } + + //! Get register index. + ASMJIT_INLINE uint32_t getPhysId() const noexcept { return _physId; } + //! Set register index. + ASMJIT_INLINE void setPhysId(uint32_t physId) { + ASMJIT_ASSERT(physId <= Globals::kInvalidRegId); + _physId = static_cast(physId); + } + //! Reset register index. + ASMJIT_INLINE void resetPhysId() { + _physId = static_cast(Globals::kInvalidRegId); + } + + //! Get home registers mask. + ASMJIT_INLINE uint32_t getHomeMask() const { return _homeMask; } + //! Add a home register index to the home registers mask. + ASMJIT_INLINE void addHomeId(uint32_t physId) { _homeMask |= Utils::mask(physId); } + + ASMJIT_INLINE bool isFixed() const noexcept { return static_cast(_isFixed); } + + //! Get whether the VirtReg is only memory allocated on the stack. + ASMJIT_INLINE bool isStack() const noexcept { return static_cast(_isStack); } + + //! Get whether to save variable when it's unused (spill). + ASMJIT_INLINE bool saveOnUnuse() const noexcept { return static_cast(_saveOnUnuse); } + + //! Get whether the variable was changed. + ASMJIT_INLINE bool isModified() const noexcept { return static_cast(_modified); } + //! Set whether the variable was changed. + ASMJIT_INLINE void setModified(bool modified) noexcept { _modified = modified; } + + //! Get home memory offset. + ASMJIT_INLINE int32_t getMemOffset() const noexcept { return _memOffset; } + //! Set home memory offset. + ASMJIT_INLINE void setMemOffset(int32_t offset) noexcept { _memOffset = offset; } + + //! Get home memory cell. + ASMJIT_INLINE RACell* getMemCell() const noexcept { return _memCell; } + //! Set home memory cell. + ASMJIT_INLINE void setMemCell(RACell* cell) noexcept { _memCell = cell; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; //!< Virtual register id. + RegInfo _regInfo; //!< Physical register info & signature. + const char* _name; //!< Virtual name (user provided). + uint32_t _size; //!< Virtual size (can be smaller than `regInfo._size`). + uint8_t _typeId; //!< Type-id. + uint8_t _alignment; //!< Register's natural alignment (for spilling). + uint8_t _priority; //!< Allocation priority (hint for RAPass that can be ignored). + uint8_t _isFixed : 1; //!< True if this is a fixed register, never reallocated. + uint8_t _isStack : 1; //!< True if the virtual register is only used as a stack. + uint8_t _isMaterialized : 1; //!< Register is constant that is easily created by a single instruction. + uint8_t _saveOnUnuse : 1; //!< Save on unuse (at end of the variable scope). + + // ------------------------------------------------------------------------- + // The following members are used exclusively by RAPass. They are initialized + // when the VirtReg is created and then changed during RAPass. + // ------------------------------------------------------------------------- + + uint32_t _raId; //!< Register allocator work-id (used by RAPass). + int32_t _memOffset; //!< Home memory offset. + uint32_t _homeMask; //!< Mask of all registers variable has been allocated to. + + uint8_t _state; //!< Variable state (connected with actual `RAState)`. + uint8_t _physId; //!< Actual register index (only used by `RAPass)`, during translate. + uint8_t _modified; //!< Whether variable was changed (connected with actual `RAState)`. + + RACell* _memCell; //!< Home memory cell, used by `RAPass` (initially nullptr). + + //! Temporary link to TiedReg* used by the `RAPass` used in + //! various phases, but always set back to nullptr when finished. + //! + //! This temporary data is designed to be used by algorithms that need to + //! store some data into variables themselves during compilation. But it's + //! expected that after variable is compiled & translated the data is set + //! back to zero/null. Initial value is nullptr. + TiedReg* _tied; +}; + +// ============================================================================ +// [asmjit::CCHint] +// ============================================================================ + +//! Hint for register allocator (CodeCompiler). +class CCHint : public CBNode { +public: + ASMJIT_NONCOPYABLE(CCHint) + + //! Hint type. + ASMJIT_ENUM(Hint) { + //! Alloc to physical reg. + kHintAlloc = 0, + //! Spill to memory. + kHintSpill = 1, + //! Save if modified. + kHintSave = 2, + //! Save if modified and mark it as unused. + kHintSaveAndUnuse = 3, + //! Mark as unused. + kHintUnuse = 4 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCHint` instance. + ASMJIT_INLINE CCHint(CodeBuilder* cb, VirtReg* vreg, uint32_t hint, uint32_t value) noexcept : CBNode(cb, kNodeHint) { + orFlags(kFlagIsRemovable | kFlagIsInformative); + _vreg = vreg; + _hint = hint; + _value = value; + } + + //! Destroy the `CCHint` instance (NEVER CALLED). + ASMJIT_INLINE ~CCHint() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get variable. + ASMJIT_INLINE VirtReg* getVReg() const noexcept { return _vreg; } + + //! Get hint it, see \ref Hint. + ASMJIT_INLINE uint32_t getHint() const noexcept { return _hint; } + //! Set hint it, see \ref Hint. + ASMJIT_INLINE void setHint(uint32_t hint) noexcept { _hint = hint; } + + //! Get hint value. + ASMJIT_INLINE uint32_t getValue() const noexcept { return _value; } + //! Set hint value. + ASMJIT_INLINE void setValue(uint32_t value) noexcept { _value = value; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Variable. + VirtReg* _vreg; + //! Hint id. + uint32_t _hint; + //! Value. + uint32_t _value; +}; + +// ============================================================================ +// [asmjit::CCFunc] +// ============================================================================ + +//! Function entry (CodeCompiler). +class CCFunc : public CBLabel { +public: + ASMJIT_NONCOPYABLE(CCFunc) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCFunc` instance. + //! + //! Always use `CodeCompiler::addFunc()` to create \ref CCFunc. + ASMJIT_INLINE CCFunc(CodeBuilder* cb) noexcept + : CBLabel(cb), + _exitNode(nullptr), + _funcDetail(), + _frameInfo(), + _end(nullptr), + _args(nullptr), + _isFinished(false) { + + _type = kNodeFunc; + } + + //! Destroy the `CCFunc` instance (NEVER CALLED). + ASMJIT_INLINE ~CCFunc() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get function exit `CBLabel`. + ASMJIT_INLINE CBLabel* getExitNode() const noexcept { return _exitNode; } + //! Get function exit label. + ASMJIT_INLINE Label getExitLabel() const noexcept { return _exitNode->getLabel(); } + + //! Get the function end sentinel. + ASMJIT_INLINE CBSentinel* getEnd() const noexcept { return _end; } + + //! Get function declaration. + ASMJIT_INLINE FuncDetail& getDetail() noexcept { return _funcDetail; } + //! Get function declaration. + ASMJIT_INLINE const FuncDetail& getDetail() const noexcept { return _funcDetail; } + + //! Get function declaration. + ASMJIT_INLINE FuncFrameInfo& getFrameInfo() noexcept { return _frameInfo; } + //! Get function declaration. + ASMJIT_INLINE const FuncFrameInfo& getFrameInfo() const noexcept { return _frameInfo; } + + //! Get arguments count. + ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _funcDetail.getArgCount(); } + //! Get returns count. + ASMJIT_INLINE uint32_t getRetCount() const noexcept { return _funcDetail.getRetCount(); } + + //! Get arguments list. + ASMJIT_INLINE VirtReg** getArgs() const noexcept { return _args; } + + //! Get argument at `i`. + ASMJIT_INLINE VirtReg* getArg(uint32_t i) const noexcept { + ASMJIT_ASSERT(i < getArgCount()); + return _args[i]; + } + + //! Set argument at `i`. + ASMJIT_INLINE void setArg(uint32_t i, VirtReg* vreg) noexcept { + ASMJIT_ASSERT(i < getArgCount()); + _args[i] = vreg; + } + + //! Reset argument at `i`. + ASMJIT_INLINE void resetArg(uint32_t i) noexcept { + ASMJIT_ASSERT(i < getArgCount()); + _args[i] = nullptr; + } + + ASMJIT_INLINE uint32_t getAttributes() const noexcept { return _frameInfo.getAttributes(); } + ASMJIT_INLINE void addAttributes(uint32_t attrs) noexcept { _frameInfo.addAttributes(attrs); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + FuncDetail _funcDetail; //!< Function detail. + FuncFrameInfo _frameInfo; //!< Function frame information. + + CBLabel* _exitNode; //!< Function exit. + CBSentinel* _end; //!< Function end. + + VirtReg** _args; //!< Arguments array as `VirtReg`. + + //! Function was finished by `Compiler::endFunc()`. + uint8_t _isFinished; +}; + +// ============================================================================ +// [asmjit::CCFuncRet] +// ============================================================================ + +//! Function return (CodeCompiler). +class CCFuncRet : public CBNode { +public: + ASMJIT_NONCOPYABLE(CCFuncRet) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncRet` instance. + ASMJIT_INLINE CCFuncRet(CodeBuilder* cb, const Operand_& o0, const Operand_& o1) noexcept : CBNode(cb, kNodeFuncExit) { + orFlags(kFlagIsRet); + _ret[0].copyFrom(o0); + _ret[1].copyFrom(o1); + } + + //! Destroy the `CCFuncRet` instance (NEVER CALLED). + ASMJIT_INLINE ~CCFuncRet() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the first return operand. + ASMJIT_INLINE Operand& getFirst() noexcept { return static_cast(_ret[0]); } + //! \overload + ASMJIT_INLINE const Operand& getFirst() const noexcept { return static_cast(_ret[0]); } + + //! Get the second return operand. + ASMJIT_INLINE Operand& getSecond() noexcept { return static_cast(_ret[1]); } + //! \overload + ASMJIT_INLINE const Operand& getSecond() const noexcept { return static_cast(_ret[1]); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Return operands. + Operand_ _ret[2]; +}; + +// ============================================================================ +// [asmjit::CCFuncCall] +// ============================================================================ + +//! Function call (CodeCompiler). +class CCFuncCall : public CBInst { +public: + ASMJIT_NONCOPYABLE(CCFuncCall) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncCall` instance. + ASMJIT_INLINE CCFuncCall(CodeBuilder* cb, uint32_t instId, uint32_t options, Operand* opArray, uint32_t opCount) noexcept + : CBInst(cb, instId, options, opArray, opCount), + _funcDetail(), + _args(nullptr) { + + _type = kNodeFuncCall; + _ret[0].reset(); + _ret[1].reset(); + orFlags(kFlagIsRemovable); + } + + //! Destroy the `CCFuncCall` instance (NEVER CALLED). + ASMJIT_INLINE ~CCFuncCall() noexcept {} + + // -------------------------------------------------------------------------- + // [Signature] + // -------------------------------------------------------------------------- + + //! Set function signature. + ASMJIT_INLINE Error setSignature(const FuncSignature& sign) noexcept { + return _funcDetail.init(sign); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get function declaration. + ASMJIT_INLINE FuncDetail& getDetail() noexcept { return _funcDetail; } + //! Get function declaration. + ASMJIT_INLINE const FuncDetail& getDetail() const noexcept { return _funcDetail; } + + //! Get target operand. + ASMJIT_INLINE Operand& getTarget() noexcept { return static_cast(_opArray[0]); } + //! \overload + ASMJIT_INLINE const Operand& getTarget() const noexcept { return static_cast(_opArray[0]); } + + //! Get return at `i`. + ASMJIT_INLINE Operand& getRet(uint32_t i = 0) noexcept { + ASMJIT_ASSERT(i < 2); + return static_cast(_ret[i]); + } + //! \overload + ASMJIT_INLINE const Operand& getRet(uint32_t i = 0) const noexcept { + ASMJIT_ASSERT(i < 2); + return static_cast(_ret[i]); + } + + //! Get argument at `i`. + ASMJIT_INLINE Operand& getArg(uint32_t i) noexcept { + ASMJIT_ASSERT(i < kFuncArgCountLoHi); + return static_cast(_args[i]); + } + //! \overload + ASMJIT_INLINE const Operand& getArg(uint32_t i) const noexcept { + ASMJIT_ASSERT(i < kFuncArgCountLoHi); + return static_cast(_args[i]); + } + + //! Set argument at `i` to `op`. + ASMJIT_API bool _setArg(uint32_t i, const Operand_& op) noexcept; + //! Set return at `i` to `op`. + ASMJIT_API bool _setRet(uint32_t i, const Operand_& op) noexcept; + + //! Set argument at `i` to `reg`. + ASMJIT_INLINE bool setArg(uint32_t i, const Reg& reg) noexcept { return _setArg(i, reg); } + //! Set argument at `i` to `imm`. + ASMJIT_INLINE bool setArg(uint32_t i, const Imm& imm) noexcept { return _setArg(i, imm); } + + //! Set return at `i` to `var`. + ASMJIT_INLINE bool setRet(uint32_t i, const Reg& reg) noexcept { return _setRet(i, reg); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + FuncDetail _funcDetail; //!< Function detail. + Operand_ _ret[2]; //!< Return. + Operand_* _args; //!< Arguments. +}; + +// ============================================================================ +// [asmjit::CCPushArg] +// ============================================================================ + +//! Push argument before a function call (CodeCompiler). +class CCPushArg : public CBNode { +public: + ASMJIT_NONCOPYABLE(CCPushArg) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CCPushArg` instance. + ASMJIT_INLINE CCPushArg(CodeBuilder* cb, CCFuncCall* call, VirtReg* src, VirtReg* cvt) noexcept + : CBNode(cb, kNodePushArg), + _call(call), + _src(src), + _cvt(cvt), + _args(0) { + orFlags(kFlagIsRemovable); + } + + //! Destroy the `CCPushArg` instance. + ASMJIT_INLINE ~CCPushArg() noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the associated function-call. + ASMJIT_INLINE CCFuncCall* getCall() const noexcept { return _call; } + //! Get source variable. + ASMJIT_INLINE VirtReg* getSrcReg() const noexcept { return _src; } + //! Get conversion variable. + ASMJIT_INLINE VirtReg* getCvtReg() const noexcept { return _cvt; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CCFuncCall* _call; //!< Associated `CCFuncCall`. + VirtReg* _src; //!< Source variable. + VirtReg* _cvt; //!< Temporary variable used for conversion (or null). + uint32_t _args; //!< Affected arguments bit-array. +}; + +// ============================================================================ +// [asmjit::CodeCompiler] +// ============================================================================ + +//! Code emitter that uses virtual registers and performs register allocation. +//! +//! Compiler is a high-level code-generation tool that provides register +//! allocation and automatic handling of function calling conventions. It was +//! primarily designed for merging multiple parts of code into a function +//! without worrying about registers and function calling conventions. +//! +//! CodeCompiler can be used, with a minimum effort, to handle 32-bit and 64-bit +//! code at the same time. +//! +//! CodeCompiler is based on CodeBuilder and contains all the features it +//! provides. It means that the code it stores can be modified (removed, added, +//! injected) and analyzed. When the code is finalized the compiler can emit +//! the code into an Assembler to translate the abstract representation into a +//! machine code. +class ASMJIT_VIRTAPI CodeCompiler : public CodeBuilder { +public: + ASMJIT_NONCOPYABLE(CodeCompiler) + typedef CodeBuilder Base; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `CodeCompiler` instance. + ASMJIT_API CodeCompiler() noexcept; + //! Destroy the `CodeCompiler` instance. + ASMJIT_API virtual ~CodeCompiler() noexcept; + + // -------------------------------------------------------------------------- + // [Events] + // -------------------------------------------------------------------------- + + ASMJIT_API virtual Error onAttach(CodeHolder* code) noexcept override; + ASMJIT_API virtual Error onDetach(CodeHolder* code) noexcept override; + + // -------------------------------------------------------------------------- + // [Node-Factory] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Create a new `CCHint`. + ASMJIT_API CCHint* newHintNode(Reg& reg, uint32_t hint, uint32_t value) noexcept; + + // -------------------------------------------------------------------------- + // [Func] + // -------------------------------------------------------------------------- + + //! Get the current function. + ASMJIT_INLINE CCFunc* getFunc() const noexcept { return _func; } + + //! Create a new `CCFunc`. + ASMJIT_API CCFunc* newFunc(const FuncSignature& sign) noexcept; + //! Add a function `node` to the stream. + ASMJIT_API CCFunc* addFunc(CCFunc* func); + //! Add a new function. + ASMJIT_API CCFunc* addFunc(const FuncSignature& sign); + //! Emit a sentinel that marks the end of the current function. + ASMJIT_API CBSentinel* endFunc(); + + // -------------------------------------------------------------------------- + // [Ret] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncRet`. + ASMJIT_API CCFuncRet* newRet(const Operand_& o0, const Operand_& o1) noexcept; + //! Add a new `CCFuncRet`. + ASMJIT_API CCFuncRet* addRet(const Operand_& o0, const Operand_& o1) noexcept; + + // -------------------------------------------------------------------------- + // [Call] + // -------------------------------------------------------------------------- + + //! Create a new `CCFuncCall`. + ASMJIT_API CCFuncCall* newCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept; + //! Add a new `CCFuncCall`. + ASMJIT_API CCFuncCall* addCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept; + + // -------------------------------------------------------------------------- + // [Args] + // -------------------------------------------------------------------------- + + //! Set a function argument at `argIndex` to `reg`. + ASMJIT_API Error setArg(uint32_t argIndex, const Reg& reg); + + // -------------------------------------------------------------------------- + // [Hint] + // -------------------------------------------------------------------------- + + //! Emit a new hint (purely informational node). + ASMJIT_API Error _hint(Reg& reg, uint32_t hint, uint32_t value); + + // -------------------------------------------------------------------------- + // [VirtReg / Stack] + // -------------------------------------------------------------------------- + + //! Create a new virtual register representing the given `vti` and `signature`. + //! + //! This function accepts either register type representing a machine-specific + //! register, like `X86Reg`, or RegTag representation, which represents + //! machine independent register, and from the machine-specific register + //! is deduced. + ASMJIT_API VirtReg* newVirtReg(uint32_t typeId, uint32_t signature, const char* name) noexcept; + + ASMJIT_API Error _newReg(Reg& out, uint32_t typeId, const char* name); + ASMJIT_API Error _newReg(Reg& out, uint32_t typeId, const char* nameFmt, va_list ap); + + ASMJIT_API Error _newReg(Reg& out, const Reg& ref, const char* name); + ASMJIT_API Error _newReg(Reg& out, const Reg& ref, const char* nameFmt, va_list ap); + + ASMJIT_API Error _newStack(Mem& out, uint32_t size, uint32_t alignment, const char* name); + ASMJIT_API Error _newConst(Mem& out, uint32_t scope, const void* data, size_t size); + + // -------------------------------------------------------------------------- + // [VirtReg] + // -------------------------------------------------------------------------- + + //! Get whether the virtual register `r` is valid. + ASMJIT_INLINE bool isVirtRegValid(const Reg& reg) const noexcept { + return isVirtRegValid(reg.getId()); + } + //! \overload + ASMJIT_INLINE bool isVirtRegValid(uint32_t id) const noexcept { + size_t index = Operand::unpackId(id); + return index < _vRegArray.getLength(); + } + + //! Get \ref VirtReg associated with the given `r`. + ASMJIT_INLINE VirtReg* getVirtReg(const Reg& reg) const noexcept { + return getVirtRegById(reg.getId()); + } + //! Get \ref VirtReg associated with the given `id`. + ASMJIT_INLINE VirtReg* getVirtRegById(uint32_t id) const noexcept { + ASMJIT_ASSERT(id != kInvalidValue); + size_t index = Operand::unpackId(id); + + ASMJIT_ASSERT(index < _vRegArray.getLength()); + return _vRegArray[index]; + } + + //! Get an array of all virtual registers managed by CodeCompiler. + ASMJIT_INLINE const ZoneVector& getVirtRegArray() const noexcept { return _vRegArray; } + + //! Alloc a virtual register `reg`. + ASMJIT_API Error alloc(Reg& reg); + //! Alloc a virtual register `reg` using `physId` as a register id. + ASMJIT_API Error alloc(Reg& reg, uint32_t physId); + //! Alloc a virtual register `reg` using `ref` as a register operand. + ASMJIT_API Error alloc(Reg& reg, const Reg& ref); + //! Spill a virtual register `reg`. + ASMJIT_API Error spill(Reg& reg); + //! Save a virtual register `reg` if the status is `modified` at this point. + ASMJIT_API Error save(Reg& reg); + //! Unuse a virtual register `reg`. + ASMJIT_API Error unuse(Reg& reg); + + //! Get priority of a virtual register `reg`. + ASMJIT_API uint32_t getPriority(Reg& reg) const; + //! Set priority of variable `reg` to `priority`. + ASMJIT_API void setPriority(Reg& reg, uint32_t priority); + + //! Get save-on-unuse `reg` property. + ASMJIT_API bool getSaveOnUnuse(Reg& reg) const; + //! Set save-on-unuse `reg` property to `value`. + ASMJIT_API void setSaveOnUnuse(Reg& reg, bool value); + + //! Rename variable `reg` to `name`. + //! + //! NOTE: Only new name will appear in the logger. + ASMJIT_API void rename(Reg& reg, const char* fmt, ...); + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CCFunc* _func; //!< Current function. + + Zone _vRegZone; //!< Allocates \ref VirtReg objects. + ZoneVector _vRegArray; //!< Stores array of \ref VirtReg pointers. + + CBConstPool* _localConstPool; //!< Local constant pool, flushed at the end of each function. + CBConstPool* _globalConstPool; //!< Global constant pool, flushed at the end of the compilation. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER +#endif // _ASMJIT_BASE_CODECOMPILER_H diff --git a/src/asmjit/base/codeemitter.cpp b/src/asmjit/base/codeemitter.cpp new file mode 100644 index 0000000..082f9f3 --- /dev/null +++ b/src/asmjit/base/codeemitter.cpp @@ -0,0 +1,292 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/utils.h" +#include "../base/vmem.h" + +#if defined(ASMJIT_BUILD_X86) +#include "../x86/x86inst.h" +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) +#include "../arm/arminst.h" +#endif // ASMJIT_BUILD_ARM + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::CodeEmitter - Construction / Destruction] +// ============================================================================ + +CodeEmitter::CodeEmitter(uint32_t type) noexcept + : _codeInfo(), + _code(nullptr), + _nextEmitter(nullptr), + _type(static_cast(type)), + _destroyed(false), + _finalized(false), + _reserved(false), + _lastError(kErrorNotInitialized), + _privateData(0), + _globalHints(0), + _globalOptions(kOptionMaybeFailureCase), + _options(0), + _inlineComment(nullptr), + _op4(), + _op5(), + _opExtra(), + _none(), + _nativeGpReg(), + _nativeGpArray(nullptr) {} + +CodeEmitter::~CodeEmitter() noexcept { + if (_code) { + _destroyed = true; + _code->detach(this); + } +} + +// ============================================================================ +// [asmjit::CodeEmitter - Events] +// ============================================================================ + +Error CodeEmitter::onAttach(CodeHolder* code) noexcept { + _codeInfo = code->getCodeInfo(); + _lastError = kErrorOk; + + _globalHints = code->getGlobalHints(); + _globalOptions = code->getGlobalOptions(); + + return kErrorOk; +} + +Error CodeEmitter::onDetach(CodeHolder* code) noexcept { + _codeInfo.reset(); + _finalized = false; + _lastError = kErrorNotInitialized; + + _privateData = 0; + _globalHints = 0; + _globalOptions = kOptionMaybeFailureCase; + + _options = 0; + _inlineComment = nullptr; + _op4.reset(); + _op5.reset(); + _opExtra.reset(); + _nativeGpReg.reset(); + _nativeGpArray = nullptr; + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Finalize] +// ============================================================================ + +Label CodeEmitter::getLabelByName(const char* name, size_t nameLength, uint32_t parentId) noexcept { + return Label(_code ? _code->getLabelIdByName(name, nameLength, parentId) : static_cast(0)); +} + +// ============================================================================ +// [asmjit::CodeEmitter - Finalize] +// ============================================================================ + +Error CodeEmitter::finalize() { + // Finalization does nothing by default, overridden by `CodeBuilder`. + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Error Handling] +// ============================================================================ + +Error CodeEmitter::setLastError(Error error, const char* message) { + // This is fatal, CodeEmitter can't set error without being attached to `CodeHolder`. + ASMJIT_ASSERT(_code != nullptr); + + // Special case used to reset the last error. + if (error == kErrorOk) { + _lastError = kErrorOk; + _globalOptions &= ~kOptionMaybeFailureCase; + return kErrorOk; + } + + if (!message) + message = DebugUtils::errorAsString(error); + + // Logging is skipped if the error is handled by `ErrorHandler`. + ErrorHandler* handler = _code->_errorHandler; + if (handler && handler->handleError(error, message, this)) + return error; + + // The handler->handleError() function may throw an exception or longjmp() + // to terminate the execution of `setLastError()`. This is the reason why + // we have delayed changing the `_error` member until now. + _lastError = error; + + return error; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Helpers] +// ============================================================================ + +bool CodeEmitter::isLabelValid(uint32_t id) const noexcept { + size_t index = Operand::unpackId(id); + return _code && index < _code->_labels.getLength(); +} + +Error CodeEmitter::commentf(const char* fmt, ...) { + Error err = _lastError; + if (err) return err; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) { + va_list ap; + va_start(ap, fmt); + Error err = _code->_logger->logv(fmt, ap); + va_end(ap); + } +#else + ASMJIT_UNUSED(fmt); +#endif + + return err; +} + +Error CodeEmitter::commentv(const char* fmt, va_list ap) { + Error err = _lastError; + if (err) return err; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (_globalOptions & kOptionLoggingEnabled) + err = _code->_logger->logv(fmt, ap); +#else + ASMJIT_UNUSED(fmt); + ASMJIT_UNUSED(ap); +#endif + + return err; +} + +// ============================================================================ +// [asmjit::CodeEmitter - Emit] +// ============================================================================ + +#define OP const Operand_& +#define NO _none + +Error CodeEmitter::emit(uint32_t instId) { return _emit(instId, NO, NO, NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0) { return _emit(instId, o0, NO, NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1) { return _emit(instId, o0, o1, NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2) { return _emit(instId, o0, o1, o2, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3) { return _emit(instId, o0, o1, o2, o3); } + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4) { + _op4 = o4; + + if (!o4.isNone()) _options |= kOptionOp4; + return _emit(instId, o0, o1, o2, o3); +} + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, OP o5) { + _op4 = o4; + _op5 = o5; + + if (!o4.isNone()) _options |= kOptionOp4; + if (!o5.isNone()) _options |= kOptionOp5; + return _emit(instId, o0, o1, o2, o3); +} + +Error CodeEmitter::emit(uint32_t instId, int o0) { return _emit(instId, Imm(o0), NO, NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, int o1) { return _emit(instId, o0, Imm(o1), NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, int o2) { return _emit(instId, o0, o1, Imm(o2), NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, int o3) { return _emit(instId, o0, o1, o2, Imm(o3)); } + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, int o4) { + _options |= kOptionOp4; + _op4 = Imm(o4); + return _emit(instId, o0, o1, o2, o3); +} + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, int o5) { + _op4 = o4; + _op5 = Imm(o5); + + _options |= kOptionOp4 | kOptionOp5; + return _emit(instId, o0, o1, o2, o3); +} + +Error CodeEmitter::emit(uint32_t instId, int64_t o0) { return _emit(instId, Imm(o0), NO, NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, int64_t o1) { return _emit(instId, o0, Imm(o1), NO, NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, int64_t o2) { return _emit(instId, o0, o1, Imm(o2), NO); } +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, int64_t o3) { return _emit(instId, o0, o1, o2, Imm(o3)); } + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, int64_t o4) { + _options |= kOptionOp4; + _op4 = Imm(o4); + return _emit(instId, o0, o1, o2, o3); +} + +Error CodeEmitter::emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, int64_t o5) { + _op4 = o4; + _op5 = Imm(o5); + + _options |= kOptionOp4 | kOptionOp5; + return _emit(instId, o0, o1, o2, o3); +} + +#undef NO +#undef OP + +// ============================================================================ +// [asmjit::CodeEmitter - Validation] +// ============================================================================ + +Error CodeEmitter::_validate(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) const noexcept { +#if !defined(ASMJIT_DISABLE_VALIDATION) + Operand_ opArray[6]; + opArray[0].copyFrom(o0); + opArray[1].copyFrom(o1); + opArray[2].copyFrom(o2); + opArray[3].copyFrom(o3); + opArray[4].copyFrom(_op4); + opArray[5].copyFrom(_op5); + + uint32_t archType = getArchType(); + uint32_t options = getGlobalOptions() | getOptions(); + + if (!(options & CodeEmitter::kOptionOp4)) opArray[4].reset(); + if (!(options & CodeEmitter::kOptionOp5)) opArray[5].reset(); + +#if defined(ASMJIT_BUILD_X86) + if (ArchInfo::isX86Family(archType)) + return X86Inst::validate(archType, instId, options, _opExtra, opArray, 6); +#endif + +#if defined(ASMJIT_BUILD_ARM) + if (ArchInfo::isArmFamily(archType)) + return ArmInst::validate(archType, instId, options, _opExtra, opArray, 6); +#endif + + return DebugUtils::errored(kErrorInvalidArch); +#else + return DebugUtils::errored(kErrorFeatureNotEnabled); +#endif // !ASMJIT_DISABLE_VALIDATION +} +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/codeemitter.h b/src/asmjit/base/codeemitter.h new file mode 100644 index 0000000..fab60ea --- /dev/null +++ b/src/asmjit/base/codeemitter.h @@ -0,0 +1,508 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODEEMITTER_H +#define _ASMJIT_BASE_CODEEMITTER_H + +// [Dependencies] +#include "../base/arch.h" +#include "../base/codeholder.h" +#include "../base/operand.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class ConstPool; + +// ============================================================================ +// [asmjit::CodeEmitter] +// ============================================================================ + +//! Provides a base foundation to emit code - specialized by \ref Assembler and +//! \ref CodeBuilder. +class ASMJIT_VIRTAPI CodeEmitter { +public: + //! CodeEmitter type. + ASMJIT_ENUM(Type) { + kTypeNone = 0, + kTypeAssembler = 1, + kTypeBuilder = 2, + kTypeCompiler = 3, + kTypeCount = 4 + }; + + //! CodeEmitter hints - global settings that affect machine-code generation. + ASMJIT_ENUM(Hints) { + //! Emit optimized code-alignment sequences. + //! + //! Default `true`. + //! + //! X86/X64 Specific + //! ---------------- + //! + //! Default align sequence used by X86/X64 architecture is one-byte (0x90) + //! opcode that is often shown by disassemblers as nop. However there are + //! more optimized align sequences for 2-11 bytes that may execute faster. + //! If this feature is enabled AsmJit will generate specialized sequences + //! for alignment between 2 to 11 bytes. + kHintOptimizedAlign = 0x00000001U, + + //! Emit jump-prediction hints. + //! + //! Default `false`. + //! + //! X86/X64 Specific + //! ---------------- + //! + //! Jump prediction is usually based on the direction of the jump. If the + //! jump is backward it is usually predicted as taken; and if the jump is + //! forward it is usually predicted as not-taken. The reason is that loops + //! generally use backward jumps and conditions usually use forward jumps. + //! However this behavior can be overridden by using instruction prefixes. + //! If this option is enabled these hints will be emitted. + //! + //! This feature is disabled by default, because the only processor that + //! used to take into consideration prediction hints was P4. Newer processors + //! implement heuristics for branch prediction that ignores any static hints. + kHintPredictedJumps = 0x00000002U + }; + + //! CodeEmitter options that are merged with instruction options. + ASMJIT_ENUM(Options) { + //! Reserved, used to check for errors in `Assembler::_emit()`. In addition, + //! if an emitter is in error state it will have `kOptionMaybeFailureCase` + //! set + kOptionMaybeFailureCase = 0x00000001U, + + //! Perform a strict validation before the instruction is emitted. + kOptionStrictValidation = 0x00000002U, + + //! Logging is enabled and `CodeHolder::getLogger()` should return a valid + //! \ref Logger pointer. + kOptionLoggingEnabled = 0x00000004U, + + //! Mask of all internal options that are not used to represent instruction + //! options, but are used to instrument Assembler and CodeBuilder. These + //! options are internal and should not be used outside of AsmJit itself. + //! + //! NOTE: Reserved options should never appear in `CBInst` options. + kOptionReservedMask = 0x00000007U, + + //! Instruction has `_op4` (5th operand, indexed from zero). + kOptionOp4 = 0x0000008U, + //! Instruction has `_op5` (6th operand, indexed from zero). + kOptionOp5 = 0x0000010U, + //! Instruction has `_opExtra` operand (mask-op {k} operand when using AVX-512). + kOptionOpExtra = 0x00000020U, + + //! Prevents following a jump during compilation (CodeCompiler). + kOptionUnfollow = 0x00000040U, + + //! Overwrite the destination operand (CodeCompiler). + //! + //! Hint that is important for register liveness analysis. It tells the + //! compiler that the destination operand will be overwritten now or by + //! adjacent instructions. CodeCompiler knows when a register is completely + //! overwritten by a single instruction, for example you don't have to + //! mark "movaps" or "pxor x, x", however, if a pair of instructions is + //! used and the first of them doesn't completely overwrite the content + //! of the destination, CodeCompiler fails to mark that register as dead. + //! + //! X86/X64 Specific + //! ---------------- + //! + //! - All instructions that always overwrite at least the size of the + //! register the virtual-register uses , for example "mov", "movq", + //! "movaps" don't need the overwrite option to be used - conversion, + //! shuffle, and other miscellaneous instructions included. + //! + //! - All instructions that clear the destination register if all operands + //! are the same, for example "xor x, x", "pcmpeqb x x", etc... + //! + //! - Consecutive instructions that partially overwrite the variable until + //! there is no old content require the `overwrite()` to be used. Some + //! examples (not always the best use cases thought): + //! + //! - `movlps xmm0, ?` followed by `movhps xmm0, ?` and vice versa + //! - `movlpd xmm0, ?` followed by `movhpd xmm0, ?` and vice versa + //! - `mov al, ?` followed by `and ax, 0xFF` + //! - `mov al, ?` followed by `mov ah, al` + //! - `pinsrq xmm0, ?, 0` followed by `pinsrq xmm0, ?, 1` + //! + //! - If allocated variable is used temporarily for scalar operations. For + //! example if you allocate a full vector like `X86Compiler::newXmm()` + //! and then use that vector for scalar operations you should use + //! `overwrite()` directive: + //! + //! - `sqrtss x, y` - only LO element of `x` is changed, if you don't use + //! HI elements, use `X86Compiler.overwrite().sqrtss(x, y)`. + kOptionOverwrite = 0x00000080U + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_API CodeEmitter(uint32_t type) noexcept; + ASMJIT_API virtual ~CodeEmitter() noexcept; + + // -------------------------------------------------------------------------- + // [Events] + // -------------------------------------------------------------------------- + + //! Called after the \ref CodeEmitter was attached to the \ref CodeHolder. + virtual Error onAttach(CodeHolder* code) noexcept = 0; + //! Called after the \ref CodeEmitter was detached from the \ref CodeHolder. + virtual Error onDetach(CodeHolder* code) noexcept = 0; + + // -------------------------------------------------------------------------- + // [Code-Generation] + // -------------------------------------------------------------------------- + + //! Emit instruction. + virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) = 0; + + //! Create a new label. + virtual Label newLabel() = 0; + //! Create a new named label. + virtual Label newNamedLabel( + const char* name, + size_t nameLength = Globals::kInvalidIndex, + uint32_t type = Label::kTypeGlobal, + uint32_t parentId = 0) = 0; + + //! Get a label by name. + //! + //! Returns invalid Label in case that the name is invalid or label was not found. + //! + //! NOTE: This function doesn't trigger ErrorHandler in case the name is + //! invalid or no such label exist. You must always check the validity of the + //! \ref Label returned. + ASMJIT_API Label getLabelByName( + const char* name, + size_t nameLength = Globals::kInvalidIndex, + uint32_t parentId = 0) noexcept; + + //! Bind the `label` to the current position of the current section. + //! + //! NOTE: Attempt to bind the same label multiple times will return an error. + virtual Error bind(const Label& label) = 0; + + //! Align to the `alignment` specified. + //! + //! The sequence that is used to fill the gap between the aligned location + //! and the current location depends on the align `mode`, see \ref AlignMode. + virtual Error align(uint32_t mode, uint32_t alignment) = 0; + + //! Embed raw data into the code-buffer. + virtual Error embed(const void* data, uint32_t size) = 0; + + //! Embed absolute label address as data (4 or 8 bytes). + virtual Error embedLabel(const Label& label) = 0; + + //! Embed a constant pool into the code-buffer in the following steps: + //! 1. Align by using kAlignData to the minimum `pool` alignment. + //! 2. Bind `label` so it's bound to an aligned location. + //! 3. Emit constant pool data. + virtual Error embedConstPool(const Label& label, const ConstPool& pool) = 0; + + //! Emit a comment string `s` with an optional `len` parameter. + virtual Error comment(const char* s, size_t len = Globals::kInvalidIndex) = 0; + + // -------------------------------------------------------------------------- + // [Code-Generation Status] + // -------------------------------------------------------------------------- + + //! Get if the CodeEmitter is initialized (i.e. attached to a \ref CodeHolder). + ASMJIT_INLINE bool isInitialized() const noexcept { return _code != nullptr; } + + ASMJIT_API virtual Error finalize(); + + // -------------------------------------------------------------------------- + // [Code Information] + // -------------------------------------------------------------------------- + + //! Get information about the code, see \ref CodeInfo. + ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; } + //! Get \ref CodeHolder this CodeEmitter is attached to. + ASMJIT_INLINE CodeHolder* getCode() const noexcept { return _code; } + + //! Get information about the architecture, see \ref ArchInfo. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _codeInfo.getArchInfo(); } + + //! Get if the target architecture is 32-bit. + ASMJIT_INLINE bool is32Bit() const noexcept { return getArchInfo().is32Bit(); } + //! Get if the target architecture is 64-bit. + ASMJIT_INLINE bool is64Bit() const noexcept { return getArchInfo().is64Bit(); } + + //! Get the target architecture type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return getArchInfo().getType(); } + //! Get the target architecture sub-type. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return getArchInfo().getSubType(); } + //! Get the target architecture's GP register size (4 or 8 bytes). + ASMJIT_INLINE uint32_t getGpSize() const noexcept { return getArchInfo().getGpSize(); } + //! Get the number of target GP registers. + ASMJIT_INLINE uint32_t getGpCount() const noexcept { return getArchInfo().getGpCount(); } + + // -------------------------------------------------------------------------- + // [Code-Emitter Type] + // -------------------------------------------------------------------------- + + //! Get the type of this CodeEmitter, see \ref Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + + ASMJIT_INLINE bool isAssembler() const noexcept { return _type == kTypeAssembler; } + ASMJIT_INLINE bool isCodeBuilder() const noexcept { return _type == kTypeBuilder; } + ASMJIT_INLINE bool isCodeCompiler() const noexcept { return _type == kTypeCompiler; } + + // -------------------------------------------------------------------------- + // [Global Information] + // -------------------------------------------------------------------------- + + //! Get global hints. + ASMJIT_INLINE uint32_t getGlobalHints() const noexcept { return _globalHints; } + + //! Get global options. + //! + //! Global options are merged with instruction options before the instruction + //! is encoded. These options have some bits reserved that are used for error + //! checking, logging, and strict validation. Other options are globals that + //! affect each instruction, for example if VEX3 is set globally, it will all + //! instructions, even those that don't have such option set. + ASMJIT_INLINE uint32_t getGlobalOptions() const noexcept { return _globalOptions; } + + // -------------------------------------------------------------------------- + // [Error Handling] + // -------------------------------------------------------------------------- + + //! Get if the object is in error state. + //! + //! Error state means that it does not consume anything unless the error + //! state is reset by calling `resetLastError()`. Use `getLastError()` to + //! get the last error that put the object into the error state. + ASMJIT_INLINE bool isInErrorState() const noexcept { return _lastError != kErrorOk; } + + //! Get the last error code. + ASMJIT_INLINE Error getLastError() const noexcept { return _lastError; } + //! Set the last error code and propagate it through the error handler. + ASMJIT_API Error setLastError(Error error, const char* message = nullptr); + //! Clear the last error code and return `kErrorOk`. + ASMJIT_INLINE Error resetLastError() noexcept { return setLastError(kErrorOk); } + + // -------------------------------------------------------------------------- + // [Accessors That Affect the Next Instruction] + // -------------------------------------------------------------------------- + + //! Get options of the next instruction. + ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; } + //! Set options of the next instruction. + ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _options = options; } + //! Add options of the next instruction. + ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; } + //! Reset options of the next instruction. + ASMJIT_INLINE void resetOptions() noexcept { _options = 0; } + + //! Get if the 5th operand (indexed from zero) of the next instruction is used. + ASMJIT_INLINE bool hasOp4() const noexcept { return (_options & kOptionOp4) != 0; } + //! Get if the 6th operand (indexed from zero) of the next instruction is used. + ASMJIT_INLINE bool hasOp5() const noexcept { return (_options & kOptionOp5) != 0; } + //! Get if the op-mask operand of the next instruction is used. + ASMJIT_INLINE bool hasOpExtra() const noexcept { return (_options & kOptionOpExtra) != 0; } + + ASMJIT_INLINE const Operand& getOp4() const noexcept { return static_cast(_op4); } + ASMJIT_INLINE const Operand& getOp5() const noexcept { return static_cast(_op5); } + ASMJIT_INLINE const Operand& getOpExtra() const noexcept { return static_cast(_opExtra); } + + ASMJIT_INLINE void setOp4(const Operand_& op4) noexcept { _options |= kOptionOp4; _op4 = op4; } + ASMJIT_INLINE void setOp5(const Operand_& op5) noexcept { _options |= kOptionOp5; _op5 = op5; } + ASMJIT_INLINE void setOpExtra(const Operand_& opExtra) noexcept { _options |= kOptionOpExtra; _opExtra = opExtra; } + + //! Get annotation of the next instruction. + ASMJIT_INLINE const char* getInlineComment() const noexcept { return _inlineComment; } + //! Set annotation of the next instruction. + //! + //! NOTE: This string is set back to null by `_emit()`, but until that it has + //! to remain valid as `CodeEmitter` is not required to make a copy of it (and + //! it would be slow to do that for each instruction). + ASMJIT_INLINE void setInlineComment(const char* s) noexcept { _inlineComment = s; } + //! Reset annotation of the next instruction to null. + ASMJIT_INLINE void resetInlineComment() noexcept { _inlineComment = nullptr; } + + // -------------------------------------------------------------------------- + // [Helpers] + // -------------------------------------------------------------------------- + + //! Get if the `label` is valid (i.e. registered). + ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept { + return isLabelValid(label.getId()); + } + + //! Get if the label `id` is valid (i.e. registered). + ASMJIT_API bool isLabelValid(uint32_t id) const noexcept; + + //! Emit a formatted string `fmt`. + ASMJIT_API Error commentf(const char* fmt, ...); + //! Emit a formatted string `fmt` (va_list version). + ASMJIT_API Error commentv(const char* fmt, va_list ap); + + // -------------------------------------------------------------------------- + // [Emit] + // -------------------------------------------------------------------------- + + // NOTE: These `emit()` helpers are designed to address a code-bloat generated + // by C++ compilers to call a function having many arguments. Each parameter to + // `_emit()` requires code to pass it, which means that if we default to 4 + // operand parameters in `_emit()` and instId the C++ compiler would have to + // generate a virtual function call having 5 parameters, which is quite a lot. + // Since by default asm instructions have 2 to 3 operands it's better to + // introduce helpers that pass those and fill all the remaining with `_none`. + + //! Emit an instruction. + ASMJIT_API Error emit(uint32_t instId); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5); + + //! Emit an instruction that has a 32-bit signed immediate operand. + ASMJIT_API Error emit(uint32_t instId, int o0); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, int o1); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, int o2); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, int o3); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, int o4); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, int o5); + + //! Emit an instruction that has a 64-bit signed immediate operand. + ASMJIT_API Error emit(uint32_t instId, int64_t o0); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, int64_t o1); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, int64_t o2); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, int64_t o3); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, int64_t o4); + //! \overload + ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, int64_t o5); + + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, unsigned int o0) { + return emit(instId, static_cast(o0)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, unsigned int o1) { + return emit(instId, o0, static_cast(o1)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, unsigned int o2) { + return emit(instId, o0, o1, static_cast(o2)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, unsigned int o3) { + return emit(instId, o0, o1, o2, static_cast(o3)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, unsigned int o4) { + return emit(instId, o0, o1, o2, o3, static_cast(o4)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, unsigned int o5) { + return emit(instId, o0, o1, o2, o3, o4, static_cast(o5)); + } + + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, uint64_t o0) { + return emit(instId, static_cast(o0)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, uint64_t o1) { + return emit(instId, o0, static_cast(o1)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, uint64_t o2) { + return emit(instId, o0, o1, static_cast(o2)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, uint64_t o3) { + return emit(instId, o0, o1, o2, static_cast(o3)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, uint64_t o4) { + return emit(instId, o0, o1, o2, o3, static_cast(o4)); + } + //! \overload + ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, uint64_t o5) { + return emit(instId, o0, o1, o2, o3, o4, static_cast(o5)); + } + + // -------------------------------------------------------------------------- + // [Validation] + // -------------------------------------------------------------------------- + + //! Validate instruction with current options, called by `_emit()` if validation is enabled. + ASMJIT_API Error _validate(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) const noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CodeInfo _codeInfo; //!< Basic information about the code (matches CodeHolder::_codeInfo). + CodeHolder* _code; //!< CodeHolder the CodeEmitter is attached to. + CodeEmitter* _nextEmitter; //!< Linked list of `CodeEmitter`s attached to the same \ref CodeHolder. + + uint8_t _type; //!< See CodeEmitter::Type. + uint8_t _destroyed; //!< Set by ~CodeEmitter() before calling `_code->detach()`. + uint8_t _finalized; //!< True if the CodeEmitter is finalized (CodeBuilder & CodeCompiler). + uint8_t _reserved; //!< \internal + Error _lastError; //!< Last error code. + + uint32_t _privateData; //!< Internal private data used freely by any CodeEmitter. + uint32_t _globalHints; //!< Global hints, always in sync with CodeHolder. + uint32_t _globalOptions; //!< Global options, combined with `_options` before used by each instruction. + + uint32_t _options; //!< Used to pass instruction options (affects the next instruction). + const char* _inlineComment; //!< Inline comment of the next instruction (affects the next instruction). + Operand_ _op4; //!< 5th operand data (indexed from zero) (affects the next instruction). + Operand_ _op5; //!< 6th operand data (indexed from zero) (affects the next instruction). + Operand_ _opExtra; //!< Extra operand (op-mask {k} on AVX-512) (affects the next instruction). + + Operand_ _none; //!< Used to pass unused operands to `_emit()` instead of passing null. + Reg _nativeGpReg; //!< Native GP register with zero id. + const Reg* _nativeGpArray; //!< Array of native registers indexed from zero. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_CODEEMITTER_H diff --git a/src/asmjit/base/codeholder.cpp b/src/asmjit/base/codeholder.cpp new file mode 100644 index 0000000..1bba912 --- /dev/null +++ b/src/asmjit/base/codeholder.cpp @@ -0,0 +1,696 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/assembler.h" +#include "../base/utils.h" +#include "../base/vmem.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::ErrorHandler] +// ============================================================================ + +ErrorHandler::ErrorHandler() noexcept {} +ErrorHandler::~ErrorHandler() noexcept {} + +// ============================================================================ +// [asmjit::CodeHolder - Utilities] +// ============================================================================ + +static void CodeHolder_setGlobalOption(CodeHolder* self, uint32_t clear, uint32_t add) noexcept { + // Modify global options of `CodeHolder` itself. + self->_globalOptions = (self->_globalOptions & ~clear) | add; + + // Modify all global options of all `CodeEmitter`s attached. + CodeEmitter* emitter = self->_emitters; + while (emitter) { + emitter->_globalOptions = (emitter->_globalOptions & ~clear) | add; + emitter = emitter->_nextEmitter; + } +} + +static void CodeHolder_resetInternal(CodeHolder* self, bool releaseMemory) noexcept { + // Detach all `CodeEmitter`s. + while (self->_emitters) + self->detach(self->_emitters); + + // Reset everything into its construction state. + self->_codeInfo.reset(); + self->_globalHints = 0; + self->_globalOptions = 0; + self->_logger = nullptr; + self->_errorHandler = nullptr; + + self->_unresolvedLabelsCount = 0; + self->_trampolinesSize = 0; + + // Reset all sections. + size_t numSections = self->_sections.getLength(); + for (size_t i = 0; i < numSections; i++) { + SectionEntry* section = self->_sections[i]; + if (section->_buffer.hasData() && !section->_buffer.isExternal()) + Internal::releaseMemory(section->_buffer._data); + section->_buffer._data = nullptr; + section->_buffer._capacity = 0; + } + + // Reset zone allocator and all containers using it. + ZoneHeap* heap = &self->_baseHeap; + + self->_namedLabels.reset(heap); + self->_relocations.reset(); + self->_labels.reset(); + self->_sections.reset(); + + heap->reset(&self->_baseZone); + self->_baseZone.reset(releaseMemory); +} + +// ============================================================================ +// [asmjit::CodeHolder - Construction / Destruction] +// ============================================================================ + +CodeHolder::CodeHolder() noexcept + : _codeInfo(), + _globalHints(0), + _globalOptions(0), + _emitters(nullptr), + _cgAsm(nullptr), + _logger(nullptr), + _errorHandler(nullptr), + _trampolinesSize(0), + _baseZone(16384 - Zone::kZoneOverhead), + _dataZone(16384 - Zone::kZoneOverhead), + _baseHeap(&_baseZone), + _labels(), + _sections(), + _relocations() { +} + +CodeHolder::~CodeHolder() noexcept { + CodeHolder_resetInternal(this, true); +} + +// ============================================================================ +// [asmjit::CodeHolder - Init / Reset] +// ============================================================================ + +Error CodeHolder::init(const CodeInfo& info) noexcept { + // Cannot reinitialize if it's locked or there is one or more CodeEmitter + // attached. + if (isInitialized()) + return DebugUtils::errored(kErrorAlreadyInitialized); + + // If we are just initializing there should be no emitters attached). + ASMJIT_ASSERT(_emitters == nullptr); + + // Create the default section and insert it to the `_sections` array. + Error err = _sections.willGrow(&_baseHeap); + if (err == kErrorOk) { + SectionEntry* se = _baseZone.allocZeroedT(); + if (ASMJIT_LIKELY(se)) { + se->_flags = SectionEntry::kFlagExec | SectionEntry::kFlagConst; + se->_setDefaultName('.', 't', 'e', 'x', 't'); + _sections.appendUnsafe(se); + } + else { + err = DebugUtils::errored(kErrorNoHeapMemory); + } + } + + if (ASMJIT_UNLIKELY(err)) { + _baseZone.reset(false); + return err; + } + else { + _codeInfo = info; + return kErrorOk; + } +} + +void CodeHolder::reset(bool releaseMemory) noexcept { + CodeHolder_resetInternal(this, releaseMemory); +} + +// ============================================================================ +// [asmjit::CodeHolder - Attach / Detach] +// ============================================================================ + +Error CodeHolder::attach(CodeEmitter* emitter) noexcept { + // Catch a possible misuse of the API. + if (!emitter) + return DebugUtils::errored(kErrorInvalidArgument); + + uint32_t type = emitter->getType(); + if (type == CodeEmitter::kTypeNone || type >= CodeEmitter::kTypeCount) + return DebugUtils::errored(kErrorInvalidState); + + // This is suspicious, but don't fail if `emitter` matches. + if (emitter->_code != nullptr) { + if (emitter->_code == this) return kErrorOk; + return DebugUtils::errored(kErrorInvalidState); + } + + // Special case - attach `Assembler`. + CodeEmitter** pSlot = nullptr; + if (type == CodeEmitter::kTypeAssembler) { + if (_cgAsm) + return DebugUtils::errored(kErrorSlotOccupied); + pSlot = reinterpret_cast(&_cgAsm); + } + + Error err = emitter->onAttach(this); + if (err != kErrorOk) return err; + + // Add to a single-linked list of `CodeEmitter`s. + emitter->_nextEmitter = _emitters; + _emitters = emitter; + if (pSlot) *pSlot = emitter; + + // Establish the connection. + emitter->_code = this; + return kErrorOk; +} + +Error CodeHolder::detach(CodeEmitter* emitter) noexcept { + if (!emitter) + return DebugUtils::errored(kErrorInvalidArgument); + + if (emitter->_code != this) + return DebugUtils::errored(kErrorInvalidState); + + uint32_t type = emitter->getType(); + Error err = kErrorOk; + + // NOTE: We always detach if we were asked to, if error happens during + // `emitter->onDetach()` we just propagate it, but the CodeEmitter will + // be detached. + if (!emitter->_destroyed) + err = emitter->onDetach(this); + + // Special case - detach `Assembler`. + if (type == CodeEmitter::kTypeAssembler) _cgAsm = nullptr; + + // Remove from a single-linked list of `CodeEmitter`s. + CodeEmitter** pPrev = &_emitters; + for (;;) { + ASMJIT_ASSERT(*pPrev != nullptr); + CodeEmitter* cur = *pPrev; + + if (cur == emitter) { + *pPrev = emitter->_nextEmitter; + break; + } + + pPrev = &cur->_nextEmitter; + } + + emitter->_code = nullptr; + emitter->_nextEmitter = nullptr; + + return err; +} + +// ============================================================================ +// [asmjit::CodeHolder - Sync] +// ============================================================================ + +void CodeHolder::sync() noexcept { + if (_cgAsm) _cgAsm->sync(); +} + +// ============================================================================ +// [asmjit::CodeHolder - Result Information] +// ============================================================================ + +size_t CodeHolder::getCodeSize() const noexcept { + // Reflect all changes first. + const_cast(this)->sync(); + + // TODO: Support sections. + return _sections[0]->_buffer._length + getTrampolinesSize(); +} + +// ============================================================================ +// [asmjit::CodeHolder - Logging & Error Handling] +// ============================================================================ + +#if !defined(ASMJIT_DISABLE_LOGGING) +void CodeHolder::setLogger(Logger* logger) noexcept { + uint32_t opt = 0; + if (logger) opt = CodeEmitter::kOptionLoggingEnabled; + + _logger = logger; + CodeHolder_setGlobalOption(this, CodeEmitter::kOptionLoggingEnabled, opt); +} +#endif // !ASMJIT_DISABLE_LOGGING + +Error CodeHolder::setErrorHandler(ErrorHandler* handler) noexcept { + _errorHandler = handler; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::CodeHolder - Sections] +// ============================================================================ + +static Error CodeHolder_reserveInternal(CodeHolder* self, CodeBuffer* cb, size_t n) noexcept { + uint8_t* oldData = cb->_data; + uint8_t* newData; + + if (oldData && !cb->isExternal()) + newData = static_cast(Internal::reallocMemory(oldData, n)); + else + newData = static_cast(Internal::allocMemory(n)); + + if (ASMJIT_UNLIKELY(!newData)) + return DebugUtils::errored(kErrorNoHeapMemory); + + cb->_data = newData; + cb->_capacity = n; + + // Update the `Assembler` pointers if attached. Maybe we should introduce an + // event for this, but since only one Assembler can be attached at a time it + // should not matter how these pointers are updated. + Assembler* a = self->_cgAsm; + if (a && &a->_section->_buffer == cb) { + size_t offset = a->getOffset(); + + a->_bufferData = newData; + a->_bufferEnd = newData + n; + a->_bufferPtr = newData + offset; + } + + return kErrorOk; +} + +Error CodeHolder::growBuffer(CodeBuffer* cb, size_t n) noexcept { + // This is most likely called by `Assembler` so `sync()` shouldn't be needed, + // however, if this is called by the user and the currently attached Assembler + // did generate some code we could lose that, so sync now and make sure the + // section length is updated. + if (_cgAsm) _cgAsm->sync(); + + // Now the length of the section must be valid. + size_t length = cb->getLength(); + if (ASMJIT_UNLIKELY(n > IntTraits::maxValue() - length)) + return DebugUtils::errored(kErrorNoHeapMemory); + + // We can now check if growing the buffer is really necessary. It's unlikely + // that this function is called while there is still room for `n` bytes. + size_t capacity = cb->getCapacity(); + size_t required = cb->getLength() + n; + if (ASMJIT_UNLIKELY(required <= capacity)) return kErrorOk; + + if (cb->isFixedSize()) + return DebugUtils::errored(kErrorCodeTooLarge); + + if (capacity < 8096) + capacity = 8096; + else + capacity += Globals::kAllocOverhead; + + do { + size_t old = capacity; + if (capacity < Globals::kAllocThreshold) + capacity *= 2; + else + capacity += Globals::kAllocThreshold; + + if (capacity < Globals::kAllocThreshold) + capacity *= 2; + else + capacity += Globals::kAllocThreshold; + + // Overflow. + if (ASMJIT_UNLIKELY(old > capacity)) + return DebugUtils::errored(kErrorNoHeapMemory); + } while (capacity - Globals::kAllocOverhead < required); + + return CodeHolder_reserveInternal(this, cb, capacity - Globals::kAllocOverhead); +} + +Error CodeHolder::reserveBuffer(CodeBuffer* cb, size_t n) noexcept { + size_t capacity = cb->getCapacity(); + if (n <= capacity) return kErrorOk; + + if (cb->isFixedSize()) + return DebugUtils::errored(kErrorCodeTooLarge); + + // We must sync, as mentioned in `growBuffer()` as well. + if (_cgAsm) _cgAsm->sync(); + + return CodeHolder_reserveInternal(this, cb, n); +} + +// ============================================================================ +// [asmjit::CodeHolder - Labels & Symbols] +// ============================================================================ + +namespace { + +//! \internal +//! +//! Only used to lookup a label from `_namedLabels`. +class LabelByName { +public: + ASMJIT_INLINE LabelByName(const char* name, size_t nameLength, uint32_t hVal) noexcept + : name(name), + nameLength(static_cast(nameLength)) {} + + ASMJIT_INLINE bool matches(const LabelEntry* entry) const noexcept { + return static_cast(entry->getNameLength()) == nameLength && + ::memcmp(entry->getName(), name, nameLength) == 0; + } + + const char* name; + uint32_t nameLength; + uint32_t hVal; +}; + +// Returns a hash of `name` and fixes `nameLength` if it's `Globals::kInvalidIndex`. +static uint32_t CodeHolder_hashNameAndFixLen(const char* name, size_t& nameLength) noexcept { + uint32_t hVal = 0; + if (nameLength == Globals::kInvalidIndex) { + size_t i = 0; + for (;;) { + uint8_t c = static_cast(name[i]); + if (!c) break; + hVal = Utils::hashRound(hVal, c); + i++; + } + nameLength = i; + } + else { + for (size_t i = 0; i < nameLength; i++) { + uint8_t c = static_cast(name[i]); + if (ASMJIT_UNLIKELY(!c)) return DebugUtils::errored(kErrorInvalidLabelName); + hVal = Utils::hashRound(hVal, c); + } + } + return hVal; +} + +} // anonymous namespace + +LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept { + LabelLink* link = _baseHeap.allocT(); + if (ASMJIT_UNLIKELY(!link)) return nullptr; + + link->prev = le->_links; + le->_links = link; + + link->sectionId = sectionId; + link->relocId = RelocEntry::kInvalidId; + link->offset = offset; + link->rel = rel; + + _unresolvedLabelsCount++; + return link; +} + +Error CodeHolder::newLabelId(uint32_t& idOut) noexcept { + idOut = 0; + + size_t index = _labels.getLength(); + if (ASMJIT_LIKELY(index >= Operand::kPackedIdCount)) + return DebugUtils::errored(kErrorLabelIndexOverflow); + + ASMJIT_PROPAGATE(_labels.willGrow(&_baseHeap)); + LabelEntry* le = _baseHeap.allocZeroedT(); + + if (ASMJIT_UNLIKELY(!le)) + return DebugUtils::errored(kErrorNoHeapMemory);; + + uint32_t id = Operand::packId(static_cast(index)); + le->_setId(id); + le->_parentId = 0; + le->_sectionId = SectionEntry::kInvalidId; + le->_offset = 0; + + _labels.appendUnsafe(le); + idOut = id; + return kErrorOk; +} + +Error CodeHolder::newNamedLabelId(uint32_t& idOut, const char* name, size_t nameLength, uint32_t type, uint32_t parentId) noexcept { + idOut = 0; + uint32_t hVal = CodeHolder_hashNameAndFixLen(name, nameLength); + + if (ASMJIT_UNLIKELY(nameLength == 0)) + return DebugUtils::errored(kErrorInvalidLabelName); + + if (ASMJIT_UNLIKELY(nameLength > Globals::kMaxLabelLength)) + return DebugUtils::errored(kErrorLabelNameTooLong); + + switch (type) { + case Label::kTypeLocal: + if (ASMJIT_UNLIKELY(Operand::unpackId(parentId) >= _labels.getLength())) + return DebugUtils::errored(kErrorInvalidParentLabel); + + hVal ^= parentId; + break; + + case Label::kTypeGlobal: + if (ASMJIT_UNLIKELY(parentId != 0)) + return DebugUtils::errored(kErrorNonLocalLabelCantHaveParent); + + break; + + default: + return DebugUtils::errored(kErrorInvalidArgument); + } + + // Don't allow to insert duplicates. Local labels allow duplicates that have + // different id, this is already accomplished by having a different hashes + // between the same label names having different parent labels. + LabelEntry* le = _namedLabels.get(LabelByName(name, nameLength, hVal)); + if (ASMJIT_UNLIKELY(le)) + return DebugUtils::errored(kErrorLabelAlreadyDefined); + + Error err = kErrorOk; + size_t index = _labels.getLength(); + + if (ASMJIT_UNLIKELY(index >= Operand::kPackedIdCount)) + return DebugUtils::errored(kErrorLabelIndexOverflow); + + ASMJIT_PROPAGATE(_labels.willGrow(&_baseHeap)); + le = _baseHeap.allocZeroedT(); + + if (ASMJIT_UNLIKELY(!le)) + return DebugUtils::errored(kErrorNoHeapMemory); + + uint32_t id = Operand::packId(static_cast(index)); + le->_hVal = hVal; + le->_setId(id); + le->_type = static_cast(type); + le->_parentId = 0; + le->_sectionId = SectionEntry::kInvalidId; + le->_offset = 0; + + if (le->_name.mustEmbed(nameLength)) { + le->_name.setEmbedded(name, nameLength); + } + else { + char* nameExternal = static_cast(_dataZone.dup(name, nameLength, true)); + if (ASMJIT_UNLIKELY(!nameExternal)) + return DebugUtils::errored(kErrorNoHeapMemory); + le->_name.setExternal(nameExternal, nameLength); + } + + _labels.appendUnsafe(le); + _namedLabels.put(le); + + idOut = id; + return err; +} + +uint32_t CodeHolder::getLabelIdByName(const char* name, size_t nameLength, uint32_t parentId) noexcept { + uint32_t hVal = CodeHolder_hashNameAndFixLen(name, nameLength); + if (ASMJIT_UNLIKELY(!nameLength)) return 0; + + LabelEntry* le = _namedLabels.get(LabelByName(name, nameLength, hVal)); + return le ? le->getId() : static_cast(0); +} + +// ============================================================================ +// [asmjit::CodeEmitter - Relocations] +// ============================================================================ + +//! Encode MOD byte. +static ASMJIT_INLINE uint32_t x86EncodeMod(uint32_t m, uint32_t o, uint32_t rm) noexcept { + return (m << 6) | (o << 3) | rm; +} + +Error CodeHolder::newRelocEntry(RelocEntry** dst, uint32_t type, uint32_t size) noexcept { + ASMJIT_PROPAGATE(_relocations.willGrow(&_baseHeap)); + + size_t index = _relocations.getLength(); + if (ASMJIT_UNLIKELY(index > size_t(0xFFFFFFFFU))) + return DebugUtils::errored(kErrorRelocIndexOverflow); + + RelocEntry* re = _baseHeap.allocZeroedT(); + if (ASMJIT_UNLIKELY(!re)) + return DebugUtils::errored(kErrorNoHeapMemory); + + re->_id = static_cast(index); + re->_type = static_cast(type); + re->_size = static_cast(size); + re->_sourceSectionId = SectionEntry::kInvalidId; + re->_targetSectionId = SectionEntry::kInvalidId; + _relocations.appendUnsafe(re); + + *dst = re; + return kErrorOk; +} + +// TODO: Support multiple sections, this only relocates the first. +// TODO: This should go to Runtime as it's responsible for relocating the +// code, CodeHolder should just hold it. +size_t CodeHolder::relocate(void* _dst, uint64_t baseAddress) const noexcept { + SectionEntry* section = _sections[0]; + ASMJIT_ASSERT(section != nullptr); + + uint32_t archType = getArchType(); + uint8_t* dst = static_cast(_dst); + + if (baseAddress == Globals::kNoBaseAddress) + baseAddress = static_cast((uintptr_t)dst); + +#if !defined(ASMJIT_DISABLE_LOGGING) + Logger* logger = getLogger(); +#endif // ASMJIT_DISABLE_LOGGING + + size_t minCodeSize = section->getBuffer().getLength(); // Minimum code size. + size_t maxCodeSize = getCodeSize(); // Includes all possible trampolines. + + // We will copy the exact size of the generated code. Extra code for trampolines + // is generated on-the-fly by the relocator (this code doesn't exist at the moment). + ::memcpy(dst, section->_buffer._data, minCodeSize); + + // Trampoline offset from the beginning of dst/baseAddress. + size_t trampOffset = minCodeSize; + + // Relocate all recorded locations. + size_t numRelocs = _relocations.getLength(); + const RelocEntry* const* reArray = _relocations.getData(); + + for (size_t i = 0; i < numRelocs; i++) { + const RelocEntry* re = reArray[i]; + + // Possibly deleted or optimized out relocation entry. + if (re->getType() == RelocEntry::kTypeNone) + continue; + + uint64_t ptr = re->getData(); + size_t codeOffset = static_cast(re->getSourceOffset()); + + // Make sure that the `RelocEntry` is correct, we don't want to write + // out of bounds in `dst`. + if (ASMJIT_UNLIKELY(codeOffset + re->getSize() > maxCodeSize)) + return DebugUtils::errored(kErrorInvalidRelocEntry); + + // Whether to use trampoline, can be only used if relocation type is `kRelocTrampoline`. + bool useTrampoline = false; + + switch (re->getType()) { + case RelocEntry::kTypeAbsToAbs: { + break; + } + + case RelocEntry::kTypeRelToAbs: { + ptr += baseAddress; + break; + } + + case RelocEntry::kTypeAbsToRel: { + ptr -= baseAddress + re->getSourceOffset() + re->getSize(); + break; + } + + case RelocEntry::kTypeTrampoline: { + if (re->getSize() != 4) + return DebugUtils::errored(kErrorInvalidRelocEntry); + + ptr -= baseAddress + re->getSourceOffset() + re->getSize(); + if (!Utils::isInt32(static_cast(ptr))) { + ptr = (uint64_t)trampOffset - re->getSourceOffset() - re->getSize(); + useTrampoline = true; + } + break; + } + + default: + return DebugUtils::errored(kErrorInvalidRelocEntry); + } + + switch (re->getSize()) { + case 1: + Utils::writeU8(dst + codeOffset, static_cast(ptr & 0xFFU)); + break; + + case 4: + Utils::writeU32u(dst + codeOffset, static_cast(ptr & 0xFFFFFFFFU)); + break; + + case 8: + Utils::writeU64u(dst + codeOffset, ptr); + break; + + default: + return DebugUtils::errored(kErrorInvalidRelocEntry); + } + + // Handle the trampoline case. + if (useTrampoline) { + // Bytes that replace [REX, OPCODE] bytes. + uint32_t byte0 = 0xFF; + uint32_t byte1 = dst[codeOffset - 1]; + + if (byte1 == 0xE8) { + // Patch CALL/MOD byte to FF/2 (-> 0x15). + byte1 = x86EncodeMod(0, 2, 5); + } + else if (byte1 == 0xE9) { + // Patch JMP/MOD byte to FF/4 (-> 0x25). + byte1 = x86EncodeMod(0, 4, 5); + } + else { + return DebugUtils::errored(kErrorInvalidRelocEntry); + } + + // Patch `jmp/call` instruction. + ASMJIT_ASSERT(codeOffset >= 2); + dst[codeOffset - 2] = static_cast(byte0); + dst[codeOffset - 1] = static_cast(byte1); + + // Store absolute address and advance the trampoline pointer. + Utils::writeU64u(dst + trampOffset, re->getData()); + trampOffset += 8; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (logger) + logger->logf("[reloc] dq 0x%016llX ; Trampoline\n", re->getData()); +#endif // !ASMJIT_DISABLE_LOGGING + } + } + + // If there are no trampolines this is the same as `minCodeSize`. + return trampOffset; +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/codeholder.h b/src/asmjit/base/codeholder.h new file mode 100644 index 0000000..1f70572 --- /dev/null +++ b/src/asmjit/base/codeholder.h @@ -0,0 +1,745 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_CODEHOLDER_H +#define _ASMJIT_BASE_CODEHOLDER_H + +// [Dependencies] +#include "../base/arch.h" +#include "../base/func.h" +#include "../base/logging.h" +#include "../base/operand.h" +#include "../base/simdtypes.h" +#include "../base/utils.h" +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class Assembler; +class CodeEmitter; +class CodeHolder; + +// ============================================================================ +// [asmjit::AlignMode] +// ============================================================================ + +//! Align mode. +ASMJIT_ENUM(AlignMode) { + kAlignCode = 0, //!< Align executable code. + kAlignData = 1, //!< Align non-executable code. + kAlignZero = 2, //!< Align by a sequence of zeros. + kAlignCount //!< Count of alignment modes. +}; + +// ============================================================================ +// [asmjit::ErrorHandler] +// ============================================================================ + +//! Error handler can be used to override the default behavior of error handling +//! available to all classes that inherit \ref CodeEmitter. See \ref handleError(). +class ASMJIT_VIRTAPI ErrorHandler { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `ErrorHandler` instance. + ASMJIT_API ErrorHandler() noexcept; + //! Destroy the `ErrorHandler` instance. + ASMJIT_API virtual ~ErrorHandler() noexcept; + + // -------------------------------------------------------------------------- + // [Handle Error] + // -------------------------------------------------------------------------- + + //! Error handler (abstract). + //! + //! Error handler is called after an error happened and before it's propagated + //! to the caller. There are multiple ways how the error handler can be used: + //! + //! 1. Returning `true` or `false` from `handleError()`. If `true` is returned + //! it means that the error was reported and AsmJit can continue execution. + //! The reported error still be propagated to the caller, but won't put the + //! CodeEmitter into an error state (it won't set last-error). However, + //! returning `false` means that the error cannot be handled - in such case + //! it stores the error, which can be then retrieved by using `getLastError()`. + //! Returning `false` is the default behavior when no error handler is present. + //! To put the assembler into a non-error state again a `resetLastError()` must + //! be called. + //! + //! 2. Throwing an exception. AsmJit doesn't use exceptions and is completely + //! exception-safe, but you can throw exception from your error handler if + //! this way is the preferred way of handling errors in your project. Throwing + //! an exception acts virtually as returning `true` as AsmJit won't be able + //! to store the error because the exception changes execution path. + //! + //! 3. Using plain old C's `setjmp()` and `longjmp()`. Asmjit always puts + //! `CodeEmitter` to a consistent state before calling the `handleError()` + //! so `longjmp()` can be used without any issues to cancel the code + //! generation if an error occurred. There is no difference between + //! exceptions and longjmp() from AsmJit's perspective. + virtual bool handleError(Error err, const char* message, CodeEmitter* origin) = 0; +}; + +// ============================================================================ +// [asmjit::CodeInfo] +// ============================================================================ + +//! Basic information about a code (or target). It describes its architecture, +//! code generation mode (or optimization level), and base address. +class CodeInfo { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CodeInfo() noexcept + : _archInfo(), + _stackAlignment(0), + _cdeclCallConv(CallConv::kIdNone), + _stdCallConv(CallConv::kIdNone), + _fastCallConv(CallConv::kIdNone), + _baseAddress(Globals::kNoBaseAddress) {} + ASMJIT_INLINE CodeInfo(const CodeInfo& other) noexcept { init(other); } + + explicit ASMJIT_INLINE CodeInfo(uint32_t archType, uint32_t archMode = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept + : _archInfo(archType, archMode), + _packedMiscInfo(0), + _baseAddress(baseAddress) {} + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { + return _archInfo._type != ArchInfo::kTypeNone; + } + + ASMJIT_INLINE void init(const CodeInfo& other) noexcept { + _archInfo = other._archInfo; + _packedMiscInfo = other._packedMiscInfo; + _baseAddress = other._baseAddress; + } + + ASMJIT_INLINE void init(uint32_t archType, uint32_t archMode = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept { + _archInfo.init(archType, archMode); + _packedMiscInfo = 0; + _baseAddress = baseAddress; + } + + ASMJIT_INLINE void reset() noexcept { + _archInfo.reset(); + _stackAlignment = 0; + _cdeclCallConv = CallConv::kIdNone; + _stdCallConv = CallConv::kIdNone; + _fastCallConv = CallConv::kIdNone; + _baseAddress = Globals::kNoBaseAddress; + } + + // -------------------------------------------------------------------------- + // [Architecture Information] + // -------------------------------------------------------------------------- + + //! Get architecture information, see \ref ArchInfo. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _archInfo; } + + //! Get architecture type, see \ref ArchInfo::Type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archInfo.getType(); } + //! Get architecture sub-type, see \ref ArchInfo::SubType. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _archInfo.getSubType(); } + //! Get a size of a GP register of the architecture the code is using. + ASMJIT_INLINE uint32_t getGpSize() const noexcept { return _archInfo.getGpSize(); } + //! Get number of GP registers available of the architecture the code is using. + ASMJIT_INLINE uint32_t getGpCount() const noexcept { return _archInfo.getGpCount(); } + + // -------------------------------------------------------------------------- + // [High-Level Information] + // -------------------------------------------------------------------------- + + //! Get a natural stack alignment that must be honored (or 0 if not known). + ASMJIT_INLINE uint32_t getStackAlignment() const noexcept { return _stackAlignment; } + //! Set a natural stack alignment that must be honored. + ASMJIT_INLINE void setStackAlignment(uint8_t sa) noexcept { _stackAlignment = static_cast(sa); } + + ASMJIT_INLINE uint32_t getCdeclCallConv() const noexcept { return _cdeclCallConv; } + ASMJIT_INLINE void setCdeclCallConv(uint32_t cc) noexcept { _cdeclCallConv = static_cast(cc); } + + ASMJIT_INLINE uint32_t getStdCallConv() const noexcept { return _stdCallConv; } + ASMJIT_INLINE void setStdCallConv(uint32_t cc) noexcept { _stdCallConv = static_cast(cc); } + + ASMJIT_INLINE uint32_t getFastCallConv() const noexcept { return _fastCallConv; } + ASMJIT_INLINE void setFastCallConv(uint32_t cc) noexcept { _fastCallConv = static_cast(cc); } + + // -------------------------------------------------------------------------- + // [Addressing Information] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool hasBaseAddress() const noexcept { return _baseAddress != Globals::kNoBaseAddress; } + ASMJIT_INLINE uint64_t getBaseAddress() const noexcept { return _baseAddress; } + ASMJIT_INLINE void setBaseAddress(uint64_t p) noexcept { _baseAddress = p; } + ASMJIT_INLINE void resetBaseAddress() noexcept { _baseAddress = Globals::kNoBaseAddress; } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE CodeInfo& operator=(const CodeInfo& other) noexcept { init(other); return *this; } + ASMJIT_INLINE bool operator==(const CodeInfo& other) const noexcept { return ::memcmp(this, &other, sizeof(*this)) == 0; } + ASMJIT_INLINE bool operator!=(const CodeInfo& other) const noexcept { return ::memcmp(this, &other, sizeof(*this)) != 0; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ArchInfo _archInfo; //!< Architecture information. + + union { + struct { + uint8_t _stackAlignment; //!< Natural stack alignment (ARCH+OS). + uint8_t _cdeclCallConv; //!< Default CDECL calling convention. + uint8_t _stdCallConv; //!< Default STDCALL calling convention. + uint8_t _fastCallConv; //!< Default FASTCALL calling convention. + }; + uint32_t _packedMiscInfo; //!< \internal + }; + + uint64_t _baseAddress; //!< Base address. +}; + +// ============================================================================ +// [asmjit::CodeBuffer] +// ============================================================================ + +//! Code or data buffer. +struct CodeBuffer { + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool hasData() const noexcept { return _data != nullptr; } + ASMJIT_INLINE uint8_t* getData() noexcept { return _data; } + ASMJIT_INLINE const uint8_t* getData() const noexcept { return _data; } + + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } + + ASMJIT_INLINE bool isExternal() const noexcept { return _isExternal; } + ASMJIT_INLINE bool isFixedSize() const noexcept { return _isFixedSize; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t* _data; //!< The content of the buffer (data). + size_t _length; //!< Number of bytes of `data` used. + size_t _capacity; //!< Buffer capacity (in bytes). + bool _isExternal; //!< True if this is external buffer. + bool _isFixedSize; //!< True if this buffer cannot grow. +}; + +// ============================================================================ +// [asmjit::SectionEntry] +// ============================================================================ + +//! Section entry. +class SectionEntry { +public: + ASMJIT_ENUM(Id) { + kInvalidId = 0xFFFFFFFFU //!< Invalid section id. + }; + + //! Section flags. + ASMJIT_ENUM(Flags) { + kFlagExec = 0x00000001U, //!< Executable (.text sections). + kFlagConst = 0x00000002U, //!< Read-only (.text and .data sections). + kFlagZero = 0x00000004U, //!< Zero initialized by the loader (BSS). + kFlagInfo = 0x00000008U, //!< Info / comment flag. + kFlagImplicit = 0x80000000U //!< Section created implicitly (can be deleted by the Runtime). + }; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + ASMJIT_INLINE const char* getName() const noexcept { return _name; } + + ASMJIT_INLINE void _setDefaultName( + char c0 = 0, char c1 = 0, char c2 = 0, char c3 = 0, + char c4 = 0, char c5 = 0, char c6 = 0, char c7 = 0) noexcept { + reinterpret_cast(_name)[0] = Utils::pack32_4x8(c0, c1, c2, c3); + reinterpret_cast(_name)[1] = Utils::pack32_4x8(c4, c5, c6, c7); + } + + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; } + ASMJIT_INLINE void addFlags(uint32_t flags) noexcept { _flags |= flags; } + ASMJIT_INLINE void clearFlags(uint32_t flags) noexcept { _flags &= ~flags; } + + ASMJIT_INLINE uint32_t getAlignment() const noexcept { return _alignment; } + ASMJIT_INLINE void setAlignment(uint32_t alignment) noexcept { _alignment = alignment; } + + ASMJIT_INLINE size_t getPhysicalSize() const noexcept { return _buffer.getLength(); } + + ASMJIT_INLINE size_t getVirtualSize() const noexcept { return _virtualSize; } + ASMJIT_INLINE void setVirtualSize(uint32_t size) noexcept { _virtualSize = size; } + + ASMJIT_INLINE CodeBuffer& getBuffer() noexcept { return _buffer; } + ASMJIT_INLINE const CodeBuffer& getBuffer() const noexcept { return _buffer; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _id; //!< Section id. + uint32_t _flags; //!< Section flags. + uint32_t _alignment; //!< Section alignment requirements (0 if no requirements). + uint32_t _virtualSize; //!< Virtual size of the section (zero initialized mostly). + char _name[36]; //!< Section name (max 35 characters, PE allows max 8). + CodeBuffer _buffer; //!< Code or data buffer. +}; + +// ============================================================================ +// [asmjit::LabelLink] +// ============================================================================ + +//! Data structure used to link labels. +struct LabelLink { + LabelLink* prev; //!< Previous link (single-linked list). + uint32_t sectionId; //!< Section id. + uint32_t relocId; //!< Relocation id or RelocEntry::kInvalidId. + size_t offset; //!< Label offset relative to the start of the section. + intptr_t rel; //!< Inlined rel8/rel32. +}; + +// ============================================================================ +// [asmjit::LabelEntry] +// ============================================================================ + +//! Label entry. +//! +//! Contains the following properties: +//! * Label id - This is the only thing that is set to the `Label` operand. +//! * Label name - Optional, used mostly to create executables and libraries. +//! * Label type - Type of the label, default `Label::kTypeAnonymous`. +//! * Label parent id - Derived from many assemblers that allow to define a +//! local label that falls under a global label. This allows to define +//! many labels of the same name that have different parent (global) label. +//! * Offset - offset of the label bound by `Assembler`. +//! * Links - single-linked list that contains locations of code that has +//! to be patched when the label gets bound. Every use of unbound label +//! adds one link to `_links` list. +//! * HVal - Hash value of label's name and optionally parentId. +//! * HashNext - Hash-table implementation detail. +class LabelEntry : public ZoneHashNode { +public: + // NOTE: Label id is stored in `_customData`, which is provided by ZoneHashNode + // to fill a padding that a C++ compiler targeting 64-bit CPU will add to align + // the structure to 64-bits. + + //! Get label id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _customData; } + //! Set label id (internal, used only by \ref CodeHolder). + ASMJIT_INLINE void _setId(uint32_t id) noexcept { _customData = id; } + + //! Get label type, see \ref Label::Type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + //! Get label flags, returns 0 at the moment. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + + ASMJIT_INLINE bool hasParent() const noexcept { return _parentId != 0; } + //! Get label's parent id. + ASMJIT_INLINE uint32_t getParentId() const noexcept { return _parentId; } + + //! Get label's section id where it's bound to (or `SectionEntry::kInvalidId` if it's not bound yet). + ASMJIT_INLINE uint32_t getSectionId() const noexcept { return _sectionId; } + + //! Get if the label has name. + ASMJIT_INLINE bool hasName() const noexcept { return !_name.isEmpty(); } + + //! Get the label's name. + //! + //! NOTE: Local labels will return their local name without their parent + //! part, for example ".L1". + ASMJIT_INLINE const char* getName() const noexcept { return _name.getData(); } + + //! Get length of label's name. + //! + //! NOTE: Label name is always null terminated, so you can use `strlen()` to + //! get it, however, it's also cached in `LabelEntry`, so if you want to know + //! the length the easiest way is to use `LabelEntry::getNameLength()`. + ASMJIT_INLINE size_t getNameLength() const noexcept { return _name.getLength(); } + + //! Get if the label is bound. + ASMJIT_INLINE bool isBound() const noexcept { return _sectionId != SectionEntry::kInvalidId; } + //! Get the label offset (only useful if the label is bound). + ASMJIT_INLINE intptr_t getOffset() const noexcept { return _offset; } + + //! Get the hash-value of label's name and its parent label (if any). + //! + //! Label hash is calculated as `HASH(Name) ^ ParentId`. The hash function + //! is implemented in `Utils::hashString()` and `Utils::hashRound()`. + ASMJIT_INLINE uint32_t getHVal() const noexcept { return _hVal; } + + // ------------------------------------------------------------------------ + // [Members] + // ------------------------------------------------------------------------ + + // Let's round the size of `LabelEntry` to 64 bytes (as ZoneHeap has 32 + // bytes granularity anyway). This gives `_name` the remaining space, which + // is roughly 16 bytes on 64-bit and 28 bytes on 32-bit architectures. + enum { kNameBytes = 64 - (sizeof(ZoneHashNode) + 16 + sizeof(intptr_t) + sizeof(LabelLink*)) }; + + uint8_t _type; //!< Label type, see Label::Type. + uint8_t _flags; //!< Must be zero. + uint16_t _reserved16; //!< Reserved. + uint32_t _parentId; //!< Label parent id or zero. + uint32_t _sectionId; //!< Section id or `SectionEntry::kInvalidId`. + uint32_t _reserved32; //!< Reserved. + intptr_t _offset; //!< Label offset. + LabelLink* _links; //!< Label links. + SmallString _name; //!< Label name. +}; + +// ============================================================================ +// [asmjit::RelocEntry] +// ============================================================================ + +//! Relocation entry. +struct RelocEntry { + ASMJIT_ENUM(Id) { + kInvalidId = 0xFFFFFFFFU //!< Invalid relocation id. + }; + + //! Relocation type. + ASMJIT_ENUM(Type) { + kTypeNone = 0, //!< Deleted entry (no relocation). + kTypeAbsToAbs = 1, //!< Relocate absolute to absolute. + kTypeRelToAbs = 2, //!< Relocate relative to absolute. + kTypeAbsToRel = 3, //!< Relocate absolute to relative. + kTypeTrampoline = 4 //!< Relocate absolute to relative or use trampoline. + }; + + // ------------------------------------------------------------------------ + // [Accessors] + // ------------------------------------------------------------------------ + + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + + ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } + ASMJIT_INLINE uint32_t getSize() const noexcept { return _size; } + + ASMJIT_INLINE uint32_t getSourceSectionId() const noexcept { return _sourceSectionId; } + ASMJIT_INLINE uint32_t getTargetSectionId() const noexcept { return _targetSectionId; } + + ASMJIT_INLINE uint64_t getSourceOffset() const noexcept { return _sourceOffset; } + ASMJIT_INLINE uint64_t getData() const noexcept { return _data; } + + // ------------------------------------------------------------------------ + // [Members] + // ------------------------------------------------------------------------ + + uint32_t _id; //!< Relocation id. + uint8_t _type; //!< Type of the relocation. + uint8_t _size; //!< Size of the relocation (1, 2, 4 or 8 bytes). + uint8_t _reserved[2]; //!< Reserved. + uint32_t _sourceSectionId; //!< Source section id. + uint32_t _targetSectionId; //!< Destination section id. + uint64_t _sourceOffset; //!< Source offset (relative to start of the section). + uint64_t _data; //!< Relocation data (target offset, target address, etc). +}; + +// ============================================================================ +// [asmjit::CodeHolder] +// ============================================================================ + +//! Contains basic information about the target architecture plus its settings, +//! and holds code & data (including sections, labels, and relocation information). +//! CodeHolder can store both binary and intermediate representation of assembly, +//! which can be generated by \ref Assembler and/or \ref CodeBuilder. +//! +//! NOTE: CodeHolder has ability to attach an \ref ErrorHandler, however, this +//! error handler is not triggered by CodeHolder itself, it's only used by the +//! attached code generators. +class ASMJIT_VIRTAPI CodeHolder { +public: + ASMJIT_NONCOPYABLE(CodeHolder) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create an uninitialized CodeHolder (you must init() it before it can be used). + ASMJIT_API CodeHolder() noexcept; + //! Destroy the CodeHolder. + ASMJIT_API ~CodeHolder() noexcept; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { return _codeInfo.isInitialized(); } + + //! Initialize to CodeHolder to hold code described by `codeInfo`. + ASMJIT_API Error init(const CodeInfo& info) noexcept; + //! Detach all code-generators attached and reset the \ref CodeHolder. + ASMJIT_API void reset(bool releaseMemory = false) noexcept; + + // -------------------------------------------------------------------------- + // [Attach / Detach] + // -------------------------------------------------------------------------- + + //! Attach a \ref CodeEmitter to this \ref CodeHolder. + ASMJIT_API Error attach(CodeEmitter* emitter) noexcept; + //! Detach a \ref CodeEmitter from this \ref CodeHolder. + ASMJIT_API Error detach(CodeEmitter* emitter) noexcept; + + // -------------------------------------------------------------------------- + // [Sync] + // -------------------------------------------------------------------------- + + //! Synchronize all states of all `CodeEmitter`s associated with the CodeHolder. + //! This is required as some code generators don't sync every time they do + //! something - for example \ref Assembler generally syncs when it needs to + //! reallocate the \ref CodeBuffer, but not each time it encodes instruction + //! or directive. + ASMJIT_API void sync() noexcept; + + // -------------------------------------------------------------------------- + // [Code-Information] + // -------------------------------------------------------------------------- + + //! Get code/target information, see \ref CodeInfo. + ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; } + //! Get architecture information, see \ref ArchInfo. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _codeInfo.getArchInfo(); } + + //! Get the target's architecture type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return getArchInfo().getType(); } + //! Get the target's architecture sub-type. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return getArchInfo().getSubType(); } + + //! Get if a static base-address is set. + ASMJIT_INLINE bool hasBaseAddress() const noexcept { return _codeInfo.hasBaseAddress(); } + //! Get a static base-address (uint64_t). + ASMJIT_INLINE uint64_t getBaseAddress() const noexcept { return _codeInfo.getBaseAddress(); } + + // -------------------------------------------------------------------------- + // [Global Information] + // -------------------------------------------------------------------------- + + //! Get global hints, internally propagated to all `CodeEmitter`s attached. + ASMJIT_INLINE uint32_t getGlobalHints() const noexcept { return _globalHints; } + //! Get global options, internally propagated to all `CodeEmitter`s attached. + ASMJIT_INLINE uint32_t getGlobalOptions() const noexcept { return _globalOptions; } + + // -------------------------------------------------------------------------- + // [Result Information] + // -------------------------------------------------------------------------- + + //! Get the size code & data of all sections. + ASMJIT_API size_t getCodeSize() const noexcept; + + //! Get size of all possible trampolines. + //! + //! Trampolines are needed to successfully generate relative jumps to absolute + //! addresses. This value is only non-zero if jmp of call instructions were + //! used with immediate operand (this means jumping or calling an absolute + //! address directly). + ASMJIT_INLINE size_t getTrampolinesSize() const noexcept { return _trampolinesSize; } + + // -------------------------------------------------------------------------- + // [Logging & Error Handling] + // -------------------------------------------------------------------------- + +#if !defined(ASMJIT_DISABLE_LOGGING) + //! Get if a logger attached. + ASMJIT_INLINE bool hasLogger() const noexcept { return _logger != nullptr; } + //! Get the attached logger. + ASMJIT_INLINE Logger* getLogger() const noexcept { return _logger; } + //! Attach a `logger` to CodeHolder and propagate it to all attached `CodeEmitter`s. + ASMJIT_API void setLogger(Logger* logger) noexcept; + //! Reset the logger (does nothing if not attached). + ASMJIT_INLINE void resetLogger() noexcept { setLogger(nullptr); } +#endif // !ASMJIT_DISABLE_LOGGING + + //! Get if error-handler is attached. + ASMJIT_INLINE bool hasErrorHandler() const noexcept { return _errorHandler != nullptr; } + //! Get the error-handler. + ASMJIT_INLINE ErrorHandler* getErrorHandler() const noexcept { return _errorHandler; } + //! Set the error handler, will affect all attached `CodeEmitter`s. + ASMJIT_API Error setErrorHandler(ErrorHandler* handler) noexcept; + //! Reset the error handler (does nothing if not attached). + ASMJIT_INLINE void resetErrorHandler() noexcept { setErrorHandler(nullptr); } + + // -------------------------------------------------------------------------- + // [Sections] + // -------------------------------------------------------------------------- + + //! Get array of `SectionEntry*` records. + ASMJIT_INLINE const ZoneVector& getSections() const noexcept { return _sections; } + + //! Get a section entry of the given index. + ASMJIT_INLINE SectionEntry* getSectionEntry(size_t index) const noexcept { return _sections[index]; } + + ASMJIT_API Error growBuffer(CodeBuffer* cb, size_t n) noexcept; + ASMJIT_API Error reserveBuffer(CodeBuffer* cb, size_t n) noexcept; + + // -------------------------------------------------------------------------- + // [Labels & Symbols] + // -------------------------------------------------------------------------- + + //! Create a new anonymous label and return its id in `idOut`. + //! + //! Returns `Error`, does not report error to \ref ErrorHandler. + ASMJIT_API Error newLabelId(uint32_t& idOut) noexcept; + + //! Create a new named label label-type `type`. + //! + //! Returns `Error`, does not report error to \ref ErrorHandler. + ASMJIT_API Error newNamedLabelId(uint32_t& idOut, const char* name, size_t nameLength, uint32_t type, uint32_t parentId) noexcept; + + //! Get a label id by name. + ASMJIT_API uint32_t getLabelIdByName(const char* name, size_t nameLength = Globals::kInvalidIndex, uint32_t parentId = 0) noexcept; + + //! Create a new label-link used to store information about yet unbound labels. + //! + //! Returns `null` if the allocation failed. + ASMJIT_API LabelLink* newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept; + + //! Get array of `LabelEntry*` records. + ASMJIT_INLINE const ZoneVector& getLabelEntries() const noexcept { return _labels; } + + //! Get number of labels created. + ASMJIT_INLINE size_t getLabelsCount() const noexcept { return _labels.getLength(); } + + //! Get number of label references, which are unresolved at the moment. + ASMJIT_INLINE size_t getUnresolvedLabelsCount() const noexcept { return _unresolvedLabelsCount; } + + //! Get if the `label` is valid (i.e. created by `newLabelId()`). + ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept { + return isLabelValid(label.getId()); + } + //! Get if the label having `id` is valid (i.e. created by `newLabelId()`). + ASMJIT_INLINE bool isLabelValid(uint32_t labelId) const noexcept { + size_t index = Operand::unpackId(labelId); + return index < _labels.getLength(); + } + + //! Get if the `label` is already bound. + //! + //! Returns `false` if the `label` is not valid. + ASMJIT_INLINE bool isLabelBound(const Label& label) const noexcept { + return isLabelBound(label.getId()); + } + //! \overload + ASMJIT_INLINE bool isLabelBound(uint32_t id) const noexcept { + size_t index = Operand::unpackId(id); + return index < _labels.getLength() && _labels[index]->isBound(); + } + + //! Get a `label` offset or -1 if the label is not yet bound. + ASMJIT_INLINE intptr_t getLabelOffset(const Label& label) const noexcept { + return getLabelOffset(label.getId()); + } + //! \overload + ASMJIT_INLINE intptr_t getLabelOffset(uint32_t id) const noexcept { + ASMJIT_ASSERT(isLabelValid(id)); + return _labels[Operand::unpackId(id)]->getOffset(); + } + + //! Get information about the given `label`. + ASMJIT_INLINE LabelEntry* getLabelEntry(const Label& label) const noexcept { + return getLabelEntry(label.getId()); + } + //! Get information about a label having the given `id`. + ASMJIT_INLINE LabelEntry* getLabelEntry(uint32_t id) const noexcept { + size_t index = static_cast(Operand::unpackId(id)); + return index < _labels.getLength() ? _labels[index] : static_cast(nullptr); + } + + // -------------------------------------------------------------------------- + // [Relocations] + // -------------------------------------------------------------------------- + + //! Create a new relocation entry of type `type` and size `size`. + //! + //! Additional fields can be set after the relocation entry was created. + ASMJIT_API Error newRelocEntry(RelocEntry** dst, uint32_t type, uint32_t size) noexcept; + + //! Get if the code contains relocations. + ASMJIT_INLINE bool hasRelocations() const noexcept { return !_relocations.isEmpty(); } + //! Get array of `RelocEntry*` records. + ASMJIT_INLINE const ZoneVector& getRelocEntries() const noexcept { return _relocations; } + + ASMJIT_INLINE RelocEntry* getRelocEntry(uint32_t id) const noexcept { return _relocations[id]; } + + //! Relocate the code to `baseAddress` and copy it to `dst`. + //! + //! \param dst Contains the location where the relocated code should be + //! copied. The pointer can be address returned by virtual memory allocator + //! or any other address that has sufficient space. + //! + //! \param baseAddress Base address used for relocation. `JitRuntime` always + //! sets the `baseAddress` to be the same as `dst`. + //! + //! \return The number bytes actually used. If the code emitter reserved + //! space for possible trampolines, but didn't use it, the number of bytes + //! used can actually be less than the expected worst case. Virtual memory + //! allocator can shrink the memory it allocated initially. + //! + //! A given buffer will be overwritten, to get the number of bytes required, + //! use `getCodeSize()`. + ASMJIT_API size_t relocate(void* dst, uint64_t baseAddress = Globals::kNoBaseAddress) const noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CodeInfo _codeInfo; //!< Basic information about the code (architecture and other info). + + uint32_t _globalHints; //!< Global hints, propagated to all `CodeEmitter`s. + uint32_t _globalOptions; //!< Global options, propagated to all `CodeEmitter`s. + + CodeEmitter* _emitters; //!< Linked-list of all attached `CodeEmitter`s. + Assembler* _cgAsm; //!< Attached \ref Assembler (only one at a time). + + Logger* _logger; //!< Attached \ref Logger, used by all consumers. + ErrorHandler* _errorHandler; //!< Attached \ref ErrorHandler. + + uint32_t _unresolvedLabelsCount; //!< Count of label references which were not resolved. + uint32_t _trampolinesSize; //!< Size of all possible trampolines. + + Zone _baseZone; //!< Base zone (used to allocate core structures). + Zone _dataZone; //!< Data zone (used to allocate extra data like label names). + ZoneHeap _baseHeap; //!< Zone allocator, used to manage internal containers. + + ZoneVector _sections; //!< Section entries. + ZoneVector _labels; //!< Label entries (each label is stored here). + ZoneVector _relocations; //!< Relocation entries. + ZoneHash _namedLabels; //!< Label name -> LabelEntry (only named labels). +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_CODEHOLDER_H diff --git a/src/asmjit/base/compiler.cpp b/src/asmjit/base/compiler.cpp deleted file mode 100644 index 8b7116d..0000000 --- a/src/asmjit/base/compiler.cpp +++ /dev/null @@ -1,630 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Export] -#define ASMJIT_EXPORTS - -// [Guard] -#include "../build.h" -#if !defined(ASMJIT_DISABLE_COMPILER) - -// [Dependencies] -#include "../base/assembler.h" -#include "../base/compiler.h" -#include "../base/compilercontext_p.h" -#include "../base/cpuinfo.h" -#include "../base/logger.h" -#include "../base/utils.h" -#include - -// [Api-Begin] -#include "../apibegin.h" - -namespace asmjit { - -// ============================================================================ -// [Constants] -// ============================================================================ - -static const char noName[1] = { '\0' }; -enum { kCompilerDefaultLookAhead = 64 }; - -// ============================================================================ -// [asmjit::Compiler - Construction / Destruction] -// ============================================================================ - -Compiler::Compiler() noexcept - : _features(0), - _maxLookAhead(kCompilerDefaultLookAhead), - _instOptions(0), - _tokenGenerator(0), - _nodeFlowId(0), - _nodeFlags(0), - _targetVarMapping(nullptr), - _firstNode(nullptr), - _lastNode(nullptr), - _cursor(nullptr), - _func(nullptr), - _zoneAllocator(8192 - Zone::kZoneOverhead), - _varAllocator(4096 - Zone::kZoneOverhead), - _stringAllocator(4096 - Zone::kZoneOverhead), - _constAllocator(4096 - Zone::kZoneOverhead), - _localConstPool(&_constAllocator), - _globalConstPool(&_zoneAllocator) {} -Compiler::~Compiler() noexcept {} - -// ============================================================================ -// [asmjit::Compiler - Attach / Reset] -// ============================================================================ - -void Compiler::reset(bool releaseMemory) noexcept { - Assembler* assembler = getAssembler(); - if (assembler != nullptr) - assembler->_detached(this); - - _arch = kArchNone; - _regSize = 0; - _finalized = false; - _lastError = kErrorNotInitialized; - - _features = 0; - _maxLookAhead = kCompilerDefaultLookAhead; - - _instOptions = 0; - _tokenGenerator = 0; - - _nodeFlowId = 0; - _nodeFlags = 0; - - _firstNode = nullptr; - _lastNode = nullptr; - - _cursor = nullptr; - _func = nullptr; - - _localConstPool.reset(); - _globalConstPool.reset(); - - _localConstPoolLabel.reset(); - _globalConstPoolLabel.reset(); - - _zoneAllocator.reset(releaseMemory); - _varAllocator.reset(releaseMemory); - _stringAllocator.reset(releaseMemory); - _constAllocator.reset(releaseMemory); - - _varList.reset(releaseMemory); -} - -// ============================================================================ -// [asmjit::Compiler - Node-Factory] -// ============================================================================ - -HLData* Compiler::newDataNode(const void* data, uint32_t size) noexcept { - if (size > HLData::kInlineBufferSize) { - void* clonedData = _stringAllocator.alloc(size); - if (clonedData == nullptr) - return nullptr; - - if (data != nullptr) - ::memcpy(clonedData, data, size); - data = clonedData; - } - - return newNode(const_cast(data), size); -} - -HLAlign* Compiler::newAlignNode(uint32_t alignMode, uint32_t offset) noexcept { - return newNode(alignMode, offset); -} - -HLLabel* Compiler::newLabelNode() noexcept { - Assembler* assembler = getAssembler(); - if (assembler == nullptr) return nullptr; - - uint32_t id = assembler->_newLabelId(); - LabelData* ld = assembler->getLabelData(id); - - HLLabel* node = newNode(id); - if (node == nullptr) return nullptr; - - // These have to be zero now. - ASMJIT_ASSERT(ld->exId == 0); - ASMJIT_ASSERT(ld->exData == nullptr); - - ld->exId = _exId; - ld->exData = node; - - return node; -} - -HLComment* Compiler::newCommentNode(const char* str) noexcept { - if (str != nullptr && str[0]) { - str = _stringAllocator.sdup(str); - if (str == nullptr) - return nullptr; - } - - return newNode(str); -} - -HLHint* Compiler::newHintNode(Var& var, uint32_t hint, uint32_t value) noexcept { - if (var.getId() == kInvalidValue) - return nullptr; - - VarData* vd = getVd(var); - return newNode(vd, hint, value); -} - -// ============================================================================ -// [asmjit::Compiler - Code-Stream] -// ============================================================================ - -HLNode* Compiler::addFunc(HLFunc* func) noexcept { - ASMJIT_ASSERT(_func == nullptr); - _func = func; - - addNode(func); // Add function node. - addNode(func->getEntryNode()); // Add function entry. - HLNode* cursor = getCursor(); - - addNode(func->getExitNode()); // Add function exit / epilog marker. - addNode(func->getEnd()); // Add function end. - setCursor(cursor); - - return func; -} - -HLNode* Compiler::addNode(HLNode* node) noexcept { - ASMJIT_ASSERT(node != nullptr); - ASMJIT_ASSERT(node->_prev == nullptr); - ASMJIT_ASSERT(node->_next == nullptr); - - if (_cursor == nullptr) { - if (_firstNode == nullptr) { - _firstNode = node; - _lastNode = node; - } - else { - node->_next = _firstNode; - _firstNode->_prev = node; - _firstNode = node; - } - } - else { - HLNode* prev = _cursor; - HLNode* next = _cursor->_next; - - node->_prev = prev; - node->_next = next; - - prev->_next = node; - if (next) - next->_prev = node; - else - _lastNode = node; - } - - _cursor = node; - return node; -} - -HLNode* Compiler::addNodeBefore(HLNode* node, HLNode* ref) noexcept { - ASMJIT_ASSERT(node != nullptr); - ASMJIT_ASSERT(node->_prev == nullptr); - ASMJIT_ASSERT(node->_next == nullptr); - ASMJIT_ASSERT(ref != nullptr); - - HLNode* prev = ref->_prev; - HLNode* next = ref; - - node->_prev = prev; - node->_next = next; - - next->_prev = node; - if (prev) - prev->_next = node; - else - _firstNode = node; - - return node; -} - -HLNode* Compiler::addNodeAfter(HLNode* node, HLNode* ref) noexcept { - ASMJIT_ASSERT(node != nullptr); - ASMJIT_ASSERT(node->_prev == nullptr); - ASMJIT_ASSERT(node->_next == nullptr); - ASMJIT_ASSERT(ref != nullptr); - - HLNode* prev = ref; - HLNode* next = ref->_next; - - node->_prev = prev; - node->_next = next; - - prev->_next = node; - if (next) - next->_prev = node; - else - _lastNode = node; - - return node; -} - -static ASMJIT_INLINE void Compiler_nodeRemoved(Compiler* self, HLNode* node_) noexcept { - if (node_->isJmpOrJcc()) { - HLJump* node = static_cast(node_); - HLLabel* label = node->getTarget(); - - if (label != nullptr) { - // Disconnect. - HLJump** pPrev = &label->_from; - for (;;) { - ASMJIT_ASSERT(*pPrev != nullptr); - HLJump* current = *pPrev; - - if (current == nullptr) - break; - - if (current == node) { - *pPrev = node->_jumpNext; - break; - } - - pPrev = ¤t->_jumpNext; - } - - label->subNumRefs(); - } - } -} - -HLNode* Compiler::removeNode(HLNode* node) noexcept { - HLNode* prev = node->_prev; - HLNode* next = node->_next; - - if (_firstNode == node) - _firstNode = next; - else - prev->_next = next; - - if (_lastNode == node) - _lastNode = prev; - else - next->_prev = prev; - - node->_prev = nullptr; - node->_next = nullptr; - - if (_cursor == node) - _cursor = prev; - Compiler_nodeRemoved(this, node); - - return node; -} - -void Compiler::removeNodes(HLNode* first, HLNode* last) noexcept { - if (first == last) { - removeNode(first); - return; - } - - HLNode* prev = first->_prev; - HLNode* next = last->_next; - - if (_firstNode == first) - _firstNode = next; - else - prev->_next = next; - - if (_lastNode == last) - _lastNode = prev; - else - next->_prev = prev; - - HLNode* node = first; - for (;;) { - HLNode* next = node->getNext(); - ASMJIT_ASSERT(next != nullptr); - - node->_prev = nullptr; - node->_next = nullptr; - - if (_cursor == node) - _cursor = prev; - Compiler_nodeRemoved(this, node); - - if (node == last) - break; - node = next; - } -} - -HLNode* Compiler::setCursor(HLNode* node) noexcept { - HLNode* old = _cursor; - _cursor = node; - return old; -} - -// ============================================================================ -// [asmjit::Compiler - Align] -// ============================================================================ - -Error Compiler::align(uint32_t alignMode, uint32_t offset) noexcept { - HLAlign* node = newAlignNode(alignMode, offset); - if (node == nullptr) - return setLastError(kErrorNoHeapMemory); - - addNode(node); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::Compiler - Label] -// ============================================================================ - -HLLabel* Compiler::getHLLabel(uint32_t id) const noexcept { - Assembler* assembler = getAssembler(); - if (assembler == nullptr) return nullptr; - - LabelData* ld = assembler->getLabelData(id); - if (ld->exId == _exId) - return static_cast(ld->exData); - else - return nullptr; -} - -bool Compiler::isLabelValid(uint32_t id) const noexcept { - Assembler* assembler = getAssembler(); - if (assembler == nullptr) return false; - - return static_cast(id) < assembler->getLabelsCount(); -} - -uint32_t Compiler::_newLabelId() noexcept { - HLLabel* node = newLabelNode(); - if (node == nullptr) { - setLastError(kErrorNoHeapMemory); - return kInvalidValue; - } - - return node->getLabelId(); -} - -Error Compiler::bind(const Label& label) noexcept { - HLLabel* node = getHLLabel(label); - if (node == nullptr) - return setLastError(kErrorInvalidState); - addNode(node); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::Compiler - Embed] -// ============================================================================ - -Error Compiler::embed(const void* data, uint32_t size) noexcept { - HLData* node = newDataNode(data, size); - if (node == nullptr) - return setLastError(kErrorNoHeapMemory); - - addNode(node); - return kErrorOk; -} - -Error Compiler::embedConstPool(const Label& label, const ConstPool& pool) noexcept { - if (label.getId() == kInvalidValue) - return kErrorInvalidState; - - align(kAlignData, static_cast(pool.getAlignment())); - bind(label); - - HLData* embedNode = newDataNode(nullptr, static_cast(pool.getSize())); - if (embedNode == nullptr) - return kErrorNoHeapMemory; - - pool.fill(embedNode->getData()); - addNode(embedNode); - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::Compiler - Comment] -// ============================================================================ - -Error Compiler::comment(const char* fmt, ...) noexcept { - char buf[256]; - char* p = buf; - - if (fmt) { - va_list ap; - va_start(ap, fmt); - p += vsnprintf(p, 254, fmt, ap); - va_end(ap); - } - - p[0] = '\0'; - - HLComment* node = newCommentNode(buf); - if (node == nullptr) - return setLastError(kErrorNoHeapMemory); - - addNode(node); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::Compiler - Hint] -// ============================================================================ - -Error Compiler::_hint(Var& var, uint32_t hint, uint32_t value) noexcept { - if (var.getId() == kInvalidValue) - return kErrorOk; - - HLHint* node = newHintNode(var, hint, value); - if (node == nullptr) - return setLastError(kErrorNoHeapMemory); - - addNode(node); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::Compiler - Vars] -// ============================================================================ - -VarData* Compiler::_newVd(const VarInfo& vi, const char* name) noexcept { - VarData* vd = reinterpret_cast(_varAllocator.alloc(sizeof(VarData))); - if (ASMJIT_UNLIKELY(vd == nullptr)) - goto _NoMemory; - - vd->_name = noName; - vd->_id = OperandUtil::makeVarId(static_cast(_varList.getLength())); - vd->_localId = kInvalidValue; - -#if !defined(ASMJIT_DISABLE_LOGGER) - if (name != nullptr && name[0] != '\0') { - vd->_name = _stringAllocator.sdup(name); - } -#endif // !ASMJIT_DISABLE_LOGGER - - vd->_type = static_cast(vi.getTypeId()); - vd->_class = static_cast(vi.getRegClass()); - vd->_flags = 0; - vd->_priority = 10; - - vd->_state = kVarStateNone; - vd->_regIndex = kInvalidReg; - vd->_isStack = false; - vd->_isMemArg = false; - vd->_isCalculated = false; - vd->_saveOnUnuse = false; - vd->_modified = false; - vd->_reserved0 = 0; - vd->_alignment = static_cast(Utils::iMin(vi.getSize(), 64)); - - vd->_size = vi.getSize(); - vd->_homeMask = 0; - - vd->_memOffset = 0; - vd->_memCell = nullptr; - - vd->rReadCount = 0; - vd->rWriteCount = 0; - vd->mReadCount = 0; - vd->mWriteCount = 0; - - vd->_va = nullptr; - - if (ASMJIT_UNLIKELY(_varList.append(vd) != kErrorOk)) - goto _NoMemory; - return vd; - -_NoMemory: - setLastError(kErrorNoHeapMemory); - return nullptr; -} - -Error Compiler::alloc(Var& var) noexcept { - if (var.getId() == kInvalidValue) - return kErrorOk; - return _hint(var, kVarHintAlloc, kInvalidValue); -} - -Error Compiler::alloc(Var& var, uint32_t regIndex) noexcept { - if (var.getId() == kInvalidValue) - return kErrorOk; - return _hint(var, kVarHintAlloc, regIndex); -} - -Error Compiler::alloc(Var& var, const Reg& reg) noexcept { - if (var.getId() == kInvalidValue) - return kErrorOk; - return _hint(var, kVarHintAlloc, reg.getRegIndex()); -} - -Error Compiler::save(Var& var) noexcept { - if (var.getId() == kInvalidValue) - return kErrorOk; - return _hint(var, kVarHintSave, kInvalidValue); -} - -Error Compiler::spill(Var& var) noexcept { - if (var.getId() == kInvalidValue) - return kErrorOk; - return _hint(var, kVarHintSpill, kInvalidValue); -} - -Error Compiler::unuse(Var& var) noexcept { - if (var.getId() == kInvalidValue) - return kErrorOk; - return _hint(var, kVarHintUnuse, kInvalidValue); -} - -uint32_t Compiler::getPriority(Var& var) const noexcept { - if (var.getId() == kInvalidValue) - return kInvalidValue; - - VarData* vd = getVdById(var.getId()); - return vd->getPriority(); -} - -void Compiler::setPriority(Var& var, uint32_t priority) noexcept { - if (var.getId() == kInvalidValue) - return; - - if (priority > 255) - priority = 255; - - VarData* vd = getVdById(var.getId()); - vd->_priority = static_cast(priority); -} - -bool Compiler::getSaveOnUnuse(Var& var) const noexcept { - if (var.getId() == kInvalidValue) - return false; - - VarData* vd = getVdById(var.getId()); - return static_cast(vd->_saveOnUnuse); -} - -void Compiler::setSaveOnUnuse(Var& var, bool value) noexcept { - if (var.getId() == kInvalidValue) - return; - - VarData* vd = getVdById(var.getId()); - vd->_saveOnUnuse = value; -} - -void Compiler::rename(Var& var, const char* fmt, ...) noexcept { - if (var.getId() == kInvalidValue) - return; - - VarData* vd = getVdById(var.getId()); - vd->_name = noName; - - if (fmt != nullptr && fmt[0] != '\0') { - char buf[64]; - - va_list ap; - va_start(ap, fmt); - - vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap); - buf[ASMJIT_ARRAY_SIZE(buf) - 1] = '\0'; - - vd->_name = _stringAllocator.sdup(buf); - va_end(ap); - } -} - -} // asmjit namespace - -// [Api-End] -#include "../apiend.h" - -// [Guard] -#endif // !ASMJIT_DISABLE_COMPILER diff --git a/src/asmjit/base/compiler.h b/src/asmjit/base/compiler.h deleted file mode 100644 index e9eafd1..0000000 --- a/src/asmjit/base/compiler.h +++ /dev/null @@ -1,576 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Guard] -#ifndef _ASMJIT_BASE_COMPILER_H -#define _ASMJIT_BASE_COMPILER_H - -#include "../build.h" -#if !defined(ASMJIT_DISABLE_COMPILER) - -// [Dependencies] -#include "../base/assembler.h" -#include "../base/compilerfunc.h" -#include "../base/constpool.h" -#include "../base/containers.h" -#include "../base/hlstream.h" -#include "../base/operand.h" -#include "../base/podvector.h" -#include "../base/utils.h" -#include "../base/zone.h" - -// [Api-Begin] -#include "../apibegin.h" - -namespace asmjit { - -// ============================================================================ -// [Forward Declarations] -// ============================================================================ - -struct VarAttr; -struct VarData; -struct VarMap; -struct VarState; - -//! \addtogroup asmjit_base -//! \{ - -// ============================================================================ -// [asmjit::CompilerFeatures] -// ============================================================================ - -ASMJIT_ENUM(CompilerFeatures) { - //! Schedule instructions so they can be executed faster (`Compiler` only). - //! - //! Default `false` - has to be explicitly enabled as the scheduler needs - //! some time to run. - //! - //! X86/X64 Specific - //! ---------------- - //! - //! If scheduling is enabled AsmJit will try to reorder instructions to - //! minimize the dependency chain. Scheduler always runs after the registers - //! are allocated so it doesn't change count of register allocs/spills. - //! - //! This feature is highly experimental and untested. - kCompilerFeatureEnableScheduler = 0 -}; - -// ============================================================================ -// [asmjit::ConstScope] -// ============================================================================ - -//! Scope of the constant. -ASMJIT_ENUM(ConstScope) { - //! Local constant, always embedded right after the current function. - kConstScopeLocal = 0, - //! Global constant, embedded at the end of the currently compiled code. - kConstScopeGlobal = 1 -}; - -// ============================================================================ -// [asmjit::VarInfo] -// ============================================================================ - -struct VarInfo { - // ============================================================================ - // [Flags] - // ============================================================================ - - //! \internal - //! - //! Variable flags. - ASMJIT_ENUM(Flags) { - //! Variable contains one or more single-precision floating point. - kFlagSP = 0x10, - //! Variable contains one or more double-precision floating point. - kFlagDP = 0x20, - //! Variable is a vector, contains packed data. - kFlagSIMD = 0x80 - }; - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get type id. - ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _typeId; } - //! Get type name. - ASMJIT_INLINE const char* getTypeName() const noexcept { return _typeName; } - - //! Get register size in bytes. - ASMJIT_INLINE uint32_t getSize() const noexcept { return _size; } - //! Get variable class, see \ref RegClass. - ASMJIT_INLINE uint32_t getRegClass() const noexcept { return _regClass; } - //! Get register type, see `X86RegType`. - ASMJIT_INLINE uint32_t getRegType() const noexcept { return _regType; } - //! Get type flags, see `VarFlag`. - ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Variable type id. - uint8_t _typeId; - //! Variable and register size (in bytes). - uint8_t _size; - //! Register class, see `RegClass`. - uint8_t _regClass; - //! Register type the variable is mapped to. - uint8_t _regType; - - //! Variable info flags, see \ref Flags. - uint32_t _flags; - - //! Variable type name. - char _typeName[8]; -}; - -// ============================================================================ -// [asmjit::Compiler] -// ============================================================================ - -//! Compiler interface. -//! -//! \sa Assembler. -class ASMJIT_VIRTAPI Compiler : public ExternalTool { - public: - ASMJIT_NO_COPY(Compiler) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `Compiler` instance. - ASMJIT_API Compiler() noexcept; - //! Destroy the `Compiler` instance. - ASMJIT_API virtual ~Compiler() noexcept; - - // -------------------------------------------------------------------------- - // [Reset] - // -------------------------------------------------------------------------- - - //! \override - ASMJIT_API virtual void reset(bool releaseMemory) noexcept; - - // -------------------------------------------------------------------------- - // [Compiler Features] - // -------------------------------------------------------------------------- - - //! Get code-generator features. - ASMJIT_INLINE uint32_t getFeatures() const noexcept { - return _features; - } - //! Set code-generator features. - ASMJIT_INLINE void setFeatures(uint32_t features) noexcept { - _features = features; - } - - //! Get code-generator `feature`. - ASMJIT_INLINE bool hasFeature(uint32_t feature) const noexcept { - ASMJIT_ASSERT(feature < 32); - return (_features & (1 << feature)) != 0; - } - - //! Set code-generator `feature` to `value`. - ASMJIT_INLINE void setFeature(uint32_t feature, bool value) noexcept { - ASMJIT_ASSERT(feature < 32); - feature = static_cast(value) << feature; - _features = (_features & ~feature) | feature; - } - - //! Get maximum look ahead. - ASMJIT_INLINE uint32_t getMaxLookAhead() const noexcept { - return _maxLookAhead; - } - //! Set maximum look ahead to `val`. - ASMJIT_INLINE void setMaxLookAhead(uint32_t val) noexcept { - _maxLookAhead = val; - } - - // -------------------------------------------------------------------------- - // [Token ID] - // -------------------------------------------------------------------------- - - //! \internal - //! - //! Reset the token-id generator. - ASMJIT_INLINE void _resetTokenGenerator() noexcept { - _tokenGenerator = 0; - } - - //! \internal - //! - //! Generate a new unique token id. - ASMJIT_INLINE uint32_t _generateUniqueToken() noexcept { - return ++_tokenGenerator; - } - - // -------------------------------------------------------------------------- - // [Instruction Options] - // -------------------------------------------------------------------------- - - //! Get options of the next instruction. - ASMJIT_INLINE uint32_t getInstOptions() const noexcept { - return _instOptions; - } - //! Set options of the next instruction. - ASMJIT_INLINE void setInstOptions(uint32_t instOptions) noexcept { - _instOptions = instOptions; - } - - //! Get options of the next instruction and reset them. - ASMJIT_INLINE uint32_t getInstOptionsAndReset() { - uint32_t instOptions = _instOptions; - _instOptions = 0; - return instOptions; - }; - - // -------------------------------------------------------------------------- - // [Node-Factory] - // -------------------------------------------------------------------------- - - //! \internal - template - ASMJIT_INLINE T* newNode() noexcept { - void* p = _zoneAllocator.alloc(sizeof(T)); - return new(p) T(this); - } - - //! \internal - template - ASMJIT_INLINE T* newNode(P0 p0) noexcept { - void* p = _zoneAllocator.alloc(sizeof(T)); - return new(p) T(this, p0); - } - - //! \internal - template - ASMJIT_INLINE T* newNode(P0 p0, P1 p1) noexcept { - void* p = _zoneAllocator.alloc(sizeof(T)); - return new(p) T(this, p0, p1); - } - - //! \internal - template - ASMJIT_INLINE T* newNode(P0 p0, P1 p1, P2 p2) noexcept { - void* p = _zoneAllocator.alloc(sizeof(T)); - return new(p) T(this, p0, p1, p2); - } - - //! \internal - //! - //! Create a new `HLData` node. - ASMJIT_API HLData* newDataNode(const void* data, uint32_t size) noexcept; - - //! \internal - //! - //! Create a new `HLAlign` node. - ASMJIT_API HLAlign* newAlignNode(uint32_t alignMode, uint32_t offset) noexcept; - - //! \internal - //! - //! Create a new `HLLabel` node. - ASMJIT_API HLLabel* newLabelNode() noexcept; - - //! \internal - //! - //! Create a new `HLComment`. - ASMJIT_API HLComment* newCommentNode(const char* str) noexcept; - - //! \internal - //! - //! Create a new `HLHint`. - ASMJIT_API HLHint* newHintNode(Var& var, uint32_t hint, uint32_t value) noexcept; - - // -------------------------------------------------------------------------- - // [Code-Stream] - // -------------------------------------------------------------------------- - - //! Add a function `node` to the stream. - ASMJIT_API HLNode* addFunc(HLFunc* func) noexcept; - - //! Add node `node` after current and set current to `node`. - ASMJIT_API HLNode* addNode(HLNode* node) noexcept; - //! Insert `node` before `ref`. - ASMJIT_API HLNode* addNodeBefore(HLNode* node, HLNode* ref) noexcept; - //! Insert `node` after `ref`. - ASMJIT_API HLNode* addNodeAfter(HLNode* node, HLNode* ref) noexcept; - //! Remove `node`. - ASMJIT_API HLNode* removeNode(HLNode* node) noexcept; - //! Remove multiple nodes. - ASMJIT_API void removeNodes(HLNode* first, HLNode* last) noexcept; - - //! Get the first node. - ASMJIT_INLINE HLNode* getFirstNode() const noexcept { return _firstNode; } - //! Get the last node. - ASMJIT_INLINE HLNode* getLastNode() const noexcept { return _lastNode; } - - //! Get current node. - //! - //! \note If this method returns `nullptr` it means that nothing has been - //! emitted yet. - ASMJIT_INLINE HLNode* getCursor() const noexcept { return _cursor; } - //! \internal - //! - //! Set the current node without returning the previous node. - ASMJIT_INLINE void _setCursor(HLNode* node) noexcept { _cursor = node; } - //! Set the current node to `node` and return the previous one. - ASMJIT_API HLNode* setCursor(HLNode* node) noexcept; - - // -------------------------------------------------------------------------- - // [Func] - // -------------------------------------------------------------------------- - - //! Get current function. - ASMJIT_INLINE HLFunc* getFunc() const noexcept { return _func; } - - // -------------------------------------------------------------------------- - // [Align] - // -------------------------------------------------------------------------- - - //! Align target buffer to the `offset` specified. - //! - //! The sequence that is used to fill the gap between the aligned location - //! and the current depends on `alignMode`, see \ref AlignMode. - ASMJIT_API Error align(uint32_t alignMode, uint32_t offset) noexcept; - - // -------------------------------------------------------------------------- - // [Label] - // -------------------------------------------------------------------------- - - //! Get `HLLabel` by `id`. - //! - //! NOTE: The label has to be valid, see `isLabelValid()`. - ASMJIT_API HLLabel* getHLLabel(uint32_t id) const noexcept; - - //! Get `HLLabel` by `label`. - //! - //! NOTE: The label has to be valid, see `isLabelValid()`. - ASMJIT_INLINE HLLabel* getHLLabel(const Label& label) noexcept { - return getHLLabel(label.getId()); - } - - //! Get whether the label `id` is valid. - ASMJIT_API bool isLabelValid(uint32_t id) const noexcept; - //! Get whether the `label` is valid. - ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept { - return isLabelValid(label.getId()); - } - - //! \internal - //! - //! Create a new label and return its ID. - ASMJIT_API uint32_t _newLabelId() noexcept; - - //! Create and return a new `Label`. - ASMJIT_INLINE Label newLabel() noexcept { return Label(_newLabelId()); } - - //! Bind label to the current offset. - //! - //! NOTE: Label can be bound only once! - ASMJIT_API Error bind(const Label& label) noexcept; - - // -------------------------------------------------------------------------- - // [Embed] - // -------------------------------------------------------------------------- - - //! Embed data. - ASMJIT_API Error embed(const void* data, uint32_t size) noexcept; - - //! Embed a constant pool data, adding the following in order: - //! 1. Data alignment. - //! 2. Label. - //! 3. Constant pool data. - ASMJIT_API Error embedConstPool(const Label& label, const ConstPool& pool) noexcept; - - // -------------------------------------------------------------------------- - // [Comment] - // -------------------------------------------------------------------------- - - //! Emit a single comment line. - ASMJIT_API Error comment(const char* fmt, ...) noexcept; - - // -------------------------------------------------------------------------- - // [Hint] - // -------------------------------------------------------------------------- - - //! Emit a new hint (purery informational node). - ASMJIT_API Error _hint(Var& var, uint32_t hint, uint32_t value) noexcept; - - // -------------------------------------------------------------------------- - // [Vars] - // -------------------------------------------------------------------------- - - //! Get whether variable `var` is created. - ASMJIT_INLINE bool isVarValid(const Var& var) const noexcept { - return static_cast(var.getId() & Operand::kIdIndexMask) < _varList.getLength(); - } - - //! \internal - //! - //! Get `VarData` by `var`. - ASMJIT_INLINE VarData* getVd(const Var& var) const noexcept { - return getVdById(var.getId()); - } - - //! \internal - //! - //! Get `VarData` by `id`. - ASMJIT_INLINE VarData* getVdById(uint32_t id) const noexcept { - ASMJIT_ASSERT(id != kInvalidValue); - ASMJIT_ASSERT(static_cast(id & Operand::kIdIndexMask) < _varList.getLength()); - - return _varList[id & Operand::kIdIndexMask]; - } - - //! \internal - //! - //! Get an array of 'VarData*'. - ASMJIT_INLINE VarData** _getVdArray() const noexcept { - return const_cast(_varList.getData()); - } - - //! \internal - //! - //! Create a new `VarData`. - ASMJIT_API VarData* _newVd(const VarInfo& vi, const char* name) noexcept; - - //! Alloc variable `var`. - ASMJIT_API Error alloc(Var& var) noexcept; - //! Alloc variable `var` using `regIndex` as a register index. - ASMJIT_API Error alloc(Var& var, uint32_t regIndex) noexcept; - //! Alloc variable `var` using `reg` as a register operand. - ASMJIT_API Error alloc(Var& var, const Reg& reg) noexcept; - //! Spill variable `var`. - ASMJIT_API Error spill(Var& var) noexcept; - //! Save variable `var` if the status is `modified` at this point. - ASMJIT_API Error save(Var& var) noexcept; - //! Unuse variable `var`. - ASMJIT_API Error unuse(Var& var) noexcept; - - //! Get priority of variable `var`. - ASMJIT_API uint32_t getPriority(Var& var) const noexcept; - //! Set priority of variable `var` to `priority`. - ASMJIT_API void setPriority(Var& var, uint32_t priority) noexcept; - - //! Get save-on-unuse `var` property. - ASMJIT_API bool getSaveOnUnuse(Var& var) const noexcept; - //! Set save-on-unuse `var` property to `value`. - ASMJIT_API void setSaveOnUnuse(Var& var, bool value) noexcept; - - //! Rename variable `var` to `name`. - //! - //! NOTE: Only new name will appear in the logger. - ASMJIT_API void rename(Var& var, const char* fmt, ...) noexcept; - - // -------------------------------------------------------------------------- - // [Stack] - // -------------------------------------------------------------------------- - - //! \internal - //! - //! Create a new memory chunk allocated on the current function's stack. - virtual Error _newStack(BaseMem* mem, uint32_t size, uint32_t alignment, const char* name) noexcept = 0; - - // -------------------------------------------------------------------------- - // [Const] - // -------------------------------------------------------------------------- - - //! \internal - //! - //! Put data to a constant-pool and get a memory reference to it. - virtual Error _newConst(BaseMem* mem, uint32_t scope, const void* data, size_t size) noexcept = 0; - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Code-Generation features, used by \ref hasFeature() and \ref setFeature(). - uint32_t _features; - //! Maximum count of nodes to look ahead when allocating/spilling - //! registers. - uint32_t _maxLookAhead; - - //! Options affecting the next instruction. - uint32_t _instOptions; - //! Processing token generator. - //! - //! Used to get a unique token that is then used to process `HLNode`s. See - //! `Compiler::_getUniqueToken()` for more details. - uint32_t _tokenGenerator; - - //! Flow id added to each node created (used only by `Context)`. - uint32_t _nodeFlowId; - //! Flags added to each node created (used only by `Context)`. - uint32_t _nodeFlags; - - //! Variable mapping (translates incoming VarType into target). - const uint8_t* _targetVarMapping; - - //! First node. - HLNode* _firstNode; - //! Last node. - HLNode* _lastNode; - - //! Current node. - HLNode* _cursor; - //! Current function. - HLFunc* _func; - - //! General purpose zone allocator. - Zone _zoneAllocator; - //! Variable zone. - Zone _varAllocator; - //! String/data zone. - Zone _stringAllocator; - //! Local constant pool zone. - Zone _constAllocator; - - //! VarData list. - PodVector _varList; - - //! Local constant pool, flushed at the end of each function. - ConstPool _localConstPool; - //! Global constant pool, flushed at the end of the compilation. - ConstPool _globalConstPool; - - //! Label to start of the local constant pool. - Label _localConstPoolLabel; - //! Label to start of the global constant pool. - Label _globalConstPoolLabel; -}; - -//! \} - -// ============================================================================ -// [Defined-Later] -// ============================================================================ - -ASMJIT_INLINE HLNode::HLNode(Compiler* compiler, uint32_t type) noexcept { - _prev = nullptr; - _next = nullptr; - _type = static_cast(type); - _opCount = 0; - _flags = static_cast(compiler->_nodeFlags); - _flowId = compiler->_nodeFlowId; - _tokenId = 0; - _comment = nullptr; - _map = nullptr; - _liveness = nullptr; - _state = nullptr; -} - -} // asmjit namespace - -// [Api-End] -#include "../apiend.h" - -// [Guard] -#endif // !ASMJIT_DISABLE_COMPILER -#endif // _ASMJIT_BASE_COMPILER_H diff --git a/src/asmjit/base/compilercontext.cpp b/src/asmjit/base/compilercontext.cpp deleted file mode 100644 index 664314c..0000000 --- a/src/asmjit/base/compilercontext.cpp +++ /dev/null @@ -1,653 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Export] -#define ASMJIT_EXPORTS - -// [Guard] -#include "../build.h" -#if !defined(ASMJIT_DISABLE_COMPILER) - -// [Dependencies] -#include "../base/compilercontext_p.h" -#include "../base/utils.h" - -// [Api-Begin] -#include "../apibegin.h" - -namespace asmjit { - -// ============================================================================ -// [asmjit::Context - Construction / Destruction] -// ============================================================================ - -Context::Context(Compiler* compiler) : - _compiler(compiler), - _zoneAllocator(8192 - Zone::kZoneOverhead), - _traceNode(nullptr), - _varMapToVaListOffset(0) { - - Context::reset(); -} -Context::~Context() {} - -// ============================================================================ -// [asmjit::Context - Reset] -// ============================================================================ - -void Context::reset(bool releaseMemory) { - _zoneAllocator.reset(releaseMemory); - - _func = nullptr; - _start = nullptr; - _end = nullptr; - _extraBlock = nullptr; - _stop = nullptr; - - _unreachableList.reset(); - _returningList.reset(); - _jccList.reset(); - _contextVd.reset(releaseMemory); - - _memVarCells = nullptr; - _memStackCells = nullptr; - - _mem1ByteVarsUsed = 0; - _mem2ByteVarsUsed = 0; - _mem4ByteVarsUsed = 0; - _mem8ByteVarsUsed = 0; - _mem16ByteVarsUsed = 0; - _mem32ByteVarsUsed = 0; - _mem64ByteVarsUsed = 0; - _memStackCellsUsed = 0; - - _memMaxAlign = 0; - _memVarTotal = 0; - _memStackTotal = 0; - _memAllTotal = 0; - _annotationLength = 12; - - _state = nullptr; -} - -// ============================================================================ -// [asmjit::Context - Mem] -// ============================================================================ - -static ASMJIT_INLINE uint32_t BaseContext_getDefaultAlignment(uint32_t size) { - if (size > 32) - return 64; - else if (size > 16) - return 32; - else if (size > 8) - return 16; - else if (size > 4) - return 8; - else if (size > 2) - return 4; - else if (size > 1) - return 2; - else - return 1; -} - -VarCell* Context::_newVarCell(VarData* vd) { - ASMJIT_ASSERT(vd->_memCell == nullptr); - - VarCell* cell; - uint32_t size = vd->getSize(); - - if (vd->isStack()) { - cell = _newStackCell(size, vd->getAlignment()); - - if (cell == nullptr) - return nullptr; - } - else { - cell = static_cast(_zoneAllocator.alloc(sizeof(VarCell))); - if (cell == nullptr) - goto _NoMemory; - - cell->_next = _memVarCells; - _memVarCells = cell; - - cell->_offset = 0; - cell->_size = size; - cell->_alignment = size; - - _memMaxAlign = Utils::iMax(_memMaxAlign, size); - _memVarTotal += size; - - switch (size) { - case 1: _mem1ByteVarsUsed++ ; break; - case 2: _mem2ByteVarsUsed++ ; break; - case 4: _mem4ByteVarsUsed++ ; break; - case 8: _mem8ByteVarsUsed++ ; break; - case 16: _mem16ByteVarsUsed++; break; - case 32: _mem32ByteVarsUsed++; break; - case 64: _mem64ByteVarsUsed++; break; - - default: - ASMJIT_NOT_REACHED(); - } - } - - vd->_memCell = cell; - return cell; - -_NoMemory: - _compiler->setLastError(kErrorNoHeapMemory); - return nullptr; -} - -VarCell* Context::_newStackCell(uint32_t size, uint32_t alignment) { - VarCell* cell = static_cast(_zoneAllocator.alloc(sizeof(VarCell))); - if (cell == nullptr) - goto _NoMemory; - - if (alignment == 0) - alignment = BaseContext_getDefaultAlignment(size); - - if (alignment > 64) - alignment = 64; - - ASMJIT_ASSERT(Utils::isPowerOf2(alignment)); - size = Utils::alignTo(size, alignment); - - // Insert it sorted according to the alignment and size. - { - VarCell** pPrev = &_memStackCells; - VarCell* cur = *pPrev; - - while (cur != nullptr) { - if ((cur->getAlignment() > alignment) || - (cur->getAlignment() == alignment && cur->getSize() > size)) { - pPrev = &cur->_next; - cur = *pPrev; - continue; - } - - break; - } - - cell->_next = cur; - cell->_offset = 0; - cell->_size = size; - cell->_alignment = alignment; - - *pPrev = cell; - _memStackCellsUsed++; - - _memMaxAlign = Utils::iMax(_memMaxAlign, alignment); - _memStackTotal += size; - } - - return cell; - -_NoMemory: - _compiler->setLastError(kErrorNoHeapMemory); - return nullptr; -} - -Error Context::resolveCellOffsets() { - VarCell* varCell = _memVarCells; - VarCell* stackCell = _memStackCells; - - uint32_t stackAlignment = 0; - if (stackCell != nullptr) - stackAlignment = stackCell->getAlignment(); - - uint32_t pos64 = 0; - uint32_t pos32 = pos64 + _mem64ByteVarsUsed * 64; - uint32_t pos16 = pos32 + _mem32ByteVarsUsed * 32; - uint32_t pos8 = pos16 + _mem16ByteVarsUsed * 16; - uint32_t pos4 = pos8 + _mem8ByteVarsUsed * 8 ; - uint32_t pos2 = pos4 + _mem4ByteVarsUsed * 4 ; - uint32_t pos1 = pos2 + _mem2ByteVarsUsed * 2 ; - - uint32_t stackPos = pos1 + _mem1ByteVarsUsed; - - uint32_t gapAlignment = stackAlignment; - uint32_t gapSize = 0; - - // TODO: Not used! - if (gapAlignment) - Utils::alignDiff(stackPos, gapAlignment); - stackPos += gapSize; - - uint32_t gapPos = stackPos; - uint32_t allTotal = stackPos; - - // Vars - Allocated according to alignment/width. - while (varCell != nullptr) { - uint32_t size = varCell->getSize(); - uint32_t offset = 0; - - switch (size) { - case 1: offset = pos1 ; pos1 += 1 ; break; - case 2: offset = pos2 ; pos2 += 2 ; break; - case 4: offset = pos4 ; pos4 += 4 ; break; - case 8: offset = pos8 ; pos8 += 8 ; break; - case 16: offset = pos16; pos16 += 16; break; - case 32: offset = pos32; pos32 += 32; break; - case 64: offset = pos64; pos64 += 64; break; - - default: - ASMJIT_NOT_REACHED(); - } - - varCell->setOffset(static_cast(offset)); - varCell = varCell->_next; - } - - // Stack - Allocated according to alignment/width. - while (stackCell != nullptr) { - uint32_t size = stackCell->getSize(); - uint32_t alignment = stackCell->getAlignment(); - uint32_t offset; - - // Try to fill the gap between variables/stack first. - if (size <= gapSize && alignment <= gapAlignment) { - offset = gapPos; - - gapSize -= size; - gapPos -= size; - - if (alignment < gapAlignment) - gapAlignment = alignment; - } - else { - offset = stackPos; - - stackPos += size; - allTotal += size; - } - - stackCell->setOffset(offset); - stackCell = stackCell->_next; - } - - _memAllTotal = allTotal; - return kErrorOk; -} - -// ============================================================================ -// [asmjit::Context - RemoveUnreachableCode] -// ============================================================================ - -Error Context::removeUnreachableCode() { - Compiler* compiler = getCompiler(); - - PodList::Link* link = _unreachableList.getFirst(); - HLNode* stop = getStop(); - - while (link != nullptr) { - HLNode* node = link->getValue(); - if (node != nullptr && node->getPrev() != nullptr && node != stop) { - // Locate all unreachable nodes. - HLNode* first = node; - do { - if (node->isFetched()) - break; - node = node->getNext(); - } while (node != stop); - - // Remove unreachable nodes that are neither informative nor directives. - if (node != first) { - HLNode* end = node; - node = first; - - // NOTE: The strategy is as follows: - // 1. The algorithm removes everything until it finds a first label. - // 2. After the first label is found it removes only removable nodes. - bool removeEverything = true; - do { - HLNode* next = node->getNext(); - bool remove = node->isRemovable(); - - if (!remove) { - if (node->isLabel()) - removeEverything = false; - remove = removeEverything; - } - - if (remove) { - ASMJIT_TSEC({ - this->_traceNode(this, node, "[REMOVED UNREACHABLE] "); - }); - compiler->removeNode(node); - } - - node = next; - } while (node != end); - } - } - - link = link->getNext(); - } - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::Context - Liveness Analysis] -// ============================================================================ - -//! \internal -struct LivenessTarget { - //! Previous target. - LivenessTarget* prev; - - //! Target node. - HLLabel* node; - //! Jumped from. - HLJump* from; -}; - -Error Context::livenessAnalysis() { - uint32_t bLen = static_cast( - ((_contextVd.getLength() + BitArray::kEntityBits - 1) / BitArray::kEntityBits)); - - // No variables. - if (bLen == 0) - return kErrorOk; - - HLFunc* func = getFunc(); - HLJump* from = nullptr; - - LivenessTarget* ltCur = nullptr; - LivenessTarget* ltUnused = nullptr; - - PodList::Link* retPtr = _returningList.getFirst(); - ASMJIT_ASSERT(retPtr != nullptr); - - HLNode* node = retPtr->getValue(); - - size_t varMapToVaListOffset = _varMapToVaListOffset; - BitArray* bCur = newBits(bLen); - - if (bCur == nullptr) - goto _NoMemory; - - // Allocate bits for code visited first time. -_OnVisit: - for (;;) { - if (node->hasLiveness()) { - if (bCur->_addBitsDelSource(node->getLiveness(), bCur, bLen)) - goto _OnPatch; - else - goto _OnDone; - } - - BitArray* bTmp = copyBits(bCur, bLen); - if (bTmp == nullptr) - goto _NoMemory; - - node->setLiveness(bTmp); - VarMap* map = node->getMap(); - - if (map != nullptr) { - uint32_t vaCount = map->getVaCount(); - VarAttr* vaList = reinterpret_cast(((uint8_t*)map) + varMapToVaListOffset); - - for (uint32_t i = 0; i < vaCount; i++) { - VarAttr* va = &vaList[i]; - VarData* vd = va->getVd(); - - uint32_t flags = va->getFlags(); - uint32_t localId = vd->getLocalId(); - - if ((flags & kVarAttrWAll) && !(flags & kVarAttrRAll)) { - // Write-Only. - bTmp->setBit(localId); - bCur->delBit(localId); - } - else { - // Read-Only or Read/Write. - bTmp->setBit(localId); - bCur->setBit(localId); - } - } - } - - if (node->getType() == HLNode::kTypeLabel) - goto _OnTarget; - - if (node == func) - goto _OnDone; - - ASMJIT_ASSERT(node->getPrev()); - node = node->getPrev(); - } - - // Patch already generated liveness bits. -_OnPatch: - for (;;) { - ASMJIT_ASSERT(node->hasLiveness()); - BitArray* bNode = node->getLiveness(); - - if (!bNode->_addBitsDelSource(bCur, bLen)) - goto _OnDone; - - if (node->getType() == HLNode::kTypeLabel) - goto _OnTarget; - - if (node == func) - goto _OnDone; - - node = node->getPrev(); - } - -_OnTarget: - if (static_cast(node)->getNumRefs() != 0) { - // Push a new LivenessTarget onto the stack if needed. - if (ltCur == nullptr || ltCur->node != node) { - // Allocate a new LivenessTarget object (from pool or zone). - LivenessTarget* ltTmp = ltUnused; - - if (ltTmp != nullptr) { - ltUnused = ltUnused->prev; - } - else { - ltTmp = _zoneAllocator.allocT( - sizeof(LivenessTarget) - sizeof(BitArray) + bLen * sizeof(uintptr_t)); - - if (ltTmp == nullptr) - goto _NoMemory; - } - - // Initialize and make current - ltTmp->from will be set later on. - ltTmp->prev = ltCur; - ltTmp->node = static_cast(node); - ltCur = ltTmp; - - from = static_cast(node)->getFrom(); - ASMJIT_ASSERT(from != nullptr); - } - else { - from = ltCur->from; - goto _OnJumpNext; - } - - // Visit/Patch. - do { - ltCur->from = from; - bCur->copyBits(node->getLiveness(), bLen); - - if (!from->hasLiveness()) { - node = from; - goto _OnVisit; - } - - // Issue #25: Moved '_OnJumpNext' here since it's important to patch - // code again if there are more live variables than before. -_OnJumpNext: - if (bCur->delBits(from->getLiveness(), bLen)) { - node = from; - goto _OnPatch; - } - - from = from->getJumpNext(); - } while (from != nullptr); - - // Pop the current LivenessTarget from the stack. - { - LivenessTarget* ltTmp = ltCur; - - ltCur = ltCur->prev; - ltTmp->prev = ltUnused; - ltUnused = ltTmp; - } - } - - bCur->copyBits(node->getLiveness(), bLen); - node = node->getPrev(); - - if (node->isJmp() || !node->isFetched()) - goto _OnDone; - - if (!node->hasLiveness()) - goto _OnVisit; - - if (bCur->delBits(node->getLiveness(), bLen)) - goto _OnPatch; - -_OnDone: - if (ltCur != nullptr) { - node = ltCur->node; - from = ltCur->from; - - goto _OnJumpNext; - } - - retPtr = retPtr->getNext(); - if (retPtr != nullptr) { - node = retPtr->getValue(); - goto _OnVisit; - } - - return kErrorOk; - -_NoMemory: - return setLastError(kErrorNoHeapMemory); -} - -// ============================================================================ -// [asmjit::Context - Annotate] -// ============================================================================ - -Error Context::formatInlineComment(StringBuilder& dst, HLNode* node) { -#if !defined(ASMJIT_DISABLE_LOGGER) - if (node->getComment()) - dst.appendString(node->getComment()); - - if (node->hasLiveness()) { - if (dst.getLength() < _annotationLength) - dst.appendChars(' ', _annotationLength - dst.getLength()); - - uint32_t vdCount = static_cast(_contextVd.getLength()); - size_t offset = dst.getLength() + 1; - - dst.appendChar('['); - dst.appendChars(' ', vdCount); - dst.appendChar(']'); - - BitArray* liveness = node->getLiveness(); - VarMap* map = node->getMap(); - - uint32_t i; - for (i = 0; i < vdCount; i++) { - if (liveness->getBit(i)) - dst.getData()[offset + i] = '.'; - } - - if (map != nullptr) { - uint32_t vaCount = map->getVaCount(); - VarAttr* vaList = reinterpret_cast(((uint8_t*)map) + _varMapToVaListOffset); - - for (i = 0; i < vaCount; i++) { - VarAttr* va = &vaList[i]; - VarData* vd = va->getVd(); - - uint32_t flags = va->getFlags(); - char c = 'u'; - - if ( (flags & kVarAttrRAll) && !(flags & kVarAttrWAll)) c = 'r'; - if (!(flags & kVarAttrRAll) && (flags & kVarAttrWAll)) c = 'w'; - if ( (flags & kVarAttrRAll) && (flags & kVarAttrWAll)) c = 'x'; - - // Uppercase if unused. - if ((flags & kVarAttrUnuse)) - c -= 'a' - 'A'; - - ASMJIT_ASSERT(offset + vd->getLocalId() < dst.getLength()); - dst._data[offset + vd->getLocalId()] = c; - } - } - } -#endif // !ASMJIT_DISABLE_LOGGER - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::Context - Cleanup] -// ============================================================================ - -void Context::cleanup() { - VarData** array = _contextVd.getData(); - size_t length = _contextVd.getLength(); - - for (size_t i = 0; i < length; i++) { - VarData* vd = array[i]; - vd->resetLocalId(); - vd->resetRegIndex(); - } - - _contextVd.reset(false); - _extraBlock = nullptr; -} - -// ============================================================================ -// [asmjit::Context - CompileFunc] -// ============================================================================ - -Error Context::compile(HLFunc* func) { - HLNode* end = func->getEnd(); - HLNode* stop = end->getNext(); - - _func = func; - _stop = stop; - _extraBlock = end; - - ASMJIT_PROPAGATE_ERROR(fetch()); - ASMJIT_PROPAGATE_ERROR(removeUnreachableCode()); - ASMJIT_PROPAGATE_ERROR(livenessAnalysis()); - - Compiler* compiler = getCompiler(); - -#if !defined(ASMJIT_DISABLE_LOGGER) - if (compiler->getAssembler()->hasLogger()) - ASMJIT_PROPAGATE_ERROR(annotate()); -#endif // !ASMJIT_DISABLE_LOGGER - - ASMJIT_PROPAGATE_ERROR(translate()); - - // We alter the compiler cursor, because it doesn't make sense to reference - // it after compilation - some nodes may disappear and it's forbidden to add - // new code after the compilation is done. - compiler->_setCursor(nullptr); - - return kErrorOk; -} - -} // asmjit namespace - -// [Api-End] -#include "../apiend.h" - -// [Guard] -#endif // !ASMJIT_DISABLE_COMPILER diff --git a/src/asmjit/base/compilercontext_p.h b/src/asmjit/base/compilercontext_p.h deleted file mode 100644 index ac06e97..0000000 --- a/src/asmjit/base/compilercontext_p.h +++ /dev/null @@ -1,901 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Guard] -#ifndef _ASMJIT_BASE_COMPILERCONTEXT_P_H -#define _ASMJIT_BASE_COMPILERCONTEXT_P_H - -#include "../build.h" -#if !defined(ASMJIT_DISABLE_COMPILER) - -// [Dependencies] -#include "../base/compiler.h" -#include "../base/podvector.h" -#include "../base/zone.h" - -// [Api-Begin] -#include "../apibegin.h" - -namespace asmjit { - -//! \addtogroup asmjit_base -//! \{ - -// ============================================================================ -// [asmjit::VarAttrFlags] -// ============================================================================ - -//! \internal -//! -//! Variable attribute flags. -ASMJIT_ENUM(VarAttrFlags) { - //! Read from register. - kVarAttrRReg = 0x00000001, - //! Write to register. - kVarAttrWReg = 0x00000002, - //! Read/Write from/to register. - kVarAttrXReg = 0x00000003, - - //! Read from memory. - kVarAttrRMem = 0x00000004, - //! Write to memory. - kVarAttrWMem = 0x00000008, - //! Read/Write from/to memory. - kVarAttrXMem = 0x0000000C, - - //! Register allocator can decide if input will be in register or memory. - kVarAttrRDecide = 0x00000010, - //! Register allocator can decide if output will be in register or memory. - kVarAttrWDecide = 0x00000020, - //! Register allocator can decide if in/out will be in register or memory. - kVarAttrXDecide = 0x00000030, - - //! Variable is converted to other type/class on the input. - kVarAttrRConv = 0x00000040, - //! Variable is converted from other type/class on the output. - kVarAttrWConv = 0x00000080, - //! Combination of `kVarAttrRConv` and `kVarAttrWConv`. - kVarAttrXConv = 0x000000C0, - - //! Variable is a function call operand. - kVarAttrRCall = 0x00000100, - //! Variable is a function argument passed in register. - kVarAttrRFunc = 0x00000200, - //! Variable is a function return value passed in register. - kVarAttrWFunc = 0x00000400, - - //! Variable should be spilled. - kVarAttrSpill = 0x00000800, - //! Variable should be unused at the end of the instruction/node. - kVarAttrUnuse = 0x00001000, - - //! All in-flags. - kVarAttrRAll = kVarAttrRReg | kVarAttrRMem | kVarAttrRDecide | kVarAttrRCall | kVarAttrRFunc, - //! All out-flags. - kVarAttrWAll = kVarAttrWReg | kVarAttrWMem | kVarAttrWDecide | kVarAttrWFunc, - - //! Variable is already allocated on the input. - kVarAttrAllocRDone = 0x00400000, - //! Variable is already allocated on the output. - kVarAttrAllocWDone = 0x00800000, - - kVarAttrX86GpbLo = 0x10000000, - kVarAttrX86GpbHi = 0x20000000, - kVarAttrX86Fld4 = 0x40000000, - kVarAttrX86Fld8 = 0x80000000 -}; - -// ============================================================================ -// [asmjit::VarHint] -// ============================================================================ - -//! \internal -//! -//! Variable hint (used by `Compiler)`. -//! -//! \sa Compiler. -ASMJIT_ENUM(VarHint) { - //! Alloc variable. - kVarHintAlloc = 0, - //! Spill variable. - kVarHintSpill = 1, - //! Save variable if modified. - kVarHintSave = 2, - //! Save variable if modified and mark it as unused. - kVarHintSaveAndUnuse = 3, - //! Mark variable as unused. - kVarHintUnuse = 4 -}; - -// ============================================================================ -// [asmjit::kVarState] -// ============================================================================ - -// TODO: Rename `kVarState` or `VarState`. - -//! \internal -//! -//! State of variable. -//! -//! NOTE: Variable states are used only during register allocation. -ASMJIT_ENUM(kVarState) { - //! Variable is currently not used. - kVarStateNone = 0, - //! Variable is currently allocated in register. - kVarStateReg = 1, - //! Variable is currently allocated in memory (or has been spilled). - kVarStateMem = 2 -}; - -// ============================================================================ -// [asmjit::VarCell] -// ============================================================================ - -struct VarCell { - ASMJIT_NO_COPY(VarCell) - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get cell offset. - ASMJIT_INLINE int32_t getOffset() const { return _offset; } - //! Set cell offset. - ASMJIT_INLINE void setOffset(int32_t offset) { _offset = offset; } - - //! Get cell size. - ASMJIT_INLINE uint32_t getSize() const { return _size; } - //! Set cell size. - ASMJIT_INLINE void setSize(uint32_t size) { _size = size; } - - //! Get cell alignment. - ASMJIT_INLINE uint32_t getAlignment() const { return _alignment; } - //! Set cell alignment. - ASMJIT_INLINE void setAlignment(uint32_t alignment) { _alignment = alignment; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Next active cell. - VarCell* _next; - - //! Offset, relative to base-offset. - int32_t _offset; - //! Size. - uint32_t _size; - //! Alignment. - uint32_t _alignment; -}; - -// ============================================================================ -// [asmjit::VarData] -// ============================================================================ - -//! HL variable data (base). -struct VarData { - // -------------------------------------------------------------------------- - // [Accessors - Base] - // -------------------------------------------------------------------------- - - //! Get variable name. - ASMJIT_INLINE const char* getName() const { return _name; } - //! Get variable id. - ASMJIT_INLINE uint32_t getId() const { return _id; } - //! Get variable type. - ASMJIT_INLINE uint32_t getType() const { return _type; } - //! Get variable class. - ASMJIT_INLINE uint32_t getClass() const { return _class; } - - // -------------------------------------------------------------------------- - // [Accessors - LocalId] - // -------------------------------------------------------------------------- - - //! Get whether the variable has a local id. - ASMJIT_INLINE bool hasLocalId() const { return _localId != kInvalidValue; } - //! Get a variable's local id. - ASMJIT_INLINE uint32_t getLocalId() const { return _localId; } - //! Set a variable's local id. - ASMJIT_INLINE void setLocalId(uint32_t localId) { _localId = localId; } - //! Reset a variable's local id. - ASMJIT_INLINE void resetLocalId() { _localId = kInvalidValue; } - - // -------------------------------------------------------------------------- - // [Accessors - Priority] - // -------------------------------------------------------------------------- - - //! Get variable priority, used by compiler to decide which variable to spill. - ASMJIT_INLINE uint32_t getPriority() const { return _priority; } - //! Set variable priority. - ASMJIT_INLINE void setPriority(uint32_t priority) { - ASMJIT_ASSERT(priority <= 0xFF); - _priority = static_cast(priority); - } - - // -------------------------------------------------------------------------- - // [Accessors - State] - // -------------------------------------------------------------------------- - - //! Get variable state, only used by `Context`. - ASMJIT_INLINE uint32_t getState() const { return _state; } - //! Set variable state, only used by `Context`. - ASMJIT_INLINE void setState(uint32_t state) { - ASMJIT_ASSERT(state <= 0xFF); - _state = static_cast(state); - } - - // -------------------------------------------------------------------------- - // [Accessors - RegIndex] - // -------------------------------------------------------------------------- - - //! Get register index. - ASMJIT_INLINE uint32_t getRegIndex() const { return _regIndex; } - //! Set register index. - ASMJIT_INLINE void setRegIndex(uint32_t regIndex) { - ASMJIT_ASSERT(regIndex <= kInvalidReg); - _regIndex = static_cast(regIndex); - } - //! Reset register index. - ASMJIT_INLINE void resetRegIndex() { - _regIndex = static_cast(kInvalidReg); - } - - // -------------------------------------------------------------------------- - // [Accessors - HomeIndex/Mask] - // -------------------------------------------------------------------------- - - //! Get home registers mask. - ASMJIT_INLINE uint32_t getHomeMask() const { return _homeMask; } - //! Add a home register index to the home registers mask. - ASMJIT_INLINE void addHomeIndex(uint32_t regIndex) { _homeMask |= Utils::mask(regIndex); } - - // -------------------------------------------------------------------------- - // [Accessors - Flags] - // -------------------------------------------------------------------------- - - //! Get variable flags. - ASMJIT_INLINE uint32_t getFlags() const { return _flags; } - - //! Get whether the VarData is only memory allocated on the stack. - ASMJIT_INLINE bool isStack() const { return static_cast(_isStack); } - //! Get whether the variable is a function argument passed through memory. - ASMJIT_INLINE bool isMemArg() const { return static_cast(_isMemArg); } - - //! Get variable content can be calculated by a simple instruction. - ASMJIT_INLINE bool isCalculated() const { return static_cast(_isCalculated); } - //! Get whether to save variable when it's unused (spill). - ASMJIT_INLINE bool saveOnUnuse() const { return static_cast(_saveOnUnuse); } - - //! Get whether the variable was changed. - ASMJIT_INLINE bool isModified() const { return static_cast(_modified); } - //! Set whether the variable was changed. - ASMJIT_INLINE void setModified(bool modified) { _modified = modified; } - - //! Get variable alignment. - ASMJIT_INLINE uint32_t getAlignment() const { return _alignment; } - //! Get variable size. - ASMJIT_INLINE uint32_t getSize() const { return _size; } - - //! Get home memory offset. - ASMJIT_INLINE int32_t getMemOffset() const { return _memOffset; } - //! Set home memory offset. - ASMJIT_INLINE void setMemOffset(int32_t offset) { _memOffset = offset; } - - //! Get home memory cell. - ASMJIT_INLINE VarCell* getMemCell() const { return _memCell; } - //! Set home memory cell. - ASMJIT_INLINE void setMemCell(VarCell* cell) { _memCell = cell; } - - // -------------------------------------------------------------------------- - // [Accessors - Temporary Usage] - // -------------------------------------------------------------------------- - - //! Get temporary VarAttr. - ASMJIT_INLINE VarAttr* getVa() const { return _va; } - //! Set temporary VarAttr. - ASMJIT_INLINE void setVa(VarAttr* va) { _va = va; } - //! Reset temporary VarAttr. - ASMJIT_INLINE void resetVa() { _va = nullptr; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Variable name. - const char* _name; - - //! Variable id. - uint32_t _id; - //! Variable's local id (initially `kInvalidValue`). - uint32_t _localId; - - //! Variable type. - uint8_t _type; - //! Variable class. - uint8_t _class; - //! Variable flags. - uint8_t _flags; - //! Variable priority. - uint8_t _priority; - - //! Variable state (connected with actual `VarState)`. - uint8_t _state; - //! Actual register index (only used by `Context)`, during translate. - uint8_t _regIndex; - - //! Whether the variable is only used as memory allocated on the stack. - uint8_t _isStack : 1; - //! Whether the variable is a function argument passed through memory. - uint8_t _isMemArg : 1; - //! Whether variable content can be calculated by a simple instruction. - //! - //! This is used mainly by MMX and SSE2 code. This flag indicates that - //! register allocator should never reserve memory for this variable, because - //! the content can be generated by a single instruction (for example PXOR). - uint8_t _isCalculated : 1; - //! Save on unuse (at end of the variable scope). - uint8_t _saveOnUnuse : 1; - //! Whether variable was changed (connected with actual `VarState)`. - uint8_t _modified : 1; - //! \internal - uint8_t _reserved0 : 3; - //! Variable natural alignment. - uint8_t _alignment; - - //! Variable size. - uint32_t _size; - - //! Mask of all registers variable has been allocated to. - uint32_t _homeMask; - - //! Home memory offset. - int32_t _memOffset; - //! Home memory cell, used by `Context` (initially nullptr). - VarCell* _memCell; - - //! Register read access statistics. - uint32_t rReadCount; - //! Register write access statistics. - uint32_t rWriteCount; - - //! Memory read statistics. - uint32_t mReadCount; - //! Memory write statistics. - uint32_t mWriteCount; - - // -------------------------------------------------------------------------- - // [Members - Temporary Usage] - // -------------------------------------------------------------------------- - - // These variables are only used during register allocation. They are - // initialized by init() phase and reset by cleanup() phase. - - union { - //! Temporary link to VarAttr* used by the `Context` used in - //! various phases, but always set back to nullptr when finished. - //! - //! This temporary data is designed to be used by algorithms that need to - //! store some data into variables themselves during compilation. But it's - //! expected that after variable is compiled & translated the data is set - //! back to zero/null. Initial value is nullptr. - VarAttr* _va; - - //! \internal - //! - //! Same as `_va` just provided as `uintptr_t`. - uintptr_t _vaUInt; - }; -}; - -// ============================================================================ -// [asmjit::VarAttr] -// ============================================================================ - -struct VarAttr { - // -------------------------------------------------------------------------- - // [Setup] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE void setup(VarData* vd, uint32_t flags = 0, uint32_t inRegs = 0, uint32_t allocableRegs = 0) { - _vd = vd; - _flags = flags; - _varCount = 0; - _inRegIndex = kInvalidReg; - _outRegIndex = kInvalidReg; - _reserved = 0; - _inRegs = inRegs; - _allocableRegs = allocableRegs; - } - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get VarData. - ASMJIT_INLINE VarData* getVd() const { return _vd; } - //! Set VarData. - ASMJIT_INLINE void setVd(VarData* vd) { _vd = vd; } - - //! Get flags. - ASMJIT_INLINE uint32_t getFlags() const { return _flags; } - //! Set flags. - ASMJIT_INLINE void setFlags(uint32_t flags) { _flags = flags; } - - //! Get whether `flag` is on. - ASMJIT_INLINE bool hasFlag(uint32_t flag) { return (_flags & flag) != 0; } - //! Add `flags`. - ASMJIT_INLINE void orFlags(uint32_t flags) { _flags |= flags; } - //! Mask `flags`. - ASMJIT_INLINE void andFlags(uint32_t flags) { _flags &= flags; } - //! Clear `flags`. - ASMJIT_INLINE void andNotFlags(uint32_t flags) { _flags &= ~flags; } - - //! Get how many times the variable is used by the instruction/node. - ASMJIT_INLINE uint32_t getVarCount() const { return _varCount; } - //! Set how many times the variable is used by the instruction/node. - ASMJIT_INLINE void setVarCount(uint32_t count) { _varCount = static_cast(count); } - //! Add how many times the variable is used by the instruction/node. - ASMJIT_INLINE void addVarCount(uint32_t count = 1) { _varCount += static_cast(count); } - - //! Get whether the variable has to be allocated in a specific input register. - ASMJIT_INLINE uint32_t hasInRegIndex() const { return _inRegIndex != kInvalidReg; } - //! Get the input register index or `kInvalidReg`. - ASMJIT_INLINE uint32_t getInRegIndex() const { return _inRegIndex; } - //! Set the input register index. - ASMJIT_INLINE void setInRegIndex(uint32_t index) { _inRegIndex = static_cast(index); } - //! Reset the input register index. - ASMJIT_INLINE void resetInRegIndex() { _inRegIndex = kInvalidReg; } - - //! Get whether the variable has to be allocated in a specific output register. - ASMJIT_INLINE uint32_t hasOutRegIndex() const { return _outRegIndex != kInvalidReg; } - //! Get the output register index or `kInvalidReg`. - ASMJIT_INLINE uint32_t getOutRegIndex() const { return _outRegIndex; } - //! Set the output register index. - ASMJIT_INLINE void setOutRegIndex(uint32_t index) { _outRegIndex = static_cast(index); } - //! Reset the output register index. - ASMJIT_INLINE void resetOutRegIndex() { _outRegIndex = kInvalidReg; } - - //! Get whether the mandatory input registers are in used. - ASMJIT_INLINE bool hasInRegs() const { return _inRegs != 0; } - //! Get mandatory input registers (mask). - ASMJIT_INLINE uint32_t getInRegs() const { return _inRegs; } - //! Set mandatory input registers (mask). - ASMJIT_INLINE void setInRegs(uint32_t mask) { _inRegs = mask; } - //! Add mandatory input registers (mask). - ASMJIT_INLINE void addInRegs(uint32_t mask) { _inRegs |= mask; } - //! And mandatory input registers (mask). - ASMJIT_INLINE void andInRegs(uint32_t mask) { _inRegs &= mask; } - //! Clear mandatory input registers (mask). - ASMJIT_INLINE void delInRegs(uint32_t mask) { _inRegs &= ~mask; } - - //! Get allocable input registers (mask). - ASMJIT_INLINE uint32_t getAllocableRegs() const { return _allocableRegs; } - //! Set allocable input registers (mask). - ASMJIT_INLINE void setAllocableRegs(uint32_t mask) { _allocableRegs = mask; } - //! Add allocable input registers (mask). - ASMJIT_INLINE void addAllocableRegs(uint32_t mask) { _allocableRegs |= mask; } - //! And allocable input registers (mask). - ASMJIT_INLINE void andAllocableRegs(uint32_t mask) { _allocableRegs &= mask; } - //! Clear allocable input registers (mask). - ASMJIT_INLINE void delAllocableRegs(uint32_t mask) { _allocableRegs &= ~mask; } - - // -------------------------------------------------------------------------- - // [Operator Overload] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE VarAttr& operator=(const VarAttr& other) { - ::memcpy(this, &other, sizeof(VarAttr)); - return *this; - } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - VarData* _vd; - //! Flags. - uint32_t _flags; - - union { - struct { - //! How many times the variable is used by the instruction/node. - uint8_t _varCount; - //! Input register index or `kInvalidReg` if it's not given. - //! - //! Even if the input register index is not given (i.e. it may by any - //! register), register allocator should assign an index that will be - //! used to persist a variable into this specific index. It's helpful - //! in situations where one variable has to be allocated in multiple - //! registers to determine the register which will be persistent. - uint8_t _inRegIndex; - //! Output register index or `kInvalidReg` if it's not given. - //! - //! Typically `kInvalidReg` if variable is only used on input. - uint8_t _outRegIndex; - //! \internal - uint8_t _reserved; - }; - - //! \internal - //! - //! Packed data #0. - uint32_t _packed; - }; - - //! Mandatory input registers. - //! - //! Mandatory input registers are required by the instruction even if - //! there are duplicates. This schema allows us to allocate one variable - //! in one or more register when needed. Required mostly by instructions - //! that have implicit register operands (imul, cpuid, ...) and function - //! call. - uint32_t _inRegs; - - //! Allocable input registers. - //! - //! Optional input registers is a mask of all allocable registers for a given - //! variable where we have to pick one of them. This mask is usually not used - //! when _inRegs is set. If both masks are used then the register - //! allocator tries first to find an intersection between these and allocates - //! an extra slot if not found. - uint32_t _allocableRegs; -}; - -// ============================================================================ -// [asmjit::VarMap] -// ============================================================================ - -//! Variables' map related to a single node (instruction / other node). -struct VarMap { - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get count of variables (all). - ASMJIT_INLINE uint32_t getVaCount() const { - return _vaCount; - } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Variables count. - uint32_t _vaCount; -}; - -// ============================================================================ -// [asmjit::VarState] -// ============================================================================ - -//! Variables' state. -struct VarState {}; - -// ============================================================================ -// [asmjit::Context] -// ============================================================================ - -//! \internal -//! -//! Code generation context is the logic behind `Compiler`. The context is -//! used to compile the code stored in `Compiler`. -struct Context { - ASMJIT_NO_COPY(Context) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - Context(Compiler* compiler); - virtual ~Context(); - - // -------------------------------------------------------------------------- - // [Reset] - // -------------------------------------------------------------------------- - - //! Reset the whole context. - virtual void reset(bool releaseMemory = false); - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get compiler. - ASMJIT_INLINE Compiler* getCompiler() const { return _compiler; } - - //! Get function. - ASMJIT_INLINE HLFunc* getFunc() const { return _func; } - //! Get stop node. - ASMJIT_INLINE HLNode* getStop() const { return _stop; } - - //! Get start of the current scope. - ASMJIT_INLINE HLNode* getStart() const { return _start; } - //! Get end of the current scope. - ASMJIT_INLINE HLNode* getEnd() const { return _end; } - - //! Get extra block. - ASMJIT_INLINE HLNode* getExtraBlock() const { return _extraBlock; } - //! Set extra block. - ASMJIT_INLINE void setExtraBlock(HLNode* node) { _extraBlock = node; } - - // -------------------------------------------------------------------------- - // [Error] - // -------------------------------------------------------------------------- - - //! Get the last error code. - ASMJIT_INLINE Error getLastError() const { - return getCompiler()->getLastError(); - } - - //! Set the last error code and propagate it through the error handler. - ASMJIT_INLINE Error setLastError(Error error, const char* message = nullptr) { - return getCompiler()->setLastError(error, message); - } - - // -------------------------------------------------------------------------- - // [State] - // -------------------------------------------------------------------------- - - //! Get current state. - ASMJIT_INLINE VarState* getState() const { return _state; } - - //! Load current state from `target` state. - virtual void loadState(VarState* src) = 0; - - //! Save current state, returning new `VarState` instance. - virtual VarState* saveState() = 0; - - //! Change the current state to `target` state. - virtual void switchState(VarState* src) = 0; - - //! Change the current state to the intersection of two states `a` and `b`. - virtual void intersectStates(VarState* a, VarState* b) = 0; - - // -------------------------------------------------------------------------- - // [Context] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE Error _registerContextVar(VarData* vd) { - if (vd->hasLocalId()) - return kErrorOk; - - uint32_t cid = static_cast(_contextVd.getLength()); - ASMJIT_PROPAGATE_ERROR(_contextVd.append(vd)); - - vd->setLocalId(cid); - return kErrorOk; - } - - // -------------------------------------------------------------------------- - // [Mem] - // -------------------------------------------------------------------------- - - VarCell* _newVarCell(VarData* vd); - VarCell* _newStackCell(uint32_t size, uint32_t alignment); - - ASMJIT_INLINE VarCell* getVarCell(VarData* vd) { - VarCell* cell = vd->getMemCell(); - return cell ? cell : _newVarCell(vd); - } - - virtual Error resolveCellOffsets(); - - // -------------------------------------------------------------------------- - // [Bits] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE BitArray* newBits(uint32_t len) { - return static_cast( - _zoneAllocator.allocZeroed(static_cast(len) * BitArray::kEntitySize)); - } - - ASMJIT_INLINE BitArray* copyBits(const BitArray* src, uint32_t len) { - return static_cast( - _zoneAllocator.dup(src, static_cast(len) * BitArray::kEntitySize)); - } - - // -------------------------------------------------------------------------- - // [Fetch] - // -------------------------------------------------------------------------- - - //! Fetch. - //! - //! Fetch iterates over all nodes and gathers information about all variables - //! used. The process generates information required by register allocator, - //! variable liveness analysis and translator. - virtual Error fetch() = 0; - - // -------------------------------------------------------------------------- - // [Unreachable Code] - // -------------------------------------------------------------------------- - - //! Add unreachable-flow data to the unreachable flow list. - ASMJIT_INLINE Error addUnreachableNode(HLNode* node) { - PodList::Link* link = _zoneAllocator.allocT::Link>(); - if (link == nullptr) - return setLastError(kErrorNoHeapMemory); - - link->setValue(node); - _unreachableList.append(link); - - return kErrorOk; - } - - //! Remove unreachable code. - virtual Error removeUnreachableCode(); - - // -------------------------------------------------------------------------- - // [Code-Flow] - // -------------------------------------------------------------------------- - - //! Add returning node (i.e. node that returns and where liveness analysis - //! should start). - ASMJIT_INLINE Error addReturningNode(HLNode* node) { - PodList::Link* link = _zoneAllocator.allocT::Link>(); - if (link == nullptr) - return setLastError(kErrorNoHeapMemory); - - link->setValue(node); - _returningList.append(link); - - return kErrorOk; - } - - //! Add jump-flow data to the jcc flow list. - ASMJIT_INLINE Error addJccNode(HLNode* node) { - PodList::Link* link = _zoneAllocator.allocT::Link>(); - if (link == nullptr) - return setLastError(kErrorNoHeapMemory); - - link->setValue(node); - _jccList.append(link); - - return kErrorOk; - } - - // -------------------------------------------------------------------------- - // [Analyze] - // -------------------------------------------------------------------------- - - //! Perform variable liveness analysis. - //! - //! Analysis phase iterates over nodes in reverse order and generates a bit - //! array describing variables that are alive at every node in the function. - //! When the analysis start all variables are assumed dead. When a read or - //! read/write operations of a variable is detected the variable becomes - //! alive; when only write operation is detected the variable becomes dead. - //! - //! When a label is found all jumps to that label are followed and analysis - //! repeats until all variables are resolved. - virtual Error livenessAnalysis(); - - // -------------------------------------------------------------------------- - // [Annotate] - // -------------------------------------------------------------------------- - - virtual Error annotate() = 0; - virtual Error formatInlineComment(StringBuilder& dst, HLNode* node); - - // -------------------------------------------------------------------------- - // [Translate] - // -------------------------------------------------------------------------- - - //! Translate code by allocating registers and handling state changes. - virtual Error translate() = 0; - - // -------------------------------------------------------------------------- - // [Cleanup] - // -------------------------------------------------------------------------- - - virtual void cleanup(); - - // -------------------------------------------------------------------------- - // [Compile] - // -------------------------------------------------------------------------- - - virtual Error compile(HLFunc* func); - - // -------------------------------------------------------------------------- - // [Serialize] - // -------------------------------------------------------------------------- - - virtual Error serialize(Assembler* assembler, HLNode* start, HLNode* stop) = 0; - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Compiler. - Compiler* _compiler; - //! Function. - HLFunc* _func; - - //! Zone allocator. - Zone _zoneAllocator; - - //! \internal - typedef void (ASMJIT_CDECL* TraceNodeFunc)(Context* self, HLNode* node_, const char* prefix); - //! \internal - //! - //! Only non-NULL when ASMJIT_TRACE is enabled. - TraceNodeFunc _traceNode; - - //! \internal - //! - //! Offset (how many bytes to add) to `VarMap` to get `VarAttr` array. Used - //! by liveness analysis shared across all backends. This is needed because - //! `VarMap` is a base class for a specialized version that liveness analysis - //! doesn't use, it just needs `VarAttr` array. - uint32_t _varMapToVaListOffset; - - //! Start of the current active scope. - HLNode* _start; - //! End of the current active scope. - HLNode* _end; - - //! Node that is used to insert extra code after the function body. - HLNode* _extraBlock; - //! Stop node. - HLNode* _stop; - - //! Unreachable nodes. - PodList _unreachableList; - //! Returning nodes. - PodList _returningList; - //! Jump nodes. - PodList _jccList; - - //! All variables used by the current function. - PodVector _contextVd; - - //! Memory used to spill variables. - VarCell* _memVarCells; - //! Memory used to alloc memory on the stack. - VarCell* _memStackCells; - - //! Count of 1-byte cells. - uint32_t _mem1ByteVarsUsed; - //! Count of 2-byte cells. - uint32_t _mem2ByteVarsUsed; - //! Count of 4-byte cells. - uint32_t _mem4ByteVarsUsed; - //! Count of 8-byte cells. - uint32_t _mem8ByteVarsUsed; - //! Count of 16-byte cells. - uint32_t _mem16ByteVarsUsed; - //! Count of 32-byte cells. - uint32_t _mem32ByteVarsUsed; - //! Count of 64-byte cells. - uint32_t _mem64ByteVarsUsed; - //! Count of stack memory cells. - uint32_t _memStackCellsUsed; - - //! Maximum memory alignment used by the function. - uint32_t _memMaxAlign; - //! Count of bytes used by variables. - uint32_t _memVarTotal; - //! Count of bytes used by stack. - uint32_t _memStackTotal; - //! Count of bytes used by variables and stack after alignment. - uint32_t _memAllTotal; - - //! Default lenght of annotated instruction. - uint32_t _annotationLength; - - //! Current state (used by register allocator). - VarState* _state; -}; - -//! \} - -} // asmjit namespace - -// [Api-End] -#include "../apiend.h" - -// [Guard] -#endif // !ASMJIT_DISABLE_COMPILER -#endif // _ASMJIT_BASE_COMPILERCONTEXT_P_H diff --git a/src/asmjit/base/compilerfunc.h b/src/asmjit/base/compilerfunc.h deleted file mode 100644 index 86b24c6..0000000 --- a/src/asmjit/base/compilerfunc.h +++ /dev/null @@ -1,679 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Guard] -#ifndef _ASMJIT_BASE_COMPILERFUNC_H -#define _ASMJIT_BASE_COMPILERFUNC_H - -#include "../build.h" -#if !defined(ASMJIT_DISABLE_COMPILER) - -// [Dependencies] -#include "../base/operand.h" -#include "../base/utils.h" - -// [Api-Begin] -#include "../apibegin.h" - -namespace asmjit { - -//! \addtogroup asmjit_base -//! \{ - -// ============================================================================ -// [asmjit::FuncHint] -// ============================================================================ - -//! Function hints. -//! -//! For a platform specific calling conventions, see: -//! - `X86FuncHint` - X86/X64 function hints. -ASMJIT_ENUM(FuncHint) { - //! Generate a naked function by omitting its prolog and epilog (default true). - //! - //! Naked functions should always result in less code required for function's - //! prolog and epilog. In addition, on X86/64 naked functions save one register - //! (ebp or rbp), which can be used by the function instead. - kFuncHintNaked = 0, - - //! Generate a compact function prolog/epilog if possible (default true). - //! - //! X86/X64 Specific - //! ---------------- - //! - //! Use shorter, but possible slower prolog/epilog sequence to save/restore - //! registers. At the moment this only enables emitting `leave` in function's - //! epilog to make the code shorter, however, the counterpart `enter` is not - //! used in function's prolog for performance reasons. - kFuncHintCompact = 1, - - //! Emit `emms` instruction in the function's epilog. - kFuncHintX86Emms = 17, - //! Emit `sfence` instruction in the function's epilog. - kFuncHintX86SFence = 18, - //! Emit `lfence` instruction in the function's epilog. - kFuncHintX86LFence = 19 -}; - -// ============================================================================ -// [asmjit::FuncFlags] -// ============================================================================ - -//! Function flags. -ASMJIT_ENUM(FuncFlags) { - //! Whether the function is using naked (minimal) prolog / epilog. - kFuncFlagIsNaked = 0x00000001, - - //! Whether an another function is called from this function. - kFuncFlagIsCaller = 0x00000002, - - //! Whether the stack is not aligned to the required stack alignment, - //! thus it has to be aligned manually. - kFuncFlagIsStackMisaligned = 0x00000004, - - //! Whether the stack pointer is adjusted by the stack size needed - //! to save registers and function variables. - //! - //! X86/X64 Specific - //! ---------------- - //! - //! Stack pointer (ESP/RSP) is adjusted by 'sub' instruction in prolog and by - //! 'add' instruction in epilog (only if function is not naked). If function - //! needs to perform manual stack alignment more instructions are used to - //! adjust the stack (like "and zsp, -Alignment"). - kFuncFlagIsStackAdjusted = 0x00000008, - - //! Whether the function is finished using `Compiler::endFunc()`. - kFuncFlagIsFinished = 0x80000000, - - //! Whether to emit `leave` instead of two instructions in case that the - //! function saves and restores the frame pointer. - kFuncFlagX86Leave = 0x00010000, - - //! Whether it's required to move arguments to a new stack location, - //! because of manual aligning. - kFuncFlagX86MoveArgs = 0x00040000, - - //! Whether to emit `emms` instruction in epilog (auto-detected). - kFuncFlagX86Emms = 0x01000000, - - //! Whether to emit `sfence` instruction in epilog (auto-detected). - //! - //! `kFuncFlagX86SFence` with `kFuncFlagX86LFence` results in emitting `mfence`. - kFuncFlagX86SFence = 0x02000000, - - //! Whether to emit `lfence` instruction in epilog (auto-detected). - //! - //! `kFuncFlagX86SFence` with `kFuncFlagX86LFence` results in emitting `mfence`. - kFuncFlagX86LFence = 0x04000000 -}; - -// ============================================================================ -// [asmjit::FuncDir] -// ============================================================================ - -//! Function arguments direction. -ASMJIT_ENUM(FuncDir) { - //! Arguments are passed left to right. - //! - //! This arguments direction is unusual in C, however it's used in Pascal. - kFuncDirLTR = 0, - - //! Arguments are passed right ro left - //! - //! This is the default argument direction in C. - kFuncDirRTL = 1 -}; - -// ============================================================================ -// [asmjit::FuncMisc] -// ============================================================================ - -enum { - //! Function doesn't have variable number of arguments (`...`) (default). - kFuncNoVarArgs = 0xFF, - //! Invalid stack offset in function or function parameter. - kFuncStackInvalid = -1 -}; - -// ============================================================================ -// [asmjit::FuncArgIndex] -// ============================================================================ - -//! Function argument index (lo/hi). -ASMJIT_ENUM(FuncArgIndex) { - //! Maxumum number of function arguments supported by AsmJit. - kFuncArgCount = 16, - //! Extended maximum number of arguments (used internally). - kFuncArgCountLoHi = kFuncArgCount * 2, - - //! Index to the LO part of function argument (default). - //! - //! This value is typically omitted and added only if there is HI argument - //! accessed. - kFuncArgLo = 0, - - //! Index to the HI part of function argument. - //! - //! HI part of function argument depends on target architecture. On x86 it's - //! typically used to transfer 64-bit integers (they form a pair of 32-bit - //! integers). - kFuncArgHi = kFuncArgCount -}; - -// ============================================================================ -// [asmjit::FuncRet] -// ============================================================================ - -//! Function return value (lo/hi) specification. -ASMJIT_ENUM(FuncRet) { - //! Index to the LO part of function return value. - kFuncRetLo = 0, - //! Index to the HI part of function return value. - kFuncRetHi = 1 -}; - -// ============================================================================ -// [asmjit::TypeId] -// ============================================================================ - -//! Function builder's `void` type. -struct Void {}; - -//! Function builder's `int8_t` type. -struct Int8Type {}; -//! Function builder's `uint8_t` type. -struct UInt8Type {}; - -//! Function builder's `int16_t` type. -struct Int16Type {}; -//! Function builder's `uint16_t` type. -struct UInt16Type {}; - -//! Function builder's `int32_t` type. -struct Int32Type {}; -//! Function builder's `uint32_t` type. -struct UInt32Type {}; - -//! Function builder's `int64_t` type. -struct Int64Type {}; -//! Function builder's `uint64_t` type. -struct UInt64Type {}; - -//! Function builder's `intptr_t` type. -struct IntPtrType {}; -//! Function builder's `uintptr_t` type. -struct UIntPtrType {}; - -//! Function builder's `float` type. -struct FloatType {}; -//! Function builder's `double` type. -struct DoubleType {}; - -#if !defined(ASMJIT_DOCGEN) -template -struct TypeId { - // Let it fail here if `T` was not specialized. -}; - -template -struct TypeId { - enum { kId = kVarTypeIntPtr }; -}; - -template -struct TypeIdOfInt { - enum { kId = (sizeof(T) == 1) ? (int)(IntTraits::kIsSigned ? kVarTypeInt8 : kVarTypeUInt8 ) : - (sizeof(T) == 2) ? (int)(IntTraits::kIsSigned ? kVarTypeInt16 : kVarTypeUInt16) : - (sizeof(T) == 4) ? (int)(IntTraits::kIsSigned ? kVarTypeInt32 : kVarTypeUInt32) : - (sizeof(T) == 8) ? (int)(IntTraits::kIsSigned ? kVarTypeInt64 : kVarTypeUInt64) : (int)kInvalidVar - }; -}; - -#define ASMJIT_TYPE_ID(T, ID) \ - template<> struct TypeId { enum { kId = ID }; } - -ASMJIT_TYPE_ID(void , kInvalidVar); -ASMJIT_TYPE_ID(signed char , TypeIdOfInt::kId); -ASMJIT_TYPE_ID(unsigned char , TypeIdOfInt::kId); -ASMJIT_TYPE_ID(short , TypeIdOfInt::kId); -ASMJIT_TYPE_ID(unsigned short , TypeIdOfInt::kId); -ASMJIT_TYPE_ID(int , TypeIdOfInt::kId); -ASMJIT_TYPE_ID(unsigned int , TypeIdOfInt::kId); -ASMJIT_TYPE_ID(long , TypeIdOfInt::kId); -ASMJIT_TYPE_ID(unsigned long , TypeIdOfInt::kId); -ASMJIT_TYPE_ID(float , kVarTypeFp32); -ASMJIT_TYPE_ID(double , kVarTypeFp64); - -#if ASMJIT_CC_HAS_NATIVE_CHAR -ASMJIT_TYPE_ID(char , TypeIdOfInt::kId); -#endif -#if ASMJIT_CC_HAS_NATIVE_WCHAR_T -ASMJIT_TYPE_ID(wchar_t , TypeIdOfInt::kId); -#endif -#if ASMJIT_CC_HAS_NATIVE_CHAR16_T -ASMJIT_TYPE_ID(char16_t , TypeIdOfInt::kId); -#endif -#if ASMJIT_CC_HAS_NATIVE_CHAR32_T -ASMJIT_TYPE_ID(char32_t , TypeIdOfInt::kId); -#endif - -#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(16, 0, 0) -ASMJIT_TYPE_ID(__int64 , TypeIdOfInt<__int64>::kId); -ASMJIT_TYPE_ID(unsigned __int64 , TypeIdOfInt::kId); -#else -ASMJIT_TYPE_ID(long long , TypeIdOfInt::kId); -ASMJIT_TYPE_ID(unsigned long long, TypeIdOfInt::kId); -#endif - -ASMJIT_TYPE_ID(Void , kInvalidVar); -ASMJIT_TYPE_ID(Int8Type , kVarTypeInt8); -ASMJIT_TYPE_ID(UInt8Type , kVarTypeUInt8); -ASMJIT_TYPE_ID(Int16Type , kVarTypeInt16); -ASMJIT_TYPE_ID(UInt16Type , kVarTypeUInt16); -ASMJIT_TYPE_ID(Int32Type , kVarTypeInt32); -ASMJIT_TYPE_ID(UInt32Type , kVarTypeUInt32); -ASMJIT_TYPE_ID(Int64Type , kVarTypeInt64); -ASMJIT_TYPE_ID(UInt64Type , kVarTypeUInt64); -ASMJIT_TYPE_ID(IntPtrType , kVarTypeIntPtr); -ASMJIT_TYPE_ID(UIntPtrType , kVarTypeUIntPtr); -ASMJIT_TYPE_ID(FloatType , kVarTypeFp32); -ASMJIT_TYPE_ID(DoubleType , kVarTypeFp64); -#endif // !ASMJIT_DOCGEN - -// ============================================================================ -// [asmjit::FuncInOut] -// ============================================================================ - -//! Function in/out - argument or return value translated from `FuncPrototype`. -struct FuncInOut { - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE uint32_t getVarType() const noexcept { return _varType; } - - ASMJIT_INLINE bool hasRegIndex() const noexcept { return _regIndex != kInvalidReg; } - ASMJIT_INLINE uint32_t getRegIndex() const noexcept { return _regIndex; } - - ASMJIT_INLINE bool hasStackOffset() const noexcept { return _stackOffset != kFuncStackInvalid; } - ASMJIT_INLINE int32_t getStackOffset() const noexcept { return static_cast(_stackOffset); } - - //! Get whether the argument / return value is assigned. - ASMJIT_INLINE bool isSet() const noexcept { - return (_regIndex != kInvalidReg) | (_stackOffset != kFuncStackInvalid); - } - - // -------------------------------------------------------------------------- - // [Reset] - // -------------------------------------------------------------------------- - - //! Reset the function argument to "unassigned state". - ASMJIT_INLINE void reset() noexcept { _packed = 0xFFFFFFFFU; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - union { - struct { - //! Variable type, see \ref VarType. - uint8_t _varType; - //! Register index if argument / return value is a register. - uint8_t _regIndex; - //! Stack offset if argument / return value is on the stack. - int16_t _stackOffset; - }; - - //! All members packed into single 32-bit integer. - uint32_t _packed; - }; -}; - -// ============================================================================ -// [asmjit::FuncPrototype] -// ============================================================================ - -//! Function prototype. -//! -//! Function prototype contains information about function return type, count -//! of arguments and their types. Function prototype is a low level structure -//! which doesn't contain platform specific or calling convention specific -//! information. Function prototype is used to create a `FuncDecl`. -struct FuncPrototype { - // -------------------------------------------------------------------------- - // [Setup] - // -------------------------------------------------------------------------- - - //! Setup the prototype. - ASMJIT_INLINE void setup( - uint32_t callConv, - uint32_t ret, - const uint32_t* args, uint32_t numArgs) noexcept { - - ASMJIT_ASSERT(callConv <= 0xFF); - ASMJIT_ASSERT(numArgs <= 0xFF); - - _callConv = static_cast(callConv); - _varArgs = kFuncNoVarArgs; - _numArgs = static_cast(numArgs); - _reserved = 0; - - _ret = ret; - _args = args; - } - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get the function's calling convention. - ASMJIT_INLINE uint32_t getCallConv() const noexcept { return _callConv; } - //! Get the variable arguments `...` index, `kFuncNoVarArgs` if none. - ASMJIT_INLINE uint32_t getVarArgs() const noexcept { return _varArgs; } - //! Get the number of function arguments. - ASMJIT_INLINE uint32_t getNumArgs() const noexcept { return _numArgs; } - - //! Get the return value type. - ASMJIT_INLINE uint32_t getRet() const noexcept { return _ret; } - //! Get the type of the argument at index `i`. - ASMJIT_INLINE uint32_t getArg(uint32_t i) const noexcept { - ASMJIT_ASSERT(i < _numArgs); - return _args[i]; - } - //! Get the array of function arguments' types. - ASMJIT_INLINE const uint32_t* getArgs() const noexcept { return _args; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - uint8_t _callConv; - uint8_t _varArgs; - uint8_t _numArgs; - uint8_t _reserved; - - uint32_t _ret; - const uint32_t* _args; -}; - -// ============================================================================ -// [asmjit::FuncBuilderX] -// ============================================================================ - -// TODO: Rename to `DynamicFuncBuilder` -//! Custom function builder for up to 32 function arguments. -struct FuncBuilderX : public FuncPrototype { - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE FuncBuilderX(uint32_t callConv = kCallConvHost) noexcept { - setup(callConv, kInvalidVar, _builderArgList, 0); - } - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE void setCallConv(uint32_t callConv) noexcept { - ASMJIT_ASSERT(callConv <= 0xFF); - _callConv = static_cast(callConv); - } - - //! Set the return type to `retType`. - ASMJIT_INLINE void setRet(uint32_t retType) noexcept { - _ret = retType; - } - //! Set the return type based on `T`. - template - ASMJIT_INLINE void setRetT() noexcept { setRet(TypeId::kId); } - - //! Set the argument at index `i` to the `type` - ASMJIT_INLINE void setArg(uint32_t i, uint32_t type) noexcept { - ASMJIT_ASSERT(i < _numArgs); - _builderArgList[i] = type; - } - //! Set the argument at index `i` to the type based on `T`. - template - ASMJIT_INLINE void setArgT(uint32_t i) noexcept { setArg(i, TypeId::kId); } - - //! Append an argument of `type` to the function prototype. - ASMJIT_INLINE void addArg(uint32_t type) noexcept { - ASMJIT_ASSERT(_numArgs < kFuncArgCount); - _builderArgList[_numArgs++] = type; - } - //! Append an argument of type based on `T` to the function prototype. - template - ASMJIT_INLINE void addArgT() noexcept { addArg(TypeId::kId); } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - uint32_t _builderArgList[kFuncArgCount]; -}; - -//! \internal -#define T(_Type_) TypeId<_Type_>::kId - -//! Function prototype (no args). -template -struct FuncBuilder0 : public FuncPrototype { - ASMJIT_INLINE FuncBuilder0(uint32_t callConv = kCallConvHost) noexcept { - setup(callConv, T(RET), nullptr, 0); - } -}; - -//! Function prototype (1 argument). -template -struct FuncBuilder1 : public FuncPrototype { - ASMJIT_INLINE FuncBuilder1(uint32_t callConv = kCallConvHost) noexcept { - static const uint32_t args[] = { T(P0) }; - setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args)); - } -}; - -//! Function prototype (2 arguments). -template -struct FuncBuilder2 : public FuncPrototype { - ASMJIT_INLINE FuncBuilder2(uint32_t callConv = kCallConvHost) noexcept { - static const uint32_t args[] = { T(P0), T(P1) }; - setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args)); - } -}; - -//! Function prototype (3 arguments). -template -struct FuncBuilder3 : public FuncPrototype { - ASMJIT_INLINE FuncBuilder3(uint32_t callConv = kCallConvHost) noexcept { - static const uint32_t args[] = { T(P0), T(P1), T(P2) }; - setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args)); - } -}; - -//! Function prototype (4 arguments). -template -struct FuncBuilder4 : public FuncPrototype { - ASMJIT_INLINE FuncBuilder4(uint32_t callConv = kCallConvHost) noexcept { - static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3) }; - setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args)); - } -}; - -//! Function prototype (5 arguments). -template -struct FuncBuilder5 : public FuncPrototype { - ASMJIT_INLINE FuncBuilder5(uint32_t callConv = kCallConvHost) noexcept { - static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4) }; - setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args)); - } -}; - -//! Function prototype (6 arguments). -template -struct FuncBuilder6 : public FuncPrototype { - ASMJIT_INLINE FuncBuilder6(uint32_t callConv = kCallConvHost) noexcept { - static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5) }; - setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args)); - } -}; - -//! Function prototype (7 arguments). -template -struct FuncBuilder7 : public FuncPrototype { - ASMJIT_INLINE FuncBuilder7(uint32_t callConv = kCallConvHost) noexcept { - static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6) }; - setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args)); - } -}; - -//! Function prototype (8 arguments). -template -struct FuncBuilder8 : public FuncPrototype { - ASMJIT_INLINE FuncBuilder8(uint32_t callConv = kCallConvHost) noexcept { - static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6), T(P7) }; - setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args)); - } -}; - -//! Function prototype (9 arguments). -template -struct FuncBuilder9 : public FuncPrototype { - ASMJIT_INLINE FuncBuilder9(uint32_t callConv = kCallConvHost) noexcept { - static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6), T(P7), T(P8) }; - setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args)); - } -}; - -//! Function prototype (10 arguments). -template -struct FuncBuilder10 : public FuncPrototype { - ASMJIT_INLINE FuncBuilder10(uint32_t callConv = kCallConvHost) noexcept { - static const uint32_t args[] = { T(P0), T(P1), T(P2), T(P3), T(P4), T(P5), T(P6), T(P7), T(P8), T(P9) }; - setup(callConv, T(RET), args, ASMJIT_ARRAY_SIZE(args)); - } -}; -#undef T - -// ============================================================================ -// [asmjit::FuncDecl] -// ============================================================================ - -//! Function declaration. -struct FuncDecl { - // -------------------------------------------------------------------------- - // [Accessors - Calling Convention] - // -------------------------------------------------------------------------- - - //! Get the function's calling convention, see `CallConv`. - ASMJIT_INLINE uint32_t getCallConv() const noexcept { return _callConv; } - - //! Get whether the callee pops the stack. - ASMJIT_INLINE uint32_t getCalleePopsStack() const noexcept { return _calleePopsStack; } - - //! Get direction of arguments passed on the stack. - //! - //! Direction should be always `kFuncDirRTL`. - //! - //! NOTE: This is related to used calling convention, it's not affected by - //! number of function arguments or their types. - ASMJIT_INLINE uint32_t getArgsDirection() const noexcept { return _argsDirection; } - - //! Get stack size needed for function arguments passed on the stack. - ASMJIT_INLINE uint32_t getArgStackSize() const noexcept { return _argStackSize; } - //! Get size of "Red Zone". - ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _redZoneSize; } - //! Get size of "Spill Zone". - ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _spillZoneSize; } - - // -------------------------------------------------------------------------- - // [Accessors - Arguments and Return] - // -------------------------------------------------------------------------- - - //! Get whether the function has a return value. - ASMJIT_INLINE bool hasRet() const noexcept { return _retCount != 0; } - //! Get count of function return values. - ASMJIT_INLINE uint32_t getRetCount() const noexcept { return _retCount; } - - //! Get function return value. - ASMJIT_INLINE FuncInOut& getRet(uint32_t index = kFuncRetLo) noexcept { return _rets[index]; } - //! Get function return value. - ASMJIT_INLINE const FuncInOut& getRet(uint32_t index = kFuncRetLo) const noexcept { return _rets[index]; } - - //! Get the number of function arguments. - ASMJIT_INLINE uint32_t getNumArgs() const noexcept { return _numArgs; } - - //! Get function arguments array. - ASMJIT_INLINE FuncInOut* getArgs() noexcept { return _args; } - //! Get function arguments array (const). - ASMJIT_INLINE const FuncInOut* getArgs() const noexcept { return _args; } - - //! Get function argument at index `index`. - ASMJIT_INLINE FuncInOut& getArg(size_t index) noexcept { - ASMJIT_ASSERT(index < kFuncArgCountLoHi); - return _args[index]; - } - - //! Get function argument at index `index`. - ASMJIT_INLINE const FuncInOut& getArg(size_t index) const noexcept { - ASMJIT_ASSERT(index < kFuncArgCountLoHi); - return _args[index]; - } - - ASMJIT_INLINE void resetArg(size_t index) noexcept { - ASMJIT_ASSERT(index < kFuncArgCountLoHi); - _args[index].reset(); - } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Calling convention. - uint8_t _callConv; - //! Whether a callee pops stack. - uint8_t _calleePopsStack : 1; - //! Direction for arguments passed on the stack, see `FuncDir`. - uint8_t _argsDirection : 1; - //! Reserved #0 (alignment). - uint8_t _reserved0 : 6; - - //! Number of function arguments. - uint8_t _numArgs; - //! Number of function return values. - uint8_t _retCount; - - //! Count of bytes consumed by arguments on the stack (aligned). - uint32_t _argStackSize; - - //! Size of "Red Zone". - //! - //! NOTE: Used by AMD64-ABI (128 bytes). - uint16_t _redZoneSize; - - //! Size of "Spill Zone". - //! - //! NOTE: Used by WIN64-ABI (32 bytes). - uint16_t _spillZoneSize; - - //! Function arguments (LO & HI) mapped to physical registers and stack. - FuncInOut _args[kFuncArgCountLoHi]; - - //! Function return value(s). - FuncInOut _rets[2]; -}; - -//! \} - -} // asmjit namespace - -// [Api-End] -#include "../apiend.h" - -// [Guard] -#endif // !ASMJIT_DISABLE_COMPILER -#endif // _ASMJIT_BASE_COMPILERFUNC_H diff --git a/src/asmjit/base/constpool.cpp b/src/asmjit/base/constpool.cpp index 38bf492..799abd1 100644 --- a/src/asmjit/base/constpool.cpp +++ b/src/asmjit/base/constpool.cpp @@ -11,8 +11,10 @@ #include "../base/constpool.h" #include "../base/utils.h" +#include + // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { @@ -31,7 +33,7 @@ static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_skewNode(ConstPool::Node* no ConstPool::Node* link = node->_link[0]; uint32_t level = node->_level; - if (level != 0 && link != nullptr && link->_level == level) { + if (level != 0 && link && link->_level == level) { node->_link[0] = link->_link[1]; link->_link[1] = node; @@ -48,7 +50,7 @@ static ASMJIT_INLINE ConstPool::Node* ConstPoolTree_splitNode(ConstPool::Node* n ConstPool::Node* link = node->_link[1]; uint32_t level = node->_level; - if (level != 0 && link != nullptr && link->_link[1] != nullptr && link->_link[1]->_level == level) { + if (level != 0 && link && link->_link[1] && link->_link[1]->_level == level) { node->_link[1] = link->_link[0]; link->_link[0] = node; @@ -63,7 +65,7 @@ ConstPool::Node* ConstPool::Tree::get(const void* data) noexcept { ConstPool::Node* node = _root; size_t dataSize = _dataSize; - while (node != nullptr) { + while (node) { int c = ::memcmp(node->getData(), data, dataSize); if (c == 0) return node; @@ -75,9 +77,9 @@ ConstPool::Node* ConstPool::Tree::get(const void* data) noexcept { void ConstPool::Tree::put(ConstPool::Node* newNode) noexcept { size_t dataSize = _dataSize; - _length++; - if (_root == nullptr) { + + if (!_root) { _root = newNode; return; } @@ -94,8 +96,7 @@ void ConstPool::Tree::put(ConstPool::Node* newNode) noexcept { dir = ::memcmp(node->getData(), newNode->getData(), dataSize) < 0; ConstPool::Node* link = node->_link[dir]; - if (link == nullptr) - break; + if (!link) break; node = link; } @@ -126,31 +127,22 @@ void ConstPool::Tree::put(ConstPool::Node* newNode) noexcept { // [asmjit::ConstPool - Construction / Destruction] // ============================================================================ -ConstPool::ConstPool(Zone* zone) noexcept { - _zone = zone; - - size_t dataSize = 1; - for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) { - _tree[i].setDataSize(dataSize); - _gaps[i] = nullptr; - dataSize <<= 1; - } - - _gapPool = nullptr; - _size = 0; - _alignment = 0; -} - +ConstPool::ConstPool(Zone* zone) noexcept { reset(zone); } ConstPool::~ConstPool() noexcept {} // ============================================================================ // [asmjit::ConstPool - Reset] // ============================================================================ -void ConstPool::reset() noexcept { +void ConstPool::reset(Zone* zone) noexcept { + _zone = zone; + + size_t dataSize = 1; for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_tree); i++) { _tree[i].reset(); + _tree[i].setDataSize(dataSize); _gaps[i] = nullptr; + dataSize <<= 1; } _gapPool = nullptr; @@ -164,8 +156,7 @@ void ConstPool::reset() noexcept { static ASMJIT_INLINE ConstPool::Gap* ConstPool_allocGap(ConstPool* self) noexcept { ConstPool::Gap* gap = self->_gapPool; - if (gap == nullptr) - return self->_zone->allocT(); + if (!gap) return self->_zone->allocT(); self->_gapPool = gap->_next; return gap; @@ -183,8 +174,8 @@ static void ConstPool_addGap(ConstPool* self, size_t offset, size_t length) noex size_t gapIndex; size_t gapLength; - if (length >= 16 && Utils::isAligned(offset, 16)) { gapIndex = ConstPool::kIndex16; + if (length >= 16 && Utils::isAligned(offset, 16)) { gapLength = 16; } else if (length >= 8 && Utils::isAligned(offset, 8)) { @@ -208,8 +199,7 @@ static void ConstPool_addGap(ConstPool* self, size_t offset, size_t length) noex // happened (just the gap won't be visible) and it will fail again at // place where checking will cause kErrorNoHeapMemory. ConstPool::Gap* gap = ConstPool_allocGap(self); - if (gap == nullptr) - return; + if (!gap) return; gap->_next = self->_gaps[gapIndex]; self->_gaps[gapIndex] = gap; @@ -238,10 +228,10 @@ Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept else if (size == 1) treeIndex = kIndex1; else - return kErrorInvalidArgument; + return DebugUtils::errored(kErrorInvalidArgument); ConstPool::Node* node = _tree[treeIndex].get(data); - if (node != nullptr) { + if (node) { dstOffset = node->_offset; return kErrorOk; } @@ -255,7 +245,7 @@ Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept ConstPool::Gap* gap = _gaps[treeIndex]; // Check if there is a gap. - if (gap != nullptr) { + if (gap) { size_t gapOffset = gap->_offset; size_t gapLength = gap->_length; @@ -290,11 +280,10 @@ Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept // Add the initial node to the right index. node = ConstPool::Tree::_newNode(_zone, data, size, offset, false); - if (node == nullptr) - return kErrorNoHeapMemory; + if (!node) return DebugUtils::errored(kErrorNoHeapMemory); _tree[treeIndex].put(node); - _alignment = Utils::iMax(_alignment, size); + _alignment = std::max(_alignment, size); dstOffset = offset; @@ -312,9 +301,7 @@ Error ConstPool::add(const void* data, size_t size, size_t& dstOffset) noexcept const uint8_t* pData = static_cast(data); for (size_t i = 0; i < pCount; i++, pData += size) { node = _tree[treeIndex].get(pData); - - if (node != nullptr) - continue; + if (node) continue; node = ConstPool::Tree::_newNode(_zone, pData, size, offset + (i * size), true); _tree[treeIndex].put(node); @@ -372,23 +359,23 @@ UNIT(base_constpool) { uint64_t c = ASMJIT_UINT64_C(0x0101010101010101); EXPECT(pool.add(&c, 8, prevOffset) == kErrorOk, - "pool.add() - Returned error."); + "pool.add() - Returned error"); EXPECT(prevOffset == 0, - "pool.add() - First constant should have zero offset."); + "pool.add() - First constant should have zero offset"); for (i = 1; i < kCount; i++) { c++; EXPECT(pool.add(&c, 8, curOffset) == kErrorOk, - "pool.add() - Returned error."); + "pool.add() - Returned error"); EXPECT(prevOffset + 8 == curOffset, - "pool.add() - Returned incorrect curOffset."); + "pool.add() - Returned incorrect curOffset"); EXPECT(pool.getSize() == (i + 1) * 8, - "pool.getSize() - Reported incorrect size."); + "pool.getSize() - Reported incorrect size"); prevOffset = curOffset; } EXPECT(pool.getAlignment() == 8, - "pool.getAlignment() - Expected 8-byte alignment."); + "pool.getAlignment() - Expected 8-byte alignment"); } INFO("Retrieving %u constants from the pool.", kCount); @@ -398,74 +385,75 @@ UNIT(base_constpool) { for (i = 0; i < kCount; i++) { size_t offset; EXPECT(pool.add(&c, 8, offset) == kErrorOk, - "pool.add() - Returned error."); + "pool.add() - Returned error"); EXPECT(offset == i * 8, - "pool.add() - Should have reused constant."); + "pool.add() - Should have reused constant"); c++; } } - INFO("Checking if the constants were split into 4-byte patterns."); + INFO("Checking if the constants were split into 4-byte patterns"); { uint32_t c = 0x01010101; for (i = 0; i < kCount; i++) { size_t offset; EXPECT(pool.add(&c, 4, offset) == kErrorOk, - "pool.add() - Returned error."); + "pool.add() - Returned error"); EXPECT(offset == i * 8, - "pool.add() - Should reuse existing constant."); + "pool.add() - Should reuse existing constant"); c++; } } - INFO("Adding 2 byte constant to misalign the current offset."); + INFO("Adding 2 byte constant to misalign the current offset"); { uint16_t c = 0xFFFF; size_t offset; EXPECT(pool.add(&c, 2, offset) == kErrorOk, - "pool.add() - Returned error."); + "pool.add() - Returned error"); EXPECT(offset == kCount * 8, - "pool.add() - Didn't return expected position."); + "pool.add() - Didn't return expected position"); EXPECT(pool.getAlignment() == 8, - "pool.getAlignment() - Expected 8-byte alignment."); + "pool.getAlignment() - Expected 8-byte alignment"); } - INFO("Adding 8 byte constant to check if pool gets aligned again."); + INFO("Adding 8 byte constant to check if pool gets aligned again"); { uint64_t c = ASMJIT_UINT64_C(0xFFFFFFFFFFFFFFFF); size_t offset; EXPECT(pool.add(&c, 8, offset) == kErrorOk, - "pool.add() - Returned error."); + "pool.add() - Returned error"); EXPECT(offset == kCount * 8 + 8, - "pool.add() - Didn't return aligned offset."); + "pool.add() - Didn't return aligned offset"); } - INFO("Adding 2 byte constant to verify the gap is filled."); + INFO("Adding 2 byte constant to verify the gap is filled"); { uint16_t c = 0xFFFE; size_t offset; EXPECT(pool.add(&c, 2, offset) == kErrorOk, - "pool.add() - Returned error."); + "pool.add() - Returned error"); EXPECT(offset == kCount * 8 + 2, - "pool.add() - Didn't fill the gap."); + "pool.add() - Didn't fill the gap"); EXPECT(pool.getAlignment() == 8, - "pool.getAlignment() - Expected 8-byte alignment."); + "pool.getAlignment() - Expected 8-byte alignment"); } - INFO("Checking reset functionality."); + INFO("Checking reset functionality"); { - pool.reset(); + pool.reset(&zone); + zone.reset(); EXPECT(pool.getSize() == 0, - "pool.getSize() - Expected pool size to be zero."); + "pool.getSize() - Expected pool size to be zero"); EXPECT(pool.getAlignment() == 0, - "pool.getSize() - Expected pool alignment to be zero."); + "pool.getSize() - Expected pool alignment to be zero"); } - INFO("Checking pool alignment when combined constants are added."); + INFO("Checking pool alignment when combined constants are added"); { uint8_t bytes[32] = { 0 }; size_t offset; @@ -473,46 +461,46 @@ UNIT(base_constpool) { pool.add(bytes, 1, offset); EXPECT(pool.getSize() == 1, - "pool.getSize() - Expected pool size to be 1 byte."); + "pool.getSize() - Expected pool size to be 1 byte"); EXPECT(pool.getAlignment() == 1, - "pool.getSize() - Expected pool alignment to be 1 byte."); + "pool.getSize() - Expected pool alignment to be 1 byte"); EXPECT(offset == 0, - "pool.getSize() - Expected offset returned to be zero."); + "pool.getSize() - Expected offset returned to be zero"); pool.add(bytes, 2, offset); EXPECT(pool.getSize() == 4, - "pool.getSize() - Expected pool size to be 4 bytes."); + "pool.getSize() - Expected pool size to be 4 bytes"); EXPECT(pool.getAlignment() == 2, - "pool.getSize() - Expected pool alignment to be 2 bytes."); + "pool.getSize() - Expected pool alignment to be 2 bytes"); EXPECT(offset == 2, - "pool.getSize() - Expected offset returned to be 2."); + "pool.getSize() - Expected offset returned to be 2"); pool.add(bytes, 4, offset); EXPECT(pool.getSize() == 8, - "pool.getSize() - Expected pool size to be 8 bytes."); + "pool.getSize() - Expected pool size to be 8 bytes"); EXPECT(pool.getAlignment() == 4, - "pool.getSize() - Expected pool alignment to be 4 bytes."); + "pool.getSize() - Expected pool alignment to be 4 bytes"); EXPECT(offset == 4, - "pool.getSize() - Expected offset returned to be 4."); + "pool.getSize() - Expected offset returned to be 4"); pool.add(bytes, 4, offset); EXPECT(pool.getSize() == 8, - "pool.getSize() - Expected pool size to be 8 bytes."); + "pool.getSize() - Expected pool size to be 8 bytes"); EXPECT(pool.getAlignment() == 4, - "pool.getSize() - Expected pool alignment to be 4 bytes."); + "pool.getSize() - Expected pool alignment to be 4 bytes"); EXPECT(offset == 4, - "pool.getSize() - Expected offset returned to be 8."); + "pool.getSize() - Expected offset returned to be 8"); pool.add(bytes, 32, offset); EXPECT(pool.getSize() == 64, - "pool.getSize() - Expected pool size to be 64 bytes."); + "pool.getSize() - Expected pool size to be 64 bytes"); EXPECT(pool.getAlignment() == 32, - "pool.getSize() - Expected pool alignment to be 32 bytes."); + "pool.getSize() - Expected pool alignment to be 32 bytes"); EXPECT(offset == 32, - "pool.getSize() - Expected offset returned to be 32."); + "pool.getSize() - Expected offset returned to be 32"); } } #endif // ASMJIT_TEST @@ -520,4 +508,4 @@ UNIT(base_constpool) { } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/constpool.h b/src/asmjit/base/constpool.h index 4b25c68..945ea64 100644 --- a/src/asmjit/base/constpool.h +++ b/src/asmjit/base/constpool.h @@ -12,7 +12,7 @@ #include "../base/zone.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { @@ -25,8 +25,8 @@ namespace asmjit { //! Constant pool. class ConstPool { - public: - ASMJIT_NO_COPY(ConstPool) +public: + ASMJIT_NONCOPYABLE(ConstPool) enum { kIndex1 = 0, @@ -46,12 +46,9 @@ class ConstPool { //! //! Zone-allocated const-pool gap. struct Gap { - //! Link to the next gap - Gap* _next; - //! Offset of the gap. - size_t _offset; - //! Remaining bytes of the gap (basically a gap size). - size_t _length; + Gap* _next; //!< Pointer to the next gap + size_t _offset; //!< Offset of the gap. + size_t _length; //!< Remaining bytes of the gap (basically a gap size). }; // -------------------------------------------------------------------------- @@ -62,26 +59,14 @@ class ConstPool { //! //! Zone-allocated const-pool node. struct Node { - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - ASMJIT_INLINE void* getData() const noexcept { return static_cast(const_cast(this) + 1); } - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Left/Right nodes. - Node* _link[2]; - //! Horizontal level for balance. - uint32_t _level : 31; - //! Whether this constant is shared with another. - uint32_t _shared : 1; - //! Data offset from the beginning of the pool. - uint32_t _offset; + Node* _link[2]; //!< Left/Right nodes. + uint32_t _level : 31; //!< Horizontal level for balance. + uint32_t _shared : 1; //!< If this constant is shared with another. + uint32_t _offset; //!< Data offset from the beginning of the pool. }; // -------------------------------------------------------------------------- @@ -142,8 +127,7 @@ class ConstPool { template ASMJIT_INLINE void iterate(Visitor& visitor) const noexcept { Node* node = const_cast(_root); - if (node == nullptr) - return; + if (!node) return; Node* stack[kHeightLimit]; size_t top = 0; @@ -158,7 +142,7 @@ class ConstPool { continue; } -L_Visit: +Visit: visitor.visit(node); node = node->_link[1]; if (node != nullptr) @@ -168,7 +152,7 @@ L_Visit: return; node = stack[--top]; - goto L_Visit; + goto Visit; } } @@ -178,8 +162,7 @@ L_Visit: static ASMJIT_INLINE Node* _newNode(Zone* zone, const void* data, size_t size, size_t offset, bool shared) noexcept { Node* node = zone->allocT(sizeof(Node) + size); - if (node == nullptr) - return nullptr; + if (ASMJIT_UNLIKELY(!node)) return nullptr; node->_link[0] = nullptr; node->_link[1] = nullptr; @@ -195,12 +178,9 @@ L_Visit: // [Members] // -------------------------------------------------------------------------- - //! Root of the tree - Node* _root; - //! Length of the tree (count of nodes). - size_t _length; - //! Size of the data. - size_t _dataSize; + Node* _root; //!< Root of the tree + size_t _length; //!< Length of the tree (count of nodes). + size_t _dataSize; //!< Size of the data. }; // -------------------------------------------------------------------------- @@ -214,7 +194,7 @@ L_Visit: // [Reset] // -------------------------------------------------------------------------- - ASMJIT_API void reset() noexcept; + ASMJIT_API void reset(Zone* zone) noexcept; // -------------------------------------------------------------------------- // [Ops] @@ -257,19 +237,13 @@ L_Visit: // [Members] // -------------------------------------------------------------------------- - //! Zone allocator. - Zone* _zone; - //! Tree per size. - Tree _tree[kIndexCount]; - //! Gaps per size. - Gap* _gaps[kIndexCount]; - //! Gaps pool - Gap* _gapPool; + Zone* _zone; //!< Zone allocator. + Tree _tree[kIndexCount]; //!< Tree per size. + Gap* _gaps[kIndexCount]; //!< Gaps per size. + Gap* _gapPool; //!< Gaps pool - //! Size of the pool (in bytes). - size_t _size; - //! Alignemnt. - size_t _alignment; + size_t _size; //!< Size of the pool (in bytes). + size_t _alignment; //!< Required pool alignment. }; //! \} @@ -277,7 +251,7 @@ L_Visit: } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" // [Guard] #endif // _ASMJIT_BASE_CONSTPOOL_H diff --git a/src/asmjit/base/containers.h b/src/asmjit/base/containers.h deleted file mode 100644 index 3a843bf..0000000 --- a/src/asmjit/base/containers.h +++ /dev/null @@ -1,550 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Guard] -#ifndef _ASMJIT_BASE_CONTAINERS_H -#define _ASMJIT_BASE_CONTAINERS_H - -// [Dependencies] -#include "../base/globals.h" - -// [Api-Begin] -#include "../apibegin.h" - -namespace asmjit { - -//! \addtogroup asmjit_base -//! \{ - -// ============================================================================ -// [asmjit::BitArray] -// ============================================================================ - -//! Fixed size bit-array. -//! -//! Used by variable liveness analysis. -struct BitArray { - // -------------------------------------------------------------------------- - // [Enums] - // -------------------------------------------------------------------------- - - enum { - kEntitySize = static_cast(sizeof(uintptr_t)), - kEntityBits = kEntitySize * 8 - }; - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE uintptr_t getBit(uint32_t index) const noexcept { - return (data[index / kEntityBits] >> (index % kEntityBits)) & 1; - } - - ASMJIT_INLINE void setBit(uint32_t index) noexcept { - data[index / kEntityBits] |= static_cast(1) << (index % kEntityBits); - } - - ASMJIT_INLINE void delBit(uint32_t index) noexcept { - data[index / kEntityBits] &= ~(static_cast(1) << (index % kEntityBits)); - } - - // -------------------------------------------------------------------------- - // [Interface] - // -------------------------------------------------------------------------- - - //! Copy bits from `s0`, returns `true` if at least one bit is set in `s0`. - ASMJIT_INLINE bool copyBits(const BitArray* s0, uint32_t len) noexcept { - uintptr_t r = 0; - for (uint32_t i = 0; i < len; i++) { - uintptr_t t = s0->data[i]; - data[i] = t; - r |= t; - } - return r != 0; - } - - ASMJIT_INLINE bool addBits(const BitArray* s0, uint32_t len) noexcept { - return addBits(this, s0, len); - } - - ASMJIT_INLINE bool addBits(const BitArray* s0, const BitArray* s1, uint32_t len) noexcept { - uintptr_t r = 0; - for (uint32_t i = 0; i < len; i++) { - uintptr_t t = s0->data[i] | s1->data[i]; - data[i] = t; - r |= t; - } - return r != 0; - } - - ASMJIT_INLINE bool andBits(const BitArray* s1, uint32_t len) noexcept { - return andBits(this, s1, len); - } - - ASMJIT_INLINE bool andBits(const BitArray* s0, const BitArray* s1, uint32_t len) noexcept { - uintptr_t r = 0; - for (uint32_t i = 0; i < len; i++) { - uintptr_t t = s0->data[i] & s1->data[i]; - data[i] = t; - r |= t; - } - return r != 0; - } - - ASMJIT_INLINE bool delBits(const BitArray* s1, uint32_t len) noexcept { - return delBits(this, s1, len); - } - - ASMJIT_INLINE bool delBits(const BitArray* s0, const BitArray* s1, uint32_t len) noexcept { - uintptr_t r = 0; - for (uint32_t i = 0; i < len; i++) { - uintptr_t t = s0->data[i] & ~s1->data[i]; - data[i] = t; - r |= t; - } - return r != 0; - } - - ASMJIT_INLINE bool _addBitsDelSource(BitArray* s1, uint32_t len) noexcept { - return _addBitsDelSource(this, s1, len); - } - - ASMJIT_INLINE bool _addBitsDelSource(const BitArray* s0, BitArray* s1, uint32_t len) noexcept { - uintptr_t r = 0; - for (uint32_t i = 0; i < len; i++) { - uintptr_t a = s0->data[i]; - uintptr_t b = s1->data[i]; - - this->data[i] = a | b; - b &= ~a; - - s1->data[i] = b; - r |= b; - } - return r != 0; - } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - uintptr_t data[1]; -}; - -// ============================================================================ -// [asmjit::PodList] -// ============================================================================ - -//! \internal -template -class PodList { - public: - ASMJIT_NO_COPY(PodList) - - // -------------------------------------------------------------------------- - // [Link] - // -------------------------------------------------------------------------- - - struct Link { - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get next node. - ASMJIT_INLINE Link* getNext() const noexcept { return _next; } - - //! Get value. - ASMJIT_INLINE T getValue() const noexcept { return _value; } - //! Set value to `value`. - ASMJIT_INLINE void setValue(const T& value) noexcept { _value = value; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - Link* _next; - T _value; - }; - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE PodList() noexcept : _first(nullptr), _last(nullptr) {} - ASMJIT_INLINE ~PodList() noexcept {} - - // -------------------------------------------------------------------------- - // [Data] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE bool isEmpty() const noexcept { return _first != nullptr; } - - ASMJIT_INLINE Link* getFirst() const noexcept { return _first; } - ASMJIT_INLINE Link* getLast() const noexcept { return _last; } - - // -------------------------------------------------------------------------- - // [Ops] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE void reset() noexcept { - _first = nullptr; - _last = nullptr; - } - - ASMJIT_INLINE void prepend(Link* link) noexcept { - link->_next = _first; - if (_first == nullptr) - _last = link; - _first = link; - } - - ASMJIT_INLINE void append(Link* link) noexcept { - link->_next = nullptr; - if (_first == nullptr) - _first = link; - else - _last->_next = link; - _last = link; - } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - Link* _first; - Link* _last; -}; - -// ============================================================================ -// [asmjit::StringBuilder] -// ============================================================================ - -//! String builder. -//! -//! String builder was designed to be able to build a string using append like -//! operation to append numbers, other strings, or signle characters. It can -//! allocate it's own buffer or use a buffer created on the stack. -//! -//! String builder contains method specific to AsmJit functionality, used for -//! logging or HTML output. -class StringBuilder { - public: - ASMJIT_NO_COPY(StringBuilder) - - // -------------------------------------------------------------------------- - // [Enums] - // -------------------------------------------------------------------------- - - //! \internal - //! - //! String operation. - ASMJIT_ENUM(StringOp) { - //! Replace the current string by a given content. - kStringOpSet = 0, - //! Append a given content to the current string. - kStringOpAppend = 1 - }; - - //! \internal - //! - //! String format flags. - ASMJIT_ENUM(StringFormatFlags) { - kStringFormatShowSign = 0x00000001, - kStringFormatShowSpace = 0x00000002, - kStringFormatAlternate = 0x00000004, - kStringFormatSigned = 0x80000000 - }; - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - ASMJIT_API StringBuilder() noexcept; - ASMJIT_API ~StringBuilder() noexcept; - - ASMJIT_INLINE StringBuilder(const _NoInit&) noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get string builder capacity. - ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } - //! Get length. - ASMJIT_INLINE size_t getLength() const noexcept { return _length; } - - //! Get null-terminated string data. - ASMJIT_INLINE char* getData() noexcept { return _data; } - //! Get null-terminated string data (const). - ASMJIT_INLINE const char* getData() const noexcept { return _data; } - - // -------------------------------------------------------------------------- - // [Prepare / Reserve] - // -------------------------------------------------------------------------- - - //! Prepare to set/append. - ASMJIT_API char* prepare(uint32_t op, size_t len) noexcept; - - //! Reserve `to` bytes in string builder. - ASMJIT_API bool reserve(size_t to) noexcept; - - // -------------------------------------------------------------------------- - // [Clear] - // -------------------------------------------------------------------------- - - //! Clear the content in String builder. - ASMJIT_API void clear() noexcept; - - // -------------------------------------------------------------------------- - // [Op] - // -------------------------------------------------------------------------- - - ASMJIT_API bool _opString(uint32_t op, const char* str, size_t len = kInvalidIndex) noexcept; - ASMJIT_API bool _opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept; - ASMJIT_API bool _opChar(uint32_t op, char c) noexcept; - ASMJIT_API bool _opChars(uint32_t op, char c, size_t len) noexcept; - ASMJIT_API bool _opNumber(uint32_t op, uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept; - ASMJIT_API bool _opHex(uint32_t op, const void* data, size_t len) noexcept; - - // -------------------------------------------------------------------------- - // [Set] - // -------------------------------------------------------------------------- - - //! Replace the current content by `str` of `len`. - ASMJIT_INLINE bool setString(const char* str, size_t len = kInvalidIndex) noexcept { - return _opString(kStringOpSet, str, len); - } - - //! Replace the current content by formatted string `fmt`. - ASMJIT_INLINE bool setVFormat(const char* fmt, va_list ap) noexcept { - return _opVFormat(kStringOpSet, fmt, ap); - } - - //! Replace the current content by formatted string `fmt`. - ASMJIT_API bool setFormat(const char* fmt, ...) noexcept; - - //! Replace the current content by `c` character. - ASMJIT_INLINE bool setChar(char c) noexcept { - return _opChar(kStringOpSet, c); - } - - //! Replace the current content by `c` of `len`. - ASMJIT_INLINE bool setChars(char c, size_t len) noexcept { - return _opChars(kStringOpSet, c, len); - } - - //! Replace the current content by formatted integer `i`. - ASMJIT_INLINE bool setInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { - return _opNumber(kStringOpSet, i, base, width, flags | kStringFormatSigned); - } - - //! Replace the current content by formatted integer `i`. - ASMJIT_INLINE bool setUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { - return _opNumber(kStringOpSet, i, base, width, flags); - } - - //! Replace the current content by the given `data` converted to a HEX string. - ASMJIT_INLINE bool setHex(const void* data, size_t len) noexcept { - return _opHex(kStringOpSet, data, len); - } - - // -------------------------------------------------------------------------- - // [Append] - // -------------------------------------------------------------------------- - - //! Append `str` of `len`. - ASMJIT_INLINE bool appendString(const char* str, size_t len = kInvalidIndex) noexcept { - return _opString(kStringOpAppend, str, len); - } - - //! Append a formatted string `fmt` to the current content. - ASMJIT_INLINE bool appendVFormat(const char* fmt, va_list ap) noexcept { - return _opVFormat(kStringOpAppend, fmt, ap); - } - - //! Append a formatted string `fmt` to the current content. - ASMJIT_API bool appendFormat(const char* fmt, ...) noexcept; - - //! Append `c` character. - ASMJIT_INLINE bool appendChar(char c) noexcept { - return _opChar(kStringOpAppend, c); - } - - //! Append `c` of `len`. - ASMJIT_INLINE bool appendChars(char c, size_t len) noexcept { - return _opChars(kStringOpAppend, c, len); - } - - //! Append `i`. - ASMJIT_INLINE bool appendInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { - return _opNumber(kStringOpAppend, static_cast(i), base, width, flags | kStringFormatSigned); - } - - //! Append `i`. - ASMJIT_INLINE bool appendUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { - return _opNumber(kStringOpAppend, i, base, width, flags); - } - - //! Append the given `data` converted to a HEX string. - ASMJIT_INLINE bool appendHex(const void* data, size_t len) noexcept { - return _opHex(kStringOpAppend, data, len); - } - - // -------------------------------------------------------------------------- - // [_Append] - // -------------------------------------------------------------------------- - - //! Append `str` of `len`, inlined, without buffer overflow check. - ASMJIT_INLINE void _appendString(const char* str, size_t len = kInvalidIndex) noexcept { - // len should be a constant if we are inlining. - if (len == kInvalidIndex) { - char* p = &_data[_length]; - - while (*str) { - ASMJIT_ASSERT(p < _data + _capacity); - *p++ = *str++; - } - - *p = '\0'; - _length = (size_t)(p - _data); - } - else { - ASMJIT_ASSERT(_capacity - _length >= len); - - char* p = &_data[_length]; - char* pEnd = p + len; - - while (p < pEnd) - *p++ = *str++; - - *p = '\0'; - _length += len; - } - } - - //! Append `c` character, inlined, without buffer overflow check. - ASMJIT_INLINE void _appendChar(char c) noexcept { - ASMJIT_ASSERT(_capacity - _length >= 1); - - _data[_length] = c; - _length++; - _data[_length] = '\0'; - } - - //! Append `c` of `len`, inlined, without buffer overflow check. - ASMJIT_INLINE void _appendChars(char c, size_t len) noexcept { - ASMJIT_ASSERT(_capacity - _length >= len); - - char* p = &_data[_length]; - char* pEnd = p + len; - - while (p < pEnd) - *p++ = c; - - *p = '\0'; - _length += len; - } - - ASMJIT_INLINE void _appendUInt32(uint32_t i) noexcept { - char buf_[32]; - - char* pEnd = buf_ + ASMJIT_ARRAY_SIZE(buf_); - char* pBuf = pEnd; - - do { - uint32_t d = i / 10; - uint32_t r = i % 10; - - *--pBuf = static_cast(r + '0'); - i = d; - } while (i); - - ASMJIT_ASSERT(_capacity - _length >= (size_t)(pEnd - pBuf)); - char* p = &_data[_length]; - - do { - *p++ = *pBuf; - } while (++pBuf != pEnd); - - *p = '\0'; - _length = (size_t)(p - _data); - } - - // -------------------------------------------------------------------------- - // [Eq] - // -------------------------------------------------------------------------- - - //! Check for equality with other `str` of `len`. - ASMJIT_API bool eq(const char* str, size_t len = kInvalidIndex) const noexcept; - //! Check for equality with `other`. - ASMJIT_INLINE bool eq(const StringBuilder& other) const noexcept { return eq(other._data); } - - // -------------------------------------------------------------------------- - // [Operator Overload] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE bool operator==(const StringBuilder& other) const noexcept { return eq(other); } - ASMJIT_INLINE bool operator!=(const StringBuilder& other) const noexcept { return !eq(other); } - - ASMJIT_INLINE bool operator==(const char* str) const noexcept { return eq(str); } - ASMJIT_INLINE bool operator!=(const char* str) const noexcept { return !eq(str); } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! String data. - char* _data; - //! Length. - size_t _length; - //! Capacity. - size_t _capacity; - //! Whether the string can be freed. - size_t _canFree; -}; - -// ============================================================================ -// [asmjit::StringBuilderTmp] -// ============================================================================ - -//! Temporary string builder, has statically allocated `N` bytes. -template -class StringBuilderTmp : public StringBuilder { - public: - ASMJIT_NO_COPY(StringBuilderTmp) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE StringBuilderTmp() noexcept : StringBuilder(NoInit) { - _data = _embeddedData; - _data[0] = 0; - - _length = 0; - _capacity = N; - _canFree = false; - } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Embedded data. - char _embeddedData[static_cast( - N + 1 + sizeof(intptr_t)) & ~static_cast(sizeof(intptr_t) - 1)]; -}; - -//! \} - -} // asmjit namespace - -// [Api-End] -#include "../apiend.h" - -// [Guard] -#endif // _ASMJIT_BASE_CONTAINERS_H diff --git a/src/asmjit/base/cpuinfo.cpp b/src/asmjit/base/cpuinfo.cpp index 20f84e4..8e17e80 100644 --- a/src/asmjit/base/cpuinfo.cpp +++ b/src/asmjit/base/cpuinfo.cpp @@ -31,32 +31,44 @@ #endif // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { // ============================================================================ -// [asmjit::CpuInfo - Detect ARM & ARM64] +// [asmjit::CpuInfo - Detect ARM] // ============================================================================ // ARM information has to be retrieved by the OS (this is how ARM was designed). #if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 -#if ASMJIT_ARCH_ARM64 -static void armPopulateBaseline64Features(CpuInfo* cpuInfo) noexcept { - // Thumb (including all variations) is only supported on ARM32. +#if ASMJIT_ARCH_ARM32 +static ASMJIT_INLINE void armPopulateBaselineA32Features(CpuInfo* cpuInfo) noexcept { + cpuInfo->_archInfo.init(ArchInfo::kTypeA32); +} +#endif // ASMJIT_ARCH_ARM32 - // ARM64 is based on ARMv8 and newer. +#if ASMJIT_ARCH_ARM64 +static ASMJIT_INLINE void armPopulateBaselineA64Features(CpuInfo* cpuInfo) noexcept { + cpuInfo->_archInfo.init(ArchInfo::kTypeA64); + + // Thumb (including all variations) is supported on A64 (but not accessible from A64). + cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB); + cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB2); + + // A64 is based on ARMv8 and newer. cpuInfo->addFeature(CpuInfo::kArmFeatureV6); cpuInfo->addFeature(CpuInfo::kArmFeatureV7); cpuInfo->addFeature(CpuInfo::kArmFeatureV8); - // ARM64 comes with these features by default. - cpuInfo->addFeature(CpuInfo::kArmFeatureDSP); - cpuInfo->addFeature(CpuInfo::kArmFeatureIDIV); - cpuInfo->addFeature(CpuInfo::kArmFeatureVFP2); - cpuInfo->addFeature(CpuInfo::kArmFeatureVFP3); - cpuInfo->addFeature(CpuInfo::kArmFeatureVFP4); + // A64 comes with these features by default. + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2); + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv3); + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv4); + cpuInfo->addFeature(CpuInfo::kArmFeatureEDSP); + cpuInfo->addFeature(CpuInfo::kArmFeatureASIMD); + cpuInfo->addFeature(CpuInfo::kArmFeatureIDIVA); + cpuInfo->addFeature(CpuInfo::kArmFeatureIDIVT); } #endif // ASMJIT_ARCH_ARM64 @@ -66,39 +78,39 @@ static void armPopulateBaseline64Features(CpuInfo* cpuInfo) noexcept { //! Detect ARM CPU features on Windows. //! //! The detection is based on `IsProcessorFeaturePresent()` API call. -static void armDetectCpuInfoOnWindows(CpuInfo* cpuInfo) noexcept { +static ASMJIT_INLINE void armDetectCpuInfoOnWindows(CpuInfo* cpuInfo) noexcept { #if ASMJIT_ARCH_ARM32 - cpuInfo->setArch(kArchArm32); + armPopulateBaselineA32Features(cpuInfo); // Windows for ARM requires at least ARMv7 with DSP extensions. cpuInfo->addFeature(CpuInfo::kArmFeatureV6); cpuInfo->addFeature(CpuInfo::kArmFeatureV7); - cpuInfo->addFeature(CpuInfo::kArmFeatureDSP); + cpuInfo->addFeature(CpuInfo::kArmFeatureEDSP); - // Windows for ARM requires VFP3. - cpuInfo->addFeature(CpuInfo::kArmFeatureVFP2); - cpuInfo->addFeature(CpuInfo::kArmFeatureVFP3); + // Windows for ARM requires VFPv3. + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2); + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv3); // Windows for ARM requires and uses THUMB2. cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB); cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB2); #else - cpuInfo->setArch(kArchArm64); - armPopulateBaseline64Features(cpuInfo); + armPopulateBaselineA64Features(cpuInfo); #endif - // Windows for ARM requires NEON. - cpuInfo->addFeature(CpuInfo::kArmFeatureNEON); + // Windows for ARM requires ASIMD. + cpuInfo->addFeature(CpuInfo::kArmFeatureASIMD); // Detect additional CPU features by calling `IsProcessorFeaturePresent()`. struct WinPFPMapping { - uint32_t pfpId, featureId; + uint32_t pfpId; + uint32_t featureId; }; static const WinPFPMapping mapping[] = { - { PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE , CpuInfo::kArmFeatureVFP4 }, + { PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE , CpuInfo::kArmFeatureVFPv4 }, { PF_ARM_VFP_32_REGISTERS_AVAILABLE , CpuInfo::kArmFeatureVFP_D32 }, - { PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE, CpuInfo::kArmFeatureIDIV }, + { PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE, CpuInfo::kArmFeatureIDIVT }, { PF_ARM_64BIT_LOADSTORE_ATOMIC , CpuInfo::kArmFeatureAtomics64 } }; @@ -110,13 +122,13 @@ static void armDetectCpuInfoOnWindows(CpuInfo* cpuInfo) noexcept { #if ASMJIT_OS_LINUX struct LinuxHWCapMapping { - uint32_t hwcapMask, featureId; + uint32_t hwcapMask; + uint32_t featureId; }; -static void armDetectHWCaps(CpuInfo* cpuInfo, - unsigned long type, const LinuxHWCapMapping* mapping, size_t length) noexcept { - +static void armDetectHWCaps(CpuInfo* cpuInfo, unsigned long type, const LinuxHWCapMapping* mapping, size_t length) noexcept { unsigned long mask = getauxval(type); + for (size_t i = 0; i < length; i++) if ((mask & mapping[i].hwcapMask) == mapping[i].hwcapMask) cpuInfo->addFeature(mapping[i].featureId); @@ -127,41 +139,46 @@ static void armDetectHWCaps(CpuInfo* cpuInfo, //! Detect ARM CPU features on Linux. //! //! The detection is based on `getauxval()`. -static void armDetectCpuInfoOnLinux(CpuInfo* cpuInfo) noexcept { +ASMJIT_FAVOR_SIZE static void armDetectCpuInfoOnLinux(CpuInfo* cpuInfo) noexcept { #if ASMJIT_ARCH_ARM32 - cpuInfo->setArch(kArchArm32); + armPopulateBaselineA32Features(cpuInfo); // `AT_HWCAP` provides ARMv7 (and less) related flags. static const LinuxHWCapMapping hwCapMapping[] = { - { /* HWCAP_VFPv3 */ (1 << 13), CpuInfo::kArmFeatureVFP3 }, - { /* HWCAP_VFPv4 */ (1 << 16), CpuInfo::kArmFeatureVFP4 }, - { /* HWCAP_IDIVA */ (3 << 17), CpuInfo::kArmFeatureIDIV }, - { /* HWCAP_VFPD32 */ (1 << 19), CpuInfo::kArmFeatureVFP_D32 }, - { /* HWCAP_NEON */ (1 << 12), CpuInfo::kArmFeatureNEON }, - { /* HWCAP_EDSP */ (1 << 7), CpuInfo::kArmFeatureDSP } + { /* HWCAP_VFP */ (1 << 6), CpuInfo::kArmFeatureVFPv2 }, + { /* HWCAP_EDSP */ (1 << 7), CpuInfo::kArmFeatureEDSP }, + { /* HWCAP_NEON */ (1 << 12), CpuInfo::kArmFeatureASIMD }, + { /* HWCAP_VFPv3 */ (1 << 13), CpuInfo::kArmFeatureVFPv3 }, + { /* HWCAP_VFPv4 */ (1 << 16), CpuInfo::kArmFeatureVFPv4 }, + { /* HWCAP_IDIVA */ (1 << 17), CpuInfo::kArmFeatureIDIVA }, + { /* HWCAP_IDIVT */ (1 << 18), CpuInfo::kArmFeatureIDIVT }, + { /* HWCAP_VFPD32 */ (1 << 19), CpuInfo::kArmFeatureVFP_D32 } }; armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping)); - // VFP3 implies VFP2. - if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFP3)) - cpuInfo->addFeature(CpuInfo::kArmFeatureVFP2); + // VFPv3 implies VFPv2. + if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv3)) { + cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2); + } - // VFP2 implies ARMv6. - if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFP2)) + // VFPv2 implies ARMv6. + if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv2)) { cpuInfo->addFeature(CpuInfo::kArmFeatureV6); + } - // VFP3 or NEON implies ARMv7. - if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFP3) || - cpuInfo->hasFeature(CpuInfo::kArmFeatureNEON)) + // VFPv3 or ASIMD implies ARMv7. + if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv3) || + cpuInfo->hasFeature(CpuInfo::kArmFeatureASIMD)) { cpuInfo->addFeature(CpuInfo::kArmFeatureV7); + } - // `AT_HWCAP2` provides ARMv8 related flags. + // `AT_HWCAP2` provides ARMv8+ related flags. static const LinuxHWCapMapping hwCap2Mapping[] = { { /* HWCAP2_AES */ (1 << 0), CpuInfo::kArmFeatureAES }, - { /* HWCAP2_CRC32 */ (1 << 4), CpuInfo::kArmFeatureCRC32 }, { /* HWCAP2_PMULL */ (1 << 1), CpuInfo::kArmFeaturePMULL }, { /* HWCAP2_SHA1 */ (1 << 2), CpuInfo::kArmFeatureSHA1 }, - { /* HWCAP2_SHA2 */ (1 << 3), CpuInfo::kArmFeatureSHA256 } + { /* HWCAP2_SHA2 */ (1 << 3), CpuInfo::kArmFeatureSHA256 }, + { /* HWCAP2_CRC32 */ (1 << 4), CpuInfo::kArmFeatureCRC32 } }; armDetectHWCaps(cpuInfo, AT_HWCAP2, hwCap2Mapping, ASMJIT_ARRAY_SIZE(hwCap2Mapping)); @@ -173,17 +190,16 @@ static void armDetectCpuInfoOnLinux(CpuInfo* cpuInfo) noexcept { cpuInfo->addFeature(CpuInfo::kArmFeatureV8); } #else - cpuInfo->setArch(kArchArm64); - armPopulateBaseline64Features(cpuInfo); + armPopulateBaselineA64Features(cpuInfo); - // `AT_HWCAP` provides ARMv8 related flags. + // `AT_HWCAP` provides ARMv8+ related flags. static const LinuxHWCapMapping hwCapMapping[] = { - { /* HWCAP_ASIMD */ (1 << 1), CpuInfo::kArmFeatureNEON }, + { /* HWCAP_ASIMD */ (1 << 1), CpuInfo::kArmFeatureASIMD }, { /* HWCAP_AES */ (1 << 3), CpuInfo::kArmFeatureAES }, { /* HWCAP_CRC32 */ (1 << 7), CpuInfo::kArmFeatureCRC32 }, { /* HWCAP_PMULL */ (1 << 4), CpuInfo::kArmFeaturePMULL }, { /* HWCAP_SHA1 */ (1 << 5), CpuInfo::kArmFeatureSHA1 }, - { /* HWCAP_SHA2 */ (1 << 6), CpuInfo::kArmFeatureSHA256 } + { /* HWCAP_SHA2 */ (1 << 6), CpuInfo::kArmFeatureSHA256 }, { /* HWCAP_ATOMICS */ (1 << 8), CpuInfo::kArmFeatureAtomics64 } }; armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping)); @@ -193,7 +209,7 @@ static void armDetectCpuInfoOnLinux(CpuInfo* cpuInfo) noexcept { } #endif // ASMJIT_OS_LINUX -static void armDetectCpuInfo(CpuInfo* cpuInfo) noexcept { +ASMJIT_FAVOR_SIZE static void armDetectCpuInfo(CpuInfo* cpuInfo) noexcept { #if ASMJIT_OS_WINDOWS armDetectCpuInfoOnWindows(cpuInfo); #elif ASMJIT_OS_LINUX @@ -205,7 +221,7 @@ static void armDetectCpuInfo(CpuInfo* cpuInfo) noexcept { #endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 // ============================================================================ -// [asmjit::CpuInfo - Detect X86 & X64] +// [asmjit::CpuInfo - Detect X86] // ============================================================================ #if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 @@ -228,7 +244,7 @@ struct XGetBVResult { //! \internal //! //! HACK: VS2008 or less, 64-bit mode - `__cpuidex` doesn't exist! However, -//! 64-bit calling convention specifies the first parameter to be passed in +//! 64-bit calling convention specifies the first parameter to be passed by //! ECX, so we may be lucky if compiler doesn't move the register, otherwise //! the result would be wrong. static void ASMJIT_NOINLINE void x86CallCpuIdWorkaround(uint32_t inEcx, uint32_t inEax, CpuIdResult* result) noexcept { @@ -291,7 +307,7 @@ static void ASMJIT_INLINE x86CallCpuId(CpuIdResult* result, uint32_t inEax, uint //! \internal //! //! Wrapper to call `xgetbv` instruction. -static void x86CallXGetBV(XGetBVResult* result, uint32_t inEcx) noexcept { +static ASMJIT_INLINE void x86CallXGetBV(XGetBVResult* result, uint32_t inEcx) noexcept { #if ASMJIT_CC_MSC_GE(16, 0, 40219) // 2010SP1+ uint64_t value = _xgetbv(inEcx); result->eax = static_cast(value & 0xFFFFFFFFU); @@ -315,7 +331,7 @@ static void x86CallXGetBV(XGetBVResult* result, uint32_t inEcx) noexcept { //! \internal //! //! Map a 12-byte vendor string returned by `cpuid` into a `CpuInfo::Vendor` ID. -static uint32_t x86GetCpuVendorID(const char* vendorString) noexcept { +static ASMJIT_INLINE uint32_t x86GetCpuVendorID(const char* vendorString) noexcept { struct VendorData { uint32_t id; char text[12]; @@ -372,14 +388,13 @@ L_Skip: d[0] = '\0'; } -static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept { +ASMJIT_FAVOR_SIZE static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept { uint32_t i, maxId; CpuIdResult regs; XGetBVResult xcr0 = { 0, 0 }; - // Architecture is known at compile-time. - cpuInfo->setArch(ASMJIT_ARCH_X86 ? kArchX86 : kArchX64); + cpuInfo->_archInfo.init(ArchInfo::kTypeHost); // -------------------------------------------------------------------------- // [CPUID EAX=0x0] @@ -443,13 +458,10 @@ static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept { .addFeature(CpuInfo::kX86FeatureSSE2); if (regs.edx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMT); - // AMD sets multi-threading ON if it has two or more cores. - if (cpuInfo->_hwThreadsCount == 1 && cpuInfo->_vendorId == CpuInfo::kVendorAMD && (regs.edx & 0x10000000U)) - cpuInfo->_hwThreadsCount = 2; - // Get the content of XCR0 if supported by CPU and enabled by OS. - if ((regs.ecx & 0x0C000000U) == 0x0C000000U) + if ((regs.ecx & 0x0C000000U) == 0x0C000000U) { x86CallXGetBV(&xcr0, 0); + } // Detect AVX+. if (regs.ecx & 0x10000000U) { @@ -492,8 +504,9 @@ static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept { if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeaturePREFETCHWT1); // Detect AVX2. - if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX)) + if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX)) { if (regs.ebx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX2); + } // Detect AVX-512+. if (regs.ebx & 0x00010000U) { @@ -502,16 +515,19 @@ static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept { // - XCR0[7:5] == 111b // Upper 256-bit of ZMM0-XMM15 and ZMM16-ZMM31 need to be enabled by the OS. if ((xcr0.eax & 0x000000E6U) == 0x000000E6U) { - cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512F); + cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_F); - if (regs.ebx & 0x00020000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512DQ); - if (regs.ebx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512IFMA); - if (regs.ebx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512PF); - if (regs.ebx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512ER); - if (regs.ebx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512CD); - if (regs.ebx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512BW); - if (regs.ebx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512VL); - if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512VBMI); + if (regs.ebx & 0x00020000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_DQ); + if (regs.ebx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_IFMA); + if (regs.ebx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_PFI); + if (regs.ebx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_ERI); + if (regs.ebx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_CDI); + if (regs.ebx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_BW); + if (regs.ebx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VL); + if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VBMI); + if (regs.ecx & 0x00004000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VPOPCNTDQ); + if (regs.edx & 0x00000004U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_4VNNIW); + if (regs.edx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_4FMAPS); } } } @@ -533,6 +549,9 @@ static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept { // [CPUID EAX=0x80000000...maxId] // -------------------------------------------------------------------------- + // The highest EAX that we understand. + uint32_t kHighestProcessedEAX = 0x80000008U; + // Several CPUID calls are required to get the whole branc string. It's easy // to copy one DWORD at a time instead of performing a byte copy. uint32_t* brand = reinterpret_cast(cpuInfo->_brandString); @@ -542,7 +561,7 @@ static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept { x86CallCpuId(®s, i); switch (i) { case 0x80000000U: - maxId = Utils::iMin(regs.eax, 0x80000004); + maxId = std::min(regs.eax, kHighestProcessedEAX); break; case 0x80000001U: @@ -573,14 +592,16 @@ static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept { *brand++ = regs.ebx; *brand++ = regs.ecx; *brand++ = regs.edx; + + // Go directly to the last one. + if (i == 0x80000004U) i = 0x80000008U - 1; break; - default: - // Stop the loop, additional features can be detected in the future. - i = maxId; + case 0x80000008U: + if (regs.ebx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLZERO); break; } - } while (i++ < maxId); + } while (++i <= maxId); // Simplify CPU brand string by removing unnecessary spaces. x86SimplifyBrandString(cpuInfo->_brandString); @@ -591,7 +612,7 @@ static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept { // [asmjit::CpuInfo - Detect - HWThreadsCount] // ============================================================================ -static uint32_t cpuDetectHWThreadsCount() noexcept { +static ASMJIT_INLINE uint32_t cpuDetectHWThreadsCount() noexcept { #if ASMJIT_OS_WINDOWS SYSTEM_INFO info; ::GetSystemInfo(&info); @@ -609,12 +630,9 @@ static uint32_t cpuDetectHWThreadsCount() noexcept { // [asmjit::CpuInfo - Detect] // ============================================================================ -void CpuInfo::detect() noexcept { +ASMJIT_FAVOR_SIZE void CpuInfo::detect() noexcept { reset(); - // Detect the number of hardware threads available. - _hwThreadsCount = cpuDetectHWThreadsCount(); - #if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 armDetectCpuInfo(this); #endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 @@ -622,6 +640,8 @@ void CpuInfo::detect() noexcept { #if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 x86DetectCpuInfo(this); #endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + + _hwThreadsCount = cpuDetectHWThreadsCount(); } // ============================================================================ @@ -640,4 +660,4 @@ const CpuInfo& CpuInfo::getHost() noexcept { } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/cpuinfo.h b/src/asmjit/base/cpuinfo.h index c0b38ab..0c4340a 100644 --- a/src/asmjit/base/cpuinfo.h +++ b/src/asmjit/base/cpuinfo.h @@ -1,4 +1,4 @@ -// [AsmJit] + // [AsmJit] // Complete x86/x64 JIT and Remote Assembler for C++. // // [License] @@ -9,10 +9,10 @@ #define _ASMJIT_BASE_CPUINFO_H // [Dependencies] -#include "../base/globals.h" +#include "../base/arch.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { @@ -25,11 +25,7 @@ namespace asmjit { //! CPU information. class CpuInfo { - public: - // -------------------------------------------------------------------------- - // [Vendor] - // -------------------------------------------------------------------------- - +public: //! CPU vendor ID. ASMJIT_ENUM(Vendor) { kVendorNone = 0, //!< Generic or unknown. @@ -38,38 +34,31 @@ class CpuInfo { kVendorVIA = 3 //!< VIA vendor. }; - // -------------------------------------------------------------------------- - // [ArmFeatures] - // -------------------------------------------------------------------------- - //! ARM/ARM64 CPU features. ASMJIT_ENUM(ArmFeatures) { kArmFeatureV6, //!< ARMv6 instruction set. kArmFeatureV7, //!< ARMv7 instruction set. kArmFeatureV8, //!< ARMv8 instruction set. - kArmFeatureTHUMB, //!< CPU provides THUMB v1 instruction set (ARM only). - kArmFeatureTHUMB2, //!< CPU provides THUMB v2 instruction set (ARM only). - kArmFeatureVFP2, //!< CPU provides VFPv2 instruction set. - kArmFeatureVFP3, //!< CPU provides VFPv3 instruction set. - kArmFeatureVFP4, //!< CPU provides VFPv4 instruction set. + kArmFeatureTHUMB, //!< CPU provides THUMB v1 instruction set (THUMB mode). + kArmFeatureTHUMB2, //!< CPU provides THUMB v2 instruction set (THUMB mode). + kArmFeatureVFPv2, //!< CPU provides VFPv2 instruction set. + kArmFeatureVFPv3, //!< CPU provides VFPv3 instruction set. + kArmFeatureVFPv4, //!< CPU provides VFPv4 instruction set. kArmFeatureVFP_D32, //!< CPU provides 32 VFP-D (64-bit) registers. - kArmFeatureNEON, //!< CPU provides NEON instruction set. - kArmFeatureDSP, //!< CPU provides DSP extensions. - kArmFeatureIDIV, //!< CPU provides hardware support for SDIV and UDIV. + kArmFeatureEDSP, //!< CPU provides EDSP extensions. + kArmFeatureASIMD, //!< CPU provides 'Advanced SIMD'. + kArmFeatureIDIVA, //!< CPU provides hardware SDIV and UDIV (ARM mode). + kArmFeatureIDIVT, //!< CPU provides hardware SDIV and UDIV (THUMB mode). kArmFeatureAES, //!< CPU provides AES instructions (ARM64 only). - kArmFeatureCRC32, //!< CPU provides CRC32 instructions (ARM64 only). + kArmFeatureCRC32, //!< CPU provides CRC32 instructions. kArmFeaturePMULL, //!< CPU provides PMULL instructions (ARM64 only). - kArmFeatureSHA1, //!< CPU provides SHA1 instructions (ARM64 only). - kArmFeatureSHA256, //!< CPU provides SHA256 instructions (ARM64 only). + kArmFeatureSHA1, //!< CPU provides SHA1 instructions. + kArmFeatureSHA256, //!< CPU provides SHA256 instructions. kArmFeatureAtomics64, //!< CPU provides 64-bit load/store atomics (ARM64 only). kArmFeaturesCount //!< Count of ARM/ARM64 CPU features. }; - // -------------------------------------------------------------------------- - // [X86Features] - // -------------------------------------------------------------------------- - //! X86/X64 CPU features. ASMJIT_ENUM(X86Features) { kX86FeatureNX = 0, //!< CPU has Not-Execute-Bit. @@ -82,6 +71,7 @@ class CpuInfo { kX86FeatureCLFLUSH, //!< CPU has CLFUSH. kX86FeatureCLFLUSH_OPT, //!< CPU has CLFUSH (optimized). kX86FeatureCLWB, //!< CPU has CLWB. + kX86FeatureCLZERO, //!< CPU has CLZERO. kX86FeaturePCOMMIT, //!< CPU has PCOMMIT. kX86FeaturePREFETCH, //!< CPU has PREFETCH. kX86FeaturePREFETCHWT1, //!< CPU has PREFETCHWT1. @@ -90,8 +80,8 @@ class CpuInfo { kX86FeatureFXSR_OPT, //!< CPU has FXSAVE/FXRSTOR (optimized). kX86FeatureMMX, //!< CPU has MMX. kX86FeatureMMX2, //!< CPU has extended MMX. - kX86Feature3DNOW, //!< CPU has 3dNow! - kX86Feature3DNOW2, //!< CPU has enhanced 3dNow! + kX86Feature3DNOW, //!< CPU has 3DNOW! + kX86Feature3DNOW2, //!< CPU has enhanced 3DNOW! kX86FeatureSSE, //!< CPU has SSE. kX86FeatureSSE2, //!< CPU has SSE2. kX86FeatureSSE3, //!< CPU has SSE3. @@ -128,23 +118,22 @@ class CpuInfo { kX86FeatureRTM, //!< CPU has RTM. kX86FeatureERMS, //!< CPU has ERMS (enhanced REP MOVSB/STOSB). kX86FeatureFSGSBASE, //!< CPU has FSGSBASE. - kX86FeatureAVX512F, //!< CPU has AVX-512F (foundation). - kX86FeatureAVX512CD, //!< CPU has AVX-512CD (conflict detection). - kX86FeatureAVX512PF, //!< CPU has AVX-512PF (prefetch instructions). - kX86FeatureAVX512ER, //!< CPU has AVX-512ER (exponential and reciprocal instructions). - kX86FeatureAVX512DQ, //!< CPU has AVX-512DQ (DWORD/QWORD). - kX86FeatureAVX512BW, //!< CPU has AVX-512BW (BYTE/WORD). - kX86FeatureAVX512VL, //!< CPU has AVX VL (vector length extensions). - kX86FeatureAVX512IFMA, //!< CPU has AVX IFMA (integer fused multiply add using 52-bit precision). - kX86FeatureAVX512VBMI, //!< CPU has AVX VBMI (vector byte manipulation instructions). + kX86FeatureAVX512_F, //!< CPU has AVX512-F (foundation). + kX86FeatureAVX512_CDI, //!< CPU has AVX512-CDI (conflict detection). + kX86FeatureAVX512_PFI, //!< CPU has AVX512-PFI (prefetch instructions). + kX86FeatureAVX512_ERI, //!< CPU has AVX512-ERI (exponential and reciprocal). + kX86FeatureAVX512_DQ, //!< CPU has AVX512-DQ (DWORD/QWORD). + kX86FeatureAVX512_BW, //!< CPU has AVX512-BW (BYTE/WORD). + kX86FeatureAVX512_VL, //!< CPU has AVX512-VL (vector length extensions). + kX86FeatureAVX512_IFMA, //!< CPU has AVX512-IFMA (integer fused-multiply-add using 52-bit precision). + kX86FeatureAVX512_VBMI, //!< CPU has AVX512-VBMI (vector byte manipulation). + kX86FeatureAVX512_VPOPCNTDQ, //!< CPU has AVX512-VPOPCNTDQ (VPOPCNT[D|Q] instructions). + kX86FeatureAVX512_4VNNIW, //!< CPU has AVX512-VNNIW (vector NN instructions word variable precision). + kX86FeatureAVX512_4FMAPS, //!< CPU has AVX512-FMAPS (FMA packed single). kX86FeaturesCount //!< Count of X86/X64 CPU features. }; - // -------------------------------------------------------------------------- - // [Other] - // -------------------------------------------------------------------------- - //! \internal enum { kFeaturesPerUInt32 = static_cast(sizeof(uint32_t)) * 8 @@ -175,9 +164,14 @@ class CpuInfo { ASMJIT_INLINE CpuInfo() noexcept { reset(); } // -------------------------------------------------------------------------- - // [Reset] + // [Init / Reset] // -------------------------------------------------------------------------- + //! Initialize CpuInfo to the given architecture, see \ArchInfo. + ASMJIT_INLINE void initArch(uint32_t archType, uint32_t archMode = 0) noexcept { + _archInfo.init(archType, archMode); + } + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(CpuInfo)); } // -------------------------------------------------------------------------- @@ -190,10 +184,12 @@ class CpuInfo { // [Accessors] // -------------------------------------------------------------------------- - //! Get CPU architecture, see \Arch. - ASMJIT_INLINE uint32_t getArch() const noexcept { return _arch; } - //! Set CPU architecture, see \Arch. - ASMJIT_INLINE void setArch(uint32_t arch) noexcept { _arch = static_cast(arch); } + //! Get generic architecture information. + ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _archInfo; } + //! Get CPU architecture type, see \ArchInfo::Type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archInfo.getType(); } + //! Get CPU architecture sub-type, see \ArchInfo::SubType. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _archInfo.getSubType(); } //! Get CPU vendor string. ASMJIT_INLINE const char* getVendorString() const noexcept { return _vendorString; } @@ -268,35 +264,21 @@ class CpuInfo { // -------------------------------------------------------------------------- //! Get the host CPU information. - static ASMJIT_API const CpuInfo& getHost() noexcept; + ASMJIT_API static const CpuInfo& getHost() noexcept; // -------------------------------------------------------------------------- // [Members] // -------------------------------------------------------------------------- - //! CPU vendor string. - char _vendorString[16]; - //! CPU brand string. - char _brandString[64]; - - //! CPU architecture, see \ref Arch. - uint8_t _arch; - //! \internal - uint8_t _reserved[3]; - //! CPU vendor id, see \ref CpuVendor. - uint32_t _vendorId; - //! CPU family ID. - uint32_t _family; - //! CPU model ID. - uint32_t _model; - //! CPU stepping. - uint32_t _stepping; - - //! Number of hardware threads. - uint32_t _hwThreadsCount; - - //! CPU features (bit-array). - uint32_t _features[8]; + ArchInfo _archInfo; //!< CPU architecture information. + char _vendorString[16]; //!< CPU vendor string. + char _brandString[64]; //!< CPU brand string. + uint32_t _vendorId; //!< CPU vendor id, see \ref Vendor. + uint32_t _family; //!< CPU family ID. + uint32_t _model; //!< CPU model ID. + uint32_t _stepping; //!< CPU stepping. + uint32_t _hwThreadsCount; //!< Number of hardware threads. + uint32_t _features[8]; //!< CPU features (bit-array). // Architecture specific data. union { @@ -310,7 +292,7 @@ class CpuInfo { } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" // [Guard] #endif // _ASMJIT_BASE_CPUINFO_H diff --git a/src/asmjit/base/func.cpp b/src/asmjit/base/func.cpp new file mode 100644 index 0000000..b9fc9b7 --- /dev/null +++ b/src/asmjit/base/func.cpp @@ -0,0 +1,186 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/arch.h" +#include "../base/func.h" + +#if defined(ASMJIT_BUILD_X86) +#include "../x86/x86internal_p.h" +#include "../x86/x86operand.h" +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) +#include "../arm/arminternal_p.h" +#include "../arm/armoperand.h" +#endif // ASMJIT_BUILD_ARM + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::CallConv - Init / Reset] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId) noexcept { + reset(); + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::initCallConv(*this, ccId); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::initCallConv(*this, ccId); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArgument); +} + +// ============================================================================ +// [asmjit::FuncDetail - Init / Reset] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& sign) { + uint32_t ccId = sign.getCallConv(); + CallConv& cc = _callConv; + + uint32_t argCount = sign.getArgCount(); + if (ASMJIT_UNLIKELY(argCount > kFuncArgCount)) + return DebugUtils::errored(kErrorInvalidArgument); + + ASMJIT_PROPAGATE(cc.init(ccId)); + + uint32_t gpSize = (cc.getArchType() == ArchInfo::kTypeX86) ? 4 : 8; + uint32_t deabstractDelta = TypeId::deabstractDeltaOfSize(gpSize); + + const uint8_t* args = sign.getArgs(); + for (uint32_t i = 0; i < static_cast(argCount); i++) { + Value& arg = _args[i]; + arg.initTypeId(TypeId::deabstract(args[i], deabstractDelta)); + } + _argCount = static_cast(argCount); + + uint32_t ret = sign.getRet(); + if (ret != TypeId::kVoid) { + _rets[0].initTypeId(TypeId::deabstract(ret, deabstractDelta)); + _retCount = 1; + } + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::initFuncDetail(*this, sign, gpSize); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::initFuncDetail(*this, sign, gpSize); +#endif // ASMJIT_BUILD_ARM + + // We should never bubble here as if `cc.init()` succeeded then there has to + // be an implementation for the current architecture. However, stay safe. + return DebugUtils::errored(kErrorInvalidArgument); +} + +// ============================================================================ +// [asmjit::FuncFrameLayout - Init / Reset] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncFrameLayout::init(const FuncDetail& func, const FuncFrameInfo& ffi) noexcept { + uint32_t ccId = func.getCallConv().getId(); + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::initFrameLayout(*this, func, ffi); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::initFrameLayout(*this, func, ffi); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArgument); +} + +// ============================================================================ +// [asmjit::FuncArgsMapper] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncArgsMapper::updateFrameInfo(FuncFrameInfo& ffi) const noexcept { + const FuncDetail* func = getFuncDetail(); + if (!func) return DebugUtils::errored(kErrorInvalidState); + + uint32_t ccId = func->getCallConv().getId(); + +#if defined(ASMJIT_BUILD_X86) + if (CallConv::isX86Family(ccId)) + return X86Internal::argsToFrameInfo(*this, ffi); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (CallConv::isArmFamily(ccId)) + return ArmInternal::argsToFrameInfo(*this, ffi); +#endif // ASMJIT_BUILD_X86 + + return DebugUtils::errored(kErrorInvalidArch); +} + +// ============================================================================ +// [asmjit::FuncUtils] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error FuncUtils::emitProlog(CodeEmitter* emitter, const FuncFrameLayout& layout) { +#if defined(ASMJIT_BUILD_X86) + if (emitter->getArchInfo().isX86Family()) + return X86Internal::emitProlog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (emitter->getArchInfo().isArmFamily()) + return ArmInternal::emitProlog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArch); +} + +ASMJIT_FAVOR_SIZE Error FuncUtils::emitEpilog(CodeEmitter* emitter, const FuncFrameLayout& layout) { +#if defined(ASMJIT_BUILD_X86) + if (emitter->getArchInfo().isX86Family()) + return X86Internal::emitEpilog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (emitter->getArchInfo().isArmFamily()) + return ArmInternal::emitEpilog(static_cast(emitter), layout); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArch); +} + +ASMJIT_FAVOR_SIZE Error FuncUtils::allocArgs(CodeEmitter* emitter, const FuncFrameLayout& layout, const FuncArgsMapper& args) { +#if defined(ASMJIT_BUILD_X86) + if (emitter->getArchInfo().isX86Family()) + return X86Internal::allocArgs(static_cast(emitter), layout, args); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + if (emitter->getArchInfo().isArmFamily()) + return ArmInternal::allocArgs(static_cast(emitter), layout, args); +#endif // ASMJIT_BUILD_ARM + + return DebugUtils::errored(kErrorInvalidArch); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/func.h b/src/asmjit/base/func.h new file mode 100644 index 0000000..a75d16b --- /dev/null +++ b/src/asmjit/base/func.h @@ -0,0 +1,1256 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_FUNC_H +#define _ASMJIT_BASE_FUNC_H + +#include "../asmjit_build.h" + +// [Dependencies] +#include "../base/arch.h" +#include "../base/operand.h" +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [Forward Declarations] +// ============================================================================ + +class CodeEmitter; + +// ============================================================================ +// [asmjit::CallConv] +// ============================================================================ + +//! Function calling convention. +//! +//! Function calling convention is a scheme that defines how function parameters +//! are passed and how function returns its result. AsmJit defines a variety of +//! architecture and OS specific calling conventions and also provides a compile +//! time detection to make JIT code-generation easier. +struct CallConv { + //! Calling convention id. + ASMJIT_ENUM(Id) { + //! None or invalid (can't be used). + kIdNone = 0, + + // ------------------------------------------------------------------------ + // [X86] + // ------------------------------------------------------------------------ + + //! X86 `__cdecl` calling convention (used by C runtime and libraries). + kIdX86CDecl = 1, + //! X86 `__stdcall` calling convention (used mostly by WinAPI). + kIdX86StdCall = 2, + //! X86 `__thiscall` calling convention (MSVC/Intel). + kIdX86MsThisCall = 3, + //! X86 `__fastcall` convention (MSVC/Intel). + kIdX86MsFastCall = 4, + //! X86 `__fastcall` convention (GCC and Clang). + kIdX86GccFastCall = 5, + //! X86 `regparm(1)` convention (GCC and Clang). + kIdX86GccRegParm1 = 6, + //! X86 `regparm(2)` convention (GCC and Clang). + kIdX86GccRegParm2 = 7, + //! X86 `regparm(3)` convention (GCC and Clang). + kIdX86GccRegParm3 = 8, + + //! X64 calling convention defined by WIN64-ABI. + //! + //! Links: + //! * . + kIdX86Win64 = 16, + //! X64 calling convention used by Unix platforms (SYSV/AMD64-ABI). + kIdX86SysV64 = 17, + + // ------------------------------------------------------------------------ + // [ARM] + // ------------------------------------------------------------------------ + + //! Legacy calling convention, floating point arguments are passed via GP registers. + kIdArm32SoftFP = 32, + //! Modern calling convention, uses VFP registers to pass floating point arguments. + kIdArm32HardFP = 33, + + // ------------------------------------------------------------------------ + // [Internal] + // ------------------------------------------------------------------------ + + _kIdX86Start = 1, //!< \internal + _kIdX86End = 8, //!< \internal + + _kIdX64Start = 16, //!< \internal + _kIdX64End = 17, //!< \internal + + _kIdArmStart = 32, //!< \internal + _kIdArmEnd = 33, //!< \internal + + // ------------------------------------------------------------------------ + // [Host] + // ------------------------------------------------------------------------ + +#if defined(ASMJIT_DOCGEN) + //! Default calling convention based on the current C++ compiler's settings. + //! + //! NOTE: This should be always the same as `kIdHostCDecl`, but some + //! compilers allow to override the default calling convention. Overriding + //! is not detected at the moment. + kIdHost = DETECTED_AT_COMPILE_TIME, + + //! Default CDECL calling convention based on the current C++ compiler's settings. + kIdHostCDecl = DETECTED_AT_COMPILE_TIME, + + //! Default STDCALL calling convention based on the current C++ compiler's settings. + //! + //! NOTE: If not defined by the host then it's the same as `kIdHostCDecl`. + kIdHostStdCall = DETECTED_AT_COMPILE_TIME, + + //! Compatibility for `__fastcall` calling convention. + //! + //! NOTE: If not defined by the host then it's the same as `kIdHostCDecl`. + kIdHostFastCall = DETECTED_AT_COMPILE_TIME +#elif ASMJIT_ARCH_X86 + kIdHost = kIdX86CDecl, + kIdHostCDecl = kIdX86CDecl, + kIdHostStdCall = kIdX86StdCall, + kIdHostFastCall = ASMJIT_CC_MSC ? kIdX86MsFastCall : + ASMJIT_CC_GCC ? kIdX86GccFastCall : + ASMJIT_CC_CLANG ? kIdX86GccFastCall : kIdNone +#elif ASMJIT_ARCH_X64 + kIdHost = ASMJIT_OS_WINDOWS ? kIdX86Win64 : kIdX86SysV64, + kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host. + kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host. + kIdHostFastCall = kIdHost // Doesn't exist, redirected to host. +#elif ASMJIT_ARCH_ARM32 +# if defined(__SOFTFP__) + kIdHost = kIdArm32SoftFP, +# else + kIdHost = kIdArm32HardFP, +# endif + // These don't exist on ARM. + kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host. + kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host. + kIdHostFastCall = kIdHost // Doesn't exist, redirected to host. +#else +# error "[asmjit] Couldn't determine the target's calling convention." +#endif + }; + + //! Calling convention algorithm. + //! + //! This is AsmJit specific. It basically describes how should AsmJit convert + //! the function arguments defined by `FuncSignature` into register ids or + //! stack offsets. The default algorithm is a standard algorithm that assigns + //! registers first, and then assigns stack. The Win64 algorithm does register + //! shadowing as defined by `WIN64` calling convention - it applies to 64-bit + //! calling conventions only. + ASMJIT_ENUM(Algorithm) { + kAlgorithmDefault = 0, //!< Default algorithm (cross-platform). + kAlgorithmWin64 = 1 //!< WIN64 specific algorithm. + }; + + //! Calling convention flags. + ASMJIT_ENUM(Flags) { + kFlagCalleePopsStack = 0x01, //!< Callee is responsible for cleaning up the stack. + kFlagPassFloatsByVec = 0x02, //!< Pass F32 and F64 arguments by VEC128 register. + kFlagVectorCall = 0x04, //!< This is a '__vectorcall' calling convention. + kFlagIndirectVecArgs = 0x08 //!< Pass vector arguments indirectly (as a pointer). + }; + + //! Internal limits of AsmJit/CallConv. + ASMJIT_ENUM(Limits) { + kMaxVRegKinds = Globals::kMaxVRegKinds, + kNumRegArgsPerKind = 8 + }; + + //! Passed registers' order. + struct RegOrder { + uint8_t id[kNumRegArgsPerKind]; //!< Passed registers, ordered. + }; + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE bool isX86Family(uint32_t ccId) noexcept { return ccId >= _kIdX86Start && ccId <= _kIdX64End; } + static ASMJIT_INLINE bool isArmFamily(uint32_t ccId) noexcept { return ccId >= _kIdArmStart && ccId <= _kIdArmEnd; } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_API Error init(uint32_t ccId) noexcept; + + ASMJIT_INLINE void reset() noexcept { + ::memset(this, 0, sizeof(*this)); + ::memset(_passedOrder, 0xFF, sizeof(_passedOrder)); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get calling convention id, see \ref Id. + ASMJIT_INLINE uint32_t getId() const noexcept { return _id; } + //! Set calling convention id, see \ref Id. + ASMJIT_INLINE void setId(uint32_t id) noexcept { _id = static_cast(id); } + + //! Get architecture type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archType; } + //! Set architecture type. + ASMJIT_INLINE void setArchType(uint32_t archType) noexcept { _archType = static_cast(archType); } + + //! Get calling convention algorithm, see \ref Algorithm. + ASMJIT_INLINE uint32_t getAlgorithm() const noexcept { return _algorithm; } + //! Set calling convention algorithm, see \ref Algorithm. + ASMJIT_INLINE void setAlgorithm(uint32_t algorithm) noexcept { _algorithm = static_cast(algorithm); } + + //! Get if the calling convention has the given `flag` set. + ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; } + //! Get calling convention flags, see \ref Flags. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } + //! Add calling convention flags, see \ref Flags. + ASMJIT_INLINE void setFlags(uint32_t flag) noexcept { _flags = flag; }; + //! Add calling convention flags, see \ref Flags. + ASMJIT_INLINE void addFlags(uint32_t flag) noexcept { _flags |= flag; }; + + //! Get a natural stack alignment. + ASMJIT_INLINE uint32_t getNaturalStackAlignment() const noexcept { return _naturalStackAlignment; } + + //! Set a natural stack alignment. + //! + //! This function can be used to override the default stack alignment in case + //! that you know that it's alignment is different. For example it allows to + //! implement custom calling conventions that guarantee higher stack alignment. + ASMJIT_INLINE void setNaturalStackAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _naturalStackAlignment = static_cast(value); + } + + //! Get if this calling convention specifies 'SpillZone'. + ASMJIT_INLINE bool hasSpillZone() const noexcept { return _spillZoneSize != 0; } + //! Get size of 'SpillZone'. + ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _spillZoneSize; } + //! Set size of 'SpillZone'. + ASMJIT_INLINE void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = static_cast(size); } + + //! Get if this calling convention specifies 'RedZone'. + ASMJIT_INLINE bool hasRedZone() const noexcept { return _redZoneSize != 0; } + //! Get size of 'RedZone'. + ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _redZoneSize; } + //! Set size of 'RedZone'. + ASMJIT_INLINE void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = static_cast(size); } + + ASMJIT_INLINE const uint8_t* getPassedOrder(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _passedOrder[kind].id; + } + + ASMJIT_INLINE uint32_t getPassedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _passedRegs[kind]; + } + + ASMJIT_INLINE void _setPassedPacked(uint32_t kind, uint32_t p0, uint32_t p1) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + + reinterpret_cast(_passedOrder[kind].id)[0] = p0; + reinterpret_cast(_passedOrder[kind].id)[1] = p1; + } + + ASMJIT_INLINE void setPassedToNone(uint32_t kind) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + + _setPassedPacked(kind, ASMJIT_PACK32_4x8(0xFF, 0xFF, 0xFF, 0xFF), + ASMJIT_PACK32_4x8(0xFF, 0xFF, 0xFF, 0xFF)); + _passedRegs[kind] = 0; + } + + ASMJIT_INLINE void setPassedOrder(uint32_t kind, uint32_t a0, uint32_t a1 = 0xFF, uint32_t a2 = 0xFF, uint32_t a3 = 0xFF, uint32_t a4 = 0xFF, uint32_t a5 = 0xFF, uint32_t a6 = 0xFF, uint32_t a7 = 0xFF) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + + _setPassedPacked(kind, ASMJIT_PACK32_4x8(a0, a1, a2, a3), + ASMJIT_PACK32_4x8(a4, a5, a6, a7)); + + // NOTE: This should always be called with all arguments known at compile + // time, so even if it looks scary it should be translated to a single + // instruction. + _passedRegs[kind] = (a0 != 0xFF ? 1U << a0 : 0U) | + (a1 != 0xFF ? 1U << a1 : 0U) | + (a2 != 0xFF ? 1U << a2 : 0U) | + (a3 != 0xFF ? 1U << a3 : 0U) | + (a4 != 0xFF ? 1U << a4 : 0U) | + (a5 != 0xFF ? 1U << a5 : 0U) | + (a6 != 0xFF ? 1U << a6 : 0U) | + (a7 != 0xFF ? 1U << a7 : 0U) ; + } + + ASMJIT_INLINE uint32_t getPreservedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _preservedRegs[kind]; + } + + + ASMJIT_INLINE void setPreservedRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + _preservedRegs[kind] = regs; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _id; //!< Calling convention id, see \ref Id. + uint8_t _archType; //!< Architecture type (see \ref ArchInfo::Type). + uint8_t _algorithm; //!< Calling convention algorithm. + uint8_t _flags; //!< Calling convention flags. + + uint8_t _naturalStackAlignment; //!< Natural stack alignment as defined by OS/ABI. + uint8_t _spillZoneSize; //!< Spill zone size (WIN64 == 32 bytes). + uint16_t _redZoneSize; //!< Red zone size (AMD64 == 128 bytes). + + RegOrder _passedOrder[kMaxVRegKinds]; //!< Passed registers' order, per kind. + uint32_t _passedRegs[kMaxVRegKinds]; //!< Mask of all passed registers, per kind. + uint32_t _preservedRegs[kMaxVRegKinds];//!< Mask of all preserved registers, per kind. +}; + +// ============================================================================ +// [asmjit::FuncArgIndex] +// ============================================================================ + +//! Function argument index (lo/hi). +ASMJIT_ENUM(FuncArgIndex) { + //! Maximum number of function arguments supported by AsmJit. + kFuncArgCount = 16, + //! Extended maximum number of arguments (used internally). + kFuncArgCountLoHi = kFuncArgCount * 2, + + //! Index to the LO part of function argument (default). + //! + //! This value is typically omitted and added only if there is HI argument + //! accessed. + kFuncArgLo = 0, + + //! Index to the HI part of function argument. + //! + //! HI part of function argument depends on target architecture. On x86 it's + //! typically used to transfer 64-bit integers (they form a pair of 32-bit + //! integers). + kFuncArgHi = kFuncArgCount +}; + +// ============================================================================ +// [asmjit::FuncSignature] +// ============================================================================ + +//! Function signature. +//! +//! Contains information about function return type, count of arguments and +//! their TypeIds. Function signature is a low level structure which doesn't +//! contain platform specific or calling convention specific information. +struct FuncSignature { + enum { + //! Doesn't have variable number of arguments (`...`). + kNoVarArgs = 0xFF + }; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Initialize the function signature. + ASMJIT_INLINE void init(uint32_t ccId, uint32_t ret, const uint8_t* args, uint32_t argCount) noexcept { + ASMJIT_ASSERT(ccId <= 0xFF); + ASMJIT_ASSERT(argCount <= 0xFF); + + _callConv = static_cast(ccId); + _argCount = static_cast(argCount); + _vaIndex = kNoVarArgs; + _ret = ret; + _args = args; + } + + ASMJIT_INLINE void reset() noexcept { + memset(this, 0, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the function's calling convention. + ASMJIT_INLINE uint32_t getCallConv() const noexcept { return _callConv; } + + //! Get if the function has variable number of arguments (...). + ASMJIT_INLINE bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; } + //! Get the variable arguments (...) index, `kNoVarArgs` if none. + ASMJIT_INLINE uint32_t getVAIndex() const noexcept { return _vaIndex; } + + //! Get the number of function arguments. + ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _argCount; } + + ASMJIT_INLINE bool hasRet() const noexcept { return _ret != TypeId::kVoid; } + //! Get the return value type. + ASMJIT_INLINE uint32_t getRet() const noexcept { return _ret; } + + //! Get the type of the argument at index `i`. + ASMJIT_INLINE uint32_t getArg(uint32_t i) const noexcept { + ASMJIT_ASSERT(i < _argCount); + return _args[i]; + } + //! Get the array of function arguments' types. + ASMJIT_INLINE const uint8_t* getArgs() const noexcept { return _args; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _callConv; //!< Calling convention id. + uint8_t _argCount; //!< Count of arguments. + uint8_t _vaIndex; //!< Index to a first vararg or `kNoVarArgs`. + uint8_t _ret; //!< TypeId of a return value. + const uint8_t* _args; //!< TypeIds of function arguments. +}; + +// ============================================================================ +// [asmjit::FuncSignatureT] +// ============================================================================ + +//! \internal +#define T(TYPE) TypeIdOf::kTypeId + +//! Static function signature (no arguments). +template +class FuncSignature0 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature0(uint32_t ccId = CallConv::kIdHost) noexcept { + init(ccId, T(RET), nullptr, 0); + } +}; + +//! Static function signature (1 argument). +template +class FuncSignature1 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature1(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (2 arguments). +template +class FuncSignature2 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature2(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (3 arguments). +template +class FuncSignature3 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature3(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (4 arguments). +template +class FuncSignature4 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature4(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (5 arguments). +template +class FuncSignature5 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature5(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (6 arguments). +template +class FuncSignature6 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature6(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (7 arguments). +template +class FuncSignature7 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature7(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (8 arguments). +template +class FuncSignature8 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature8(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (9 arguments). +template +class FuncSignature9 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature9(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7), T(A8) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +//! Static function signature (10 arguments). +template +class FuncSignature10 : public FuncSignature { +public: + ASMJIT_INLINE FuncSignature10(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7), T(A8), T(A9) }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; + +#if ASMJIT_CC_HAS_VARIADIC_TEMPLATES +//! Static function signature (variadic). +template +class FuncSignatureT : public FuncSignature { +public: + ASMJIT_INLINE FuncSignatureT(uint32_t ccId = CallConv::kIdHost) noexcept { + static const uint8_t args[] = { (T(ARGS))... }; + init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args)); + } +}; +#endif // ASMJIT_CC_HAS_VARIADIC_TEMPLATES + +#undef T + +// ============================================================================ +// [asmjit::FuncSignatureX] +// ============================================================================ + +//! Dynamic function signature. +class FuncSignatureX : public FuncSignature { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE FuncSignatureX(uint32_t ccId = CallConv::kIdHost) noexcept { + init(ccId, TypeId::kVoid, _builderArgList, 0); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void setCallConv(uint32_t ccId) noexcept { + ASMJIT_ASSERT(ccId <= 0xFF); + _callConv = static_cast(ccId); + } + + //! Set the return type to `retType`. + ASMJIT_INLINE void setRet(uint32_t retType) noexcept { _ret = retType; } + //! Set the return type based on `T`. + template + ASMJIT_INLINE void setRetT() noexcept { setRet(TypeIdOf::kTypeId); } + + //! Set the argument at index `i` to the `type` + ASMJIT_INLINE void setArg(uint32_t i, uint32_t type) noexcept { + ASMJIT_ASSERT(i < _argCount); + _builderArgList[i] = type; + } + //! Set the argument at index `i` to the type based on `T`. + template + ASMJIT_INLINE void setArgT(uint32_t i) noexcept { setArg(i, TypeIdOf::kTypeId); } + + //! Append an argument of `type` to the function prototype. + ASMJIT_INLINE void addArg(uint32_t type) noexcept { + ASMJIT_ASSERT(_argCount < kFuncArgCount); + _builderArgList[_argCount++] = static_cast(type); + } + //! Append an argument of type based on `T` to the function prototype. + template + ASMJIT_INLINE void addArgT() noexcept { addArg(TypeIdOf::kTypeId); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _builderArgList[kFuncArgCount]; +}; + +// ============================================================================ +// [asmjit::FuncDetail] +// ============================================================================ + +//! Function detail - CallConv and expanded FuncSignature. +//! +//! Function details is architecture and OS dependent representation of function. +//! It contains calling convention and expanded function signature so all +//! arguments have assigned either register type & id or stack address. +class FuncDetail { +public: + ASMJIT_ENUM(Limits) { + kMaxVRegKinds = Globals::kMaxVRegKinds + }; + + //! Argument or return value as defined by `FuncSignature`, but with register + //! or stack address (and other metadata) assigned. + struct Value { + ASMJIT_ENUM(Parts) { + kTypeIdShift = 24, + kTypeIdMask = 0xFF000000U, + + kRegTypeShift = 8, + kRegTypeMask = 0x0000FF00U, + + kRegIdShift = 0, + kRegIdMask = 0x000000FFU, + + kStackOffsetShift = 0, + kStackOffsetMask = 0x0000FFFFU, + + kIsByReg = 0x00010000U, + kIsByStack = 0x00020000U, + kIsIndirect = 0x00040000U + }; + + //! Get if this value is initialized (i.e. contains a valid data). + ASMJIT_INLINE bool isInitialized() const noexcept { return _value != 0; } + //! Initialize this in/out by a given `typeId`. + ASMJIT_INLINE void initTypeId(uint32_t typeId) noexcept { _value = typeId << kTypeIdShift; } + //! Initialize this in/out by a given `typeId`, `regType`, and `regId`. + ASMJIT_INLINE void initReg(uint32_t typeId, uint32_t regType, uint32_t regId) noexcept { + _value = (typeId << kTypeIdShift) | (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsByReg; + } + //! Initialize this in/out by a given `typeId` and `offset`. + ASMJIT_INLINE void initStack(uint32_t typeId, uint32_t stackOffset) noexcept { + _value = (typeId << kTypeIdShift) | (stackOffset << kStackOffsetShift) | kIsByStack; + } + //! Reset the value to its uninitialized and unassigned state. + ASMJIT_INLINE void reset() noexcept { _value = 0; } + + ASMJIT_INLINE void assignToReg(uint32_t regType, uint32_t regId) noexcept { + ASMJIT_ASSERT(!isAssigned()); + _value |= (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsByReg; + } + + ASMJIT_INLINE void assignToStack(int32_t offset) noexcept { + ASMJIT_ASSERT(!isAssigned()); + _value |= (offset << kStackOffsetShift) | kIsByStack; + } + + //! Get if this argument is passed by register. + ASMJIT_INLINE bool byReg() const noexcept { return (_value & kIsByReg) != 0; } + //! Get if this argument is passed by stack. + ASMJIT_INLINE bool byStack() const noexcept { return (_value & kIsByStack) != 0; } + //! Get if this argument is passed by register. + ASMJIT_INLINE bool isAssigned() const noexcept { return (_value & (kIsByReg | kIsByStack)) != 0; } + //! Get if this argument is passed through a pointer (used by WIN64 to pass XMM|YMM|ZMM). + ASMJIT_INLINE bool isIndirect() const noexcept { return (_value & kIsIndirect) != 0; } + + //! Get virtual type of this argument or return value. + ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _value >> kTypeIdShift; } + //! Get a register type of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegType() const noexcept { return (_value & kRegTypeMask) >> kRegTypeShift; } + //! Get a physical id of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegId() const noexcept { return (_value & kRegIdMask) >> kRegIdShift; } + //! Get a stack offset of this argument (always positive). + ASMJIT_INLINE int32_t getStackOffset() const noexcept { return (_value & kStackOffsetMask) >> kStackOffsetShift; } + + uint32_t _value; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE FuncDetail() noexcept { reset(); } + ASMJIT_INLINE FuncDetail(const FuncDetail& other) noexcept { + ::memcpy(this, &other, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Initialize this `FuncDetail` to the given signature. + ASMJIT_API Error init(const FuncSignature& sign); + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); } + + // -------------------------------------------------------------------------- + // [Accessors - Calling Convention] + // -------------------------------------------------------------------------- + + //! Get the function's calling convention, see `CallConv`. + ASMJIT_INLINE const CallConv& getCallConv() const noexcept { return _callConv; } + + //! Get CallConv flags, see \ref CallConv::Flags. + ASMJIT_INLINE uint32_t getFlags() const noexcept { return _callConv.getFlags(); } + //! Check if a CallConv `flag` is set, see \ref CallConv::Flags. + ASMJIT_INLINE bool hasFlag(uint32_t ccFlag) const noexcept { return _callConv.hasFlag(ccFlag); } + + // -------------------------------------------------------------------------- + // [Accessors - Arguments and Return] + // -------------------------------------------------------------------------- + + //! Get count of function return values. + ASMJIT_INLINE uint32_t getRetCount() const noexcept { return _retCount; } + //! Get the number of function arguments. + ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _argCount; } + + //! Get whether the function has a return value. + ASMJIT_INLINE bool hasRet() const noexcept { return _retCount != 0; } + //! Get function return value. + ASMJIT_INLINE Value& getRet(size_t index = 0) noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets)); + return _rets[index]; + } + //! Get function return value (const). + ASMJIT_INLINE const Value& getRet(size_t index = 0) const noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets)); + return _rets[index]; + } + + //! Get function arguments array. + ASMJIT_INLINE Value* getArgs() noexcept { return _args; } + //! Get function arguments array (const). + ASMJIT_INLINE const Value* getArgs() const noexcept { return _args; } + + ASMJIT_INLINE bool hasArg(size_t index) const noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + return _args[index].isInitialized(); + } + + //! Get function argument at index `index`. + ASMJIT_INLINE Value& getArg(size_t index) noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + return _args[index]; + } + + //! Get function argument at index `index`. + ASMJIT_INLINE const Value& getArg(size_t index) const noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + return _args[index]; + } + + ASMJIT_INLINE void resetArg(size_t index) noexcept { + ASMJIT_ASSERT(index < kFuncArgCountLoHi); + _args[index].reset(); + } + + //! Get if the function passes one or more argument by stack. + ASMJIT_INLINE bool hasStackArgs() const noexcept { return _argStackSize != 0; } + //! Get stack size needed for function arguments passed on the stack. + ASMJIT_INLINE uint32_t getArgStackSize() const noexcept { return _argStackSize; } + + ASMJIT_INLINE uint32_t getNaturalStackAlignment() const noexcept { return _callConv.getNaturalStackAlignment(); } + ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _callConv.getSpillZoneSize(); } + ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _callConv.getRedZoneSize(); } + + ASMJIT_INLINE uint32_t getPassedRegs(uint32_t kind) const noexcept { return _callConv.getPassedRegs(kind); } + ASMJIT_INLINE uint32_t getPreservedRegs(uint32_t kind) const noexcept { return _callConv.getPreservedRegs(kind); } + + ASMJIT_INLINE uint32_t getUsedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _usedRegs[kind]; + } + + ASMJIT_INLINE void addUsedRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + _usedRegs[kind] |= regs; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + CallConv _callConv; //!< Calling convention. + uint8_t _argCount; //!< Number of function arguments. + uint8_t _retCount; //!< Number of function return values. + uint32_t _usedRegs[kMaxVRegKinds]; //!< Registers that contains arguments (signature dependent). + uint32_t _argStackSize; //!< Size of arguments passed by stack. + Value _rets[2]; //!< Function return values. + Value _args[kFuncArgCountLoHi]; //!< Function arguments. +}; + +// ============================================================================ +// [asmjit::FuncFrameInfo] +// ============================================================================ + +//! Function-frame information. +//! +//! This structure can be used to create a function frame in a cross-platform +//! way. It contains information about the function's stack to be used and +//! registers to be saved and restored. Based on this information in can +//! calculate the optimal layout of a function as \ref FuncFrameLayout. +struct FuncFrameInfo { + ASMJIT_ENUM(Limits) { + kMaxVRegKinds = Globals::kMaxVRegKinds + }; + + //! Attributes. + //! + //! Attributes are designed in a way that all are initially false, and user + //! or function-frame finalizer sets them when necessary. Architecture-specific + //! attributes are prefixed with the architecture name. + ASMJIT_ENUM(Attributes) { + kAttrPreserveFP = 0x00000001U, //!< Preserve frame pointer (EBP|RBP). + kAttrCompactPE = 0x00000002U, //!< Use smaller, but possibly slower prolog/epilog. + kAttrHasCalls = 0x00000004U, //!< Function calls other functions (is not leaf). + + kX86AttrAlignedVecSR = 0x00010000U, //!< Use aligned save/restore of VEC regs. + kX86AttrMmxCleanup = 0x00020000U, //!< Emit EMMS instruction in epilog (X86). + kX86AttrAvxCleanup = 0x00040000U, //!< Emit VZEROUPPER instruction in epilog (X86). + kX86AttrAvxEnabled = 0x00080000U //!< Use AVX instead of SSE for all operations (X86). + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE FuncFrameInfo() noexcept { reset(); } + + ASMJIT_INLINE FuncFrameInfo(const FuncFrameInfo& other) noexcept { + ::memcpy(this, &other, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset() noexcept { + ::memset(this, 0, sizeof(*this)); + _stackArgsRegId = Globals::kInvalidRegId; + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get frame-info flags, see \ref Attributes. + ASMJIT_INLINE uint32_t getAttributes() const noexcept { return _attributes; } + //! Check if a frame-info `flag` is set, see \ref Attributes. + ASMJIT_INLINE bool hasAttribute(uint32_t attr) const noexcept { return (_attributes & attr) != 0; } + //! Add `flags` to the frame-info, see \ref Attributes. + ASMJIT_INLINE void addAttributes(uint32_t attrs) noexcept { _attributes |= attrs; } + //! Clear `flags` from the frame-info, see \ref Attributes. + ASMJIT_INLINE void clearAttributes(uint32_t attrs) noexcept { _attributes &= ~attrs; } + + //! Get if the function preserves frame pointer (EBP|ESP on X86). + ASMJIT_INLINE bool hasPreservedFP() const noexcept { return (_attributes & kAttrPreserveFP) != 0; } + //! Enable preserved frame pointer. + ASMJIT_INLINE void enablePreservedFP() noexcept { _attributes |= kAttrPreserveFP; } + //! Disable preserved frame pointer. + ASMJIT_INLINE void disablePreservedFP() noexcept { _attributes &= ~kAttrPreserveFP; } + + //! Get if the function prolog and epilog should be compacted (as small as possible). + ASMJIT_INLINE bool hasCompactPE() const noexcept { return (_attributes & kAttrCompactPE) != 0; } + //! Enable compact prolog/epilog. + ASMJIT_INLINE void enableCompactPE() noexcept { _attributes |= kAttrCompactPE; } + //! Disable compact prolog/epilog. + ASMJIT_INLINE void disableCompactPE() noexcept { _attributes &= ~kAttrCompactPE; } + + //! Get if the function calls other functions. + ASMJIT_INLINE bool hasCalls() const noexcept { return (_attributes & kAttrHasCalls) != 0; } + //! Set `kFlagHasCalls` to true. + ASMJIT_INLINE void enableCalls() noexcept { _attributes |= kAttrHasCalls; } + //! Set `kFlagHasCalls` to false. + ASMJIT_INLINE void disableCalls() noexcept { _attributes &= ~kAttrHasCalls; } + + //! Get if the function contains MMX cleanup - 'emms' instruction in epilog. + ASMJIT_INLINE bool hasMmxCleanup() const noexcept { return (_attributes & kX86AttrMmxCleanup) != 0; } + //! Enable MMX cleanup. + ASMJIT_INLINE void enableMmxCleanup() noexcept { _attributes |= kX86AttrMmxCleanup; } + //! Disable MMX cleanup. + ASMJIT_INLINE void disableMmxCleanup() noexcept { _attributes &= ~kX86AttrMmxCleanup; } + + //! Get if the function contains AVX cleanup - 'vzeroupper' instruction in epilog. + ASMJIT_INLINE bool hasAvxCleanup() const noexcept { return (_attributes & kX86AttrAvxCleanup) != 0; } + //! Enable AVX cleanup. + ASMJIT_INLINE void enableAvxCleanup() noexcept { _attributes |= kX86AttrAvxCleanup; } + //! Disable AVX cleanup. + ASMJIT_INLINE void disableAvxCleanup() noexcept { _attributes &= ~kX86AttrAvxCleanup; } + + //! Get if the function contains AVX cleanup - 'vzeroupper' instruction in epilog. + ASMJIT_INLINE bool isAvxEnabled() const noexcept { return (_attributes & kX86AttrAvxEnabled) != 0; } + //! Enable AVX cleanup. + ASMJIT_INLINE void enableAvx() noexcept { _attributes |= kX86AttrAvxEnabled; } + //! Disable AVX cleanup. + ASMJIT_INLINE void disableAvx() noexcept { _attributes &= ~kX86AttrAvxEnabled; } + + //! Get which registers (by `kind`) are saved/restored in prolog/epilog, respectively. + ASMJIT_INLINE uint32_t getDirtyRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _dirtyRegs[kind]; + } + + //! Set which registers (by `kind`) are saved/restored in prolog/epilog, respectively. + ASMJIT_INLINE void setDirtyRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + _dirtyRegs[kind] = regs; + } + + //! Add registers (by `kind`) to saved/restored registers. + ASMJIT_INLINE void addDirtyRegs(uint32_t kind, uint32_t regs) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + _dirtyRegs[kind] |= regs; + } + + ASMJIT_INLINE void setAllDirty() noexcept { + _dirtyRegs[0] = 0xFFFFFFFFU; + _dirtyRegs[1] = 0xFFFFFFFFU; + _dirtyRegs[2] = 0xFFFFFFFFU; + _dirtyRegs[3] = 0xFFFFFFFFU; + } + + ASMJIT_INLINE void setAllDirty(uint32_t kind) noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + _dirtyRegs[kind] = 0xFFFFFFFFU; + } + + //! Get stack-frame size used by the function. + ASMJIT_INLINE uint32_t getStackFrameSize() const noexcept { return _stackFrameSize; } + //! Get call-frame size used by the function. + ASMJIT_INLINE uint32_t getCallFrameSize() const noexcept { return _callFrameSize; } + + //! Get minimum stack-frame alignment required by the function. + ASMJIT_INLINE uint32_t getStackFrameAlignment() const noexcept { return _stackFrameAlignment; } + //! Get minimum call-frame alignment required by the function. + ASMJIT_INLINE uint32_t getCallFrameAlignment() const noexcept { return _callFrameAlignment; } + + ASMJIT_INLINE void setStackFrameSize(uint32_t size) noexcept { _stackFrameSize = size; } + ASMJIT_INLINE void setCallFrameSize(uint32_t size) noexcept { _callFrameSize = size; } + + ASMJIT_INLINE void setStackFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _stackFrameAlignment = static_cast(value); + } + + ASMJIT_INLINE void setCallFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _callFrameAlignment = static_cast(value); + } + + ASMJIT_INLINE void mergeStackFrameSize(uint32_t size) noexcept { _stackFrameSize = std::max(_stackFrameSize, size); } + ASMJIT_INLINE void mergeCallFrameSize(uint32_t size) noexcept { _callFrameSize = std::max(_callFrameSize, size); } + + ASMJIT_INLINE void mergeStackFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _stackFrameAlignment = static_cast(std::max(_stackFrameAlignment, value)); + } + + ASMJIT_INLINE void mergeCallFrameAlignment(uint32_t value) noexcept { + ASMJIT_ASSERT(value < 256); + _callFrameAlignment = static_cast(std::max(_callFrameAlignment, value)); + } + + ASMJIT_INLINE bool hasStackArgsRegId() const noexcept { + return _stackArgsRegId != Globals::kInvalidRegId; + } + ASMJIT_INLINE uint32_t getStackArgsRegId() const noexcept { return _stackArgsRegId; } + ASMJIT_INLINE void setStackArgsRegId(uint32_t regId) { _stackArgsRegId = regId; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint32_t _attributes; //!< Function attributes. + uint32_t _dirtyRegs[kMaxVRegKinds]; //!< Registers used by the function. + + uint8_t _stackFrameAlignment; //!< Minimum alignment of stack-frame. + uint8_t _callFrameAlignment; //!< Minimum alignment of call-frame. + uint8_t _stackArgsRegId; //!< Register that holds base-address to arguments passed by stack. + + uint32_t _stackFrameSize; //!< Size of a stack-frame used by the function. + uint32_t _callFrameSize; //!< Size of a call-frame (not part of _stackFrameSize). +}; + +// ============================================================================ +// [asmjit::FuncFrameLayout] +// ============================================================================ + +//! Function-frame layout. +//! +//! Function layout is used directly by prolog and epilog insertion helpers. It +//! contains only information necessary to insert proper prolog and epilog, and +//! should be always calculated from \ref FuncDetail and \ref FuncFrameInfo, where +//! \ref FuncDetail defines function's calling convention and signature, and \ref +//! FuncFrameInfo specifies how much stack is used, and which registers are dirty. +struct FuncFrameLayout { + ASMJIT_ENUM(Limits) { + kMaxVRegKinds = Globals::kMaxVRegKinds + }; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_API Error init(const FuncDetail& func, const FuncFrameInfo& ffi) noexcept; + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool hasPreservedFP() const noexcept { return static_cast(_preservedFP); } + ASMJIT_INLINE bool hasDsaSlotUsed() const noexcept { return static_cast(_dsaSlotUsed); } + ASMJIT_INLINE bool hasAlignedVecSR() const noexcept { return static_cast(_alignedVecSR); } + ASMJIT_INLINE bool hasDynamicAlignment() const noexcept { return static_cast(_dynamicAlignment); } + + ASMJIT_INLINE bool hasMmxCleanup() const noexcept { return static_cast(_mmxCleanup); } + ASMJIT_INLINE bool hasAvxCleanup() const noexcept { return static_cast(_avxCleanup); } + ASMJIT_INLINE bool isAvxEnabled() const noexcept { return static_cast(_avxEnabled); } + + ASMJIT_INLINE uint32_t getSavedRegs(uint32_t kind) const noexcept { + ASMJIT_ASSERT(kind < kMaxVRegKinds); + return _savedRegs[kind]; + } + + //! Get stack size. + ASMJIT_INLINE uint32_t getStackSize() const noexcept { return _stackSize; } + //! Get stack alignment. + ASMJIT_INLINE uint32_t getStackAlignment() const noexcept { return _stackAlignment; } + //! Get the offset needed to access the function's stack (it skips call-stack). + ASMJIT_INLINE uint32_t getStackBaseOffset() const noexcept { return _stackBaseOffset; } + + //! Get stack size required to save GP registers. + ASMJIT_INLINE uint32_t getGpStackSize() const noexcept { return _gpStackSize; } + //! Get stack size required to save VEC registers. + ASMJIT_INLINE uint32_t getVecStackSize() const noexcept { return _vecStackSize; } + + ASMJIT_INLINE uint32_t getGpStackOffset() const noexcept { return _gpStackOffset; } + ASMJIT_INLINE uint32_t getVecStackOffset() const noexcept { return _vecStackOffset; } + + ASMJIT_INLINE uint32_t getStackArgsRegId() const noexcept { return _stackArgsRegId; } + ASMJIT_INLINE uint32_t getStackArgsOffset() const noexcept { return _stackArgsOffset; } + + ASMJIT_INLINE bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; } + ASMJIT_INLINE uint32_t getStackAdjustment() const noexcept { return _stackAdjustment; } + + ASMJIT_INLINE bool hasCalleeStackCleanup() const noexcept { return _calleeStackCleanup != 0; } + ASMJIT_INLINE uint32_t getCalleeStackCleanup() const noexcept { return _calleeStackCleanup; } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uint8_t _stackAlignment; //!< Final stack alignment of the functions. + uint8_t _stackBaseRegId; //!< GP register that holds address of base stack address. + uint8_t _stackArgsRegId; //!< GP register that holds address of the first argument passed by stack. + + uint32_t _savedRegs[kMaxVRegKinds]; //!< Registers that will be saved/restored in prolog/epilog. + + uint32_t _preservedFP : 1; //!< Function preserves frame-pointer. + uint32_t _dsaSlotUsed : 1; //!< True if `_dsaSlot` contains a valid memory slot/offset. + uint32_t _alignedVecSR : 1; //!< Use instructions that perform aligned ops to save/restore XMM regs. + uint32_t _dynamicAlignment : 1; //!< Function must dynamically align the stack. + + uint32_t _mmxCleanup : 1; //!< Emit 'emms' in epilog (X86). + uint32_t _avxCleanup : 1; //!< Emit 'vzeroupper' in epilog (X86). + uint32_t _avxEnabled : 1; //!< Use AVX instead of SSE for SIMD saves/restores (X86). + + uint32_t _stackSize; //!< Stack size (sum of function's stack and call stack). + uint32_t _stackBaseOffset; //!< Stack offset (non-zero if kFlagHasCalls is set). + uint32_t _stackAdjustment; //!< Stack adjustment in prolog/epilog. + uint32_t _stackArgsOffset; //!< Offset to the first argument passed by stack of _stackArgsRegId. + + uint32_t _dsaSlot; //!< Memory slot where the prolog inserter stores previous (unaligned) ESP. + uint16_t _calleeStackCleanup; //!< How many bytes the callee should add to the stack (X86 STDCALL). + uint16_t _gpStackSize; //!< Stack size required to save GP regs. + uint16_t _vecStackSize; //!< Stack size required to save VEC regs. + uint32_t _gpStackOffset; //!< Offset where saved GP regs are stored. + uint32_t _vecStackOffset; //!< Offset where saved GP regs are stored. +}; + +// ============================================================================ +// [asmjit::FuncArgsMapper] +// ============================================================================ + +//! Assign a physical register to each function argument. +//! +//! This is used to specify where each function argument should be shuffled +//! or allocated (in case it's passed by stack). +class FuncArgsMapper { +public: + struct Value { + // NOTE: The layout is compatible with FuncDetail::Value except stack. + ASMJIT_ENUM(Parts) { + kTypeIdShift = 24, + kTypeIdMask = 0xFF000000U, + + kRegTypeShift = 8, + kRegTypeMask = 0x0000FF00U, + + kRegIdShift = 0, + kRegIdMask = 0x000000FFU, + + kIsAssigned = 0x00010000U + }; + + //! Get if this value is initialized (i.e. contains a valid data). + ASMJIT_INLINE bool isAssigned() const noexcept { return _value != 0; } + //! Initialize this in/out by a given `typeId`, `regType`, and `regId`. + ASMJIT_INLINE void assign(uint32_t typeId, uint32_t regType, uint32_t regId) noexcept { + _value = (typeId << kTypeIdShift) | (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsAssigned; + } + //! Reset the value to its unassigned state. + ASMJIT_INLINE void reset() noexcept { _value = 0; } + + //! Get virtual type of this argument or return value. + ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _value >> kTypeIdShift; } + //! Get a register type of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegType() const noexcept { return (_value & kRegTypeMask) >> kRegTypeShift; } + //! Get a physical id of the register used to pass the argument or return the value. + ASMJIT_INLINE uint32_t getRegId() const noexcept { return (_value & kRegIdMask) >> kRegIdShift; } + + uint32_t _value; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + explicit ASMJIT_INLINE FuncArgsMapper(const FuncDetail* fd) noexcept { reset(fd); } + ASMJIT_INLINE FuncArgsMapper(const FuncArgsMapper& other) noexcept { + ::memcpy(this, &other, sizeof(*this)); + } + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset(const FuncDetail* fd = nullptr) noexcept { + _funcDetail = fd; + ::memset(_args, 0, sizeof(_args)); + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE const FuncDetail* getFuncDetail() const noexcept { return _funcDetail; } + ASMJIT_INLINE void setFuncDetail(const FuncDetail* fd) noexcept { _funcDetail = fd; } + + ASMJIT_INLINE Value& getArg(size_t index) noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + return _args[index]; + } + ASMJIT_INLINE const Value& getArg(size_t index) const noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + return _args[index]; + } + + ASMJIT_INLINE bool isAssigned(size_t index) const noexcept { + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + return _args[index].isAssigned(); + } + + ASMJIT_INLINE void assign(size_t index, const Reg& reg, uint32_t typeId = TypeId::kVoid) noexcept { + // Not designed for virtual registers. + ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args)); + ASMJIT_ASSERT(reg.isPhysReg()); + + _args[index].assign(typeId, reg.getType(), reg.getId()); + } + + // NOTE: All `assignAll()` methods are shortcuts to assign all arguments at + // once, however, since registers are passed all at once these initializers + // don't provide any way to pass TypeId and/or to keep any argument between + // the arguments passed uninitialized. + ASMJIT_INLINE void assignAll(const Reg& a0) noexcept { + assign(0, a0); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1) noexcept { + assign(0, a0); assign(1, a1); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); assign(5, a5); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5, const Reg& a6) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); assign(5, a5); assign(6, a6); + } + ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5, const Reg& a6, const Reg& a7) noexcept { + assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3); + assign(4, a4); assign(5, a5); assign(6, a6); assign(7, a7); + } + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + //! Update `FuncFrameInfo` accordingly to FuncArgsMapper. + //! + //! This method must be called if you use `FuncArgsMapper` and you plan to + //! use `FuncUtils::allocArgs()` to remap all arguments after the prolog is + //! inserted. + ASMJIT_API Error updateFrameInfo(FuncFrameInfo& ffi) const noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + const FuncDetail* _funcDetail; //!< Function detail. + Value _args[kFuncArgCountLoHi]; //!< Mapping of each function argument. +}; + +// ============================================================================ +// [asmjit::FuncUtils] +// ============================================================================ + +struct FuncUtils { + ASMJIT_API static Error emitProlog(CodeEmitter* emitter, const FuncFrameLayout& layout); + ASMJIT_API static Error emitEpilog(CodeEmitter* emitter, const FuncFrameLayout& layout); + ASMJIT_API static Error allocArgs(CodeEmitter* emitter, const FuncFrameLayout& layout, const FuncArgsMapper& args); +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_FUNC_H diff --git a/src/asmjit/base/globals.cpp b/src/asmjit/base/globals.cpp index 1674b82..e6c71c2 100644 --- a/src/asmjit/base/globals.cpp +++ b/src/asmjit/base/globals.cpp @@ -9,9 +9,10 @@ // [Dependencies] #include "../base/globals.h" +#include "../base/utils.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { @@ -20,7 +21,7 @@ namespace asmjit { // ============================================================================ #if !defined(ASMJIT_DISABLE_TEXT) -static const char errorMessages[] = { +static const char errorMessages[] = "Ok\0" "No heap memory\0" "No virtual memory\0" @@ -28,45 +29,60 @@ static const char errorMessages[] = { "Invalid state\0" "Invalid architecture\0" "Not initialized\0" + "Already initialized\0" + "Feature not enabled\0" + "Slot occupied\0" "No code generated\0" "Code too large\0" + "Invalid label\0" + "Label index overflow\0" "Label already bound\0" - "Unknown instruction\0" - "Illegal instruction\0" - "Illegal addressing\0" - "Illegal displacement\0" - "Overlapped arguments\0" - "Unknown error\0" -}; - -static const char* findPackedString(const char* p, uint32_t id, uint32_t maxId) noexcept { - uint32_t i = 0; - - if (id > maxId) - id = maxId; - - while (i < id) { - while (p[0]) - p++; - - p++; - i++; - } - - return p; -} + "Label already defined\0" + "Label name too long\0" + "Invalid label name\0" + "Invalid parent label\0" + "Non-local label can't have parent\0" + "Relocation index overflow\0" + "Invalid relocation entry\0" + "Invalid instruction\0" + "Invalid register type\0" + "Invalid register kind\0" + "Invalid register's physical id\0" + "Invalid register's virtual id\0" + "Invalid rex prefix\0" + "Invalid mask, expected {k}\0" + "Invalid use of {k}\0" + "Invalid use of {k}{z}\0" + "Invalid broadcast {1tox}\0" + "Invalid {sae} or {rc} option\0" + "Invalid address\0" + "Invalid address index\0" + "Invalid address scale\0" + "Invalid use of 64-bit address\0" + "Invalid displacement\0" + "Invalid segment\0" + "Operand size mismatch\0" + "Ambiguous operand size\0" + "Invalid type-info\0" + "Invalid use of a low 8-bit GPB register\0" + "Invalid use of a 64-bit GPQ register in 32-bit mode\0" + "Invalid use of an 80-bit float\0" + "No more physical registers\0" + "Overlapped registers\0" + "Overlapping register and arguments base-address register\0" + "Unknown error\0"; #endif // ASMJIT_DISABLE_TEXT -const char* DebugUtils::errorAsString(Error err) noexcept { +ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept { #if !defined(ASMJIT_DISABLE_TEXT) - return findPackedString(errorMessages, err, kErrorCount); + return Utils::findPackedString(errorMessages, std::min(err, kErrorCount)); #else static const char noMessage[] = ""; return noMessage; #endif } -void DebugUtils::debugOutput(const char* str) noexcept { +ASMJIT_FAVOR_SIZE void DebugUtils::debugOutput(const char* str) noexcept { #if ASMJIT_OS_WINDOWS ::OutputDebugStringA(str); #else @@ -74,7 +90,7 @@ void DebugUtils::debugOutput(const char* str) noexcept { #endif } -void DebugUtils::assertionFailed(const char* file, int line, const char* msg) noexcept { +ASMJIT_FAVOR_SIZE void DebugUtils::assertionFailed(const char* file, int line, const char* msg) noexcept { char str[1024]; snprintf(str, 1024, @@ -91,4 +107,4 @@ void DebugUtils::assertionFailed(const char* file, int line, const char* msg) no } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/globals.h b/src/asmjit/base/globals.h index deaf221..9d29840 100644 --- a/src/asmjit/base/globals.h +++ b/src/asmjit/base/globals.h @@ -9,10 +9,10 @@ #define _ASMJIT_BASE_GLOBALS_H // [Dependencies] -#include "../build.h" +#include "../asmjit_build.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { @@ -20,25 +20,13 @@ namespace asmjit { //! \{ // ============================================================================ -// [asmjit::TypeDefs] +// [asmjit::Globals] // ============================================================================ -//! AsmJit error core (unsigned integer). -typedef uint32_t Error; +enum { kInvalidValue = 0xFFFFFFFFU }; -//! 64-bit unsigned pointer, compatible with JIT and non-JIT generators. -//! -//! This is the preferred pointer type to use with AsmJit library. It has a -//! capability to hold any pointer for any architecture making it an ideal -//! candidate for a cross-platform code generator. -typedef uint64_t Ptr; - -//! like \ref Ptr, but signed. -typedef int64_t SignedPtr; - -// ============================================================================ -// [asmjit::GlobalDefs] -// ============================================================================ +//! AsmJit globals. +namespace Globals { //! Invalid index //! @@ -48,432 +36,69 @@ typedef int64_t SignedPtr; static const size_t kInvalidIndex = ~static_cast(0); //! Invalid base address. -static const Ptr kNoBaseAddress = static_cast(static_cast(-1)); +static const uint64_t kNoBaseAddress = ~static_cast(0); -//! Global constants. -ASMJIT_ENUM(GlobalDefs) { - //! Invalid value or operand id. - kInvalidValue = 0xFFFFFFFF, - - //! Invalid register index. - kInvalidReg = 0xFF, - //! Invalid variable type. - kInvalidVar = 0xFF, +//! Global definitions. +ASMJIT_ENUM(Defs) { + //! Invalid instruction id. + kInvalidInstId = 0, + //! Invalid register id. + kInvalidRegId = 0xFF, //! Host memory allocator overhead. - //! - //! The overhead is decremented from all zone allocators so the operating - //! system doesn't have to allocate one extra virtual page to keep tract of - //! the requested memory block. - //! - //! The number is actually a guess. - kMemAllocOverhead = sizeof(intptr_t) * 4, - - //! Memory grow threshold. - //! - //! After the grow threshold is reached the capacity won't be doubled - //! anymore. - kMemAllocGrowMax = 8192 * 1024 + kAllocOverhead = static_cast(sizeof(intptr_t) * 4), + //! Aggressive growing strategy threshold. + kAllocThreshold = 8192 * 1024 }; -// ============================================================================ -// [asmjit::ArchId] -// ============================================================================ +ASMJIT_ENUM(Limits) { + //! Count of register kinds that are important to Function API and CodeCompiler. + //! The target architecture can define more register kinds for special registers, + //! but these will never map to virtual registers and will never be used to pass + //! and return function arguments and function return values, respectively. + kMaxVRegKinds = 4, -//! CPU architecture identifier. -ASMJIT_ENUM(ArchId) { - //! No/Unknown architecture. - kArchNone = 0, + //! Maximum number of physical registers of all kinds of all supported + //! architectures. This is only important for \ref CodeCompiler and its + //! \ref RAPass (register allocator pass). + //! + //! NOTE: The distribution of these registers is architecture specific. + kMaxPhysRegs = 64, - //! X86 architecture (32-bit). - kArchX86 = 1, - //! X64 architecture (64-bit), also called AMD64. - kArchX64 = 2, - //! X32 architecture (64-bit with 32-bit pointers) (NOT USED ATM). - kArchX32 = 3, + //! Maximum alignment. + kMaxAlignment = 64, - //! Arm architecture (32-bit). - kArchArm32 = 4, - //! Arm64 architecture (64-bit). - kArchArm64 = 5, - -#if ASMJIT_ARCH_X86 - kArchHost = kArchX86 -#elif ASMJIT_ARCH_X64 - kArchHost = kArchX64 -#elif ASMJIT_ARCH_ARM32 - kArchHost = kArchArm32 -#elif ASMJIT_ARCH_ARM64 - kArchHost = kArchArm64 -#else -# error "[asmjit] Unsupported host architecture." -#endif + //! Maximum label or symbol length in bytes (take into consideration that a + //! single UTF-8 character can take more than single byte to encode it). + kMaxLabelLength = 2048 }; +} // Globals namespace + // ============================================================================ -// [asmjit::CallConv] +// [asmjit::AnyInst] // ============================================================================ -//! Function calling convention. -//! -//! Calling convention is a scheme that defines how function arguments are -//! passed and how the return value handled. In assembler programming it's -//! always needed to comply with function calling conventions, because even -//! small inconsistency can cause undefined behavior or application's crash. -//! -//! Platform Independent Conventions -//! -------------------------------- -//! -//! - `kCallConvHost` - Should match the current C++ compiler native calling -//! convention. -//! -//! X86/X64 Specific Conventions -//! ---------------------------- -//! -//! List of calling conventions for 32-bit x86 mode: -//! - `kCallConvX86CDecl` - Calling convention for C runtime. -//! - `kCallConvX86StdCall` - Calling convention for WinAPI functions. -//! - `kCallConvX86MsThisCall` - Calling convention for C++ members under -//! Windows (produced by MSVC and all MSVC compatible compilers). -//! - `kCallConvX86MsFastCall` - Fastest calling convention that can be used -//! by MSVC compiler. -//! - `kCallConvX86BorlandFastCall` - Borland fastcall convention. -//! - `kCallConvX86GccFastCall` - GCC fastcall convention (2 register arguments). -//! - `kCallConvX86GccRegParm1` - GCC regparm(1) convention. -//! - `kCallConvX86GccRegParm2` - GCC regparm(2) convention. -//! - `kCallConvX86GccRegParm3` - GCC regparm(3) convention. -//! -//! List of calling conventions for 64-bit x86 mode (x64): -//! - `kCallConvX64Win` - Windows 64-bit calling convention (WIN64 ABI). -//! - `kCallConvX64Unix` - Unix 64-bit calling convention (AMD64 ABI). -//! -//! ARM Specific Conventions -//! ------------------------ -//! -//! List of ARM calling conventions: -//! - `kCallConvArm32SoftFP` - Legacy calling convention, floating point -//! arguments are passed via GP registers. -//! - `kCallConvArm32HardFP` - Modern calling convention, uses VFP registers -//! to pass floating point arguments. -ASMJIT_ENUM(CallConv) { - //! Calling convention is invalid (can't be used). - kCallConvNone = 0, +//! Definitions and utilities related to instructions used by all architectures. +namespace AnyInst { - // -------------------------------------------------------------------------- - // [X86] - // -------------------------------------------------------------------------- - - //! X86 `__cdecl` calling convention (used by C runtime and libraries). - //! - //! Compatible across MSVC and GCC. - //! - //! Arguments direction: - //! - Right to left. - //! - //! Stack is cleaned by: - //! - Caller. - //! - //! Return value: - //! - Integer types - `eax:edx` registers. - //! - Floating point - `fp0` register. - kCallConvX86CDecl = 1, - - //! X86 `__stdcall` calling convention (used mostly by WinAPI). - //! - //! Compatible across MSVC and GCC. - //! - //! Arguments direction: - //! - Right to left. - //! - //! Stack is cleaned by: - //! - Callee. - //! - //! Return value: - //! - Integer types - `eax:edx` registers. - //! - Floating point - `fp0` register. - kCallConvX86StdCall = 2, - - //! X86 `__thiscall` calling convention (MSVC/Intel specific). - //! - //! This is MSVC (and Intel) specific calling convention used when targeting - //! Windows platform for C++ class methods. Implicit `this` pointer (defined - //! as the first argument) is stored in `ecx` register instead of storing it - //! on the stack. - //! - //! This calling convention is implicitly used by MSVC for class functions. - //! - //! C++ class functions that have variable number of arguments use `__cdecl` - //! calling convention instead. - //! - //! Arguments direction: - //! - Right to left (except for the first argument passed in `ecx`). - //! - //! Stack is cleaned by: - //! - Callee. - //! - //! Return value: - //! - Integer types - `eax:edx` registers. - //! - Floating point - `fp0` register. - kCallConvX86MsThisCall = 3, - - //! X86 `__fastcall` convention (MSVC/Intel specific). - //! - //! The first two arguments (evaluated from the left to the right) are passed - //! in `ecx` and `edx` registers, all others on the stack from the right to - //! the left. - //! - //! Arguments direction: - //! - Right to left (except for the first two integers passed in `ecx` and `edx`). - //! - //! Stack is cleaned by: - //! - Callee. - //! - //! Return value: - //! - Integer types - `eax:edx` registers. - //! - Floating point - `fp0` register. - //! - //! NOTE: This calling convention differs from GCC's one. - kCallConvX86MsFastCall = 4, - - //! X86 `__fastcall` convention (Borland specific). - //! - //! The first two arguments (evaluated from the left to the right) are passed - //! in `ecx` and `edx` registers, all others on the stack from the left to - //! the right. - //! - //! Arguments direction: - //! - Left to right (except for the first two integers passed in `ecx` and `edx`). - //! - //! Stack is cleaned by: - //! - Callee. - //! - //! Return value: - //! - Integer types - `eax:edx` registers. - //! - Floating point - `fp0` register. - //! - //! NOTE: Arguments on the stack are in passed in left to right order, which - //! is really Borland specific, all other `__fastcall` calling conventions - //! use right to left order. - kCallConvX86BorlandFastCall = 5, - - //! X86 `__fastcall` convention (GCC specific). - //! - //! The first two arguments (evaluated from the left to the right) are passed - //! in `ecx` and `edx` registers, all others on the stack from the right to - //! the left. - //! - //! Arguments direction: - //! - Right to left (except for the first two integers passed in `ecx` and `edx`). - //! - //! Stack is cleaned by: - //! - Callee. - //! - //! Return value: - //! - Integer types - `eax:edx` registers. - //! - Floating point - `fp0` register. - //! - //! NOTE: This calling convention should be compatible with `kCallConvX86MsFastCall`. - kCallConvX86GccFastCall = 6, - - //! X86 `regparm(1)` convention (GCC specific). - //! - //! The first argument (evaluated from the left to the right) is passed in - //! `eax` register, all others on the stack from the right to the left. - //! - //! Arguments direction: - //! - Right to left (except for the first integer passed in `eax`). - //! - //! Stack is cleaned by: - //! - Caller. - //! - //! Return value: - //! - Integer types - `eax:edx` registers. - //! - Floating point - `fp0` register. - kCallConvX86GccRegParm1 = 7, - - //! X86 `regparm(2)` convention (GCC specific). - //! - //! The first two arguments (evaluated from the left to the right) are passed - //! in `ecx` and `edx` registers, all others on the stack from the right to - //! the left. - //! - //! Arguments direction: - //! - Right to left (except for the first two integers passed in `ecx` and `edx`). - //! - //! Stack is cleaned by: - //! - Caller. - //! - //! Return value: - //! - Integer types - `eax:edx` registers. - //! - Floating point - `fp0` register. - kCallConvX86GccRegParm2 = 8, - - //! X86 `regparm(3)` convention (GCC specific). - //! - //! Three first parameters (evaluated from left-to-right) are in - //! EAX:EDX:ECX registers, all others on the stack in right-to-left direction. - //! - //! Arguments direction: - //! - Right to left (except for the first three integers passed in `ecx`, - //! `edx`, and `ecx`). - //! - //! Stack is cleaned by: - //! - Caller. - //! - //! Return value: - //! - Integer types - `eax:edx` registers. - //! - Floating point - `fp0` register. - kCallConvX86GccRegParm3 = 9, - - // -------------------------------------------------------------------------- - // [X64] - // -------------------------------------------------------------------------- - - //! X64 calling convention used by Windows platform (WIN64-ABI). - //! - //! The first 4 arguments are passed in the following registers: - //! - 1. 32/64-bit integer in `rcx` and floating point argument in `xmm0` - //! - 2. 32/64-bit integer in `rdx` and floating point argument in `xmm1` - //! - 3. 32/64-bit integer in `r8` and floating point argument in `xmm2` - //! - 4. 32/64-bit integer in `r9` and floating point argument in `xmm3` - //! - //! If one or more argument from the first four doesn't match the list above - //! it is simply skipped. WIN64-ABI is very specific about this. - //! - //! All other arguments are pushed on the stack from the right to the left. - //! Stack has to be aligned by 16 bytes, always. There is also a 32-byte - //! shadow space on the stack that can be used to save up to four 64-bit - //! registers. - //! - //! Arguments direction: - //! - Right to left (except for all parameters passed in registers). - //! - //! Stack cleaned by: - //! - Caller. - //! - //! Return value: - //! - Integer types - `rax`. - //! - Floating point - `xmm0`. - //! - //! Stack is always aligned to 16 bytes. - //! - //! More information about this calling convention can be found on MSDN - //! . - kCallConvX64Win = 10, - - //! X64 calling convention used by Unix platforms (AMD64-ABI). - //! - //! First six 32 or 64-bit integer arguments are passed in `rdi`, `rsi`, - //! `rdx`, `rcx`, `r8`, and `r9` registers. First eight floating point or xmm - //! arguments are passed in `xmm0`, `xmm1`, `xmm2`, `xmm3`, `xmm4`, `xmm5`, - //! `xmm6`, and `xmm7` registers. - //! - //! There is also a red zene below the stack pointer that can be used by the - //! function. The red zone is typically from [rsp-128] to [rsp-8], however, - //! red zone can also be disabled. - //! - //! Arguments direction: - //! - Right to left (except for all arguments passed in registers). - //! - //! Stack cleaned by: - //! - Caller. - //! - //! Return value: - //! - Integer types - `rax`. - //! - Floating point - `xmm0`. - //! - //! Stack is always aligned to 16 bytes. - kCallConvX64Unix = 11, - - // -------------------------------------------------------------------------- - // [ARM] - // -------------------------------------------------------------------------- - - kCallConvArm32SoftFP = 16, - kCallConvArm32HardFP = 17, - - // -------------------------------------------------------------------------- - // [Internal] - // -------------------------------------------------------------------------- - - //! \internal - _kCallConvX86Start = 1, - //! \internal - _kCallConvX86End = 9, - - //! \internal - _kCallConvX64Start = 10, - //! \internal - _kCallConvX64End = 11, - - //! \internal - _kCallConvArmStart = 16, - //! \internal - _kCallConvArmEnd = 17, - - // -------------------------------------------------------------------------- - // [Host] - // -------------------------------------------------------------------------- - -#if defined(ASMJIT_DOCGEN) - //! Default calling convention based on the current compiler's settings. - //! - //! NOTE: This should be always the same as `kCallConvHostCDecl`, but some - //! compilers allow to override the default calling convention. Overriding - //! is not detected at the moment. - kCallConvHost = DETECTED_AT_COMPILE_TIME, - //! Default C calling convention based on the current compiler's settings. - kCallConvHostCDecl = DETECTED_AT_COMPILE_TIME, - //! Compatibility for `__stdcall` calling convention. - //! - //! NOTE: This enumeration is always set to a value which is compatible with - //! the current compiler's `__stdcall` calling convention. In 64-bit mode - //! there is no such convention and the value is mapped to `kCallConvX64Win` - //! or `kCallConvX64Unix`, depending on the host architecture. - kCallConvHostStdCall = DETECTED_AT_COMPILE_TIME, - //! Compatibility for `__fastcall` calling convention. - //! - //! NOTE: This enumeration is always set to a value which is compatible with - //! the current compiler's `__fastcall` calling convention. In 64-bit mode - //! there is no such convention and the value is mapped to `kCallConvX64Win` - //! or `kCallConvX64Unix`, depending on the host architecture. - kCallConvHostFastCall = DETECTED_AT_COMPILE_TIME -#elif ASMJIT_ARCH_X86 - // X86 Host Support. - kCallConvHost = kCallConvX86CDecl, - kCallConvHostCDecl = kCallConvX86CDecl, - kCallConvHostStdCall = kCallConvX86StdCall, - kCallConvHostFastCall = - ASMJIT_CC_MSC ? kCallConvX86MsFastCall : - ASMJIT_CC_GCC ? kCallConvX86GccFastCall : - ASMJIT_CC_CLANG ? kCallConvX86GccFastCall : - ASMJIT_CC_CODEGEAR ? kCallConvX86BorlandFastCall : kCallConvNone -#elif ASMJIT_ARCH_X64 - // X64 Host Support. - kCallConvHost = ASMJIT_OS_WINDOWS ? kCallConvX64Win : kCallConvX64Unix, - // These don't exist in 64-bit mode. - kCallConvHostCDecl = kCallConvHost, - kCallConvHostStdCall = kCallConvHost, - kCallConvHostFastCall = kCallConvHost -#elif ASMJIT_ARCH_ARM32 -# if defined(__SOFTFP__) - kCallConvHost = kCallConvArm32SoftFP, -# else - kCallConvHost = kCallConvArm32HardFP, -# endif - // These don't exist on ARM. - kCallConvHostCDecl = kCallConvHost, - kCallConvHostStdCall = kCallConvHost, - kCallConvHostFastCall = kCallConvHost -#else -# error "[asmjit] Couldn't determine the target's calling convention." -#endif +ASMJIT_ENUM(JumpType) { + kJumpTypeNone = 0, //!< Instruction doesn't jump (regular instruction). + kJumpTypeDirect = 1, //!< Instruction is a unconditional (direct) jump. + kJumpTypeConditional = 2, //!< Instruction is a conditional jump. + kJumpTypeCall = 3, //!< Instruction is a function call. + kJumpTypeReturn = 4 //!< Instruction is a function return. }; +} // AnyInst namespace + // ============================================================================ -// [asmjit::ErrorCode] +// [asmjit::Error] // ============================================================================ +//! AsmJit error type (uint32_t). +typedef uint32_t Error; + //! AsmJit error codes. ASMJIT_ENUM(ErrorCode) { //! No error (success). @@ -491,72 +116,209 @@ ASMJIT_ENUM(ErrorCode) { kErrorInvalidArgument, //! Invalid state. + //! + //! If this error is returned it means that either you are doing something + //! wrong or AsmJit caught itself by doing something wrong. This error should + //! not be underestimated. kErrorInvalidState, - //! Invalid architecture. + //! Invalid or incompatible architecture. kErrorInvalidArch, //! The object is not initialized. kErrorNotInitialized, + //! The object is already initialized. + kErrorAlreadyInitialized, + + //! Built-in feature was disabled at compile time and it's not available. + kErrorFeatureNotEnabled, + + //! CodeHolder can't have attached more than one \ref Assembler at a time. + kErrorSlotOccupied, //! No code generated. //! - //! Returned by runtime if the code-generator contains no code. + //! Returned by runtime if the \ref CodeHolder contains no code. kErrorNoCodeGenerated, - - //! Code generated is too large to fit in memory reserved. - //! - //! Returned by `StaticRuntime` in case that the code generated is too large - //! to fit in the memory already reserved for it. + //! Code generated is larger than allowed. kErrorCodeTooLarge, + //! Attempt to use uninitialized label. + kErrorInvalidLabel, + //! Label index overflow - a single `Assembler` instance can hold more than + //! 2 billion labels (2147483391 to be exact). If there is an attempt to + //! create more labels this error is returned. + kErrorLabelIndexOverflow, //! Label is already bound. kErrorLabelAlreadyBound, + //! Label is already defined (named labels). + kErrorLabelAlreadyDefined, + //! Label name is too long. + kErrorLabelNameTooLong, + //! Label must always be local if it's anonymous (without a name). + kErrorInvalidLabelName, + //! Parent id passed to `CodeHolder::newNamedLabelId()` was invalid. + kErrorInvalidParentLabel, + //! Parent id specified for a non-local (global) label. + kErrorNonLocalLabelCantHaveParent, - //! Unknown instruction (an instruction ID is out of bounds or instruction - //! name is invalid). - kErrorUnknownInst, + //! Relocation index overflow. + kErrorRelocIndexOverflow, + //! Invalid relocation entry. + kErrorInvalidRelocEntry, - //! Illegal instruction. - //! - //! This status code can also be returned in X64 mode if AH, BH, CH or DH - //! registers have been used together with a REX prefix. The instruction - //! is not encodable in such case. - //! - //! Example of raising `kErrorIllegalInst` error. - //! - //! ~~~ - //! // Invalid address size. - //! a.mov(dword_ptr(eax), al); - //! - //! // Undecodable instruction - AH used with R10, however R10 can only be - //! // encoded by using REX prefix, which conflicts with AH. - //! a.mov(byte_ptr(r10), ah); - //! ~~~ - //! - //! NOTE: In debug mode assertion is raised instead of returning an error. - kErrorIllegalInst, + //! Invalid instruction. + kErrorInvalidInstruction, + //! Invalid register type. + kErrorInvalidRegType, + //! Invalid register kind. + kErrorInvalidRegKind, + //! Invalid register's physical id. + kErrorInvalidPhysId, + //! Invalid register's virtual id. + kErrorInvalidVirtId, + //! Invalid REX prefix. + kErrorInvalidRexPrefix, + //! Invalid mask register (not 'k'). + kErrorInvalidKMaskReg, + //! Invalid {k} use (not supported by the instruction). + kErrorInvalidKMaskUse, + //! Invalid {k}{z} use (not supported by the instruction). + kErrorInvalidKZeroUse, + //! Invalid broadcast - Currently only related to invalid use of AVX-512 {1tox}. + kErrorInvalidBroadcast, + //! Invalid 'embedded-rounding' {er} or 'suppress-all-exceptions' {sae} (AVX-512). + kErrorInvalidEROrSAE, + //! Invalid address used (not encodable). + kErrorInvalidAddress, + //! Invalid index register used in memory address (not encodable). + kErrorInvalidAddressIndex, + //! Invalid address scale (not encodable). + kErrorInvalidAddressScale, + //! Invalid use of 64-bit address. + kErrorInvalidAddress64Bit, + //! Invalid displacement (not encodable). + kErrorInvalidDisplacement, + //! Invalid segment. + kErrorInvalidSegment, - //! Illegal (unencodable) addressing used. - kErrorIllegalAddresing, + //! Mismatching operand size (size of multiple operands doesn't match the operation size). + kErrorOperandSizeMismatch, + //! Ambiguous operand size (memory has zero size while it's required to determine the operation type. + kErrorAmbiguousOperandSize, - //! Illegal (unencodable) displacement used. - //! - //! X86/X64 Specific - //! ---------------- - //! - //! Short form of jump instruction has been used, but the displacement is out - //! of bounds. - kErrorIllegalDisplacement, + //! Invalid TypeId. + kErrorInvalidTypeId, + //! Invalid use of a 8-bit GPB-HIGH register. + kErrorInvalidUseOfGpbHi, + //! Invalid use of a 64-bit GPQ register in 32-bit mode. + kErrorInvalidUseOfGpq, + //! Invalid use of an 80-bit float (TypeId::kF80). + kErrorInvalidUseOfF80, - //! A variable has been assigned more than once to a function argument (Compiler). - kErrorOverlappedArgs, + //! AsmJit requires a physical register, but no one is available. + kErrorNoMorePhysRegs, + //! A variable has been assigned more than once to a function argument (CodeCompiler). + kErrorOverlappedRegs, + //! Invalid register to hold stack arguments offset. + kErrorOverlappingStackRegWithRegArg, //! Count of AsmJit error codes. kErrorCount }; -//! \} +// ============================================================================ +// [asmjit::Internal] +// ============================================================================ + +namespace Internal { + +#if defined(ASMJIT_CUSTOM_ALLOC) && \ + defined(ASMJIT_CUSTOM_REALLOC) && \ + defined(ASMJIT_CUSTOM_FREE) +static ASMJIT_INLINE void* allocMemory(size_t size) noexcept { return ASMJIT_CUSTOM_ALLOC(size); } +static ASMJIT_INLINE void* reallocMemory(void* p, size_t size) noexcept { return ASMJIT_CUSTOM_REALLOC(p, size); } +static ASMJIT_INLINE void releaseMemory(void* p) noexcept { ASMJIT_CUSTOM_FREE(p); } +#elif !defined(ASMJIT_CUSTOM_ALLOC) && \ + !defined(ASMJIT_CUSTOM_REALLOC) && \ + !defined(ASMJIT_CUSTOM_FREE) +static ASMJIT_INLINE void* allocMemory(size_t size) noexcept { return ::malloc(size); } +static ASMJIT_INLINE void* reallocMemory(void* p, size_t size) noexcept { return ::realloc(p, size); } +static ASMJIT_INLINE void releaseMemory(void* p) noexcept { ::free(p); } +#else +# error "[asmjit] You must provide either none or all of ASMJIT_CUSTOM_[ALLOC|REALLOC|FREE]" +#endif + +//! Cast designed to cast between function and void* pointers. +template +static ASMJIT_INLINE Dst ptr_cast(Src p) noexcept { return (Dst)p; } + +} // Internal namespace + +template +static ASMJIT_INLINE Func ptr_as_func(void* func) noexcept { return Internal::ptr_cast(func); } + +template +static ASMJIT_INLINE void* func_as_ptr(Func func) noexcept { return Internal::ptr_cast(func); } + +// ============================================================================ +// [asmjit::DebugUtils] +// ============================================================================ + +namespace DebugUtils { + +//! Returns the error `err` passed. +//! +//! Provided for debugging purposes. Putting a breakpoint inside `errored` can +//! help with tracing the origin of any error reported / returned by AsmJit. +static ASMJIT_INLINE Error errored(Error err) noexcept { return err; } + +//! Get a printable version of `asmjit::Error` code. +ASMJIT_API const char* errorAsString(Error err) noexcept; + +//! Called to output debugging message(s). +ASMJIT_API void debugOutput(const char* str) noexcept; + +//! Called on assertion failure. +//! +//! \param file Source file name where it happened. +//! \param line Line in the source file. +//! \param msg Message to display. +//! +//! If you have problems with assertions put a breakpoint at assertionFailed() +//! function (asmjit/base/globals.cpp) and check the call stack to locate the +//! failing code. +ASMJIT_API void ASMJIT_NORETURN assertionFailed(const char* file, int line, const char* msg) noexcept; + +#if defined(ASMJIT_DEBUG) +# define ASMJIT_ASSERT(exp) \ + do { \ + if (ASMJIT_LIKELY(exp)) \ + break; \ + ::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, #exp); \ + } while (0) +# define ASMJIT_NOT_REACHED() \ + do { \ + ::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, \ + "ASMJIT_NOT_REACHED has been reached"); \ + ASMJIT_ASSUME(0); \ + } while (0) +#else +# define ASMJIT_ASSERT(exp) ASMJIT_NOP +# define ASMJIT_NOT_REACHED() ASMJIT_ASSUME(0) +#endif // DEBUG + +//! \internal +//! +//! Used by AsmJit to propagate a possible `Error` produced by `...` to the caller. +#define ASMJIT_PROPAGATE(...) \ + do { \ + ::asmjit::Error _err = __VA_ARGS__; \ + if (ASMJIT_UNLIKELY(_err)) \ + return _err; \ + } while (0) + +} // DebugUtils namespace // ============================================================================ // [asmjit::Init / NoInit] @@ -570,97 +332,12 @@ struct _NoInit {}; static const _NoInit NoInit = {}; #endif // !ASMJIT_DOCGEN -// ============================================================================ -// [asmjit::DebugUtils] -// ============================================================================ - -namespace DebugUtils { - -//! Get a printable version of `asmjit::Error` value. -ASMJIT_API const char* errorAsString(Error err) noexcept; - -//! \addtogroup asmjit_base -//! \{ - -//! Called in debug build to output a debugging message caused by assertion -//! failure or tracing. -ASMJIT_API void debugOutput(const char* str) noexcept; - -//! Called in debug build on assertion failure. -//! -//! \param file Source file name where it happened. -//! \param line Line in the source file. -//! \param msg Message to display. -//! -//! If you have problems with assertions put a breakpoint at assertionFailed() -//! function (asmjit/base/globals.cpp) and check the call stack to locate the -//! failing code. -ASMJIT_API void ASMJIT_NORETURN assertionFailed(const char* file, int line, const char* msg) noexcept; - //! \} -} // DebugUtils namespace } // asmjit namespace -// ============================================================================ -// [ASMJIT_ASSERT] -// ============================================================================ - -#if defined(ASMJIT_DEBUG) -# define ASMJIT_ASSERT(exp) \ - do { \ - if (!(exp)) { \ - ::asmjit::DebugUtils::assertionFailed( \ - __FILE__ + ::asmjit::DebugUtils::kSourceRelativePathOffset, \ - __LINE__, \ - #exp); \ - } \ - } while (0) -# define ASMJIT_NOT_REACHED() \ - ::asmjit::DebugUtils::assertionFailed( \ - __FILE__ + ::asmjit::DebugUtils::kSourceRelativePathOffset, \ - __LINE__, \ - "MUST NOT BE REACHED") -#else -# define ASMJIT_ASSERT(exp) ASMJIT_NOP -# define ASMJIT_NOT_REACHED() ASMJIT_ASSUME(0) -#endif // DEBUG - -// ============================================================================ -// [ASMJIT_PROPAGATE_ERROR] -// ============================================================================ - -//! \internal -//! -//! Used by AsmJit to return the `_Exp_` result if it's an error. -#define ASMJIT_PROPAGATE_ERROR(_Exp_) \ - do { \ - ::asmjit::Error _errval = (_Exp_); \ - if (_errval != ::asmjit::kErrorOk) \ - return _errval; \ - } while (0) - -// ============================================================================ -// [asmjit_cast<>] -// ============================================================================ - -//! \addtogroup asmjit_base -//! \{ - -//! Cast used to cast pointer to function. It's like reinterpret_cast<>, -//! but uses internally C style cast to work with MinGW. -//! -//! If you are using single compiler and `reinterpret_cast<>` works for you, -//! there is no reason to use `asmjit_cast<>`. If you are writing -//! cross-platform software with various compiler support, consider using -//! `asmjit_cast<>` instead of `reinterpret_cast<>`. -template -static ASMJIT_INLINE T asmjit_cast(Z* p) noexcept { return (T)p; } - -//! \} - // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" // [Guard] #endif // _ASMJIT_BASE_GLOBALS_H diff --git a/src/asmjit/base/hlstream.cpp b/src/asmjit/base/hlstream.cpp deleted file mode 100644 index b3f5ab3..0000000 --- a/src/asmjit/base/hlstream.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Export] -#define ASMJIT_EXPORTS - -// [Dependencies] -#include "../base/hlstream.h" - -// [Api-Begin] -#include "../apibegin.h" - -namespace asmjit { -} // asmjit namespace - -// [Api-End] -#include "../apiend.h" diff --git a/src/asmjit/base/hlstream.h b/src/asmjit/base/hlstream.h deleted file mode 100644 index 89d25df..0000000 --- a/src/asmjit/base/hlstream.h +++ /dev/null @@ -1,1174 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Guard] -#ifndef _ASMJIT_BASE_HLSTREAM_H -#define _ASMJIT_BASE_HLSTREAM_H - -#include "../build.h" -#if !defined(ASMJIT_DISABLE_COMPILER) - -// [Dependencies] -#include "../base/assembler.h" -#include "../base/operand.h" - -// TODO: Cannot depend on it. -#include "../base/compilerfunc.h" - -// [Api-Begin] -#include "../apibegin.h" - -namespace asmjit { - -// ============================================================================ -// [Forward Declarations] -// ============================================================================ - -class Compiler; -struct VarData; -struct VarState; -struct VarMap; - -class HLInst; -class HLJump; -class HLLabel; -class HLSentinel; - -//! \addtogroup asmjit_base -//! \{ - -// ============================================================================ -// [asmjit::HLNode] -// ============================================================================ - -//! Base node (HL). -//! -//! Every node represents an abstract instruction, directive, label, or macro -//! instruction that can be serialized to `Assembler`. -class HLNode { - public: - ASMJIT_NO_COPY(HLNode) - - // -------------------------------------------------------------------------- - // [Type] - // -------------------------------------------------------------------------- - - //! Type of \ref HLNode. - ASMJIT_ENUM(Type) { - //! Invalid node (internal, don't use). - kTypeNone = 0, - - // -------------------------------------------------------------------------- - // [Low-Level - Assembler / Compiler] - // -------------------------------------------------------------------------- - - //! Node is \ref HLInst or \ref HLJump. - kTypeInst, - //! Node is \ref HLData. - kTypeData, - //! Node is \ref HLAlign. - kTypeAlign, - //! Node is \ref HLLabel. - kTypeLabel, - //! Node is \ref HLComment. - kTypeComment, - //! Node is \ref HLSentinel. - kTypeSentinel, - - // -------------------------------------------------------------------------- - // [High-Level - Compiler-Only] - // -------------------------------------------------------------------------- - - //! Node is \ref HLHint. - kTypeHint, - //! Node is \ref HLFunc. - kTypeFunc, - //! Node is \ref HLRet. - kTypeRet, - //! Node is \ref HLCall. - kTypeCall, - //! Node is \ref HLCallArg. - kTypeCallArg - }; - - // -------------------------------------------------------------------------- - // [Flags] - // -------------------------------------------------------------------------- - - ASMJIT_ENUM(Flags) { - //! Whether the node has been translated, thus contains only registers. - kFlagIsTranslated = 0x0001, - - //! Whether the node was scheduled - possibly reordered, but basically this - //! is a mark that is set by scheduler after the node has been visited. - kFlagIsScheduled = 0x0002, - - //! Whether the node can be safely removed by the `Compiler` in case it's - //! unreachable. - kFlagIsRemovable = 0x0004, - - //! Whether the node is informative only and can be safely removed. - kFlagIsInformative = 0x0008, - - //! Whether the `HLInst` is a jump. - kFlagIsJmp = 0x0010, - //! Whether the `HLInst` is a conditional jump. - kFlagIsJcc = 0x0020, - - //! Whether the `HLInst` is an unconditinal jump or conditional jump that is - //! likely to be taken. - kFlagIsTaken = 0x0040, - - //! Whether the `HLNode` will return from a function. - //! - //! This flag is used by both `HLSentinel` and `HLRet`. - kFlagIsRet = 0x0080, - - //! Whether the instruction is special. - kFlagIsSpecial = 0x0100, - - //! Whether the instruction is an FPU instruction. - kFlagIsFp = 0x0200 - }; - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `HLNode`. - //! - //! NOTE: Always use compiler to create nodes. - ASMJIT_INLINE HLNode(Compiler* compiler, uint32_t type) noexcept; // Defined-Later. - - //! Destroy the `HLNode`. - //! - //! NOTE: Nodes are zone allocated, there should be no code in the destructor. - ASMJIT_INLINE ~HLNode() noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors - List] - // -------------------------------------------------------------------------- - - //! Get previous node in the compiler stream. - ASMJIT_INLINE HLNode* getPrev() const noexcept { return _prev; } - //! Get next node in the compiler stream. - ASMJIT_INLINE HLNode* getNext() const noexcept { return _next; } - - // -------------------------------------------------------------------------- - // [Accessors - Comment] - // -------------------------------------------------------------------------- - - //! Get an inline comment string. - ASMJIT_INLINE const char* getComment() const noexcept { return _comment; } - //! Set an inline comment string to `comment`. - ASMJIT_INLINE void setComment(const char* comment) noexcept { _comment = comment; } - - // -------------------------------------------------------------------------- - // [Accessors - Type and Flags] - // -------------------------------------------------------------------------- - - //! Get the node type, see \ref Type. - ASMJIT_INLINE uint32_t getType() const noexcept { return _type; } - //! Get the node flags. - ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; } - - //! Get whether the instruction has flag `flag`. - ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (static_cast(_flags) & flag) != 0; } - //! Set node flags to `flags`. - ASMJIT_INLINE void setFlags(uint32_t flags) noexcept { _flags = static_cast(flags); } - //! Add instruction `flags`. - ASMJIT_INLINE void orFlags(uint32_t flags) noexcept { _flags |= static_cast(flags); } - //! And instruction `flags`. - ASMJIT_INLINE void andFlags(uint32_t flags) noexcept { _flags &= static_cast(flags); } - //! Clear instruction `flags`. - ASMJIT_INLINE void andNotFlags(uint32_t flags) noexcept { _flags &= ~static_cast(flags); } - - //! Get whether the node has beed fetched. - ASMJIT_INLINE bool isFetched() const noexcept { return _flowId != 0; } - //! Get whether the node has been translated. - ASMJIT_INLINE bool isTranslated() const noexcept { return hasFlag(kFlagIsTranslated); } - //! Get whether the node has been translated. - ASMJIT_INLINE bool isScheduled() const noexcept { return hasFlag(kFlagIsScheduled); } - - //! Get whether the node is removable if it's in unreachable code block. - ASMJIT_INLINE bool isRemovable() const noexcept { return hasFlag(kFlagIsRemovable); } - //! Get whether the node is informative only (comment, hint). - ASMJIT_INLINE bool isInformative() const noexcept { return hasFlag(kFlagIsInformative); } - - //! Whether the node is `HLLabel`. - ASMJIT_INLINE bool isLabel() const noexcept { return _type == kTypeLabel; } - //! Whether the `HLInst` node is an unconditional jump. - ASMJIT_INLINE bool isJmp() const noexcept { return hasFlag(kFlagIsJmp); } - //! Whether the `HLInst` node is a conditional jump. - ASMJIT_INLINE bool isJcc() const noexcept { return hasFlag(kFlagIsJcc); } - //! Whether the `HLInst` node is a conditional/unconditional jump. - ASMJIT_INLINE bool isJmpOrJcc() const noexcept { return hasFlag(kFlagIsJmp | kFlagIsJcc); } - //! Whether the `HLInst` node is a return. - ASMJIT_INLINE bool isRet() const noexcept { return hasFlag(kFlagIsRet); } - - //! Get whether the node is `HLInst` and the instruction is special. - ASMJIT_INLINE bool isSpecial() const noexcept { return hasFlag(kFlagIsSpecial); } - //! Get whether the node is `HLInst` and the instruction uses x87-FPU. - ASMJIT_INLINE bool isFp() const noexcept { return hasFlag(kFlagIsFp); } - - // -------------------------------------------------------------------------- - // [Accessors - FlowId] - // -------------------------------------------------------------------------- - - //! Get flow index. - ASMJIT_INLINE uint32_t getFlowId() const noexcept { return _flowId; } - //! Set flow index. - ASMJIT_INLINE void setFlowId(uint32_t flowId) noexcept { _flowId = flowId; } - - // -------------------------------------------------------------------------- - // [Accessors - TokenId] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE bool hasTokenId(uint32_t id) const noexcept { return _tokenId == id; } - ASMJIT_INLINE uint32_t getTokenId() const noexcept { return _tokenId; } - ASMJIT_INLINE void setTokenId(uint32_t id) noexcept { _tokenId = id; } - - // -------------------------------------------------------------------------- - // [Accessors - VarMap] - // -------------------------------------------------------------------------- - - //! Get whether node contains variable allocation instructions. - ASMJIT_INLINE bool hasMap() const noexcept { return _map != nullptr; } - //! Get variable allocation instructions. - ASMJIT_INLINE VarMap* getMap() const noexcept { return _map; } - //! Get variable allocation instructions casted to `T*`. - template - ASMJIT_INLINE T* getMap() const noexcept { return static_cast(_map); } - //! Set variable allocation instructions. - ASMJIT_INLINE void setMap(VarMap* map) noexcept { _map = map; } - - // -------------------------------------------------------------------------- - // [Accessors - VarState] - // -------------------------------------------------------------------------- - - //! Get whether the node has an associated `VarState`. - ASMJIT_INLINE bool hasState() const noexcept { return _state != nullptr; } - //! Get node state. - ASMJIT_INLINE VarState* getState() const noexcept { return _state; } - //! Get node state casted to `T*`. - template - ASMJIT_INLINE T* getState() const noexcept { return static_cast(_state); } - //! Set node state. - ASMJIT_INLINE void setState(VarState* state) noexcept { _state = state; } - - // -------------------------------------------------------------------------- - // [Accessors - Liveness] - // -------------------------------------------------------------------------- - - //! Get whether the node has variable liveness bits. - ASMJIT_INLINE bool hasLiveness() const noexcept { return _liveness != nullptr; } - //! Get variable liveness bits. - ASMJIT_INLINE BitArray* getLiveness() const noexcept { return _liveness; } - //! Set variable liveness bits. - ASMJIT_INLINE void setLiveness(BitArray* liveness) noexcept { _liveness = liveness; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Previous node. - HLNode* _prev; - //! Next node. - HLNode* _next; - - //! Node type, see \ref Type. - uint8_t _type; - //! Count of operands (if the node has operands, otherwise zero). - uint8_t _opCount; - //! Flags, different meaning for every type of the node. - uint16_t _flags; - - //! Flow index. - uint32_t _flowId; - - //! Processing token ID. - //! - //! Used by some algorithms to mark nodes as visited. If the token is - //! generated in an incrementing way the visitor can just mark nodes it - //! visits and them compare the `HLNode`s token with it's local token. - //! If they match the node has been visited already. Then the visitor - //! doesn't need to clean things up as the next time the token will be - //! different. - uint32_t _tokenId; - - // TODO: 32-bit gap - - //! Inline comment string, initially set to nullptr. - const char* _comment; - - //! Variable mapping (VarAttr to VarData), initially nullptr, filled during - //! fetch phase. - VarMap* _map; - - //! Variable liveness bits (initially nullptr, filled by analysis phase). - BitArray* _liveness; - - //! Saved state. - //! - //! Initially nullptr, not all nodes have saved state, only branch/flow control - //! nodes. - VarState* _state; -}; - -// ============================================================================ -// [asmjit::HLInst] -// ============================================================================ - -//! Instruction (HL). -//! -//! Wraps an instruction with its options and operands. -class HLInst : public HLNode { - public: - ASMJIT_NO_COPY(HLInst) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `HLInst` instance. - ASMJIT_INLINE HLInst(Compiler* compiler, uint32_t instId, uint32_t instOptions, Operand* opList, uint32_t opCount) noexcept - : HLNode(compiler, kTypeInst) { - - orFlags(kFlagIsRemovable); - _instId = static_cast(instId); - _reserved = 0; - _instOptions = instOptions; - - _opCount = static_cast(opCount); - _opList = opList; - - _updateMemOp(); - } - - //! Destroy the `HLInst` instance. - ASMJIT_INLINE ~HLInst() noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get the instruction id, see `X86InstId`. - ASMJIT_INLINE uint32_t getInstId() const noexcept { return _instId; } - //! Set the instruction id to `instId`. - //! - //! NOTE: Please do not modify instruction code if you don't know what you - //! are doing. Incorrect instruction code and/or operands can cause random - //! errors in production builds and will most probably trigger assertion - //! failures in debug builds. - ASMJIT_INLINE void setInstId(uint32_t instId) noexcept { _instId = static_cast(instId); } - - //! Whether the instruction is either a jump or a conditional jump likely to - //! be taken. - ASMJIT_INLINE bool isTaken() const noexcept { return hasFlag(kFlagIsTaken); } - - //! Get emit options. - ASMJIT_INLINE uint32_t getOptions() const noexcept { return _instOptions; } - //! Set emit options. - ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _instOptions = options; } - //! Add emit options. - ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _instOptions |= options; } - //! Mask emit options. - ASMJIT_INLINE void andOptions(uint32_t options) noexcept { _instOptions &= options; } - //! Clear emit options. - ASMJIT_INLINE void delOptions(uint32_t options) noexcept { _instOptions &= ~options; } - - //! Get operands count. - ASMJIT_INLINE uint32_t getOpCount() const noexcept { return _opCount; } - //! Get operands list. - ASMJIT_INLINE Operand* getOpList() noexcept { return _opList; } - //! \overload - ASMJIT_INLINE const Operand* getOpList() const noexcept { return _opList; } - - //! Get whether the instruction contains a memory operand. - ASMJIT_INLINE bool hasMemOp() const noexcept { return _memOpIndex != 0xFF; } - //! Get memory operand. - //! - //! NOTE: Can only be called if the instruction has such operand, - //! see `hasMemOp()`. - ASMJIT_INLINE BaseMem* getMemOp() const noexcept { - ASMJIT_ASSERT(hasMemOp()); - return static_cast(&_opList[_memOpIndex]); - } - //! \overload - template - ASMJIT_INLINE T* getMemOp() const noexcept { - ASMJIT_ASSERT(hasMemOp()); - return static_cast(&_opList[_memOpIndex]); - } - - //! Set memory operand index, `0xFF` means no memory operand. - ASMJIT_INLINE void setMemOpIndex(uint32_t index) noexcept { _memOpIndex = static_cast(index); } - //! Reset memory operand index to `0xFF` (no operand). - ASMJIT_INLINE void resetMemOpIndex() noexcept { _memOpIndex = 0xFF; } - - // -------------------------------------------------------------------------- - // [Utils] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE void _updateMemOp() noexcept { - Operand* opList = getOpList(); - uint32_t opCount = getOpCount(); - - uint32_t i; - for (i = 0; i < opCount; i++) - if (opList[i].isMem()) - goto L_Update; - i = 0xFF; - -L_Update: - setMemOpIndex(i); - } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Instruction ID, see `InstId`. - uint16_t _instId; - //! \internal - uint8_t _memOpIndex; - //! \internal - uint8_t _reserved; - //! Instruction options, see `InstOptions`. - uint32_t _instOptions; - - //! Operands list. - Operand* _opList; -}; - -// ============================================================================ -// [asmjit::HLJump] -// ============================================================================ - -//! Conditional or direct jump (HL). -//! -//! Extension of `HLInst` node, which stores more information about the jump. -class HLJump : public HLInst { - public: - ASMJIT_NO_COPY(HLJump) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE HLJump(Compiler* compiler, uint32_t code, uint32_t options, Operand* opList, uint32_t opCount) noexcept - : HLInst(compiler, code, options, opList, opCount), - _target(nullptr), - _jumpNext(nullptr) {} - ASMJIT_INLINE ~HLJump() noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE HLLabel* getTarget() const noexcept { return _target; } - ASMJIT_INLINE HLJump* getJumpNext() const noexcept { return _jumpNext; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Target node. - HLLabel* _target; - //! Next jump to the same target in a single linked-list. - HLJump* _jumpNext; -}; - -// ============================================================================ -// [asmjit::HLData] -// ============================================================================ - -//! Data (HL). -//! -//! Wraps `.data` directive. The node contains data that will be placed at the -//! node's position in the assembler stream. The data is considered to be RAW; -//! no analysis nor byte-order conversion is performed on RAW data. -class HLData : public HLNode { - public: - ASMJIT_NO_COPY(HLData) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - enum { kInlineBufferSize = 12 }; - - //! Create a new `HLData` instance. - ASMJIT_INLINE HLData(Compiler* compiler, void* data, uint32_t size) noexcept - : HLNode(compiler, kTypeData) { - - _size = size; - if (size <= kInlineBufferSize) { - if (data != nullptr) - ::memcpy(_data.buf, data, size); - } - else { - _data.ptr = static_cast(data); - } - } - - //! Destroy the `HLData` instance. - ASMJIT_INLINE ~HLData() noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get size of the data. - uint32_t getSize() const noexcept { return _size; } - //! Get pointer to the data. - uint8_t* getData() const noexcept { return _size <= kInlineBufferSize ? const_cast(_data.buf) : _data.ptr; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - union { - //! data buffer. - uint8_t buf[kInlineBufferSize]; - //! Data buffer. - uint8_t* ptr; - } _data; - - //! Size of the data. - uint32_t _size; -}; - -// ============================================================================ -// [asmjit::HLAlign] -// ============================================================================ - -//! Align directive (HL). -//! -//! Wraps `.align` directive. -class HLAlign : public HLNode { - public: - ASMJIT_NO_COPY(HLAlign) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `HLAlign` instance. - ASMJIT_INLINE HLAlign(Compiler* compiler, uint32_t alignMode, uint32_t offset) noexcept - : HLNode(compiler, kTypeAlign) { - - _alignMode = alignMode; - _offset = offset; - } - - //! Destroy the `HLAlign` instance. - ASMJIT_INLINE ~HLAlign() noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get align mode. - ASMJIT_INLINE uint32_t getAlignMode() const noexcept { return _alignMode; } - //! Set align mode. - ASMJIT_INLINE void setAlignMode(uint32_t alignMode) noexcept { _alignMode = alignMode; } - - //! Get align offset in bytes. - ASMJIT_INLINE uint32_t getOffset() const noexcept { return _offset; } - //! Set align offset in bytes to `offset`. - ASMJIT_INLINE void setOffset(uint32_t offset) noexcept { _offset = offset; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Align mode, see \ref AlignMode. - uint32_t _alignMode; - //! Align offset (in bytes). - uint32_t _offset; -}; - -// ============================================================================ -// [asmjit::HLLabel] -// ============================================================================ - -//! label (HL). -class HLLabel : public HLNode { - public: - ASMJIT_NO_COPY(HLLabel) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `HLLabel` instance. - ASMJIT_INLINE HLLabel(Compiler* compiler, uint32_t labelId) noexcept - : HLNode(compiler, kTypeLabel) { - - _id = labelId; - _numRefs = 0; - _from = nullptr; - } - - //! Destroy the `HLLabel` instance. - ASMJIT_INLINE ~HLLabel() noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get target label. - ASMJIT_INLINE Label getLabel() const noexcept { return Label(_id); } - //! Get target label id. - ASMJIT_INLINE uint32_t getLabelId() const noexcept { return _id; } - - //! Get first jmp instruction. - ASMJIT_INLINE HLJump* getFrom() const noexcept { return _from; } - - //! Get whether the node has assigned state. - ASMJIT_INLINE bool hasState() const noexcept { return _state != nullptr; } - //! Get state for this target. - ASMJIT_INLINE VarState* getState() const noexcept { return _state; } - //! Set state for this target. - ASMJIT_INLINE void setState(VarState* state) noexcept { _state = state; } - - //! Get number of jumps to this target. - ASMJIT_INLINE uint32_t getNumRefs() const noexcept { return _numRefs; } - //! Set number of jumps to this target. - ASMJIT_INLINE void setNumRefs(uint32_t i) noexcept { _numRefs = i; } - - //! Add number of jumps to this target. - ASMJIT_INLINE void addNumRefs(uint32_t i = 1) noexcept { _numRefs += i; } - //! Subtract number of jumps to this target. - ASMJIT_INLINE void subNumRefs(uint32_t i = 1) noexcept { _numRefs -= i; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Label id. - uint32_t _id; - //! Count of jumps here. - uint32_t _numRefs; - - //! First jump instruction that points to this target (label). - HLJump* _from; -}; - -// ============================================================================ -// [asmjit::HLComment] -// ============================================================================ - -//! Comment (HL). -class HLComment : public HLNode { - public: - ASMJIT_NO_COPY(HLComment) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `HLComment` instance. - ASMJIT_INLINE HLComment(Compiler* compiler, const char* comment) noexcept - : HLNode(compiler, kTypeComment) { - - orFlags(kFlagIsRemovable | kFlagIsInformative); - _comment = comment; - } - - //! Destroy the `HLComment` instance. - ASMJIT_INLINE ~HLComment() noexcept {} -}; - -// ============================================================================ -// [asmjit::HLSentinel] -// ============================================================================ - -//! Sentinel (HL). -class HLSentinel : public HLNode { - public: - ASMJIT_NO_COPY(HLSentinel) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `HLSentinel` instance. - ASMJIT_INLINE HLSentinel(Compiler* compiler) noexcept - : HLNode(compiler, kTypeSentinel) { - orFlags(kFlagIsRet); - } - - //! Destroy the `HLSentinel` instance. - ASMJIT_INLINE ~HLSentinel() noexcept {} -}; - -// ============================================================================ -// [asmjit::HLHint] -// ============================================================================ - -//! Hint node. -class HLHint : public HLNode { - public: - ASMJIT_NO_COPY(HLHint) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `HLHint` instance. - ASMJIT_INLINE HLHint(Compiler* compiler, VarData* vd, uint32_t hint, uint32_t value) noexcept - : HLNode(compiler, kTypeHint) { - - orFlags(kFlagIsRemovable | kFlagIsInformative); - _vd = vd; - _hint = hint; - _value = value; - } - - //! Destroy the `HLHint` instance. - ASMJIT_INLINE ~HLHint() noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get variable. - ASMJIT_INLINE VarData* getVd() const noexcept { return _vd; } - - //! Get hint it (see `kVarHint)`. - ASMJIT_INLINE uint32_t getHint() const noexcept { return _hint; } - //! Set hint it (see `kVarHint)`. - ASMJIT_INLINE void setHint(uint32_t hint) noexcept { _hint = hint; } - - //! Get hint value. - ASMJIT_INLINE uint32_t getValue() const noexcept { return _value; } - //! Set hint value. - ASMJIT_INLINE void setValue(uint32_t value) noexcept { _value = value; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Variable. - VarData* _vd; - //! Hint id. - uint32_t _hint; - //! Value. - uint32_t _value; -}; - -// ============================================================================ -// [asmjit::HLFunc] -// ============================================================================ - -//! Function (HL). -class HLFunc : public HLNode { - public: - ASMJIT_NO_COPY(HLFunc) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `HLFunc` instance. - //! - //! Always use `Compiler::addFunc()` to create an `HLFunc` instance. - ASMJIT_INLINE HLFunc(Compiler* compiler) noexcept - : HLNode(compiler, kTypeFunc), - _entryNode(nullptr), - _exitNode(nullptr), - _decl(nullptr), - _end(nullptr), - _args(nullptr), - _funcHints(Utils::mask(kFuncHintNaked)), - _funcFlags(0), - _expectedStackAlignment(0), - _requiredStackAlignment(0), - _redZoneSize(0), - _spillZoneSize(0), - _argStackSize(0), - _memStackSize(0), - _callStackSize(0) {} - - //! Destroy the `HLFunc` instance. - ASMJIT_INLINE ~HLFunc() noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get function entry `HLLabel`. - ASMJIT_INLINE HLLabel* getEntryNode() const noexcept { return _entryNode; } - //! Get function exit `HLLabel`. - ASMJIT_INLINE HLLabel* getExitNode() const noexcept { return _exitNode; } - - //! Get function entry label. - ASMJIT_INLINE Label getEntryLabel() const noexcept { return _entryNode->getLabel(); } - //! Get function exit label. - ASMJIT_INLINE Label getExitLabel() const noexcept { return _exitNode->getLabel(); } - - //! Get the function end sentinel. - ASMJIT_INLINE HLSentinel* getEnd() const noexcept { return _end; } - //! Get function declaration. - ASMJIT_INLINE FuncDecl* getDecl() const noexcept { return _decl; } - - //! Get arguments count. - ASMJIT_INLINE uint32_t getNumArgs() const noexcept { return _decl->getNumArgs(); } - //! Get arguments list. - ASMJIT_INLINE VarData** getArgs() const noexcept { return _args; } - - //! Get argument at `i`. - ASMJIT_INLINE VarData* getArg(uint32_t i) const noexcept { - ASMJIT_ASSERT(i < getNumArgs()); - return _args[i]; - } - - //! Set argument at `i`. - ASMJIT_INLINE void setArg(uint32_t i, VarData* vd) noexcept { - ASMJIT_ASSERT(i < getNumArgs()); - _args[i] = vd; - } - - //! Reset argument at `i`. - ASMJIT_INLINE void resetArg(uint32_t i) noexcept { - ASMJIT_ASSERT(i < getNumArgs()); - _args[i] = nullptr; - } - - //! Get function hints. - ASMJIT_INLINE uint32_t getFuncHints() const noexcept { return _funcHints; } - //! Get function flags. - ASMJIT_INLINE uint32_t getFuncFlags() const noexcept { return _funcFlags; } - - //! Get whether the _funcFlags has `flag` - ASMJIT_INLINE bool hasFuncFlag(uint32_t flag) const noexcept { return (_funcFlags & flag) != 0; } - //! Set function `flag`. - ASMJIT_INLINE void addFuncFlags(uint32_t flags) noexcept { _funcFlags |= flags; } - //! Clear function `flag`. - ASMJIT_INLINE void clearFuncFlags(uint32_t flags) noexcept { _funcFlags &= ~flags; } - - //! Get whether the function is naked. - ASMJIT_INLINE bool isNaked() const noexcept { return hasFuncFlag(kFuncFlagIsNaked); } - //! Get whether the function is also a caller. - ASMJIT_INLINE bool isCaller() const noexcept { return hasFuncFlag(kFuncFlagIsCaller); } - //! Get whether the required stack alignment is lower than expected one, - //! thus it has to be aligned manually. - ASMJIT_INLINE bool isStackMisaligned() const noexcept { return hasFuncFlag(kFuncFlagIsStackMisaligned); } - //! Get whether the stack pointer is adjusted inside function prolog/epilog. - ASMJIT_INLINE bool isStackAdjusted() const noexcept { return hasFuncFlag(kFuncFlagIsStackAdjusted); } - - //! Get whether the function is finished. - ASMJIT_INLINE bool isFinished() const noexcept { return hasFuncFlag(kFuncFlagIsFinished); } - - //! Get expected stack alignment. - ASMJIT_INLINE uint32_t getExpectedStackAlignment() const noexcept { - return _expectedStackAlignment; - } - - //! Set expected stack alignment. - ASMJIT_INLINE void setExpectedStackAlignment(uint32_t alignment) noexcept { - _expectedStackAlignment = alignment; - } - - //! Get required stack alignment. - ASMJIT_INLINE uint32_t getRequiredStackAlignment() const noexcept { - return _requiredStackAlignment; - } - - //! Set required stack alignment. - ASMJIT_INLINE void setRequiredStackAlignment(uint32_t alignment) noexcept { - _requiredStackAlignment = alignment; - } - - //! Update required stack alignment so it's not lower than expected - //! stack alignment. - ASMJIT_INLINE void updateRequiredStackAlignment() noexcept { - if (_requiredStackAlignment <= _expectedStackAlignment) { - _requiredStackAlignment = _expectedStackAlignment; - clearFuncFlags(kFuncFlagIsStackMisaligned); - } - else { - addFuncFlags(kFuncFlagIsStackMisaligned); - } - } - - //! Set stack "Red Zone" size. - ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _redZoneSize; } - //! Get stack "Red Zone" size. - ASMJIT_INLINE void setRedZoneSize(uint32_t s) noexcept { _redZoneSize = static_cast(s); } - - //! Set stack "Spill Zone" size. - ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _spillZoneSize; } - //! Get stack "Spill Zone" size. - ASMJIT_INLINE void setSpillZoneSize(uint32_t s) noexcept { _spillZoneSize = static_cast(s); } - - //! Get stack size used by function arguments. - ASMJIT_INLINE uint32_t getArgStackSize() const noexcept { return _argStackSize; } - - //! Get stack size used by variables and memory allocated on the stack. - ASMJIT_INLINE uint32_t getMemStackSize() const noexcept { return _memStackSize; } - - //! Get stack size used by function calls. - ASMJIT_INLINE uint32_t getCallStackSize() const noexcept { return _callStackSize; } - //! Merge stack size used by function call with `s`. - ASMJIT_INLINE void mergeCallStackSize(uint32_t s) noexcept { if (_callStackSize < s) _callStackSize = s; } - - // -------------------------------------------------------------------------- - // [Hints] - // -------------------------------------------------------------------------- - - //! Set function hint. - ASMJIT_INLINE void setHint(uint32_t hint, uint32_t value) noexcept { - ASMJIT_ASSERT(hint <= 31); - ASMJIT_ASSERT(value <= 1); - - _funcHints &= ~(1 << hint); - _funcHints |= (value << hint); - } - - //! Get function hint. - ASMJIT_INLINE uint32_t getHint(uint32_t hint) const noexcept { - ASMJIT_ASSERT(hint <= 31); - return (_funcHints >> hint) & 0x1; - } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Function entry. - HLLabel* _entryNode; - //! Function exit. - HLLabel* _exitNode; - - //! Function declaration. - FuncDecl* _decl; - //! Function end. - HLSentinel* _end; - - //! Arguments list as `VarData`. - VarData** _args; - - //! Function hints; - uint32_t _funcHints; - //! Function flags. - uint32_t _funcFlags; - - //! Expected stack alignment (we depend on this value). - //! - //! NOTE: It can be global alignment given by the OS or described by the - //! target platform ABI. - uint32_t _expectedStackAlignment; - //! Required stack alignment (required by SIMD instructions). - uint32_t _requiredStackAlignment; - - //! The "Red Zone" size - count of bytes which might be accessed by a left - //! function without adjusting the stack pointer (`esp` or `rsp`) (AMD64 ABI). - uint16_t _redZoneSize; - - //! The "Spill Zone" size - count of bytes after the function return address - //! that can be used by the function to spill variables in (WIN64 ABI). - uint16_t _spillZoneSize; - - //! Stack size needed for function arguments. - uint32_t _argStackSize; - //! Stack size needed for all variables and memory allocated on the stack. - uint32_t _memStackSize; - //! Stack size needed to call other functions. - uint32_t _callStackSize; -}; - -// ============================================================================ -// [asmjit::HLRet] -// ============================================================================ - -//! Function return (HL). -class HLRet : public HLNode { - public: - ASMJIT_NO_COPY(HLRet) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `HLRet` instance. - ASMJIT_INLINE HLRet(Compiler* compiler, const Operand& o0, const Operand& o1) noexcept - : HLNode(compiler, kTypeRet) { - - orFlags(kFlagIsRet); - _ret[0] = o0; - _ret[1] = o1; - } - - //! Destroy the `HLRet` instance. - ASMJIT_INLINE ~HLRet() noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get the first return operand. - ASMJIT_INLINE Operand& getFirst() noexcept { return _ret[0]; } - //! \overload - ASMJIT_INLINE const Operand& getFirst() const noexcept { return _ret[0]; } - - //! Get the second return operand. - ASMJIT_INLINE Operand& getSecond() noexcept { return _ret[1]; } - //! \overload - ASMJIT_INLINE const Operand& getSecond() const noexcept { return _ret[1]; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Ret operand(s). - Operand _ret[2]; -}; - -// ============================================================================ -// [asmjit::HLCall] -// ============================================================================ - -//! Function call (HL). -class HLCall : public HLNode { - public: - ASMJIT_NO_COPY(HLCall) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `HLCall` instance. - ASMJIT_INLINE HLCall(Compiler* compiler, const Operand& target) noexcept - : HLNode(compiler, kTypeCall), - _decl(nullptr), - _target(target), - _args(nullptr) { - orFlags(kFlagIsRemovable); - } - - //! Destroy the `HLCall` instance. - ASMJIT_INLINE ~HLCall() noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get function declaration. - ASMJIT_INLINE FuncDecl* getDecl() const noexcept { return _decl; } - - //! Get target operand. - ASMJIT_INLINE Operand& getTarget() noexcept { return _target; } - //! \overload - ASMJIT_INLINE const Operand& getTarget() const noexcept { return _target; } - - //! Get return at `i`. - ASMJIT_INLINE Operand& getRet(uint32_t i = 0) noexcept { - ASMJIT_ASSERT(i < 2); - return _ret[i]; - } - //! \overload - ASMJIT_INLINE const Operand& getRet(uint32_t i = 0) const noexcept { - ASMJIT_ASSERT(i < 2); - return _ret[i]; - } - - //! Get argument at `i`. - ASMJIT_INLINE Operand& getArg(uint32_t i) noexcept { - ASMJIT_ASSERT(i < kFuncArgCountLoHi); - return _args[i]; - } - //! \overload - ASMJIT_INLINE const Operand& getArg(uint32_t i) const noexcept { - ASMJIT_ASSERT(i < kFuncArgCountLoHi); - return _args[i]; - } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Function declaration. - FuncDecl* _decl; - - //! Target (address of function, register, label, ...). - Operand _target; - //! Return. - Operand _ret[2]; - //! Arguments. - Operand* _args; -}; - -// ============================================================================ -// [asmjit::HLCallArg] -// ============================================================================ - -//! Function call's argument (HL). -class HLCallArg : public HLNode { - public: - ASMJIT_NO_COPY(HLCallArg) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new `HLCallArg` instance. - ASMJIT_INLINE HLCallArg(Compiler* compiler, HLCall* call, VarData* sVd, VarData* cVd) noexcept - : HLNode(compiler, kTypeCallArg), - _call(call), - _sVd(sVd), - _cVd(cVd), - _args(0) { - orFlags(kFlagIsRemovable); - } - - //! Destroy the `HLCallArg` instance. - ASMJIT_INLINE ~HLCallArg() noexcept {} - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get the associated function-call. - ASMJIT_INLINE HLCall* getCall() const noexcept { return _call; } - //! Get source variable. - ASMJIT_INLINE VarData* getSVd() const noexcept { return _sVd; } - //! Get conversion variable. - ASMJIT_INLINE VarData* getCVd() const noexcept { return _cVd; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Associated `HLCall`. - HLCall* _call; - //! Source variable. - VarData* _sVd; - //! Temporary variable used for conversion (or nullptr). - VarData* _cVd; - - //! Affected arguments bit-array. - uint32_t _args; -}; - -// ============================================================================ -// [asmjit::HLStream] -// ============================================================================ - -// TODO: - -//! \} - -} // asmjit namespace - -// [Api-End] -#include "../apiend.h" - -// [Guard] -#endif // !ASMJIT_DISABLE_COMPILER -#endif // _ASMJIT_BASE_HLSTREAM_H diff --git a/src/asmjit/base/logger.cpp b/src/asmjit/base/logger.cpp deleted file mode 100644 index 3d542df..0000000 --- a/src/asmjit/base/logger.cpp +++ /dev/null @@ -1,194 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Export] -#define ASMJIT_EXPORTS - -// [Guard] -#include "../build.h" -#if !defined(ASMJIT_DISABLE_LOGGER) - -// [Dependencies] -#include "../base/containers.h" -#include "../base/logger.h" -#include "../base/utils.h" -#include - -// [Api-Begin] -#include "../apibegin.h" - -namespace asmjit { - -// ============================================================================ -// [asmjit::LogUtil] -// ============================================================================ - -bool LogUtil::formatLine(StringBuilder& sb, const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept { - size_t currentLen = sb.getLength(); - size_t commentLen = comment ? Utils::strLen(comment, kMaxCommentLength) : 0; - - ASMJIT_ASSERT(binLen >= dispLen); - - if ((binLen != 0 && binLen != kInvalidIndex) || commentLen) { - size_t align = kMaxInstLength; - char sep = ';'; - - for (size_t i = (binLen == kInvalidIndex); i < 2; i++) { - size_t begin = sb.getLength(); - - // Append align. - if (currentLen < align) { - if (!sb.appendChars(' ', align - currentLen)) - return false; - } - - // Append separator. - if (sep) { - if (!(sb.appendChar(sep) & sb.appendChar(' '))) - return false; - } - - // Append binary data or comment. - if (i == 0) { - if (!sb.appendHex(binData, binLen - dispLen - imLen)) - return false; - if (!sb.appendChars('.', dispLen * 2)) - return false; - if (!sb.appendHex(binData + binLen - imLen, imLen)) - return false; - if (commentLen == 0) - break; - } - else { - if (!sb.appendString(comment, commentLen)) - return false; - } - - currentLen += sb.getLength() - begin; - align += kMaxBinaryLength; - sep = '|'; - } - } - - return sb.appendChar('\n'); -} - -// ============================================================================ -// [asmjit::Logger - Construction / Destruction] -// ============================================================================ - -Logger::Logger() noexcept { - _options = 0; - ::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation)); -} - -Logger::~Logger() noexcept {} - -// ============================================================================ -// [asmjit::Logger - Logging] -// ============================================================================ - -void Logger::logFormat(uint32_t style, const char* fmt, ...) noexcept { - char buf[1024]; - size_t len; - - va_list ap; - va_start(ap, fmt); - len = vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - if (len >= sizeof(buf)) - len = sizeof(buf) - 1; - - logString(style, buf, len); -} - -void Logger::logBinary(uint32_t style, const void* data, size_t size) noexcept { - static const char prefix[] = ".data "; - static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - - const uint8_t* s = static_cast(data); - size_t i = size; - - char buffer[128]; - ::memcpy(buffer, prefix, ASMJIT_ARRAY_SIZE(prefix) - 1); - - while (i) { - uint32_t n = static_cast(Utils::iMin(i, 16)); - char* p = buffer + ASMJIT_ARRAY_SIZE(prefix) - 1; - - i -= n; - do { - uint32_t c = s[0]; - - p[0] = hex[c >> 4]; - p[1] = hex[c & 15]; - - p += 2; - s += 1; - } while (--n); - - *p++ = '\n'; - logString(style, buffer, (size_t)(p - buffer)); - } -} - -// ============================================================================ -// [asmjit::Logger - Indentation] -// ============================================================================ - -void Logger::setIndentation(const char* indentation) noexcept { - ::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation)); - if (!indentation) - return; - - size_t length = Utils::strLen(indentation, ASMJIT_ARRAY_SIZE(_indentation) - 1); - ::memcpy(_indentation, indentation, length); -} - -// ============================================================================ -// [asmjit::FileLogger - Construction / Destruction] -// ============================================================================ - -FileLogger::FileLogger(FILE* stream) noexcept : _stream(nullptr) { setStream(stream); } -FileLogger::~FileLogger() noexcept {} - -// ============================================================================ -// [asmjit::FileLogger - Logging] -// ============================================================================ - -void FileLogger::logString(uint32_t style, const char* buf, size_t len) noexcept { - if (!_stream) - return; - - if (len == kInvalidIndex) - len = strlen(buf); - - fwrite(buf, 1, len, _stream); -} - -// ============================================================================ -// [asmjit::StringLogger - Construction / Destruction] -// ============================================================================ - -StringLogger::StringLogger() noexcept {} -StringLogger::~StringLogger() noexcept {} - -// ============================================================================ -// [asmjit::StringLogger - Logging] -// ============================================================================ - -void StringLogger::logString(uint32_t style, const char* buf, size_t len) noexcept { - _stringBuilder.appendString(buf, len); -} - -} // asmjit namespace - -// [Api-End] -#include "../apiend.h" - -// [Guard] -#endif // !ASMJIT_DISABLE_LOGGER diff --git a/src/asmjit/base/logging.cpp b/src/asmjit/base/logging.cpp new file mode 100644 index 0000000..372715a --- /dev/null +++ b/src/asmjit/base/logging.cpp @@ -0,0 +1,499 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Guard] +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_LOGGING) + +// [Dependencies] +#include "../base/logging.h" +#include "../base/utils.h" + +#if !defined(ASMJIT_DISABLE_BUILDER) +#include "../base/codebuilder.h" +#endif // !ASMJIT_DISABLE_BUILDER + +#if !defined(ASMJIT_DISABLE_COMPILER) +#include "../base/codecompiler.h" +#endif // !ASMJIT_DISABLE_COMPILER + +#if defined(ASMJIT_BUILD_X86) +#include "../x86/x86logging_p.h" +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) +#include "../arm/armlogging_p.h" +#endif // ASMJIT_BUILD_ARM + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { +// ============================================================================ +// [asmjit::Logger - Construction / Destruction] +// ============================================================================ + +Logger::Logger() noexcept { + _options = 0; + ::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation)); +} +Logger::~Logger() noexcept {} + +// ============================================================================ +// [asmjit::Logger - Logging] +// ============================================================================ + +Error Logger::logf(const char* fmt, ...) noexcept { + Error err; + + va_list ap; + va_start(ap, fmt); + err = logv(fmt, ap); + va_end(ap); + + return err; +} + +Error Logger::logv(const char* fmt, va_list ap) noexcept { + char buf[1024]; + size_t len = vsnprintf(buf, sizeof(buf), fmt, ap); + + if (len >= sizeof(buf)) + len = sizeof(buf) - 1; + return log(buf, len); +} + +Error Logger::logBinary(const void* data, size_t size) noexcept { + static const char prefix[] = ".data "; + static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + const uint8_t* s = static_cast(data); + size_t i = size; + + char buffer[128]; + ::memcpy(buffer, prefix, ASMJIT_ARRAY_SIZE(prefix) - 1); + + while (i) { + uint32_t n = static_cast(std::min(i, 16)); + char* p = buffer + ASMJIT_ARRAY_SIZE(prefix) - 1; + + i -= n; + do { + uint32_t c = s[0]; + + p[0] = hex[c >> 4]; + p[1] = hex[c & 15]; + + p += 2; + s += 1; + } while (--n); + + *p++ = '\n'; + ASMJIT_PROPAGATE(log(buffer, (size_t)(p - buffer))); + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::Logger - Indentation] +// ============================================================================ + +void Logger::setIndentation(const char* indentation) noexcept { + ::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation)); + if (!indentation) + return; + + size_t length = Utils::strLen(indentation, ASMJIT_ARRAY_SIZE(_indentation) - 1); + ::memcpy(_indentation, indentation, length); +} + +// ============================================================================ +// [asmjit::FileLogger - Construction / Destruction] +// ============================================================================ + +FileLogger::FileLogger(FILE* stream) noexcept : _stream(nullptr) { setStream(stream); } +FileLogger::~FileLogger() noexcept {} + +// ============================================================================ +// [asmjit::FileLogger - Logging] +// ============================================================================ + +Error FileLogger::_log(const char* buf, size_t len) noexcept { + if (!_stream) + return kErrorOk; + + if (len == Globals::kInvalidIndex) + len = strlen(buf); + + fwrite(buf, 1, len, _stream); + return kErrorOk; +} + +// ============================================================================ +// [asmjit::StringLogger - Construction / Destruction] +// ============================================================================ + +StringLogger::StringLogger() noexcept {} +StringLogger::~StringLogger() noexcept {} + +// ============================================================================ +// [asmjit::StringLogger - Logging] +// ============================================================================ + +Error StringLogger::_log(const char* buf, size_t len) noexcept { + return _stringBuilder.appendString(buf, len); +} + +// ============================================================================ +// [asmjit::Logging] +// ============================================================================ + +Error Logging::formatLabel( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t labelId) noexcept { + + const LabelEntry* le = emitter->getCode()->getLabelEntry(labelId); + if (ASMJIT_UNLIKELY(!le)) + return sb.appendFormat("InvalidLabel[Id=%u]", static_cast(labelId)); + + if (le->hasName()) { + if (le->hasParent()) { + uint32_t parentId = le->getParentId(); + const LabelEntry* pe = emitter->getCode()->getLabelEntry(parentId); + + if (ASMJIT_UNLIKELY(!pe)) + ASMJIT_PROPAGATE(sb.appendFormat("InvalidLabel[Id=%u]", static_cast(labelId))); + else if (ASMJIT_UNLIKELY(!pe->hasName())) + ASMJIT_PROPAGATE(sb.appendFormat("L%u", Operand::unpackId(parentId))); + else + ASMJIT_PROPAGATE(sb.appendString(pe->getName())); + + ASMJIT_PROPAGATE(sb.appendChar('.')); + } + return sb.appendString(le->getName()); + } + else { + return sb.appendFormat("L%u", Operand::unpackId(labelId)); + } +} + +Error Logging::formatRegister( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + uint32_t regType, + uint32_t regId) noexcept { + +#if defined(ASMJIT_BUILD_X86) + return X86Logging::formatRegister(sb, logOptions, emitter, archType, regType, regId); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + return ArmLogging::formatRegister(sb, logOptions, emitter, archType, regType, regId); +#endif // ASMJIT_BUILD_ARM + + return kErrorInvalidArch; +} + +Error Logging::formatOperand( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + const Operand_& op) noexcept { + +#if defined(ASMJIT_BUILD_X86) + return X86Logging::formatOperand(sb, logOptions, emitter, archType, op); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + return ArmLogging::formatOperand(sb, logOptions, emitter, archType, op); +#endif // ASMJIT_BUILD_ARM + + return kErrorInvalidArch; +} + +Error Logging::formatInstruction( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + uint32_t instId, + uint32_t options, + const Operand_& opExtra, + const Operand_* opArray, uint32_t opCount) noexcept { + +#if defined(ASMJIT_BUILD_X86) + return X86Logging::formatInstruction(sb, logOptions, emitter, archType, instId, options, opExtra, opArray, opCount); +#endif // ASMJIT_BUILD_X86 + +#if defined(ASMJIT_BUILD_ARM) + return ArmLogging::formatInstruction(sb, logOptions, emitter, archType, instId, options, opExtra, opArray, opCount); +#endif // ASMJIT_BUILD_ARM + + return kErrorInvalidArch; +} + +#if !defined(ASMJIT_DISABLE_BUILDER) +static Error formatTypeId(StringBuilder& sb, uint32_t typeId) noexcept { + if (typeId == TypeId::kVoid) + return sb.appendString("void"); + + if (!TypeId::isValid(typeId)) + return sb.appendString("unknown"); + + const char* typeName = "unknown"; + uint32_t typeSize = TypeId::sizeOf(typeId); + + uint32_t elementId = TypeId::elementOf(typeId); + switch (elementId) { + case TypeId::kIntPtr : typeName = "intptr" ; break; + case TypeId::kUIntPtr: typeName = "uintptr"; break; + case TypeId::kI8 : typeName = "i8" ; break; + case TypeId::kU8 : typeName = "u8" ; break; + case TypeId::kI16 : typeName = "i16" ; break; + case TypeId::kU16 : typeName = "u16" ; break; + case TypeId::kI32 : typeName = "i32" ; break; + case TypeId::kU32 : typeName = "u32" ; break; + case TypeId::kI64 : typeName = "i64" ; break; + case TypeId::kU64 : typeName = "u64" ; break; + case TypeId::kF32 : typeName = "f32" ; break; + case TypeId::kF64 : typeName = "f64" ; break; + case TypeId::kF80 : typeName = "f80" ; break; + case TypeId::kMask8 : typeName = "mask8" ; break; + case TypeId::kMask16 : typeName = "mask16" ; break; + case TypeId::kMask32 : typeName = "mask32" ; break; + case TypeId::kMask64 : typeName = "mask64" ; break; + case TypeId::kMmx32 : typeName = "mmx32" ; break; + case TypeId::kMmx64 : typeName = "mmx64" ; break; + } + + uint32_t elementSize = TypeId::sizeOf(elementId); + if (typeSize > elementSize) { + unsigned int numElements = typeSize / elementSize; + return sb.appendFormat("%sx%u", typeName, numElements); + } + else { + return sb.appendString(typeName); + } +} + +static Error formatFuncDetailValue( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + FuncDetail::Value value) noexcept { + + uint32_t typeId = value.getTypeId(); + ASMJIT_PROPAGATE(formatTypeId(sb, typeId)); + + if (value.byReg()) { + ASMJIT_PROPAGATE(sb.appendChar(':')); + ASMJIT_PROPAGATE(Logging::formatRegister(sb, logOptions, emitter, emitter->getArchType(), value.getRegType(), value.getRegId())); + } + + if (value.byStack()) { + ASMJIT_PROPAGATE(sb.appendFormat(":[%d]", static_cast(value.getStackOffset()))); + } + + return kErrorOk; +} + +static Error formatFuncRets( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + const FuncDetail& fd, + VirtReg* const* vRegs) noexcept { + + if (!fd.hasRet()) + return sb.appendString("void"); + + for (uint32_t i = 0; i < fd.getRetCount(); i++) { + if (i) ASMJIT_PROPAGATE(sb.appendString(", ")); + ASMJIT_PROPAGATE(formatFuncDetailValue(sb, logOptions, emitter, fd.getRet(i))); + + if (vRegs) + ASMJIT_PROPAGATE(sb.appendFormat(" {%s}", vRegs[i]->getName())); + } + + return kErrorOk; +} + +static Error formatFuncArgs( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + const FuncDetail& fd, + VirtReg* const* vRegs) noexcept { + + for (uint32_t i = 0; i < fd.getArgCount(); i++) { + if (i) ASMJIT_PROPAGATE(sb.appendString(", ")); + ASMJIT_PROPAGATE(formatFuncDetailValue(sb, logOptions, emitter, fd.getArg(i))); + + if (vRegs) + ASMJIT_PROPAGATE(sb.appendFormat(" {%s}", vRegs[i]->getName())); + } + + return kErrorOk; +} + +Error Logging::formatNode( + StringBuilder& sb, + uint32_t logOptions, + const CodeBuilder* cb, + const CBNode* node_) noexcept { + + if (node_->hasPosition()) + ASMJIT_PROPAGATE(sb.appendFormat("<%04u> ", node_->getPosition())); + + switch (node_->getType()) { + case CBNode::kNodeInst: { + const CBInst* node = node_->as(); + ASMJIT_PROPAGATE( + Logging::formatInstruction(sb, logOptions, cb, + cb->getArchType(), + node->getInstId(), + node->getOptions(), + node->getOpExtra(), + node->getOpArray(), node->getOpCount())); + break; + } + + case CBNode::kNodeLabel: { + const CBLabel* node = node_->as(); + ASMJIT_PROPAGATE(sb.appendFormat("L%u:", Operand::unpackId(node->getId()))); + break; + } + + case CBNode::kNodeData: { + const CBData* node = node_->as(); + ASMJIT_PROPAGATE(sb.appendFormat(".embed (%u bytes)", node->getSize())); + break; + } + + case CBNode::kNodeAlign: { + const CBAlign* node = node_->as(); + ASMJIT_PROPAGATE( + sb.appendFormat(".align %u (%s)", + node->getAlignment(), + node->getMode() == kAlignCode ? "code" : "data")); + break; + } + + case CBNode::kNodeComment: { + const CBComment* node = node_->as(); + ASMJIT_PROPAGATE(sb.appendFormat("; %s", node->getInlineComment())); + break; + } + + case CBNode::kNodeSentinel: { + const CBSentinel* node = node_->as(); + ASMJIT_PROPAGATE(sb.appendString("[sentinel]")); + break; + } + +#if !defined(ASMJIT_DISABLE_COMPILER) + case CBNode::kNodeFunc: { + const CCFunc* node = node_->as(); + ASMJIT_PROPAGATE(formatLabel(sb, logOptions, cb, node->getId())); + + ASMJIT_PROPAGATE(sb.appendString(": [")); + ASMJIT_PROPAGATE(formatFuncRets(sb, logOptions, cb, node->getDetail(), nullptr)); + ASMJIT_PROPAGATE(sb.appendString("]")); + + ASMJIT_PROPAGATE(sb.appendString("(")); + ASMJIT_PROPAGATE(formatFuncArgs(sb, logOptions, cb, node->getDetail(), node->getArgs())); + ASMJIT_PROPAGATE(sb.appendString(")")); + break; + } + + case CBNode::kNodeFuncExit: { + const CCFuncRet* node = node_->as(); + ASMJIT_PROPAGATE(sb.appendString("[ret]")); + break; + } + + case CBNode::kNodeFuncCall: { + const CCFuncCall* node = node_->as(); + ASMJIT_PROPAGATE( + Logging::formatInstruction(sb, logOptions, cb, + cb->getArchType(), + node->getInstId(), + node->getOptions(), + node->getOpExtra(), + node->getOpArray(), node->getOpCount())); + break; + } +#endif // !ASMJIT_DISABLE_COMPILER + + default: { + ASMJIT_PROPAGATE(sb.appendFormat("[unknown (type=%u)]", node_->getType())); + break; + } + } + + return kErrorOk; +} +#endif // !ASMJIT_DISABLE_BUILDER + +Error Logging::formatLine(StringBuilder& sb, const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept { + size_t currentLen = sb.getLength(); + size_t commentLen = comment ? Utils::strLen(comment, kMaxCommentLength) : 0; + + ASMJIT_ASSERT(binLen >= dispLen); + + if ((binLen != 0 && binLen != Globals::kInvalidIndex) || commentLen) { + size_t align = kMaxInstLength; + char sep = ';'; + + for (size_t i = (binLen == Globals::kInvalidIndex); i < 2; i++) { + size_t begin = sb.getLength(); + + // Append align. + if (currentLen < align) + ASMJIT_PROPAGATE(sb.appendChars(' ', align - currentLen)); + + // Append separator. + if (sep) { + ASMJIT_PROPAGATE(sb.appendChar(sep)); + ASMJIT_PROPAGATE(sb.appendChar(' ')); + } + + // Append binary data or comment. + if (i == 0) { + ASMJIT_PROPAGATE(sb.appendHex(binData, binLen - dispLen - imLen)); + ASMJIT_PROPAGATE(sb.appendChars('.', dispLen * 2)); + ASMJIT_PROPAGATE(sb.appendHex(binData + binLen - imLen, imLen)); + if (commentLen == 0) break; + } + else { + ASMJIT_PROPAGATE(sb.appendString(comment, commentLen)); + } + + currentLen += sb.getLength() - begin; + align += kMaxBinaryLength; + sep = '|'; + } + } + + return sb.appendChar('\n'); +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_LOGGING diff --git a/src/asmjit/base/logger.h b/src/asmjit/base/logging.h similarity index 56% rename from src/asmjit/base/logger.h rename to src/asmjit/base/logging.h index 635ecae..03ca1a2 100644 --- a/src/asmjit/base/logger.h +++ b/src/asmjit/base/logging.h @@ -5,61 +5,53 @@ // Zlib - See LICENSE.md file in the package. // [Guard] -#ifndef _ASMJIT_BASE_LOGGER_H -#define _ASMJIT_BASE_LOGGER_H +#ifndef _ASMJIT_BASE_LOGGING_H +#define _ASMJIT_BASE_LOGGING_H -#include "../build.h" +#include "../asmjit_build.h" // [Dependencies] -#include "../base/containers.h" -#include +#include "../base/string.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { //! \addtogroup asmjit_base //! \{ -#if !defined(ASMJIT_DISABLE_LOGGER) +#if !defined(ASMJIT_DISABLE_LOGGING) // ============================================================================ -// [asmjit::LogUtil] +// [Forward Declarations] // ============================================================================ -// Only used by asmjit internals, not available to consumers. -#if defined(ASMJIT_EXPORTS) -struct LogUtil { - enum { - // Has to be big to be able to hold all metadata compiler can assign to a - // single instruction. - kMaxCommentLength = 512, - kMaxInstLength = 40, - kMaxBinaryLength = 26 - }; +class CodeEmitter; +class Reg; +struct Operand_; - static bool formatLine( - StringBuilder& sb, - const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept; -}; -#endif // ASMJIT_EXPORTS +#if !defined(ASMJIT_DISABLE_BUILDER) +class CodeBuilder; +class CBNode; +#endif // !ASMJIT_DISABLE_BUILDER // ============================================================================ // [asmjit::Logger] // ============================================================================ -//! Abstract logging class. +//! Abstract logging interface and helpers. //! //! This class can be inherited and reimplemented to fit into your logging -//! subsystem. When reimplementing use `Logger::log()` method to log into +//! subsystem. When reimplementing use `Logger::_log()` method to log into //! a custom stream. //! -//! This class also contain `_enabled` member that can be used to enable -//! or disable logging. +//! There are two \ref Logger implementations offered by AsmJit: +//! - \ref FileLogger - allows to log into a `FILE*` stream. +//! - \ref StringLogger - logs into a \ref StringBuilder. class ASMJIT_VIRTAPI Logger { - public: - ASMJIT_NO_COPY(Logger) +public: + ASMJIT_NONCOPYABLE(Logger) // -------------------------------------------------------------------------- // [Options] @@ -68,23 +60,9 @@ class ASMJIT_VIRTAPI Logger { //! Logger options. ASMJIT_ENUM(Options) { kOptionBinaryForm = 0x00000001, //! Output instructions also in binary form. - kOptionHexImmediate = 0x00000002, //! Output immediates as hexadecimal numbers. - kOptionHexDisplacement = 0x00000004 //! Output displacements as hexadecimal numbers. - }; - - // -------------------------------------------------------------------------- - // [Style] - // -------------------------------------------------------------------------- - - //! Logger style. - ASMJIT_ENUM(Style) { - kStyleDefault = 0, - kStyleDirective = 1, - kStyleLabel = 2, - kStyleData = 3, - kStyleComment = 4, - - kStyleCount = 5 + kOptionImmExtended = 0x00000002, //! Output a meaning of some immediates. + kOptionHexImmediate = 0x00000004, //! Output constants in hexadecimal form. + kOptionHexDisplacement = 0x00000008 //! Output displacements in hexadecimal form. }; // -------------------------------------------------------------------------- @@ -100,13 +78,20 @@ class ASMJIT_VIRTAPI Logger { // [Logging] // -------------------------------------------------------------------------- - //! Log output. - virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex) noexcept = 0; + //! Log `str` - must be reimplemented. + virtual Error _log(const char* str, size_t len) noexcept = 0; - //! Log formatter message (like sprintf) sending output to `logString()` method. - ASMJIT_API void logFormat(uint32_t style, const char* fmt, ...) noexcept; + //! Log a string `str`, which is either null terminated or having `len` length. + ASMJIT_INLINE Error log(const char* str, size_t len = Globals::kInvalidIndex) noexcept { return _log(str, len); } + //! Log a content of a `StringBuilder` `str`. + ASMJIT_INLINE Error log(const StringBuilder& str) noexcept { return _log(str.getData(), str.getLength()); } + + //! Format the message by using `sprintf()` and then send to `log()`. + ASMJIT_API Error logf(const char* fmt, ...) noexcept; + //! Format the message by using `vsprintf()` and then send to `log()`. + ASMJIT_API Error logv(const char* fmt, va_list ap) noexcept; //! Log binary data. - ASMJIT_API void logBinary(uint32_t style, const void* data, size_t size) noexcept; + ASMJIT_API Error logBinary(const void* data, size_t size) noexcept; // -------------------------------------------------------------------------- // [Options] @@ -114,11 +99,8 @@ class ASMJIT_VIRTAPI Logger { //! Get all logger options as a single integer. ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; } - //! Get the given logger option. - ASMJIT_INLINE bool hasOption(uint32_t option) const noexcept { - return (_options & option) != 0; - } + ASMJIT_INLINE bool hasOption(uint32_t option) const noexcept { return (_options & option) != 0; } ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; } ASMJIT_INLINE void clearOptions(uint32_t options) noexcept { _options &= ~options; } @@ -127,17 +109,11 @@ class ASMJIT_VIRTAPI Logger { // -------------------------------------------------------------------------- //! Get indentation. - ASMJIT_INLINE const char* getIndentation() const noexcept { - return _indentation; - } - + ASMJIT_INLINE const char* getIndentation() const noexcept { return _indentation; } //! Set indentation. ASMJIT_API void setIndentation(const char* indentation) noexcept; - //! Reset indentation. - ASMJIT_INLINE void resetIndentation() noexcept { - setIndentation(nullptr); - } + ASMJIT_INLINE void resetIndentation() noexcept { setIndentation(nullptr); } // -------------------------------------------------------------------------- // [Members] @@ -154,10 +130,10 @@ class ASMJIT_VIRTAPI Logger { // [asmjit::FileLogger] // ============================================================================ -//! Logger that can log to standard C `FILE*` stream. +//! Logger that can log to a `FILE*` stream. class ASMJIT_VIRTAPI FileLogger : public Logger { - public: - ASMJIT_NO_COPY(FileLogger) +public: + ASMJIT_NONCOPYABLE(FileLogger) // -------------------------------------------------------------------------- // [Construction / Destruction] @@ -165,7 +141,6 @@ class ASMJIT_VIRTAPI FileLogger : public Logger { //! Create a new `FileLogger` that logs to a `FILE` stream. ASMJIT_API FileLogger(FILE* stream = nullptr) noexcept; - //! Destroy the `FileLogger`. ASMJIT_API virtual ~FileLogger() noexcept; @@ -173,24 +148,21 @@ class ASMJIT_VIRTAPI FileLogger : public Logger { // [Accessors] // -------------------------------------------------------------------------- - //! Get `FILE*` stream. - //! - //! NOTE: Return value can be `nullptr`. - ASMJIT_INLINE FILE* getStream() const noexcept { - return _stream; - } + //! Get the logging out put stream or null. + ASMJIT_INLINE FILE* getStream() const noexcept { return _stream; } - //! Set `FILE*` stream, can be set to `nullptr` to disable logging, although - //! the `ExternalTool` will still call `logString` even if there is no stream. - ASMJIT_INLINE void setStream(FILE* stream) noexcept { - _stream = stream; - } + //! Set the logging output stream to `stream` or null. + //! + //! NOTE: If the `stream` is null it will disable logging, but it won't + //! stop calling `log()` unless the logger is detached from the + //! \ref Assembler. + ASMJIT_INLINE void setStream(FILE* stream) noexcept { _stream = stream; } // -------------------------------------------------------------------------- // [Logging] // -------------------------------------------------------------------------- - ASMJIT_API virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex) noexcept; + ASMJIT_API Error _log(const char* buf, size_t len = Globals::kInvalidIndex) noexcept override; // -------------------------------------------------------------------------- // [Members] @@ -204,10 +176,10 @@ class ASMJIT_VIRTAPI FileLogger : public Logger { // [asmjit::StringLogger] // ============================================================================ -//! String logger. +//! Logger that stores everything in an internal string buffer. class ASMJIT_VIRTAPI StringLogger : public Logger { - public: - ASMJIT_NO_COPY(StringLogger) +public: + ASMJIT_NONCOPYABLE(StringLogger) // -------------------------------------------------------------------------- // [Construction / Destruction] @@ -215,7 +187,6 @@ class ASMJIT_VIRTAPI StringLogger : public Logger { //! Create new `StringLogger`. ASMJIT_API StringLogger() noexcept; - //! Destroy the `StringLogger`. ASMJIT_API virtual ~StringLogger() noexcept; @@ -226,43 +197,96 @@ class ASMJIT_VIRTAPI StringLogger : public Logger { //! Get `char*` pointer which represents the resulting string. //! //! The pointer is owned by `StringLogger`, it can't be modified or freed. - ASMJIT_INLINE const char* getString() const noexcept { - return _stringBuilder.getData(); - } + ASMJIT_INLINE const char* getString() const noexcept { return _stringBuilder.getData(); } + //! Clear the resulting string. + ASMJIT_INLINE void clearString() noexcept { _stringBuilder.clear(); } //! Get the length of the string returned by `getString()`. - ASMJIT_INLINE size_t getLength() const noexcept { - return _stringBuilder.getLength(); - } - - //! Clear the resulting string. - ASMJIT_INLINE void clearString() noexcept { - _stringBuilder.clear(); - } + ASMJIT_INLINE size_t getLength() const noexcept { return _stringBuilder.getLength(); } // -------------------------------------------------------------------------- // [Logging] // -------------------------------------------------------------------------- - ASMJIT_API virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex) noexcept; + ASMJIT_API Error _log(const char* buf, size_t len = Globals::kInvalidIndex) noexcept override; // -------------------------------------------------------------------------- // [Members] // -------------------------------------------------------------------------- - //! Output. + //! Output string. StringBuilder _stringBuilder; }; + +// ============================================================================ +// [asmjit::Logging] +// ============================================================================ + +struct Logging { + ASMJIT_API static Error formatRegister( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + uint32_t regType, + uint32_t regId) noexcept; + + ASMJIT_API static Error formatLabel( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t labelId) noexcept; + + ASMJIT_API static Error formatOperand( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + const Operand_& op) noexcept; + + ASMJIT_API static Error formatInstruction( + StringBuilder& sb, + uint32_t logOptions, + const CodeEmitter* emitter, + uint32_t archType, + uint32_t instId, + uint32_t options, + const Operand_& opExtra, + const Operand_* opArray, uint32_t opCount) noexcept; + +#if !defined(ASMJIT_DISABLE_BUILDER) + ASMJIT_API static Error formatNode( + StringBuilder& sb, + uint32_t logOptions, + const CodeBuilder* cb, + const CBNode* node_) noexcept; +#endif // !ASMJIT_DISABLE_BUILDER + +// Only used by AsmJit internals, not available for users. +#if defined(ASMJIT_EXPORTS) + enum { + // Has to be big to be able to hold all metadata compiler can assign to a + // single instruction. + kMaxCommentLength = 512, + kMaxInstLength = 40, + kMaxBinaryLength = 26 + }; + + static Error formatLine( + StringBuilder& sb, + const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept; +#endif // ASMJIT_EXPORTS +}; #else -struct Logger; -#endif // !ASMJIT_DISABLE_LOGGER +class Logger; +#endif // !ASMJIT_DISABLE_LOGGING //! \} } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" // [Guard] #endif // _ASMJIT_BASE_LOGGER_H diff --git a/src/asmjit/base/misc_p.h b/src/asmjit/base/misc_p.h new file mode 100644 index 0000000..5024f1c --- /dev/null +++ b/src/asmjit/base/misc_p.h @@ -0,0 +1,74 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_MISC_P_H +#define _ASMJIT_BASE_MISC_P_H + +// [Dependencies] +#include "../asmjit_build.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +//! \internal +//! +//! Macro used to populate a table with 16 elements starting at `I`. +#define ASMJIT_TABLE_16(DEF, I) DEF(I + 0), DEF(I + 1), DEF(I + 2), DEF(I + 3), \ + DEF(I + 4), DEF(I + 5), DEF(I + 6), DEF(I + 7), \ + DEF(I + 8), DEF(I + 9), DEF(I + 10), DEF(I + 11), \ + DEF(I + 12), DEF(I + 13), DEF(I + 14), DEF(I + 15) + +#define ASMJIT_TABLE_T_8(TABLE, VALUE, I) \ + TABLE< I + 0 >::VALUE, TABLE< I + 1 >::VALUE, \ + TABLE< I + 2 >::VALUE, TABLE< I + 3 >::VALUE, \ + TABLE< I + 4 >::VALUE, TABLE< I + 5 >::VALUE, \ + TABLE< I + 6 >::VALUE, TABLE< I + 7 >::VALUE + +#define ASMJIT_TABLE_T_16(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_8(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_8(TABLE, VALUE, I + 8) + +#define ASMJIT_TABLE_T_32(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_16(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_16(TABLE, VALUE, I + 16) + +#define ASMJIT_TABLE_T_64(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_32(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_32(TABLE, VALUE, I + 32) + +#define ASMJIT_TABLE_T_128(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_64(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_64(TABLE, VALUE, I + 64) + +#define ASMJIT_TABLE_T_256(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_128(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_128(TABLE, VALUE, I + 128) + +#define ASMJIT_TABLE_T_512(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_256(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_256(TABLE, VALUE, I + 256) + +#define ASMJIT_TABLE_T_1024(TABLE, VALUE, I) \ + ASMJIT_TABLE_T_512(TABLE, VALUE, I), \ + ASMJIT_TABLE_T_512(TABLE, VALUE, I + 512) + +//! \} + +} // asmjit namespace + +//! \} + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_MISC_P_H diff --git a/src/asmjit/base/operand.cpp b/src/asmjit/base/operand.cpp index 76e0b0a..09eeea8 100644 --- a/src/asmjit/base/operand.cpp +++ b/src/asmjit/base/operand.cpp @@ -8,45 +8,202 @@ #define ASMJIT_EXPORTS // [Dependencies] -#include "../base/globals.h" +#include "../base/operand.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { // ============================================================================ -// [asmjit::Operand] +// [asmjit::TypeId] // ============================================================================ -// Prevent static initialization. -class Operand { - public: - struct BaseOp { - uint8_t op; - uint8_t size; - uint8_t reserved_2_1; - uint8_t reserved_3_1; - - uint32_t id; - - uint32_t reserved_8_4; - uint32_t reserved_12_4; - }; - - // Kept in union to prevent LTO warnings. - union { - BaseOp _base; - - // Required to properly align this _fake_ `Operand`, not used. - uint64_t _data[2]; +template +struct TypeIdSizeOf_T { + enum { + kValue = (ID == TypeId::kI8 ) ? 1 : + (ID == TypeId::kU8 ) ? 1 : + (ID == TypeId::kI16 ) ? 2 : + (ID == TypeId::kU16 ) ? 2 : + (ID == TypeId::kI32 ) ? 4 : + (ID == TypeId::kU32 ) ? 4 : + (ID == TypeId::kI64 ) ? 8 : + (ID == TypeId::kU64 ) ? 8 : + (ID == TypeId::kF32 ) ? 4 : + (ID == TypeId::kF64 ) ? 8 : + (ID == TypeId::kF80 ) ? 10 : + (ID == TypeId::kMask8 ) ? 1 : + (ID == TypeId::kMask16) ? 2 : + (ID == TypeId::kMask32) ? 4 : + (ID == TypeId::kMask64) ? 8 : + (ID == TypeId::kMmx32 ) ? 4 : + (ID == TypeId::kMmx64 ) ? 8 : + (ID >= TypeId::_kVec32Start && ID <= TypeId::_kVec32End ) ? 4 : + (ID >= TypeId::_kVec64Start && ID <= TypeId::_kVec64End ) ? 8 : + (ID >= TypeId::_kVec128Start && ID <= TypeId::_kVec128End) ? 16 : + (ID >= TypeId::_kVec256Start && ID <= TypeId::_kVec256End) ? 32 : + (ID >= TypeId::_kVec512Start && ID <= TypeId::_kVec512End) ? 64 : 0 }; }; -ASMJIT_VARAPI const Operand noOperand; -const Operand noOperand = {{ 0, 0, 0, 0, kInvalidValue, 0, 0 }}; +template +struct TypeIdElementOf_T { + enum { + kValue = (ID == TypeId::kMask8 ) ? TypeId::kU8 : + (ID == TypeId::kMask16) ? TypeId::kU16 : + (ID == TypeId::kMask32) ? TypeId::kU32 : + (ID == TypeId::kMask64) ? TypeId::kU64 : + (ID == TypeId::kMmx32 ) ? TypeId::kI32 : + (ID == TypeId::kMmx64 ) ? TypeId::kI64 : + (ID >= TypeId::kI8 && ID <= TypeId::kF80 ) ? ID : + (ID >= TypeId::_kVec32Start && ID <= TypeId::_kVec32End ) ? ID - TypeId::_kVec32Start + TypeId::kI8 : + (ID >= TypeId::_kVec64Start && ID <= TypeId::_kVec64End ) ? ID - TypeId::_kVec64Start + TypeId::kI8 : + (ID >= TypeId::_kVec128Start && ID <= TypeId::_kVec128End) ? ID - TypeId::_kVec128Start + TypeId::kI8 : + (ID >= TypeId::_kVec256Start && ID <= TypeId::_kVec256End) ? ID - TypeId::_kVec256Start + TypeId::kI8 : + (ID >= TypeId::_kVec512Start && ID <= TypeId::_kVec512End) ? ID - TypeId::_kVec512Start + TypeId::kI8 : 0 + }; +}; + +#define R(TMPL, I) TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue, \ + TMPL::kValue, TMPL::kValue +ASMJIT_API const TypeId::Info TypeId::_info = { + // SizeOf[128] + { + R(TypeIdSizeOf_T, 0), R(TypeIdSizeOf_T, 16), + R(TypeIdSizeOf_T, 32), R(TypeIdSizeOf_T, 48), + R(TypeIdSizeOf_T, 64), R(TypeIdSizeOf_T, 80), + R(TypeIdSizeOf_T, 96), R(TypeIdSizeOf_T, 112) + }, + + // ElementOf[128] + { + R(TypeIdElementOf_T, 0), R(TypeIdElementOf_T, 16), + R(TypeIdElementOf_T, 32), R(TypeIdElementOf_T, 48), + R(TypeIdElementOf_T, 64), R(TypeIdElementOf_T, 80), + R(TypeIdElementOf_T, 96), R(TypeIdElementOf_T, 112) + } +}; +#undef R + +// ============================================================================ +// [asmjit::Operand - Test] +// ============================================================================ + +#if defined(ASMJIT_TEST) +UNIT(base_operand) { + INFO("Checking operand sizes"); + EXPECT(sizeof(Operand) == 16); + EXPECT(sizeof(Reg) == 16); + EXPECT(sizeof(Mem) == 16); + EXPECT(sizeof(Imm) == 16); + EXPECT(sizeof(Label) == 16); + + INFO("Checking basic functionality of Operand"); + Operand a, b; + Operand dummy; + + EXPECT(a.isNone() == true); + EXPECT(a.isReg() == false); + EXPECT(a.isMem() == false); + EXPECT(a.isImm() == false); + EXPECT(a.isLabel() == false); + EXPECT(a == b); + + EXPECT(a._any.reserved8_4 == 0, "Default constructed Operand should zero its 'reserved8_4' field"); + EXPECT(a._any.reserved12_4 == 0, "Default constructed Operand should zero its 'reserved12_4' field"); + + INFO("Checking basic functionality of Label"); + Label label; + EXPECT(label.isValid() == false); + EXPECT(label.getId() == 0); + + INFO("Checking basic functionality of Reg"); + EXPECT(Reg().isValid() == false, + "Default constructed Reg() should not be valid"); + EXPECT(Reg()._any.reserved8_4 == 0, + "A default constructed Reg() should zero its 'reserved8_4' field"); + EXPECT(Reg()._any.reserved12_4 == 0, + "A default constructed Reg() should zero its 'reserved12_4' field"); + + EXPECT(Reg().isReg() == false, + "Default constructed register should not isReg()"); + EXPECT(dummy.as().isValid() == false, + "Default constructed Operand casted to Reg should not be valid"); + + // Create some register (not specific to any architecture). + uint32_t rSig = Operand::kOpReg | (1 << Operand::kSignatureRegTypeShift) | + (2 << Operand::kSignatureRegKindShift) | + (8 << Operand::kSignatureSizeShift ) ; + Reg r1(Reg::fromSignature(rSig, 5)); + + EXPECT(r1.isValid() == true); + EXPECT(r1.isReg() == true); + EXPECT(r1.isReg(1) == true); + EXPECT(r1.isPhysReg() == true); + EXPECT(r1.isVirtReg() == false); + EXPECT(r1.getSignature() == rSig); + EXPECT(r1.getType() == 1); + EXPECT(r1.getKind() == 2); + EXPECT(r1.getSize() == 8); + EXPECT(r1.getId() == 5); + EXPECT(r1.isReg(1, 5) == true); // RegType and Id. + + EXPECT(r1._any.reserved8_4 == 0, "Reg should have 'reserved8_4' zero"); + EXPECT(r1._any.reserved12_4 == 0, "Reg should have 'reserved12_4' zero"); + + // The same type of register having different id. + Reg r2(r1, 6); + EXPECT(r2.isValid() == true); + EXPECT(r2.isReg() == true); + EXPECT(r2.isReg(1) == true); + EXPECT(r2.isPhysReg() == true); + EXPECT(r2.isVirtReg() == false); + EXPECT(r2.getSignature() == rSig); + EXPECT(r2.getType() == r1.getType()); + EXPECT(r2.getKind() == r1.getKind()); + EXPECT(r2.getSize() == r1.getSize()); + EXPECT(r2.getId() == 6); + EXPECT(r2.isReg(1, 6) == true); + + r1.reset(); + EXPECT(!r1.isValid(), + "Reset register should not be valid"); + EXPECT(!r1.isReg(), + "Reset register should not isReg()"); + + INFO("Checking basic functionality of Mem"); + Mem m; + EXPECT(m.isMem() , "Default constructed Mem() should isMem()"); + EXPECT(m == Mem() , "Two default constructed Mem() operands should be equal"); + EXPECT(m.hasBase() == false , "Default constructed Mem() should not have base specified"); + EXPECT(m.hasIndex() == false , "Default constructed Mem() should not have index specified"); + EXPECT(m.has64BitOffset() == true , "Default constructed Mem() should report 64-bit offset"); + EXPECT(m.getOffset() == 0 , "Default constructed Mem() should have be zero offset / address"); + + m.setOffset(-1); + EXPECT(m.getOffsetLo32() == -1 , "Memory operand must hold a 32-bit offset"); + EXPECT(m.getOffset() == -1 , "32-bit offset must be sign extended to 64 bits"); + + int64_t x = int64_t(ASMJIT_UINT64_C(0xFF00FF0000000001)); + m.setOffset(x); + EXPECT(m.getOffset() == x , "Memory operand must hold a 64-bit offset"); + EXPECT(m.getOffsetLo32() == 1 , "Memory operand must return correct low offset DWORD"); + EXPECT(m.getOffsetHi32() == 0xFF00FF00, "Memory operand must return correct high offset DWORD"); + + INFO("Checking basic functionality of Imm"); + EXPECT(Imm(-1).getInt64() == int64_t(-1), + "Immediate values should by default sign-extend to 64-bits"); +} +#endif // ASMJIT_TEST } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/operand.h b/src/asmjit/base/operand.h index c130407..2874053 100644 --- a/src/asmjit/base/operand.h +++ b/src/asmjit/base/operand.h @@ -12,761 +12,1005 @@ #include "../base/utils.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { -// ============================================================================ -// [Forward Declarations] -// ============================================================================ - -class Assembler; -class Compiler; - //! \addtogroup asmjit_base //! \{ // ============================================================================ -// [asmjit::RegClass] +// [asmjit::Operand_] // ============================================================================ -//! Register class. -ASMJIT_ENUM(RegClass) { - //! Gp register class, compatible with all architectures. - kRegClassGp = 0 -}; - -// ============================================================================ -// [asmjit::SizeDefs] -// ============================================================================ - -//! Common sizes of registers and data elements. -ASMJIT_ENUM(SizeDefs) { - //! 1 byte size (BYTE). - kSizeByte = 1, - //! 2 bytes size (WORD). - kSizeWord = 2, - //! 4 bytes size (DWORD). - kSizeDWord = 4, - //! 8 bytes size (QWORD). - kSizeQWord = 8, - //! 10 bytes size (TWORD). - kSizeTWord = 10, - //! 16 bytes size (DQWORD). - kSizeDQWord = 16, - //! 32 bytes size (YWORD / QQWORD). - kSizeYWord = 32, - //! 64 bytes size (ZWORD / DQQWORD). - kSizeZWord = 64 -}; - -// ============================================================================ -// [asmjit::MemType] -// ============================================================================ - -//! Type of memory operand. -ASMJIT_ENUM(MemType) { - //! Memory operand is a combination of a base register, an optional index - //! register, and displacement. - //! - //! The `Assembler` interprets `kMemTypeBaseIndex` and `kMemTypeStackIndex` - //! types the same way, but `Compiler` interprets `kMemTypeBaseIndex` as - //! `[base + index]` and `kMemTypeStackIndex` as `[stack(base) + index]`. - kMemTypeBaseIndex = 0, - - //! Memory operand is a combination of variable's memory location, an - //! optional index register, and displacement. - //! - //! The `Assembler` interprets `kMemTypeBaseIndex` and `kMemTypeStackIndex` - //! types the same way, but `Compiler` interprets `kMemTypeBaseIndex` as - //! `[base + index]` and `kMemTypeStackIndex` as `[stack(base) + index]`. - kMemTypeStackIndex = 1, - - //! Memory operand is an absolute memory location. - //! - //! Supported mostly by x86, truncated to a 32-bit value when running in - //! 64-bit mode (x64). - kMemTypeAbsolute = 2, - - //! Memory operand refers to the memory location specified by a label. - kMemTypeLabel = 3, - - //! Memory operand is an address specified by RIP. - kMemTypeRip = 4 -}; - -// ============================================================================ -// [asmjit::VarType] -// ============================================================================ - -ASMJIT_ENUM(VarType) { - //! Variable is 8-bit signed integer. - kVarTypeInt8 = 0, - //! Variable is 8-bit unsigned integer. - kVarTypeUInt8 = 1, - //! Variable is 16-bit signed integer. - kVarTypeInt16 = 2, - //! Variable is 16-bit unsigned integer. - kVarTypeUInt16 = 3, - //! Variable is 32-bit signed integer. - kVarTypeInt32 = 4, - //! Variable is 32-bit unsigned integer. - kVarTypeUInt32 = 5, - //! Variable is 64-bit signed integer. - kVarTypeInt64 = 6, - //! Variable is 64-bit unsigned integer. - kVarTypeUInt64 = 7, - - //! Variable is target `intptr_t`, compatible with the target's `intptr_t` (not hosts). - kVarTypeIntPtr = 8, - //! Variable is target `uintptr_t`, compatible with the target's `uintptr_t` (not hosts). - kVarTypeUIntPtr = 9, - - //! Variable is 32-bit floating point (single precision). - kVarTypeFp32 = 10, - //! Variable is 64-bit floating point (double precision). - kVarTypeFp64 = 11, - - //! \internal - _kVarTypeIntStart = kVarTypeInt8, - //! \internal - _kVarTypeIntEnd = kVarTypeUIntPtr, - - //! \internal - _kVarTypeFpStart = kVarTypeFp32, - //! \internal - _kVarTypeFpEnd = kVarTypeFp64 -}; - -// ============================================================================ -// [asmjit::Operand] -// ============================================================================ - -//! Operand can contain register, memory location, immediate, or label. -class Operand { - public: +//! Constructor-less \ref Operand. +//! +//! Contains no initialization code and can be used safely to define an array +//! of operands that won't be initialized. This is a \ref Operand compatible +//! data structure designed to be statically initialized or `static const`. +struct Operand_ { // -------------------------------------------------------------------------- - // [Type] + // [Operand Type] // -------------------------------------------------------------------------- //! Operand types that can be encoded in \ref Operand. - ASMJIT_ENUM(Type) { - //! Invalid operand, used only internally (not initialized Operand). - kTypeNone = 0, - //! Operand is a register. - kTypeReg = 1, - //! Operand is a variable. - kTypeVar = 2, - //! Operand is a memory. - kTypeMem = 3, - //! Operand is an immediate value. - kTypeImm = 4, - //! Operand is a label. - kTypeLabel = 5 + ASMJIT_ENUM(OpType) { + kOpNone = 0, //!< Not an operand or not initialized. + kOpReg = 1, //!< Operand is a register. + kOpMem = 2, //!< Operand is a memory. + kOpImm = 3, //!< Operand is an immediate value. + kOpLabel = 4 //!< Operand is a label. }; // -------------------------------------------------------------------------- - // [Id] + // [Operand Signature (Bits)] // -------------------------------------------------------------------------- - //! Operand ID masks used to determine the operand type. - ASMJIT_ENUM(IdTag) { - //! Operand id refers to a variable (\ref Var). - kIdVarTag = 0x80000000U, - //! Operand id refers to a label (\ref Label). - kIdLabelTag = 0x00000000U, - //! Valid bits stored in operand ID (for extracting array index from ID). - kIdIndexMask = 0x7FFFFFFFU + ASMJIT_ENUM(SignatureBits) { + // Operand type (3 least significant bits). + // |........|........|........|.....XXX| + kSignatureOpShift = 0, + kSignatureOpBits = 0x07U, + kSignatureOpMask = kSignatureOpBits << kSignatureOpShift, + + // Operand size (8 most significant bits). + // |XXXXXXXX|........|........|........| + kSignatureSizeShift = 24, + kSignatureSizeBits = 0xFFU, + kSignatureSizeMask = kSignatureSizeBits << kSignatureSizeShift, + + // Register type (5 bits). + // |........|........|........|XXXXX...| + kSignatureRegTypeShift = 3, + kSignatureRegTypeBits = 0x1FU, + kSignatureRegTypeMask = kSignatureRegTypeBits << kSignatureRegTypeShift, + + // Register kind (4 bits). + // |........|........|....XXXX|........| + kSignatureRegKindShift = 8, + kSignatureRegKindBits = 0x0FU, + kSignatureRegKindMask = kSignatureRegKindBits << kSignatureRegKindShift, + + // Memory base type (5 bits). + // |........|........|........|XXXXX...| + kSignatureMemBaseTypeShift = 3, + kSignatureMemBaseTypeBits = 0x1FU, + kSignatureMemBaseTypeMask = kSignatureMemBaseTypeBits << kSignatureMemBaseTypeShift, + + // Memory index type (5 bits). + // |........|........|...XXXXX|........| + kSignatureMemIndexTypeShift = 8, + kSignatureMemIndexTypeBits = 0x1FU, + kSignatureMemIndexTypeMask = kSignatureMemIndexTypeBits << kSignatureMemIndexTypeShift, + + // Memory base+index combined (10 bits). + // |........|........|...XXXXX|XXXXX...| + kSignatureMemBaseIndexShift = 3, + kSignatureMemBaseIndexBits = 0x3FFU, + kSignatureMemBaseIndexMask = kSignatureMemBaseIndexBits << kSignatureMemBaseIndexShift, + + // Memory should be encoded as absolute immediate (X86|X64). + // |........|........|..X.....|........| + kSignatureMemAbsoluteShift = 13, + kSignatureMemAbsoluteBits = 0x01U, + kSignatureMemAbsoluteFlag = kSignatureMemAbsoluteBits << kSignatureMemAbsoluteShift, + + // This memory operand represents a function argument's stack location (CodeCompiler) + // |........|........|.X......|........| + kSignatureMemArgHomeShift = 14, + kSignatureMemArgHomeBits = 0x01U, + kSignatureMemArgHomeFlag = kSignatureMemArgHomeBits << kSignatureMemArgHomeShift, + + // This memory operand represents a virtual register's home-slot (CodeCompiler). + // |........|........|X.......|........| + kSignatureMemRegHomeShift = 15, + kSignatureMemRegHomeBits = 0x01U, + kSignatureMemRegHomeFlag = kSignatureMemRegHomeBits << kSignatureMemRegHomeShift }; // -------------------------------------------------------------------------- - // [Structs] + // [Operand Id] // -------------------------------------------------------------------------- - //! \internal - //! - //! Base operand data. - struct BaseOp { - //! Type of the operand (see \ref Type). - uint8_t op; - //! Size of the operand (register, address, immediate, or variable). - uint8_t size; - //! \internal - uint8_t reserved_2_1; - //! \internal - uint8_t reserved_3_1; - - //! Operand id, identifier used by `Assembler` and `Compiler`. - //! - //! NOTE: Uninitialized operand has always set id to `kInvalidValue`. - uint32_t id; - - //! \internal - uint32_t reserved_8_4; - //! \internal - uint32_t reserved_12_4; + //! Operand id helpers useful for id <-> index translation. + ASMJIT_ENUM(PackedId) { + //! Minimum valid packed-id. + kPackedIdMin = 0x00000100U, + //! Maximum valid packed-id. + kPackedIdMax = 0xFFFFFFFFU, + //! Count of valid packed-ids. + kPackedIdCount = kPackedIdMax - kPackedIdMin + 1 }; - //! \internal - //! - //! Register or Variable operand data. - struct VRegOp { - //! Type of the operand (\ref kTypeReg or \ref kTypeVar). - uint8_t op; - //! Size of the operand (register or variable). - uint8_t size; + // -------------------------------------------------------------------------- + // [Operand Utilities] + // -------------------------------------------------------------------------- + //! Get if the given `id` is a valid packed-id that can be used by Operand. + //! Packed ids are those equal or greater than `kPackedIdMin` and lesser or + //! equal to `kPackedIdMax`. This concept was created to support virtual + //! registers and to make them distinguishable from physical ones. It allows + //! a single uint32_t to contain either physical register id or virtual + //! register id represented as `packed-id`. This concept is used also for + //! labels to make the API consistent. + static ASMJIT_INLINE bool isPackedId(uint32_t id) noexcept { return id - kPackedIdMin < kPackedIdCount; } + //! Convert a real-id into a packed-id that can be stored in Operand. + static ASMJIT_INLINE uint32_t packId(uint32_t id) noexcept { return id + kPackedIdMin; } + //! Convert a packed-id back to real-id. + static ASMJIT_INLINE uint32_t unpackId(uint32_t id) noexcept { return id - kPackedIdMin; } + + // -------------------------------------------------------------------------- + // [Operand Data] + // -------------------------------------------------------------------------- + + //! Any operand. + struct AnyData { + uint32_t signature; //!< Type of the operand (see \ref OpType) and other data. + uint32_t id; //!< Operand id or `0`. + uint32_t reserved8_4; //!< \internal + uint32_t reserved12_4; //!< \internal + }; + + //! Register operand data. + struct RegData { + uint32_t signature; //!< Type of the operand (always \ref kOpReg) and other data. + uint32_t id; //!< Physical or virtual register id. + uint32_t reserved8_4; //!< \internal + uint32_t reserved12_4; //!< \internal + }; + + //! Memory operand data. + struct MemData { + uint32_t signature; //!< Type of the operand (always \ref kOpMem) and other data. + uint32_t index; //!< INDEX register id or `0`. + + // [BASE + OFF32] vs just [OFF64]. union { - //! Register code = (type << 8) | index. - uint16_t code; - - //! Register type and index access. + uint64_t offset64; //!< 64-bit offset, combining low and high 32-bit parts. struct { #if ASMJIT_ARCH_LE - //! Register index. - uint8_t index; - //! Register type. - uint8_t type; + uint32_t offsetLo32; //!< 32-bit low offset part. + uint32_t base; //!< 32-bit high offset part or BASE. #else - //! Register type. - uint8_t type; - //! Register index. - uint8_t index; + uint32_t base; //!< 32-bit high offset part or BASE. + uint32_t offsetLo32; //!< 32-bit low offset part. #endif }; }; - - //! Variable id, used by `Compiler` to identify variables. - uint32_t id; - - union { - struct { - //! Variable type. - uint32_t vType; - //! \internal - uint32_t reserved_12_4; - }; - - //! \internal - //! - //! This is not needed or used, it's just to force compiler to always - //! align this struct to 8-bytes (it should fix LTO warning as well). - uint64_t reserved8_8; - }; }; - //! \internal - //! - //! Memory or Variable operand data. - struct VMemOp { - //! Type of the operand (\ref kTypeMem). - uint8_t op; - //! Size of the memory in bytes or zero. - uint8_t size; - //! Type of the memory operand, see `MemType`. - uint8_t type; - //! X86/X64 layout: - //! - segment [3 bits], see `X86Seg`. - //! - shift [2 bits], index register shift (0 to 3). - uint8_t flags; - - //! Base register, variable or label id. - uint32_t base; - //! Index register or variable. - uint32_t index; - //! 32-bit displacement or absolute address. - int32_t displacement; - }; - - //! \internal - //! //! Immediate operand data. - struct ImmOp { - //! Type of the operand (\ref kTypeImm). - uint8_t op; - //! Size of the immediate (or 0 to autodetect). - uint8_t size; - //! \internal - uint8_t reserved_2_1; - //! \internal - uint8_t reserved_3_1; - - //! Operand id, always set to `kInvalidValue` (immediates don't have IDs). - uint32_t id; - - union { - //! 8x8-bit signed immediate values. - int8_t _i8[8]; - //! 8x8-bit unsigned immediate values. - uint8_t _u8[8]; - - //! 4x16-bit signed immediate values. - int16_t _i16[4]; - //! 4x16-bit unsigned immediate values. - uint16_t _u16[4]; - - //! 2x32-bit signed immediate values. - int32_t _i32[2]; - //! 2x32-bit unsigned immediate values. - uint32_t _u32[2]; - - //! 1x64-bit signed immediate value. - int64_t _i64[1]; - //! 1x64-bit unsigned immediate value. - uint64_t _u64[1]; - - //! 2x SP-FP values. - float _f32[2]; - //! 1x DP-FP value. - double _f64[1]; - } value; + struct ImmData { + uint32_t signature; //!< Type of the operand (always \ref kOpImm) and other data. + uint32_t id; //!< Immediate id (always `0`). + UInt64 value; //!< Immediate value. }; - //! \internal - //! //! Label operand data. - struct LabelOp { - //! Type of the operand (\ref kTypeLabel). - uint8_t op; - //! Always zero. - uint8_t size; - //! \internal - uint8_t reserved_2_1; - //! \internal - uint8_t reserved_3_1; - - //! Operand id (`kInvalidValue` if the label is not initialized by code - //! generator). - uint32_t id; - - //! \internal - uint32_t reserved_8_4; - //! \internal - uint32_t reserved_12_4; + struct LabelData { + uint32_t signature; //!< Type of the operand (always \ref kOpLabel) and other data. + uint32_t id; //!< Label id (`0` if not initialized). + uint32_t reserved8_4; //!< \internal + uint32_t reserved12_4; //!< \internal }; - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create an uninitialized operand. - ASMJIT_INLINE Operand() noexcept { - reset(); - } - - //! Create a reference to `other` operand. - ASMJIT_INLINE Operand(const Operand& other) noexcept { - _init(other); - } - - explicit ASMJIT_INLINE Operand(const _NoInit&) noexcept {} - - // -------------------------------------------------------------------------- - // [Base] - // -------------------------------------------------------------------------- - - //! Clone the `Operand`. - ASMJIT_INLINE Operand clone() const noexcept { return Operand(*this); } - - //! Reset the `Operand`. - ASMJIT_INLINE void reset() noexcept { - _init_packed_op_sz_b0_b1_id(kTypeNone, 0, 0, 0, kInvalidValue); - _init_packed_d2_d3(0, 0); - } - // -------------------------------------------------------------------------- // [Init & Copy] // -------------------------------------------------------------------------- //! \internal //! - //! Initialize operand to `other` (used by constructors). - ASMJIT_INLINE void _init(const Operand& other) noexcept { - ::memcpy(this, &other, sizeof(Operand)); + //! Initialize the operand to `other` (used by constructors). + ASMJIT_INLINE void _init(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); } + + //! \internal + ASMJIT_INLINE void _initReg(uint32_t signature, uint32_t rd) { + _init_packed_d0_d1(signature, rd); + _init_packed_d2_d3(0, 0); } - ASMJIT_INLINE void _init_packed_op_sz_b0_b1_id(uint32_t op, uint32_t sz, uint32_t r0, uint32_t r1, uint32_t id) noexcept { - // This hack is not for performance, but to decrease the size of the binary - // generated when constructing AsmJit operands (mostly for third parties). - // Some compilers are not able to join four BYTE writes to a single DWORD - // write. Because the 'a', 'b', 'c' and 'd' variables are usually compile - // time constants the compiler can do a really nice job if they are joined - // by using bitwise operations. - _packed[0].setPacked_2x32(Utils::pack32_4x8(op, sz, r0, r1), id); + //! \internal + ASMJIT_INLINE void _init_packed_d0_d1(uint32_t d0, uint32_t d1) noexcept { _packed[0].setPacked_2x32(d0, d1); } + //! \internal + ASMJIT_INLINE void _init_packed_d2_d3(uint32_t d2, uint32_t d3) noexcept { _packed[1].setPacked_2x32(d2, d3); } + + //! \internal + //! + //! Initialize the operand from `other` (used by operator overloads). + ASMJIT_INLINE void copyFrom(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get if the operand matches the given signature `sign`. + ASMJIT_INLINE bool hasSignature(uint32_t signature) const noexcept { return _signature == signature; } + + //! Get if the operand matches a signature of the `other` operand. + ASMJIT_INLINE bool hasSignature(const Operand_& other) const noexcept { + return _signature == other.getSignature(); } - ASMJIT_INLINE void _init_packed_op_sz_w0_id(uint32_t op, uint32_t sz, uint32_t w0, uint32_t id) noexcept { - _packed[0].setPacked_2x32(Utils::pack32_2x8_1x16(op, sz, w0), id); - } + //! Get a 32-bit operand signature. + //! + //! Signature is first 4 bytes of the operand data. It's used mostly for + //! operand checking as it's much faster to check 4 bytes at once than having + //! to check these bytes individually. + ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; } - ASMJIT_INLINE void _init_packed_d0_d1(uint32_t u0, uint32_t u1) noexcept { - _packed[0].setPacked_2x32(u0, u1); - } + //! Set the operand signature (see \ref getSignature). + //! + //! Improper use of `setSignature()` can lead to hard-to-debug errors. + ASMJIT_INLINE void setSignature(uint32_t signature) noexcept { _signature = signature; } - ASMJIT_INLINE void _init_packed_d2_d3(uint32_t u2, uint32_t u3) noexcept { - _packed[1].setPacked_2x32(u2, u3); + ASMJIT_INLINE bool _hasSignatureData(uint32_t bits) const noexcept { + return (_signature & bits) != 0; } //! \internal //! - //! Initialize operand to `other` (used by assign operators). - ASMJIT_INLINE void _copy(const Operand& other) noexcept { - ::memcpy(this, &other, sizeof(Operand)); + //! Unpacks information from operand's signature. + ASMJIT_INLINE uint32_t _getSignatureData(uint32_t bits, uint32_t shift) const noexcept { + return (_signature >> shift) & bits; } - // -------------------------------------------------------------------------- - // [Data] - // -------------------------------------------------------------------------- - - template - ASMJIT_INLINE T& getData() noexcept { - return reinterpret_cast(_base); + //! \internal + //! + //! Packs information to operand's signature. + ASMJIT_INLINE void _setSignatureData(uint32_t value, uint32_t bits, uint32_t shift) noexcept { + ASMJIT_ASSERT(value <= bits); + _signature = (_signature & ~(bits << shift)) | (value << shift); } - template - ASMJIT_INLINE const T& getData() const noexcept { - return reinterpret_cast(_base); + ASMJIT_INLINE void _addSignatureData(uint32_t data) noexcept { _signature |= data; } + + //! Get type of the operand, see \ref OpType. + ASMJIT_INLINE uint32_t getOp() const noexcept { return _getSignatureData(kSignatureOpBits, kSignatureOpShift); } + //! Get if the operand is none (\ref kOpNone). + ASMJIT_INLINE bool isNone() const noexcept { return getOp() == 0; } + //! Get if the operand is a register (\ref kOpReg). + ASMJIT_INLINE bool isReg() const noexcept { return getOp() == kOpReg; } + //! Get if the operand is a memory location (\ref kOpMem). + ASMJIT_INLINE bool isMem() const noexcept { return getOp() == kOpMem; } + //! Get if the operand is an immediate (\ref kOpImm). + ASMJIT_INLINE bool isImm() const noexcept { return getOp() == kOpImm; } + //! Get if the operand is a label (\ref kOpLabel). + ASMJIT_INLINE bool isLabel() const noexcept { return getOp() == kOpLabel; } + + //! Get if the operand is a physical register. + ASMJIT_INLINE bool isPhysReg() const noexcept { return isReg() && _reg.id < Globals::kInvalidRegId; } + //! Get if the operand is a virtual register. + ASMJIT_INLINE bool isVirtReg() const noexcept { return isReg() && isPackedId(_reg.id); } + + //! Get if the operand specifies a size (i.e. the size is not zero). + ASMJIT_INLINE bool hasSize() const noexcept { return _hasSignatureData(kSignatureSizeMask); } + //! Get if the size of the operand matches `size`. + ASMJIT_INLINE bool hasSize(uint32_t size) const noexcept { return getSize() == size; } + + //! Get size of the operand (in bytes). + //! + //! The value returned depends on the operand type: + //! * None - Should always return zero size. + //! * Reg - Should always return the size of the register. If the register + //! size depends on architecture (like \ref X86CReg and \ref X86DReg) + //! the size returned should be the greatest possible (so it should + //! return 64-bit size in such case). + //! * Mem - Size is optional and will be in most cases zero. + //! * Imm - Should always return zero size. + //! * Label - Should always return zero size. + ASMJIT_INLINE uint32_t getSize() const noexcept { return _getSignatureData(kSignatureSizeBits, kSignatureSizeShift); } + + //! Get the operand id. + //! + //! The value returned should be interpreted accordingly to the operand type: + //! * None - Should be `0`. + //! * Reg - Physical or virtual register id. + //! * Mem - Multiple meanings - BASE address (register or label id), or + //! high value of a 64-bit absolute address. + //! * Imm - Should be `0`. + //! * Label - Label id if it was created by using `newLabel()` or `0` + //! if the label is invalid or uninitialized. + ASMJIT_INLINE uint32_t getId() const noexcept { return _any.id; } + + //! Get if the operand is 100% equal to `other`. + ASMJIT_INLINE bool isEqual(const Operand_& other) const noexcept { + return (_packed[0] == other._packed[0]) & + (_packed[1] == other._packed[1]) ; } - // -------------------------------------------------------------------------- - // [Type] - // -------------------------------------------------------------------------- - - //! Get type of the operand, see \ref Type. - ASMJIT_INLINE uint32_t getOp() const noexcept { return _base.op; } - - //! Get whether the operand is none (\ref kTypeNone). - ASMJIT_INLINE bool isNone() const noexcept { return (_base.op == kTypeNone); } - //! Get whether the operand is a register (\ref kTypeReg). - ASMJIT_INLINE bool isReg() const noexcept { return (_base.op == kTypeReg); } - //! Get whether the operand is a variable (\ref kTypeVar). - ASMJIT_INLINE bool isVar() const noexcept { return (_base.op == kTypeVar); } - //! Get whether the operand is a memory location (\ref kTypeMem). - ASMJIT_INLINE bool isMem() const noexcept { return (_base.op == kTypeMem); } - //! Get whether the operand is an immediate (\ref kTypeImm). - ASMJIT_INLINE bool isImm() const noexcept { return (_base.op == kTypeImm); } - //! Get whether the operand is a label (\ref kTypeLabel). - ASMJIT_INLINE bool isLabel() const noexcept { return (_base.op == kTypeLabel); } - - // -------------------------------------------------------------------------- - // [Type - Combined] - // -------------------------------------------------------------------------- - - //! Get register type. - ASMJIT_INLINE uint32_t getRegType() const noexcept { return _vreg.type; } - //! Get register index. - ASMJIT_INLINE uint32_t getRegIndex() const noexcept { return _vreg.index; } - - //! Get whether the operand is register of `type`. - ASMJIT_INLINE bool isRegType(uint32_t type) const noexcept { - return (_packed[0].u32[0] & Utils::pack32_2x8_1x16(0xFF, 0, 0xFF00)) == Utils::pack32_2x8_1x16(kTypeReg, 0, (type << 8)); + //! Get if the operand is a register matching `rType`. + ASMJIT_INLINE bool isReg(uint32_t rType) const noexcept { + const uint32_t kMsk = (kSignatureOpBits << kSignatureOpShift) | (kSignatureRegTypeBits << kSignatureRegTypeShift); + const uint32_t kSgn = (kOpReg << kSignatureOpShift) | (rType << kSignatureRegTypeShift); + return (_signature & kMsk) == kSgn; } - //! Get whether the operand is register and of `type` and `index`. - ASMJIT_INLINE bool isRegCode(uint32_t type, uint32_t index) const noexcept { - return (_packed[0].u32[0] & Utils::pack32_2x8_1x16(0xFF, 0, 0xFFFF)) == Utils::pack32_2x8_1x16(kTypeReg, 0, (type << 8) + index); + //! Get whether the operand is register and of `type` and `id`. + ASMJIT_INLINE bool isReg(uint32_t rType, uint32_t rId) const noexcept { + return isReg(rType) && getId() == rId; } //! Get whether the operand is a register or memory. ASMJIT_INLINE bool isRegOrMem() const noexcept { - ASMJIT_ASSERT(kTypeReg == 1); - ASMJIT_ASSERT(kTypeMem == 3); - return (static_cast(_base.op) | 0x2U) == 0x3U; + ASMJIT_ASSERT(kOpMem - kOpReg == 1); + return Utils::inInterval(getOp(), kOpReg, kOpMem); } - //! Get whether the operand is variable or memory. - ASMJIT_INLINE bool isVarOrMem() const noexcept { - ASMJIT_ASSERT(kTypeVar == 2); - ASMJIT_ASSERT(kTypeMem == 3); - return (static_cast(_base.op) - 2U) <= 1; - } + //! Cast this operand to `T` type. + template + ASMJIT_INLINE T& as() noexcept { return static_cast(*this); } + //! Cast this operand to `T` type (const). + template + ASMJIT_INLINE const T& as() const noexcept { return static_cast(*this); } // -------------------------------------------------------------------------- - // [Size] + // [Reset] // -------------------------------------------------------------------------- - //! Get size of the operand in bytes. - ASMJIT_INLINE uint32_t getSize() const noexcept { return _base.size; } - - // -------------------------------------------------------------------------- - // [Id] - // -------------------------------------------------------------------------- - - //! Get operand id. + //! Reset the `Operand` to none. //! - //! Operand id's are used internally by `Assembler` and `Compiler`. + //! None operand is defined the following way: + //! - Its signature is zero (kOpNone, and the rest zero as well). + //! - Its id is `0`. + //! - The reserved8_4 field is set to `0`. + //! - The reserved12_4 field is set to zero. //! - //! There is no way to change or remove operand id. Unneeded operands can be - //! simply reassigned by `operator=`. - ASMJIT_INLINE uint32_t getId() const noexcept { return _base.id; } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - union { - //! Base data. - BaseOp _base; - //! Register or variable data. - VRegOp _vreg; - //! Memory data. - VMemOp _vmem; - //! Immediate data. - ImmOp _imm; - //! Label data. - LabelOp _label; - - //! Packed operand as two 64-bit integers. - UInt64 _packed[2]; - }; -}; - -// ============================================================================ -// [asmjit::OperandUtil] -// ============================================================================ - -//! Operand utilities. -struct OperandUtil { - //! Make variable id. - static ASMJIT_INLINE uint32_t makeVarId(uint32_t id) noexcept { - return id | Operand::kIdVarTag; - } - - //! Make label id. - static ASMJIT_INLINE uint32_t makeLabelId(uint32_t id) noexcept { - return id | Operand::kIdLabelTag; - } - - //! Strip variable id bit so it becomes a pure index to `VarData[]` array. - static ASMJIT_INLINE uint32_t stripVarId(uint32_t id) noexcept { - return id & Operand::kIdIndexMask; - } - - //! Get whether the id refers to `Var`. + //! In other words, reset operands have all members set to zero. Reset operand + //! must match the Operand state right after its construction. Alternatively, + //! if you have an array of operands, you can simply use `memset()`. //! - //! NOTE: The function will never return `true` if the id is `kInvalidValue`. - //! The trick is to compare a given id to -1 (kInvalidValue) so we check both - //! using only one comparison. - static ASMJIT_INLINE bool isVarId(uint32_t id) noexcept { - return static_cast(id) < -1; - } - - //! Get whether the id refers to `Label`. + //! ``` + //! using namespace asmjit; //! - //! NOTE: The function will never return `true` if the id is `kInvalidValue`. - static ASMJIT_INLINE bool isLabelId(uint32_t id) noexcept { - return static_cast(id) >= 0; - } -}; - -// ============================================================================ -// [asmjit::Reg] -// ============================================================================ - -//! Base class for all register operands. -class Reg : public Operand { - public: - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a dummy base register. - ASMJIT_INLINE Reg() noexcept : Operand(NoInit) { - _init_packed_op_sz_w0_id(kTypeReg, 0, (kInvalidReg << 8) + kInvalidReg, kInvalidValue); - _init_packed_d2_d3(kInvalidVar, 0); - } - - //! Create a new base register. - ASMJIT_INLINE Reg(uint32_t type, uint32_t index, uint32_t size) noexcept : Operand(NoInit) { - _init_packed_op_sz_w0_id(kTypeReg, size, (type << 8) + index, kInvalidValue); - _init_packed_d2_d3(kInvalidVar, 0); - } - - //! Create a new reference to `other`. - ASMJIT_INLINE Reg(const Reg& other) noexcept : Operand(other) {} - - //! Create a new reference to `other` and change the index to `index`. - ASMJIT_INLINE Reg(const Reg& other, uint32_t index) noexcept : Operand(other) { - _vreg.index = static_cast(index); - } - - explicit ASMJIT_INLINE Reg(const _NoInit&) noexcept : Operand(NoInit) {} - - // -------------------------------------------------------------------------- - // [Reg Specific] - // -------------------------------------------------------------------------- - - //! Clone `Reg` operand. - ASMJIT_INLINE Reg clone() const noexcept { - return Reg(*this); - } - - //! Get whether register code is equal to `type`. - ASMJIT_INLINE bool isRegType(uint32_t type) const noexcept { - return _vreg.type == type; - } - - //! Get whether register code is equal to `type`. - ASMJIT_INLINE bool isRegCode(uint32_t code) const noexcept { - return _vreg.code == code; - } - - //! Get whether register code is equal to `type`. - ASMJIT_INLINE bool isRegCode(uint32_t type, uint32_t index) const noexcept { - return _vreg.code == (type << 8) + index; - } - - //! Get register code that equals to '(type << 8) + index'. - ASMJIT_INLINE uint32_t getRegCode() const noexcept { - return _vreg.code; - } - - //! Get register type. - ASMJIT_INLINE uint32_t getRegType() const noexcept { - return _vreg.type; - } - - //! Get register index. - ASMJIT_INLINE uint32_t getRegIndex() const noexcept { - return _vreg.index; - } - -#define ASMJIT_REG_OP(_Type_) \ - ASMJIT_INLINE _Type_ clone() const ASMJIT_NOEXCEPT { \ - return _Type_(*this); \ - } \ - \ - /*! Set register `size`. */ \ - ASMJIT_INLINE _Type_& setSize(uint32_t size) ASMJIT_NOEXCEPT { \ - _vreg.size = static_cast(size); \ - return *this; \ - } \ - \ - /*! Set register `code`. */ \ - ASMJIT_INLINE _Type_& setCode(uint32_t code) ASMJIT_NOEXCEPT { \ - _vreg.code = static_cast(code); \ - return *this; \ - } \ - \ - /*! Set register `type` and `index`. */ \ - ASMJIT_INLINE _Type_& setCode(uint32_t type, uint32_t index) ASMJIT_NOEXCEPT { \ - _vreg.type = static_cast(type); \ - _vreg.index = static_cast(index); \ - return *this; \ - } \ - \ - /*! Set register `type`. */ \ - ASMJIT_INLINE _Type_& setType(uint32_t type) ASMJIT_NOEXCEPT { \ - _vreg.type = static_cast(type); \ - return *this; \ - } \ - \ - /*! Set register `index`. */ \ - ASMJIT_INLINE _Type_& setIndex(uint32_t index) ASMJIT_NOEXCEPT { \ - _vreg.index = static_cast(index); \ - return *this; \ - } \ - \ - ASMJIT_INLINE _Type_& operator=(const _Type_& other) ASMJIT_NOEXCEPT { \ - _copy(other); return *this; \ - } \ - \ - ASMJIT_INLINE bool operator==(const _Type_& other) const ASMJIT_NOEXCEPT { \ - return _packed[0].u32[0] == other._packed[0].u32[0]; \ - } \ - \ - ASMJIT_INLINE bool operator!=(const _Type_& other) const ASMJIT_NOEXCEPT { \ - return !operator==(other); \ - } -}; - -// ============================================================================ -// [asmjit::BaseMem] -// ============================================================================ - -//! Base class for all memory operands. -class BaseMem : public Operand { - public: - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE BaseMem() noexcept : Operand(NoInit) { - reset(); - } - - ASMJIT_INLINE BaseMem(const BaseMem& other) noexcept : Operand(other) {} - explicit ASMJIT_INLINE BaseMem(const _NoInit&) noexcept : Operand(NoInit) {} - - // -------------------------------------------------------------------------- - // [BaseMem Specific] - // -------------------------------------------------------------------------- - - //! Clone `BaseMem` operand. - ASMJIT_INLINE BaseMem clone() const noexcept { - return BaseMem(*this); - } - - //! Reset `BaseMem` operand. + //! Operand a; + //! Operand b; + //! assert(a == b); + //! + //! b = x86::eax; + //! assert(a != b); + //! + //! b.reset(); + //! assert(a == b); + //! + //! memset(&b, 0, sizeof(Operand)); + //! assert(a == b); + //! ``` ASMJIT_INLINE void reset() noexcept { - _init_packed_op_sz_b0_b1_id(kTypeMem, 0, kMemTypeBaseIndex, 0, kInvalidValue); - _init_packed_d2_d3(kInvalidValue, 0); - } - - //! Get the type of the memory operand, see `MemType`. - ASMJIT_INLINE uint32_t getMemType() const noexcept { - return _vmem.type; - } - - //! Get whether the type of the memory operand is either `kMemTypeBaseIndex` - //! or `kMemTypeStackIndex`. - ASMJIT_INLINE bool isBaseIndexType() const noexcept { - return _vmem.type <= kMemTypeStackIndex; - } - - //! Get whether the memory operand has base register. - ASMJIT_INLINE bool hasBase() const noexcept { - return _vmem.base != kInvalidValue; - } - - //! Get memory operand base id, or `kInvalidValue`. - ASMJIT_INLINE uint32_t getBase() const noexcept { - return _vmem.base; - } - - //! Set memory operand size. - ASMJIT_INLINE BaseMem& setSize(uint32_t size) noexcept { - _vmem.size = static_cast(size); - return *this; - } - - //! Get memory operand relative displacement. - ASMJIT_INLINE int32_t getDisplacement() const noexcept { - return _vmem.displacement; - } - - //! Set memory operand relative displacement. - ASMJIT_INLINE BaseMem& setDisplacement(int32_t disp) noexcept { - _vmem.displacement = disp; - return *this; + _init_packed_d0_d1(kOpNone, 0); + _init_packed_d2_d3(0, 0); } // -------------------------------------------------------------------------- // [Operator Overload] // -------------------------------------------------------------------------- - ASMJIT_INLINE BaseMem& operator=(const BaseMem& other) noexcept { - _copy(other); - return *this; + template + ASMJIT_INLINE bool operator==(const T& other) const noexcept { return isEqual(other); } + template + ASMJIT_INLINE bool operator!=(const T& other) const noexcept { return !isEqual(other); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + union { + AnyData _any; //!< Generic data. + RegData _reg; //!< Physical or virtual register data. + MemData _mem; //!< Memory address data. + ImmData _imm; //!< Immediate value data. + LabelData _label; //!< Label data. + + uint32_t _signature; //!< Operand signature (first 32-bits). + UInt64 _packed[2]; //!< Operand packed into two 64-bit integers. + }; +}; + +// ============================================================================ +// [asmjit::Operand] +// ============================================================================ + +//! Operand can contain register, memory location, immediate, or label. +class Operand : public Operand_ { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create an uninitialized operand. + ASMJIT_INLINE Operand() noexcept { reset(); } + //! Create a reference to `other` operand. + ASMJIT_INLINE Operand(const Operand& other) noexcept { _init(other); } + //! Create a reference to `other` operand. + explicit ASMJIT_INLINE Operand(const Operand_& other) noexcept { _init(other); } + //! Create a completely uninitialized operand (dangerous). + explicit ASMJIT_INLINE Operand(const _NoInit&) noexcept {} + + // -------------------------------------------------------------------------- + // [Clone] + // -------------------------------------------------------------------------- + + //! Clone the `Operand`. + ASMJIT_INLINE Operand clone() const noexcept { return Operand(*this); } + + ASMJIT_INLINE Operand& operator=(const Operand_& other) noexcept { copyFrom(other); return *this; } +}; + +// ============================================================================ +// [asmjit::Label] +// ============================================================================ + +//! Label (jump target or data location). +//! +//! Label represents a location in code typically used as a jump target, but +//! may be also a reference to some data or a static variable. Label has to be +//! explicitly created by CodeEmitter. +//! +//! Example of using labels: +//! +//! ~~~ +//! // Create a CodeEmitter (for example X86Assembler). +//! X86Assembler a; +//! +//! // Create Label instance. +//! Label L1 = a.newLabel(); +//! +//! // ... your code ... +//! +//! // Using label. +//! a.jump(L1); +//! +//! // ... your code ... +//! +//! // Bind label to the current position, see `CodeEmitter::bind()`. +//! a.bind(L1); +//! ~~~ +class Label : public Operand { +public: + //! Type of the Label. + enum Type { + kTypeAnonymous = 0, //!< Anonymous (unnamed) label. + kTypeLocal = 1, //!< Local label (always has parentId). + kTypeGlobal = 2, //!< Global label (never has parentId). + kTypeCount = 3 //!< Number of label types. + }; + + // TODO: Find a better place, find a better name. + enum { + //! Label tag is used as a sub-type, forming a unique signature across all + //! operand types as 0x1 is never associated with any register (reg-type). + //! This means that a memory operand's BASE register can be constructed + //! from virtually any operand (register vs. label) by just assigning its + //! type (reg type or label-tag) and operand id. + kLabelTag = 0x1 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create new, unassociated label. + ASMJIT_INLINE Label() noexcept : Operand(NoInit) { reset(); } + //! Create a reference to another label. + ASMJIT_INLINE Label(const Label& other) noexcept : Operand(other) {} + + explicit ASMJIT_INLINE Label(uint32_t id) noexcept : Operand(NoInit) { + _init_packed_d0_d1(kOpLabel, id); + _init_packed_d2_d3(0, 0); } - ASMJIT_INLINE bool operator==(const BaseMem& other) const noexcept { - return (_packed[0] == other._packed[0]) & (_packed[1] == other._packed[1]); + explicit ASMJIT_INLINE Label(const _NoInit&) noexcept : Operand(NoInit) {} + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + // TODO: I think that if operand is reset it shouldn't say it's a Label, it + // should be none like all other operands. + ASMJIT_INLINE void reset() noexcept { + _init_packed_d0_d1(kOpLabel, 0); + _init_packed_d2_d3(0, 0); } - ASMJIT_INLINE bool operator!=(const BaseMem& other) const noexcept { - return !(*this == other); + // -------------------------------------------------------------------------- + // [Label Specific] + // -------------------------------------------------------------------------- + + //! Get if the label was created by CodeEmitter and has an assigned id. + ASMJIT_INLINE bool isValid() const noexcept { return _label.id != 0; } + //! Set label id. + ASMJIT_INLINE void setId(uint32_t id) { _label.id = id; } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Label& operator=(const Label& other) noexcept { copyFrom(other); return *this; } +}; + +// ============================================================================ +// [asmjit::Reg] +// ============================================================================ + +#define ASMJIT_DEFINE_REG_TRAITS(TRAITS_T, REG_T, TYPE, KIND, SIZE, COUNT, TYPE_ID) \ +template<> \ +struct TRAITS_T < TYPE > { \ + typedef REG_T Reg; \ + \ + enum { \ + kValid = 1, \ + kCount = COUNT, \ + kTypeId = TYPE_ID, \ + \ + kType = TYPE, \ + kKind = KIND, \ + kSize = SIZE, \ + kSignature = (Operand::kOpReg << Operand::kSignatureOpShift ) | \ + (kType << Operand::kSignatureRegTypeShift) | \ + (kKind << Operand::kSignatureRegKindShift) | \ + (kSize << Operand::kSignatureSizeShift ) \ + }; \ +} \ + +#define ASMJIT_DEFINE_ABSTRACT_REG(REG_T, BASE_T) \ +public: \ + /*! Default constructor doesn't setup anything, it's like `Operand()`. */ \ + ASMJIT_INLINE REG_T() ASMJIT_NOEXCEPT \ + : BASE_T() {} \ + \ + /*! Copy the `other` REG_T register operand. */ \ + ASMJIT_INLINE REG_T(const REG_T& other) ASMJIT_NOEXCEPT \ + : BASE_T(other) {} \ + \ + /*! Copy the `other` REG_T register operand having its id set to `rId` */ \ + ASMJIT_INLINE REG_T(const Reg& other, uint32_t rId) ASMJIT_NOEXCEPT \ + : BASE_T(other, rId) {} \ + \ + /*! Create a REG_T register operand based on `signature` and `rId`. */ \ + ASMJIT_INLINE REG_T(const _Init& init, uint32_t signature, uint32_t rId) ASMJIT_NOEXCEPT \ + : BASE_T(init, signature, rId) {} \ + \ + /*! Create a completely uninitialized REG_T register operand (garbage). */ \ + explicit ASMJIT_INLINE REG_T(const _NoInit&) ASMJIT_NOEXCEPT \ + : BASE_T(NoInit) {} \ + \ + /*! Clone the register operand. */ \ + ASMJIT_INLINE REG_T clone() const ASMJIT_NOEXCEPT { return REG_T(*this); } \ + \ + /*! Create a new register from register type and id. */ \ + static ASMJIT_INLINE REG_T fromTypeAndId(uint32_t rType, uint32_t rId) ASMJIT_NOEXCEPT { \ + return REG_T(Init, signatureOf(rType), rId); \ + } \ + \ + /*! Create a new register from signature and id. */ \ + static ASMJIT_INLINE REG_T fromSignature(uint32_t signature, uint32_t rId) ASMJIT_NOEXCEPT { \ + return REG_T(Init, signature, rId); \ + } \ + \ + ASMJIT_INLINE REG_T& operator=(const REG_T& other) ASMJIT_NOEXCEPT { \ + copyFrom(other); return *this; \ } + +#define ASMJIT_DEFINE_FINAL_REG(REG_T, BASE_T, TRAITS_T) \ + ASMJIT_DEFINE_ABSTRACT_REG(REG_T, BASE_T) \ + \ + /*! Create a REG_T register with `id`. */ \ + explicit ASMJIT_INLINE REG_T(uint32_t rId) ASMJIT_NOEXCEPT \ + : BASE_T(Init, kSignature, rId) {} \ + \ + enum { \ + kThisType = TRAITS_T::kType, \ + kThisKind = TRAITS_T::kKind, \ + kThisSize = TRAITS_T::kSize, \ + kSignature = TRAITS_T::kSignature \ + }; + +//! Structure that contains core register information. +//! +//! This information is compatible with operand's signature (32-bit integer) +//! and `RegInfo` just provides easy way to access it. +struct RegInfo { + ASMJIT_INLINE uint32_t getSignature() const noexcept { + return _signature; + } + + ASMJIT_INLINE uint32_t getOp() const noexcept { + return (_signature >> Operand::kSignatureOpShift) & Operand::kSignatureOpBits; + } + + ASMJIT_INLINE uint32_t getType() const noexcept { + return (_signature >> Operand::kSignatureRegTypeShift) & Operand::kSignatureRegTypeBits; + } + + ASMJIT_INLINE uint32_t getKind() const noexcept { + return (_signature >> Operand::kSignatureRegKindShift) & Operand::kSignatureRegKindBits; + } + + ASMJIT_INLINE uint32_t getSize() const noexcept { + return (_signature >> Operand::kSignatureSizeShift) & Operand::kSignatureSizeBits; + } + + uint32_t _signature; +}; + +//! Physical/Virtual register operand. +class Reg : public Operand { +public: + //! Architecture neutral register types. + //! + //! These must be reused by any platform that contains that types. All GP + //! and VEC registers are also allowed by design to be part of a BASE|INDEX + //! of a memory operand. + ASMJIT_ENUM(RegType) { + kRegNone = 0, //!< No register - unused, invalid, multiple meanings. + // (1 is used as a LabelTag) + kRegGp8Lo = 2, //!< 8-bit low general purpose register (X86). + kRegGp8Hi = 3, //!< 8-bit high general purpose register (X86). + kRegGp16 = 4, //!< 16-bit general purpose register (X86). + kRegGp32 = 5, //!< 32-bit general purpose register (X86|ARM). + kRegGp64 = 6, //!< 64-bit general purpose register (X86|ARM). + kRegVec32 = 7, //!< 32-bit view of a vector register (ARM). + kRegVec64 = 8, //!< 64-bit view of a vector register (ARM). + kRegVec128 = 9, //!< 128-bit view of a vector register (X86|ARM). + kRegVec256 = 10, //!< 256-bit view of a vector register (X86). + kRegVec512 = 11, //!< 512-bit view of a vector register (X86). + kRegVec1024 = 12, //!< 1024-bit view of a vector register (future). + kRegVec2048 = 13, //!< 2048-bit view of a vector register (future). + kRegIP = 14, //!< Universal id of IP/PC register (if separate). + kRegCustom = 15, //!< Start of platform dependent register types (must be honored). + kRegMax = 31 //!< Maximum possible register id of all architectures. + }; + + //! Architecture neutral register kinds. + ASMJIT_ENUM(Kind) { + kKindGp = 0, //!< General purpose register (X86|ARM). + kKindVec = 1, //!< Vector register (X86|ARM). + kKindMax = 15 //!< Maximum possible register kind of all architectures. + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a dummy register operand. + ASMJIT_INLINE Reg() noexcept : Operand() {} + //! Create a new register operand which is the same as `other` . + ASMJIT_INLINE Reg(const Reg& other) noexcept : Operand(other) {} + //! Create a new register operand compatible with `other`, but with a different `rId`. + ASMJIT_INLINE Reg(const Reg& other, uint32_t rId) noexcept : Operand(NoInit) { + _init_packed_d0_d1(other._signature, rId); + _packed[1] = other._packed[1]; + } + + //! Create a register initialized to `signature` and `rId`. + ASMJIT_INLINE Reg(const _Init&, uint32_t signature, uint32_t rId) noexcept : Operand(NoInit) { + _initReg(signature, rId); + } + explicit ASMJIT_INLINE Reg(const _NoInit&) noexcept : Operand(NoInit) {} + + //! Create a new register based on `signature` and `rId`. + static ASMJIT_INLINE Reg fromSignature(uint32_t signature, uint32_t rId) noexcept { return Reg(Init, signature, rId); } + + // -------------------------------------------------------------------------- + // [Reg Specific] + // -------------------------------------------------------------------------- + + //! Get if the register is valid (either virtual or physical). + ASMJIT_INLINE bool isValid() const noexcept { return _signature != 0; } + //! Get if this is a physical register. + ASMJIT_INLINE bool isPhysReg() const noexcept { return _reg.id < Globals::kInvalidRegId; } + //! Get if this is a virtual register (used by \ref CodeCompiler). + ASMJIT_INLINE bool isVirtReg() const noexcept { return isPackedId(_reg.id); } + + //! Get if this register is the same as `other`. + //! + //! This is just an optimization. Registers by default only use the first + //! 8 bytes of the Operand, so this method takes advantage of this knowledge + //! and only compares these 8 bytes. If both operands were created correctly + //! then `isEqual()` and `isSame()` should give the same answer, however, if + //! some operands contains a garbage or other metadata in the upper 8 bytes + //! then `isSame()` may return `true` in cases where `isEqual()` returns + //! false. However. no such case is known at the moment. + ASMJIT_INLINE bool isSame(const Reg& other) const noexcept { return _packed[0] == _packed[1]; } + + //! Get if the register type matches `rType`. + //! Same as `isReg(rType)`, provided for convenience. + ASMJIT_INLINE bool isType(uint32_t rType) const noexcept { return (_signature & kSignatureRegTypeMask) == (rType << kSignatureRegTypeShift); } + //! Get if the register kind matches `rKind`. + ASMJIT_INLINE bool isKind(uint32_t rKind) const noexcept { return (_signature & kSignatureRegKindMask) == (rKind << kSignatureRegKindShift); } + + //! Get if the register is a general purpose register (any size). + ASMJIT_INLINE bool isGp() const noexcept { return isKind(kKindGp); } + //! Get if the register is a vector register. + ASMJIT_INLINE bool isVec() const noexcept { return isKind(kKindVec); } + + using Operand_::isReg; + + //! Same as `isType()`, provided for convenience. + ASMJIT_INLINE bool isReg(uint32_t rType) const noexcept { return isType(rType); } + //! Get if the register type matches `type` and register id matches `rId`. + ASMJIT_INLINE bool isReg(uint32_t rType, uint32_t rId) const noexcept { return isType(rType) && getId() == rId; } + + //! Get the register type. + ASMJIT_INLINE uint32_t getType() const noexcept { return _getSignatureData(kSignatureRegTypeBits, kSignatureRegTypeShift); } + //! Get the register kind. + ASMJIT_INLINE uint32_t getKind() const noexcept { return _getSignatureData(kSignatureRegKindBits, kSignatureRegKindShift); } + + //! Clone the register operand. + ASMJIT_INLINE Reg clone() const noexcept { return Reg(*this); } + + //! Cast this register to `RegT` by also changing its signature. + //! + //! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors. + template + ASMJIT_INLINE RegT cloneAs() const noexcept { return RegT(Init, RegT::kSignature, getId()); } + + //! Cast this register to `other` by also changing its signature. + //! + //! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors. + template + ASMJIT_INLINE RegT cloneAs(const RegT& other) const noexcept { return RegT(Init, other.getSignature(), getId()); } + + //! Set the register id to `id`. + ASMJIT_INLINE void setId(uint32_t rId) noexcept { _reg.id = rId; } + + //! Set a 32-bit operand signature based on traits of `RegT`. + template + ASMJIT_INLINE void setSignatureT() noexcept { _signature = RegT::kSignature; } + + //! Set register's `signature` and `rId`. + ASMJIT_INLINE void setSignatureAndId(uint32_t signature, uint32_t rId) noexcept { + _signature = signature; + _reg.id = rId; + } + + // -------------------------------------------------------------------------- + // [Reg Statics] + // -------------------------------------------------------------------------- + + static ASMJIT_INLINE bool isGp(const Operand_& op) noexcept { + // Check operand type and register kind. Not interested in register type and size. + const uint32_t kSgn = (kOpReg << kSignatureOpShift ) | + (kKindGp << kSignatureRegKindShift) ; + return (op.getSignature() & (kSignatureOpMask | kSignatureRegKindMask)) == kSgn; + } + + //! Get if the `op` operand is either a low or high 8-bit GPB register. + static ASMJIT_INLINE bool isVec(const Operand_& op) noexcept { + // Check operand type and register kind. Not interested in register type and size. + const uint32_t kSgn = (kOpReg << kSignatureOpShift ) | + (kKindVec << kSignatureRegKindShift) ; + return (op.getSignature() & (kSignatureOpMask | kSignatureRegKindMask)) == kSgn; + } + + static ASMJIT_INLINE bool isGp(const Operand_& op, uint32_t rId) noexcept { return isGp(op) & (op.getId() == rId); } + static ASMJIT_INLINE bool isVec(const Operand_& op, uint32_t rId) noexcept { return isVec(op) & (op.getId() == rId); } +}; + +// ============================================================================ +// [asmjit::Mem] +// ============================================================================ + +//! Base class for all memory operands. +//! +//! NOTE: It's tricky to pack all possible cases that define a memory operand +//! into just 16 bytes. The `Mem` splits data into the following parts: +//! +//! BASE - Base register or label - requires 36 bits total. 4 bits are used +//! to encode the type of the BASE operand (label vs. register type) and +//! the remaining 32 bits define the BASE id, which can be a physical or +//! virtual register index. If BASE type is zero, which is never used as +//! a register-type and label doesn't use it as well then BASE field +//! contains a high DWORD of a possible 64-bit absolute address, which is +//! possible on X64. +//! +//! INDEX - Index register (or theoretically Label, which doesn't make sense). +//! Encoding is similar to BASE - it also requires 36 bits and splits the +//! encoding to INDEX type (4 bits defining the register type) and id (32-bits). +//! +//! OFFSET - A relative offset of the address. Basically if BASE is specified +//! the relative displacement adjusts BASE and an optional INDEX. if BASE is +//! not specified then the OFFSET should be considered as ABSOLUTE address +//! (at least on X86/X64). In that case its low 32 bits are stored in +//! DISPLACEMENT field and the remaining high 32 bits are stored in BASE. +//! +//! OTHER FIELDS - There is rest 8 bits that can be used for whatever purpose. +//! The X86Mem operand uses these bits to store segment override +//! prefix and index shift (scale). +class Mem : public Operand { +public: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Construct a default `Mem` operand, that points to [0]. + ASMJIT_INLINE Mem() noexcept : Operand(NoInit) { reset(); } + ASMJIT_INLINE Mem(const Mem& other) noexcept : Operand(other) {} + + ASMJIT_INLINE Mem(const _Init&, + uint32_t baseType, uint32_t baseId, + uint32_t indexType, uint32_t indexId, + int32_t off, uint32_t size, uint32_t flags) noexcept : Operand(NoInit) { + + uint32_t signature = (baseType << kSignatureMemBaseTypeShift ) | + (indexType << kSignatureMemIndexTypeShift) | + (size << kSignatureSizeShift ) ; + + _init_packed_d0_d1(kOpMem | signature | flags, indexId); + _mem.base = baseId; + _mem.offsetLo32 = static_cast(off); + } + explicit ASMJIT_INLINE Mem(const _NoInit&) noexcept : Operand(NoInit) {} + + // -------------------------------------------------------------------------- + // [Mem Specific] + // -------------------------------------------------------------------------- + + //! Clone `Mem` operand. + ASMJIT_INLINE Mem clone() const noexcept { return Mem(*this); } + + //! Reset the memory operand - after reset the memory points to [0]. + ASMJIT_INLINE void reset() noexcept { + _init_packed_d0_d1(kOpMem, 0); + _init_packed_d2_d3(0, 0); + } + + ASMJIT_INLINE bool isAbs() const noexcept { return _hasSignatureData(kSignatureMemAbsoluteFlag); } + ASMJIT_INLINE bool isArgHome() const noexcept { return _hasSignatureData(kSignatureMemArgHomeFlag); } + ASMJIT_INLINE bool isRegHome() const noexcept { return _hasSignatureData(kSignatureMemRegHomeFlag); } + + ASMJIT_INLINE void setAbs() noexcept { _signature |= kSignatureMemAbsoluteFlag; } + ASMJIT_INLINE void setArgHome() noexcept { _signature |= kSignatureMemArgHomeFlag; } + ASMJIT_INLINE void setRegHome() noexcept { _signature |= kSignatureMemRegHomeFlag; } + + ASMJIT_INLINE void clearAbs() noexcept { _signature &= ~kSignatureMemAbsoluteFlag; } + ASMJIT_INLINE void clearArgHome() noexcept { _signature &= ~kSignatureMemArgHomeFlag; } + ASMJIT_INLINE void clearRegHome() noexcept { _signature &= ~kSignatureMemRegHomeFlag; } + + //! Get if the memory operand has a BASE register or label specified. + ASMJIT_INLINE bool hasBase() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0; } + //! Get if the memory operand has an INDEX register specified. + ASMJIT_INLINE bool hasIndex() const noexcept { return (_signature & kSignatureMemIndexTypeMask) != 0; } + //! Get whether the memory operand has BASE and INDEX register. + ASMJIT_INLINE bool hasBaseOrIndex() const noexcept { return (_signature & kSignatureMemBaseIndexMask) != 0; } + //! Get whether the memory operand has BASE and INDEX register. + ASMJIT_INLINE bool hasBaseAndIndex() const noexcept { + return (_signature & kSignatureMemBaseTypeMask) != 0 && + (_signature & kSignatureMemIndexTypeMask) != 0; + } + + //! Get if the BASE operand is a register. + ASMJIT_INLINE bool hasBaseReg() const noexcept { + // Registers start after kLabelTag. + return (_signature & kSignatureMemBaseTypeMask) > (Label::kLabelTag << kSignatureMemBaseTypeShift); + } + //! Get if the BASE operand is a label. + ASMJIT_INLINE bool hasBaseLabel() const noexcept { + return (_signature & kSignatureMemBaseTypeMask) == (Label::kLabelTag << kSignatureMemBaseTypeShift); + } + + //! Get if the INDEX operand is a register. + ASMJIT_INLINE bool hasIndexReg() const noexcept { + // Registers start after kLabelTag. + return (_signature & kSignatureMemIndexTypeMask) > (Label::kLabelTag << kSignatureMemIndexTypeShift); + } + + //! Get type of a BASE register (0 if this memory operand doesn't use the BASE register). + //! + //! NOTE: If the returned type is one (a value never associated to a register + //! type) the BASE is not register, but it's a label. One equals to `kLabelTag`. + //! You should always check `hasBaseLabel()` before using `getBaseId()` result. + ASMJIT_INLINE uint32_t getBaseType() const noexcept { + return _getSignatureData(kSignatureMemBaseTypeBits, kSignatureMemBaseTypeShift); + } + //! Get type of an INDEX register (0 if this memory operand doesn't use the INDEX register). + ASMJIT_INLINE uint32_t getIndexType() const noexcept { + return _getSignatureData(kSignatureMemIndexTypeBits, kSignatureMemIndexTypeShift); + } + + //! Get both BASE (4:0 bits) and INDEX (9:5 bits) types combined into a single integer. + //! + //! This is used internally for BASE+INDEX validation. + ASMJIT_INLINE uint32_t getBaseIndexType() const noexcept { + return _getSignatureData(kSignatureMemBaseIndexBits, kSignatureMemBaseIndexShift); + } + + //! Get id of the BASE register or label (if the BASE was specified as label). + ASMJIT_INLINE uint32_t getBaseId() const noexcept { return _mem.base; } + //! Get id of the INDEX register. + ASMJIT_INLINE uint32_t getIndexId() const noexcept { return _mem.index; } + + ASMJIT_INLINE void _setBase(uint32_t rType, uint32_t rId) noexcept { + _setSignatureData(rType, kSignatureMemBaseTypeBits, kSignatureMemBaseTypeShift); + _mem.base = rId; + } + + ASMJIT_INLINE void _setIndex(uint32_t rType, uint32_t rId) noexcept { + _setSignatureData(rType, kSignatureMemIndexTypeBits, kSignatureMemIndexTypeShift); + _mem.index = rId; + } + + ASMJIT_INLINE void setBase(const Reg& base) noexcept { return _setBase(base.getType(), base.getId()); } + ASMJIT_INLINE void setIndex(const Reg& index) noexcept { return _setIndex(index.getType(), index.getId()); } + + //! Reset the memory operand's BASE register / label. + ASMJIT_INLINE void resetBase() noexcept { _setBase(0, 0); } + //! Reset the memory operand's INDEX register. + ASMJIT_INLINE void resetIndex() noexcept { _setIndex(0, 0); } + + //! Set memory operand size. + ASMJIT_INLINE void setSize(uint32_t size) noexcept { + _setSignatureData(size, kSignatureSizeBits, kSignatureSizeShift); + } + + ASMJIT_INLINE bool hasOffset() const noexcept { + int32_t lo = static_cast(_mem.offsetLo32); + int32_t hi = static_cast(_mem.base) & -static_cast(getBaseType() == 0); + return (lo | hi) != 0; + } + + //! Get if the memory operand has 64-bit offset or absolute address. + //! + //! If this is true then `hasBase()` must always report false. + ASMJIT_INLINE bool has64BitOffset() const noexcept { return getBaseType() == 0; } + + //! Get a 64-bit offset or absolute address. + ASMJIT_INLINE int64_t getOffset() const noexcept { + return has64BitOffset() + ? static_cast(_mem.offset64) + : static_cast(static_cast(_mem.offsetLo32)); // Sign-Extend. + } + + //! Get a lower part of a 64-bit offset or absolute address. + ASMJIT_INLINE int32_t getOffsetLo32() const noexcept { return static_cast(_mem.offsetLo32); } + //! Get a higher part of a 64-bit offset or absolute address. + //! + //! NOTE: This function is UNSAFE and returns garbage if `has64BitOffset()` + //! returns false. Never use blindly without checking it. + ASMJIT_INLINE int32_t getOffsetHi32() const noexcept { return static_cast(_mem.base); } + + //! Set a 64-bit offset or an absolute address to `offset`. + //! + //! NOTE: This functions attempts to set both high and low parts of a 64-bit + //! offset, however, if the operand has a BASE register it will store only the + //! low 32 bits of the offset / address as there is no way to store both BASE + //! and 64-bit offset, and there is currently no architecture that has such + //! capability targeted by AsmJit. + ASMJIT_INLINE void setOffset(int64_t offset) noexcept { + if (has64BitOffset()) + _mem.offset64 = static_cast(offset); + else + _mem.offsetLo32 = static_cast(offset & 0xFFFFFFFF); + } + //! Adjust the offset by a 64-bit `off`. + ASMJIT_INLINE void addOffset(int64_t off) noexcept { + if (has64BitOffset()) + _mem.offset64 += static_cast(off); + else + _mem.offsetLo32 += static_cast(off & 0xFFFFFFFF); + } + //! Reset the memory offset to zero. + ASMJIT_INLINE void resetOffset() noexcept { setOffset(0); } + + //! Set a low 32-bit offset to `off`. + ASMJIT_INLINE void setOffsetLo32(int32_t off) noexcept { + _mem.offsetLo32 = static_cast(off); + } + //! Adjust the offset by `off`. + //! + //! NOTE: This is a fast function that doesn't use the HI 32-bits of a + //! 64-bit offset. Use it only if you know that there is a BASE register + //! and the offset is only 32 bits anyway. + ASMJIT_INLINE void addOffsetLo32(int32_t off) noexcept { + _mem.offsetLo32 += static_cast(off); + } + //! Reset the memory offset to zero. + ASMJIT_INLINE void resetOffsetLo32() noexcept { setOffsetLo32(0); } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Mem& operator=(const Mem& other) noexcept { copyFrom(other); return *this; } }; // ============================================================================ @@ -782,21 +1026,21 @@ class BaseMem : public Operand { //! To create immediate operand use `imm()` or `imm_u()` non-members or `Imm` //! constructors. class Imm : public Operand { - public: +public: // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- //! Create a new immediate value (initial value is 0). Imm() noexcept : Operand(NoInit) { - _init_packed_op_sz_b0_b1_id(kTypeImm, 0, 0, 0, kInvalidValue); - _imm.value._i64[0] = 0; + _init_packed_d0_d1(kOpImm, 0); + _imm.value.i64 = 0; } //! Create a new signed immediate value, assigning the value to `val`. explicit Imm(int64_t val) noexcept : Operand(NoInit) { - _init_packed_op_sz_b0_b1_id(kTypeImm, 0, 0, 0, kInvalidValue); - _imm.value._i64[0] = val; + _init_packed_d0_d1(kOpImm, 0); + _imm.value.i64 = val; } //! Create a new immediate value from `other`. @@ -809,41 +1053,50 @@ class Imm : public Operand { // -------------------------------------------------------------------------- //! Clone `Imm` operand. - ASMJIT_INLINE Imm clone() const noexcept { - return Imm(*this); - } + ASMJIT_INLINE Imm clone() const noexcept { return Imm(*this); } //! Get whether the immediate can be casted to 8-bit signed integer. - ASMJIT_INLINE bool isInt8() const noexcept { return Utils::isInt8(_imm.value._i64[0]); } + ASMJIT_INLINE bool isInt8() const noexcept { return Utils::isInt8(_imm.value.i64); } //! Get whether the immediate can be casted to 8-bit unsigned integer. - ASMJIT_INLINE bool isUInt8() const noexcept { return Utils::isUInt8(_imm.value._i64[0]); } + ASMJIT_INLINE bool isUInt8() const noexcept { return Utils::isUInt8(_imm.value.i64); } //! Get whether the immediate can be casted to 16-bit signed integer. - ASMJIT_INLINE bool isInt16() const noexcept { return Utils::isInt16(_imm.value._i64[0]); } + ASMJIT_INLINE bool isInt16() const noexcept { return Utils::isInt16(_imm.value.i64); } //! Get whether the immediate can be casted to 16-bit unsigned integer. - ASMJIT_INLINE bool isUInt16() const noexcept { return Utils::isUInt16(_imm.value._i64[0]); } + ASMJIT_INLINE bool isUInt16() const noexcept { return Utils::isUInt16(_imm.value.i64); } //! Get whether the immediate can be casted to 32-bit signed integer. - ASMJIT_INLINE bool isInt32() const noexcept { return Utils::isInt32(_imm.value._i64[0]); } + ASMJIT_INLINE bool isInt32() const noexcept { return Utils::isInt32(_imm.value.i64); } //! Get whether the immediate can be casted to 32-bit unsigned integer. - ASMJIT_INLINE bool isUInt32() const noexcept { return Utils::isUInt32(_imm.value._i64[0]); } + ASMJIT_INLINE bool isUInt32() const noexcept { return Utils::isUInt32(_imm.value.i64); } //! Get immediate value as 8-bit signed integer. - ASMJIT_INLINE int8_t getInt8() const noexcept { return _imm.value._i8[_ASMJIT_ARCH_INDEX(8, 0)]; } + ASMJIT_INLINE int8_t getInt8() const noexcept { return static_cast(_imm.value.i32Lo & 0xFF); } //! Get immediate value as 8-bit unsigned integer. - ASMJIT_INLINE uint8_t getUInt8() const noexcept { return _imm.value._u8[_ASMJIT_ARCH_INDEX(8, 0)]; } + ASMJIT_INLINE uint8_t getUInt8() const noexcept { return static_cast(_imm.value.u32Lo & 0xFFU); } //! Get immediate value as 16-bit signed integer. - ASMJIT_INLINE int16_t getInt16() const noexcept { return _imm.value._i16[_ASMJIT_ARCH_INDEX(4, 0)]; } + ASMJIT_INLINE int16_t getInt16() const noexcept { return static_cast(_imm.value.i32Lo & 0xFFFF);} //! Get immediate value as 16-bit unsigned integer. - ASMJIT_INLINE uint16_t getUInt16() const noexcept { return _imm.value._u16[_ASMJIT_ARCH_INDEX(4, 0)]; } + ASMJIT_INLINE uint16_t getUInt16() const noexcept { return static_cast(_imm.value.u32Lo & 0xFFFFU);} + //! Get immediate value as 32-bit signed integer. - ASMJIT_INLINE int32_t getInt32() const noexcept { return _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 0)]; } + ASMJIT_INLINE int32_t getInt32() const noexcept { return _imm.value.i32Lo; } + //! Get low 32-bit signed integer. + ASMJIT_INLINE int32_t getInt32Lo() const noexcept { return _imm.value.i32Lo; } + //! Get high 32-bit signed integer. + ASMJIT_INLINE int32_t getInt32Hi() const noexcept { return _imm.value.i32Hi; } + //! Get immediate value as 32-bit unsigned integer. - ASMJIT_INLINE uint32_t getUInt32() const noexcept { return _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)]; } + ASMJIT_INLINE uint32_t getUInt32() const noexcept { return _imm.value.u32Lo; } + //! Get low 32-bit signed integer. + ASMJIT_INLINE uint32_t getUInt32Lo() const noexcept { return _imm.value.u32Lo; } + //! Get high 32-bit signed integer. + ASMJIT_INLINE uint32_t getUInt32Hi() const noexcept { return _imm.value.u32Hi; } + //! Get immediate value as 64-bit signed integer. - ASMJIT_INLINE int64_t getInt64() const noexcept { return _imm.value._i64[0]; } + ASMJIT_INLINE int64_t getInt64() const noexcept { return _imm.value.i64; } //! Get immediate value as 64-bit unsigned integer. - ASMJIT_INLINE uint64_t getUInt64() const noexcept { return _imm.value._u64[0]; } + ASMJIT_INLINE uint64_t getUInt64() const noexcept { return _imm.value.u64; } //! Get immediate value as `intptr_t`. ASMJIT_INLINE intptr_t getIntPtr() const noexcept { @@ -861,332 +1114,381 @@ class Imm : public Operand { return static_cast(getUInt32()); } - //! Get low 32-bit signed integer. - ASMJIT_INLINE int32_t getInt32Lo() const noexcept { return _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 0)]; } - //! Get low 32-bit signed integer. - ASMJIT_INLINE uint32_t getUInt32Lo() const noexcept { return _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)]; } - //! Get high 32-bit signed integer. - ASMJIT_INLINE int32_t getInt32Hi() const noexcept { return _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 1)]; } - //! Get high 32-bit signed integer. - ASMJIT_INLINE uint32_t getUInt32Hi() const noexcept { return _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)]; } - //! Set immediate value to 8-bit signed integer `val`. - ASMJIT_INLINE Imm& setInt8(int8_t val) noexcept { - if (ASMJIT_ARCH_64BIT) { - _imm.value._i64[0] = static_cast(val); - } - else { - int32_t val32 = static_cast(val); - _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 0)] = val32; - _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 1)] = val32 >> 31; - } - return *this; - } - + ASMJIT_INLINE void setInt8(int8_t val) noexcept { _imm.value.i64 = static_cast(val); } //! Set immediate value to 8-bit unsigned integer `val`. - ASMJIT_INLINE Imm& setUInt8(uint8_t val) noexcept { - if (ASMJIT_ARCH_64BIT) { - _imm.value._u64[0] = static_cast(val); - } - else { - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)] = static_cast(val); - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0; - } - return *this; - } + ASMJIT_INLINE void setUInt8(uint8_t val) noexcept { _imm.value.u64 = static_cast(val); } //! Set immediate value to 16-bit signed integer `val`. - ASMJIT_INLINE Imm& setInt16(int16_t val) noexcept { - if (ASMJIT_ARCH_64BIT) { - _imm.value._i64[0] = static_cast(val); - } - else { - int32_t val32 = static_cast(val); - _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 0)] = val32; - _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 1)] = val32 >> 31; - } - return *this; - } - + ASMJIT_INLINE void setInt16(int16_t val) noexcept { _imm.value.i64 = static_cast(val); } //! Set immediate value to 16-bit unsigned integer `val`. - ASMJIT_INLINE Imm& setUInt16(uint16_t val) noexcept { - if (ASMJIT_ARCH_64BIT) { - _imm.value._u64[0] = static_cast(val); - } - else { - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)] = static_cast(val); - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0; - } - return *this; - } + ASMJIT_INLINE void setUInt16(uint16_t val) noexcept { _imm.value.u64 = static_cast(val); } //! Set immediate value to 32-bit signed integer `val`. - ASMJIT_INLINE Imm& setInt32(int32_t val) noexcept { - if (ASMJIT_ARCH_64BIT) { - _imm.value._i64[0] = static_cast(val); - } - else { - _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 0)] = val; - _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 1)] = val >> 31; - } - return *this; - } - + ASMJIT_INLINE void setInt32(int32_t val) noexcept { _imm.value.i64 = static_cast(val); } //! Set immediate value to 32-bit unsigned integer `val`. - ASMJIT_INLINE Imm& setUInt32(uint32_t val) noexcept { - if (ASMJIT_ARCH_64BIT) { - _imm.value._u64[0] = static_cast(val); - } - else { - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)] = val; - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0; - } - return *this; - } + ASMJIT_INLINE void setUInt32(uint32_t val) noexcept { _imm.value.u64 = static_cast(val); } //! Set immediate value to 64-bit signed integer `val`. - ASMJIT_INLINE Imm& setInt64(int64_t val) noexcept { - _imm.value._i64[0] = val; - return *this; - } - + ASMJIT_INLINE void setInt64(int64_t val) noexcept { _imm.value.i64 = val; } //! Set immediate value to 64-bit unsigned integer `val`. - ASMJIT_INLINE Imm& setUInt64(uint64_t val) noexcept { - _imm.value._u64[0] = val; - return *this; - } - + ASMJIT_INLINE void setUInt64(uint64_t val) noexcept { _imm.value.u64 = val; } //! Set immediate value to intptr_t `val`. - ASMJIT_INLINE Imm& setIntPtr(intptr_t val) noexcept { - _imm.value._i64[0] = static_cast(val); - return *this; - } - + ASMJIT_INLINE void setIntPtr(intptr_t val) noexcept { _imm.value.i64 = static_cast(val); } //! Set immediate value to uintptr_t `val`. - ASMJIT_INLINE Imm& setUIntPtr(uintptr_t val) noexcept { - _imm.value._u64[0] = static_cast(val); - return *this; - } + ASMJIT_INLINE void setUIntPtr(uintptr_t val) noexcept { _imm.value.u64 = static_cast(val); } //! Set immediate value as unsigned type to `val`. - ASMJIT_INLINE Imm& setPtr(void* p) noexcept { - return setIntPtr((intptr_t)p); - } + ASMJIT_INLINE void setPtr(void* p) noexcept { setIntPtr((uint64_t)p); } + //! Set immediate value to `val`. + template + ASMJIT_INLINE void setValue(T val) noexcept { setIntPtr((int64_t)val); } // -------------------------------------------------------------------------- // [Float] // -------------------------------------------------------------------------- - ASMJIT_INLINE Imm& setFloat(float f) noexcept { - _imm.value._f32[_ASMJIT_ARCH_INDEX(2, 0)] = f; - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0; - return *this; + ASMJIT_INLINE void setFloat(float f) noexcept { + _imm.value.f32Lo = f; + _imm.value.u32Hi = 0; } - ASMJIT_INLINE Imm& setDouble(double d) noexcept { - _imm.value._f64[0] = d; - return *this; + ASMJIT_INLINE void setDouble(double d) noexcept { + _imm.value.f64 = d; } // -------------------------------------------------------------------------- // [Truncate] // -------------------------------------------------------------------------- - ASMJIT_INLINE Imm& truncateTo8Bits() noexcept { + ASMJIT_INLINE void truncateTo8Bits() noexcept { if (ASMJIT_ARCH_64BIT) { - _imm.value._u64[0] &= static_cast(0x000000FFU); + _imm.value.u64 &= static_cast(0x000000FFU); } else { - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)] &= 0x000000FFU; - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0; + _imm.value.u32Lo &= 0x000000FFU; + _imm.value.u32Hi = 0; } - return *this; } - ASMJIT_INLINE Imm& truncateTo16Bits() noexcept { + ASMJIT_INLINE void truncateTo16Bits() noexcept { if (ASMJIT_ARCH_64BIT) { - _imm.value._u64[0] &= static_cast(0x0000FFFFU); + _imm.value.u64 &= static_cast(0x0000FFFFU); } else { - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)] &= 0x0000FFFFU; - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0; + _imm.value.u32Lo &= 0x0000FFFFU; + _imm.value.u32Hi = 0; } - return *this; } - ASMJIT_INLINE Imm& truncateTo32Bits() noexcept { - _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0; - return *this; - } + ASMJIT_INLINE void truncateTo32Bits() noexcept { _imm.value.u32Hi = 0; } // -------------------------------------------------------------------------- // [Operator Overload] // -------------------------------------------------------------------------- //! Assign `other` to the immediate operand. - ASMJIT_INLINE Imm& operator=(const Imm& other) noexcept { - _copy(other); - return *this; - } + ASMJIT_INLINE Imm& operator=(const Imm& other) noexcept { copyFrom(other); return *this; } }; -// ============================================================================ -// [asmjit::Label] -// ============================================================================ - -//! Label (jump target or data location). -//! -//! Label represents a location in code typically used as a jump target, but -//! may be also a reference to some data or a static variable. Label has to be -//! explicitly created by the `Assembler` or any `ExternalTool` by using their -//! `newLabel()` function. -//! -//! Example of using labels: -//! -//! ~~~ -//! // Create Assembler/Compiler. -//! X86Assembler a; -//! -//! // Create Label instance. -//! Label L1 = a.newLabel(); -//! -//! // ... your code ... -//! -//! // Using label. -//! a.jump(L1); -//! -//! // ... your code ... -//! -//! // Bind label to the current position, see `Assembler::bind()`. -//! a.bind(L1); -//! ~~~ -class Label : public Operand { - public: - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create new, unassociated label. - ASMJIT_INLINE Label() noexcept : Operand(NoInit) { - reset(); - } - - explicit ASMJIT_INLINE Label(uint32_t id) noexcept : Operand(NoInit) { - _init_packed_op_sz_b0_b1_id(kTypeLabel, 0, 0, 0, id); - _init_packed_d2_d3(0, 0); - } - - //! Create reference to another label. - ASMJIT_INLINE Label(const Label& other) noexcept : Operand(other) {} - - explicit ASMJIT_INLINE Label(const _NoInit&) noexcept : Operand(NoInit) {} - - // -------------------------------------------------------------------------- - // [Reset] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE void reset() noexcept { - _init_packed_op_sz_b0_b1_id(kTypeLabel, 0, 0, 0, kInvalidValue); - _init_packed_d2_d3(0, 0); - } - - // -------------------------------------------------------------------------- - // [Label Specific] - // -------------------------------------------------------------------------- - - //! Get whether the label has been initialized by `Assembler` or `Compiler`. - ASMJIT_INLINE bool isInitialized() const noexcept { return _label.id != kInvalidValue; } - - // -------------------------------------------------------------------------- - // [Operator Overload] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE Label& operator=(const Label& other) noexcept { _copy(other); return *this; } - - ASMJIT_INLINE bool operator==(const Label& other) const noexcept { return _base.id == other._base.id; } - ASMJIT_INLINE bool operator!=(const Label& other) const noexcept { return _base.id != other._base.id; } -}; - -// ============================================================================ -// [asmjit::Var] -// ============================================================================ - -#if !defined(ASMJIT_DISABLE_COMPILER) -//! Base class for all variables. -class Var : public Operand { - public: - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE Var() noexcept : Operand(NoInit) { - _init_packed_op_sz_b0_b1_id(kTypeVar, 0, 0, 0, kInvalidValue); - _init_packed_d2_d3(kInvalidValue, kInvalidValue); - } - - ASMJIT_INLINE Var(const Var& other) noexcept : Operand(other) {} - - explicit ASMJIT_INLINE Var(const _NoInit&) noexcept : Operand(NoInit) {} - - // -------------------------------------------------------------------------- - // [Var Specific] - // -------------------------------------------------------------------------- - - //! Clone `Var` operand. - ASMJIT_INLINE Var clone() const noexcept { return Var(*this); } - - //! Reset Var operand. - ASMJIT_INLINE void reset() noexcept { - _init_packed_op_sz_b0_b1_id(kTypeVar, 0, kInvalidReg, kInvalidReg, kInvalidValue); - _init_packed_d2_d3(kInvalidValue, kInvalidValue); - } - - //! Get whether the variable has been initialized by `Compiler`. - ASMJIT_INLINE bool isInitialized() const noexcept { return _vreg.id != kInvalidValue; } - //! Get variable type. - ASMJIT_INLINE uint32_t getVarType() const noexcept { return _vreg.vType; } - - // -------------------------------------------------------------------------- - // [Operator Overload] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE Var& operator=(const Var& other) noexcept { _copy(other); return *this; } - - ASMJIT_INLINE bool operator==(const Var& other) const noexcept { return _packed[0] == other._packed[0]; } - ASMJIT_INLINE bool operator!=(const Var& other) const noexcept { return !operator==(other); } -}; -#endif // !ASMJIT_DISABLE_COMPILER - -// ============================================================================ -// [asmjit::Operand - Globals] -// ============================================================================ - -//! No operand, can be used to reset an operand by assignment or to refer to an -//! operand that doesn't exist. -ASMJIT_VARAPI const Operand noOperand; - //! Create a signed immediate operand. -static ASMJIT_INLINE Imm imm(int64_t val) noexcept { - return Imm(val); -} - +static ASMJIT_INLINE Imm imm(int64_t val) noexcept { return Imm(val); } //! Create an unsigned immediate operand. -static ASMJIT_INLINE Imm imm_u(uint64_t val) noexcept { - return Imm(static_cast(val)); -} - -//! Create a `void*` immediate operand. +static ASMJIT_INLINE Imm imm_u(uint64_t val) noexcept { return Imm(static_cast(val)); } +//! Create an immediate operand from `p`. template -static ASMJIT_INLINE Imm imm_ptr(T p) noexcept { - return Imm(static_cast((intptr_t)p)); -} +static ASMJIT_INLINE Imm imm_ptr(T p) noexcept { return Imm(static_cast((intptr_t)p)); } + +// ============================================================================ +// [asmjit::TypeId] +// ============================================================================ + +//! Type-id. +//! +//! This is an additional information that can be used to describe a physical +//! or virtual register. it's used mostly by CodeCompiler to describe register +//! representation (the kind of data stored in the register and the width used) +//! and it's also used by APIs that allow to describe and work with function +//! signatures. +struct TypeId { + // -------------------------------------------------------------------------- + // [Id] + // -------------------------------------------------------------------------- + + enum Id { + kVoid = 0, + + _kIntStart = 32, + _kIntEnd = 41, + + kIntPtr = 32, + kUIntPtr = 33, + + kI8 = 34, + kU8 = 35, + kI16 = 36, + kU16 = 37, + kI32 = 38, + kU32 = 39, + kI64 = 40, + kU64 = 41, + + _kFloatStart = 42, + _kFloatEnd = 44, + + kF32 = 42, + kF64 = 43, + kF80 = 44, + + _kMaskStart = 45, + _kMaskEnd = 48, + + kMask8 = 45, + kMask16 = 46, + kMask32 = 47, + kMask64 = 48, + + _kMmxStart = 49, + _kMmxEnd = 50, + + kMmx32 = 49, + kMmx64 = 50, + + _kVec32Start = 51, + _kVec32End = 60, + + kI8x4 = 51, + kU8x4 = 52, + kI16x2 = 53, + kU16x2 = 54, + kI32x1 = 55, + kU32x1 = 56, + kF32x1 = 59, + + _kVec64Start = 61, + _kVec64End = 70, + + kI8x8 = 61, + kU8x8 = 62, + kI16x4 = 63, + kU16x4 = 64, + kI32x2 = 65, + kU32x2 = 66, + kI64x1 = 67, + kU64x1 = 68, + kF32x2 = 69, + kF64x1 = 70, + + _kVec128Start = 71, + _kVec128End = 80, + + kI8x16 = 71, + kU8x16 = 72, + kI16x8 = 73, + kU16x8 = 74, + kI32x4 = 75, + kU32x4 = 76, + kI64x2 = 77, + kU64x2 = 78, + kF32x4 = 79, + kF64x2 = 80, + + _kVec256Start = 81, + _kVec256End = 90, + + kI8x32 = 81, + kU8x32 = 82, + kI16x16 = 83, + kU16x16 = 84, + kI32x8 = 85, + kU32x8 = 86, + kI64x4 = 87, + kU64x4 = 88, + kF32x8 = 89, + kF64x4 = 90, + + _kVec512Start = 91, + _kVec512End = 100, + + kI8x64 = 91, + kU8x64 = 92, + kI16x32 = 93, + kU16x32 = 94, + kI32x16 = 95, + kU32x16 = 96, + kI64x8 = 97, + kU64x8 = 98, + kF32x16 = 99, + kF64x8 = 100, + + kCount = 101 + }; + + // -------------------------------------------------------------------------- + // [TypeName - Used by Templates] + // -------------------------------------------------------------------------- + + struct Int8 {}; //!< int8_t as C++ type-name. + struct UInt8 {}; //!< uint8_t as C++ type-name. + struct Int16 {}; //!< int16_t as C++ type-name. + struct UInt16 {}; //!< uint16_t as C++ type-name. + struct Int32 {}; //!< int32_t as C++ type-name. + struct UInt32 {}; //!< uint32_t as C++ type-name. + struct Int64 {}; //!< int64_t as C++ type-name. + struct UInt64 {}; //!< uint64_t as C++ type-name. + struct IntPtr {}; //!< intptr_t as C++ type-name. + struct UIntPtr {}; //!< uintptr_t as C++ type-name. + struct Float {}; //!< float as C++ type-name. + struct Double {}; //!< double as C++ type-name. + struct MmxReg {}; //!< MMX register as C++ type-name. + struct Vec128 {}; //!< SIMD128/XMM register as C++ type-name. + struct Vec256 {}; //!< SIMD256/YMM register as C++ type-name. + struct Vec512 {}; //!< SIMD512/ZMM register as C++ type-name. + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + struct Info { + uint8_t sizeOf[128]; + uint8_t elementOf[128]; + }; + + ASMJIT_API static const Info _info; + + static ASMJIT_INLINE bool isVoid(uint32_t typeId) noexcept { return typeId == 0; } + static ASMJIT_INLINE bool isValid(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kVec512End; } + static ASMJIT_INLINE bool isAbstract(uint32_t typeId) noexcept { return typeId >= kIntPtr && typeId <= kUIntPtr; } + static ASMJIT_INLINE bool isInt(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kIntEnd; } + static ASMJIT_INLINE bool isGpb(uint32_t typeId) noexcept { return typeId >= kI8 && typeId <= kU8; } + static ASMJIT_INLINE bool isGpw(uint32_t typeId) noexcept { return typeId >= kI16 && typeId <= kU16; } + static ASMJIT_INLINE bool isGpd(uint32_t typeId) noexcept { return typeId >= kI32 && typeId <= kU32; } + static ASMJIT_INLINE bool isGpq(uint32_t typeId) noexcept { return typeId >= kI64 && typeId <= kU64; } + static ASMJIT_INLINE bool isFloat(uint32_t typeId) noexcept { return typeId >= _kFloatStart && typeId <= _kFloatEnd; } + static ASMJIT_INLINE bool isMask(uint32_t typeId) noexcept { return typeId >= _kMaskStart && typeId <= _kMaskEnd; } + static ASMJIT_INLINE bool isMmx(uint32_t typeId) noexcept { return typeId >= _kMmxStart && typeId <= _kMmxEnd; } + + static ASMJIT_INLINE bool isVec(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec512End; } + static ASMJIT_INLINE bool isVec32(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec32End; } + static ASMJIT_INLINE bool isVec64(uint32_t typeId) noexcept { return typeId >= _kVec64Start && typeId <= _kVec64End; } + static ASMJIT_INLINE bool isVec128(uint32_t typeId) noexcept { return typeId >= _kVec128Start && typeId <= _kVec128End; } + static ASMJIT_INLINE bool isVec256(uint32_t typeId) noexcept { return typeId >= _kVec256Start && typeId <= _kVec256End; } + static ASMJIT_INLINE bool isVec512(uint32_t typeId) noexcept { return typeId >= _kVec512Start && typeId <= _kVec512End; } + + static ASMJIT_INLINE uint32_t sizeOf(uint32_t typeId) noexcept { + ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.sizeOf)); + return _info.sizeOf[typeId]; + } + + static ASMJIT_INLINE uint32_t elementOf(uint32_t typeId) noexcept { + ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.elementOf)); + return _info.elementOf[typeId]; + } + + //! Get an offset to convert a `kIntPtr` and `kUIntPtr` TypeId into a + //! type that matches `gpSize` (general-purpose register size). If you + //! find such TypeId it's then only about adding the offset to it. + //! + //! For example: + //! ~~~ + //! uint32_t gpSize = '4' or '8'; + //! uint32_t deabstractDelta = TypeId::deabstractDeltaOfSize(gpSize); + //! + //! uint32_t typeId = 'some type-id'; + //! + //! // Normalize some typeId into a non-abstract typeId. + //! if (TypeId::isAbstract(typeId)) typeId += deabstractDelta; + //! + //! // The same, but by using TypeId::deabstract() function. + //! typeId = TypeId::deabstract(typeId, deabstractDelta); + //! ~~~ + static ASMJIT_INLINE uint32_t deabstractDeltaOfSize(uint32_t gpSize) noexcept { + return gpSize >= 8 ? kI64 - kIntPtr : kI32 - kIntPtr; + } + + static ASMJIT_INLINE uint32_t deabstract(uint32_t typeId, uint32_t deabstractDelta) noexcept { + return TypeId::isAbstract(typeId) ? typeId += deabstractDelta : typeId; + } +}; + +//! TypeIdOf<> template allows to get a TypeId of a C++ type. +template struct TypeIdOf { + // Don't provide anything if not specialized. +}; +template struct TypeIdOf { + enum { kTypeId = TypeId::kUIntPtr }; +}; + +template +struct TypeIdOfInt { + enum { + kSignatureed = int(~static_cast(0) < static_cast(0)), + kTypeId = (sizeof(T) == 1) ? (int)(kSignatureed ? TypeId::kI8 : TypeId::kU8 ) : + (sizeof(T) == 2) ? (int)(kSignatureed ? TypeId::kI16 : TypeId::kU16) : + (sizeof(T) == 4) ? (int)(kSignatureed ? TypeId::kI32 : TypeId::kU32) : + (sizeof(T) == 8) ? (int)(kSignatureed ? TypeId::kI64 : TypeId::kU64) : (int)TypeId::kVoid + }; +}; + +#define ASMJIT_DEFINE_TYPE_ID(T, TYPE_ID) \ + template<> \ + struct TypeIdOf { enum { kTypeId = TYPE_ID}; } + +ASMJIT_DEFINE_TYPE_ID(signed char , TypeIdOfInt< signed char >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned char , TypeIdOfInt< unsigned char >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(short , TypeIdOfInt< short >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned short , TypeIdOfInt< unsigned short >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(int , TypeIdOfInt< int >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned int , TypeIdOfInt< unsigned int >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(long , TypeIdOfInt< long >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned long , TypeIdOfInt< unsigned long >::kTypeId); +#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(16, 0, 0) +ASMJIT_DEFINE_TYPE_ID(__int64 , TypeIdOfInt< __int64 >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned __int64 , TypeIdOfInt< unsigned __int64 >::kTypeId); +#else +ASMJIT_DEFINE_TYPE_ID(long long , TypeIdOfInt< long long >::kTypeId); +ASMJIT_DEFINE_TYPE_ID(unsigned long long, TypeIdOfInt< unsigned long long >::kTypeId); +#endif +#if ASMJIT_CC_HAS_NATIVE_CHAR +ASMJIT_DEFINE_TYPE_ID(char , TypeIdOfInt< char >::kTypeId); +#endif +#if ASMJIT_CC_HAS_NATIVE_CHAR16_T +ASMJIT_DEFINE_TYPE_ID(char16_t , TypeIdOfInt< char16_t >::kTypeId); +#endif +#if ASMJIT_CC_HAS_NATIVE_CHAR32_T +ASMJIT_DEFINE_TYPE_ID(char32_t , TypeIdOfInt< char32_t >::kTypeId); +#endif +#if ASMJIT_CC_HAS_NATIVE_WCHAR_T +ASMJIT_DEFINE_TYPE_ID(wchar_t , TypeIdOfInt< wchar_t >::kTypeId); +#endif + +ASMJIT_DEFINE_TYPE_ID(void , TypeId::kVoid); +ASMJIT_DEFINE_TYPE_ID(float , TypeId::kF32); +ASMJIT_DEFINE_TYPE_ID(double , TypeId::kF64); + +ASMJIT_DEFINE_TYPE_ID(TypeId::Int8 , TypeId::kI8); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt8 , TypeId::kU8); +ASMJIT_DEFINE_TYPE_ID(TypeId::Int16 , TypeId::kI16); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt16 , TypeId::kU16); +ASMJIT_DEFINE_TYPE_ID(TypeId::Int32 , TypeId::kI32); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt32 , TypeId::kU32); +ASMJIT_DEFINE_TYPE_ID(TypeId::Int64 , TypeId::kI64); +ASMJIT_DEFINE_TYPE_ID(TypeId::UInt64 , TypeId::kU64); +ASMJIT_DEFINE_TYPE_ID(TypeId::IntPtr , TypeId::kIntPtr); +ASMJIT_DEFINE_TYPE_ID(TypeId::UIntPtr , TypeId::kUIntPtr); +ASMJIT_DEFINE_TYPE_ID(TypeId::Float , TypeId::kF32); +ASMJIT_DEFINE_TYPE_ID(TypeId::Double , TypeId::kF64); +ASMJIT_DEFINE_TYPE_ID(TypeId::MmxReg , TypeId::kMmx64); +ASMJIT_DEFINE_TYPE_ID(TypeId::Vec128 , TypeId::kI32x4); +ASMJIT_DEFINE_TYPE_ID(TypeId::Vec256 , TypeId::kI32x8); +ASMJIT_DEFINE_TYPE_ID(TypeId::Vec512 , TypeId::kI32x16); //! \} } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" // [Guard] #endif // _ASMJIT_BASE_OPERAND_H diff --git a/src/asmjit/base/osutils.cpp b/src/asmjit/base/osutils.cpp new file mode 100644 index 0000000..08ddd7d --- /dev/null +++ b/src/asmjit/base/osutils.cpp @@ -0,0 +1,228 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Dependencies] +#include "../base/osutils.h" +#include "../base/utils.h" + +#if ASMJIT_OS_POSIX +# include +# include +# include +# include +#endif // ASMJIT_OS_POSIX + +#if ASMJIT_OS_MAC +# include +#endif // ASMJIT_OS_MAC + +#if ASMJIT_OS_WINDOWS +# if defined(_MSC_VER) && _MSC_VER >= 1400 +# include +# else +# define _InterlockedCompareExchange InterlockedCompareExchange +# endif // _MSC_VER +#endif // ASMJIT_OS_WINDOWS + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::OSUtils - Virtual Memory] +// ============================================================================ + +// Windows specific implementation using `VirtualAllocEx` and `VirtualFree`. +#if ASMJIT_OS_WINDOWS +static ASMJIT_NOINLINE const VMemInfo& OSUtils_GetVMemInfo() noexcept { + static VMemInfo vmi; + + if (ASMJIT_UNLIKELY(!vmi.hCurrentProcess)) { + SYSTEM_INFO info; + ::GetSystemInfo(&info); + + vmi.pageSize = Utils::alignToPowerOf2(info.dwPageSize); + vmi.pageGranularity = info.dwAllocationGranularity; + vmi.hCurrentProcess = ::GetCurrentProcess(); + } + + return vmi; +}; + +VMemInfo OSUtils::getVirtualMemoryInfo() noexcept { return OSUtils_GetVMemInfo(); } + +void* OSUtils::allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept { + return allocProcessMemory(static_cast(0), size, allocated, flags); +} + +Error OSUtils::releaseVirtualMemory(void* p, size_t size) noexcept { + return releaseProcessMemory(static_cast(0), p, size); +} + +void* OSUtils::allocProcessMemory(HANDLE hProcess, size_t size, size_t* allocated, uint32_t flags) noexcept { + if (size == 0) + return nullptr; + + const VMemInfo& vmi = OSUtils_GetVMemInfo(); + if (!hProcess) hProcess = vmi.hCurrentProcess; + + // VirtualAllocEx rounds the allocated size to a page size automatically, + // but we need the `alignedSize` so we can store the real allocated size + // into `allocated` output. + size_t alignedSize = Utils::alignTo(size, vmi.pageSize); + + // Windows XP SP2 / Vista+ allow data-execution-prevention (DEP). + DWORD protectFlags = 0; + + if (flags & kVMExecutable) + protectFlags |= (flags & kVMWritable) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; + else + protectFlags |= (flags & kVMWritable) ? PAGE_READWRITE : PAGE_READONLY; + + LPVOID mBase = ::VirtualAllocEx(hProcess, nullptr, alignedSize, MEM_COMMIT | MEM_RESERVE, protectFlags); + if (ASMJIT_UNLIKELY(!mBase)) return nullptr; + + ASMJIT_ASSERT(Utils::isAligned(reinterpret_cast(mBase), vmi.pageSize)); + if (allocated) *allocated = alignedSize; + return mBase; +} + +Error OSUtils::releaseProcessMemory(HANDLE hProcess, void* p, size_t size) noexcept { + const VMemInfo& vmi = OSUtils_GetVMemInfo(); + if (!hProcess) hProcess = vmi.hCurrentProcess; + + if (ASMJIT_UNLIKELY(!::VirtualFreeEx(hProcess, p, 0, MEM_RELEASE))) + return DebugUtils::errored(kErrorInvalidState); + + return kErrorOk; +} +#endif // ASMJIT_OS_WINDOWS + +// Posix specific implementation using `mmap()` and `munmap()`. +#if ASMJIT_OS_POSIX + +// Mac uses MAP_ANON instead of MAP_ANONYMOUS. +#if !defined(MAP_ANONYMOUS) +# define MAP_ANONYMOUS MAP_ANON +#endif // MAP_ANONYMOUS + +static const VMemInfo& OSUtils_GetVMemInfo() noexcept { + static VMemInfo vmi; + if (ASMJIT_UNLIKELY(!vmi.pageSize)) { + size_t pageSize = ::getpagesize(); + vmi.pageSize = pageSize; + vmi.pageGranularity = std::max(pageSize, 65536); + } + return vmi; +}; + +VMemInfo OSUtils::getVirtualMemoryInfo() noexcept { return OSUtils_GetVMemInfo(); } + +void* OSUtils::allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept { + const VMemInfo& vmi = OSUtils_GetVMemInfo(); + + size_t alignedSize = Utils::alignTo(size, vmi.pageSize); + int protection = PROT_READ; + + if (flags & kVMWritable ) protection |= PROT_WRITE; + if (flags & kVMExecutable) protection |= PROT_EXEC; + + void* mbase = ::mmap(nullptr, alignedSize, protection, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ASMJIT_UNLIKELY(mbase == MAP_FAILED)) return nullptr; + + if (allocated) *allocated = alignedSize; + return mbase; +} + +Error OSUtils::releaseVirtualMemory(void* p, size_t size) noexcept { + if (ASMJIT_UNLIKELY(::munmap(p, size) != 0)) + return DebugUtils::errored(kErrorInvalidState); + + return kErrorOk; +} +#endif // ASMJIT_OS_POSIX + +// ============================================================================ +// [asmjit::OSUtils - GetTickCount] +// ============================================================================ + +#if ASMJIT_OS_WINDOWS +static ASMJIT_INLINE uint32_t OSUtils_calcHiRes(const LARGE_INTEGER& now, double freq) noexcept { + return static_cast( + (int64_t)(double(now.QuadPart) / freq) & 0xFFFFFFFF); +} + +uint32_t OSUtils::getTickCount() noexcept { + static volatile uint32_t _hiResTicks; + static volatile double _hiResFreq; + + do { + uint32_t hiResOk = _hiResTicks; + LARGE_INTEGER qpf, now; + + // If for whatever reason this fails, bail to `GetTickCount()`. + if (!::QueryPerformanceCounter(&now)) break; + + // Expected - if we ran through this at least once `hiResTicks` will be + // either 1 or 0xFFFFFFFF. If it's '1' then the Hi-Res counter is available + // and `QueryPerformanceCounter()` can be used. + if (hiResOk == 1) return OSUtils_calcHiRes(now, _hiResFreq); + + // Hi-Res counter is not available, bail to `GetTickCount()`. + if (hiResOk != 0) break; + + // Detect availability of Hi-Res counter, if not available, bail to `GetTickCount()`. + if (!::QueryPerformanceFrequency(&qpf)) { + _InterlockedCompareExchange((LONG*)&_hiResTicks, 0xFFFFFFFF, 0); + break; + } + + double freq = double(qpf.QuadPart) / 1000.0; + _hiResFreq = freq; + + _InterlockedCompareExchange((LONG*)&_hiResTicks, 1, 0); + return OSUtils_calcHiRes(now, freq); + } while (0); + + return ::GetTickCount(); +} +#elif ASMJIT_OS_MAC +uint32_t OSUtils::getTickCount() noexcept { + static mach_timebase_info_data_t _machTime; + + // See Apple's QA1398. + if (ASMJIT_UNLIKELY(_machTime.denom == 0) || mach_timebase_info(&_machTime) != KERN_SUCCESS) + return 0; + + // `mach_absolute_time()` returns nanoseconds, we want milliseconds. + uint64_t t = mach_absolute_time() / 1000000; + + t = t * _machTime.numer / _machTime.denom; + return static_cast(t & 0xFFFFFFFFU); +} +#elif defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 +uint32_t OSUtils::getTickCount() noexcept { + struct timespec ts; + + if (ASMJIT_UNLIKELY(clock_gettime(CLOCK_MONOTONIC, &ts) != 0)) + return 0; + + uint64_t t = (uint64_t(ts.tv_sec ) * 1000) + (uint64_t(ts.tv_nsec) / 1000000); + return static_cast(t & 0xFFFFFFFFU); +} +#else +#error "[asmjit] OSUtils::getTickCount() is not implemented for your target OS." +uint32_t OSUtils::getTickCount() noexcept { return 0; } +#endif + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/osutils.h b/src/asmjit/base/osutils.h new file mode 100644 index 0000000..ccf6bee --- /dev/null +++ b/src/asmjit/base/osutils.h @@ -0,0 +1,178 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_OSUTILS_H +#define _ASMJIT_BASE_OSUTILS_H + +// [Dependencies] +#include "../base/globals.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::VMemInfo] +// ============================================================================ + +//! Information about OS virtual memory. +struct VMemInfo { +#if ASMJIT_OS_WINDOWS + HANDLE hCurrentProcess; //!< Handle of the current process (Windows). +#endif // ASMJIT_OS_WINDOWS + size_t pageSize; //!< Virtual memory page size. + size_t pageGranularity; //!< Virtual memory page granularity. +}; + +// ============================================================================ +// [asmjit::OSUtils] +// ============================================================================ + +//! OS utilities. +//! +//! Virtual Memory +//! -------------- +//! +//! Provides functions to allocate and release virtual memory that is required +//! to execute dynamically generated code. If both processor and host OS support +//! data-execution-prevention (DEP) then the only way to run machine code is to +//! allocate virtual memory that has `OSUtils::kVMExecutable` flag enabled. All +//! functions provides by OSUtils use internally platform specific API. +//! +//! Benchmarking +//! ------------ +//! +//! OSUtils also provide a function `getTickCount()` that can be used for +//! benchmarking purposes. It's similar to Windows-only `GetTickCount()`, but +//! it's cross-platform and tries to be the most reliable platform specific +//! calls to make the result usable. +struct OSUtils { + // -------------------------------------------------------------------------- + // [Virtual Memory] + // -------------------------------------------------------------------------- + + //! Virtual memory flags. + ASMJIT_ENUM(VMFlags) { + kVMWritable = 0x00000001U, //!< Virtual memory is writable. + kVMExecutable = 0x00000002U //!< Virtual memory is executable. + }; + + ASMJIT_API static VMemInfo getVirtualMemoryInfo() noexcept; + + //! Allocate virtual memory. + ASMJIT_API static void* allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept; + //! Release virtual memory previously allocated by \ref allocVirtualMemory(). + ASMJIT_API static Error releaseVirtualMemory(void* p, size_t size) noexcept; + +#if ASMJIT_OS_WINDOWS + //! Allocate virtual memory of `hProcess` (Windows). + ASMJIT_API static void* allocProcessMemory(HANDLE hProcess, size_t size, size_t* allocated, uint32_t flags) noexcept; + + //! Release virtual memory of `hProcess` (Windows). + ASMJIT_API static Error releaseProcessMemory(HANDLE hProcess, void* p, size_t size) noexcept; +#endif // ASMJIT_OS_WINDOWS + + // -------------------------------------------------------------------------- + // [GetTickCount] + // -------------------------------------------------------------------------- + + //! Get the current CPU tick count, used for benchmarking (1ms resolution). + ASMJIT_API static uint32_t getTickCount() noexcept; +}; + +// ============================================================================ +// [asmjit::Lock] +// ============================================================================ + +//! \internal +//! +//! Lock. +struct Lock { + ASMJIT_NONCOPYABLE(Lock) + + // -------------------------------------------------------------------------- + // [Windows] + // -------------------------------------------------------------------------- + +#if ASMJIT_OS_WINDOWS + typedef CRITICAL_SECTION Handle; + + //! Create a new `Lock` instance. + ASMJIT_INLINE Lock() noexcept { InitializeCriticalSection(&_handle); } + //! Destroy the `Lock` instance. + ASMJIT_INLINE ~Lock() noexcept { DeleteCriticalSection(&_handle); } + + //! Lock. + ASMJIT_INLINE void lock() noexcept { EnterCriticalSection(&_handle); } + //! Unlock. + ASMJIT_INLINE void unlock() noexcept { LeaveCriticalSection(&_handle); } +#endif // ASMJIT_OS_WINDOWS + + // -------------------------------------------------------------------------- + // [Posix] + // -------------------------------------------------------------------------- + +#if ASMJIT_OS_POSIX + typedef pthread_mutex_t Handle; + + //! Create a new `Lock` instance. + ASMJIT_INLINE Lock() noexcept { pthread_mutex_init(&_handle, nullptr); } + //! Destroy the `Lock` instance. + ASMJIT_INLINE ~Lock() noexcept { pthread_mutex_destroy(&_handle); } + + //! Lock. + ASMJIT_INLINE void lock() noexcept { pthread_mutex_lock(&_handle); } + //! Unlock. + ASMJIT_INLINE void unlock() noexcept { pthread_mutex_unlock(&_handle); } +#endif // ASMJIT_OS_POSIX + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Native handle. + Handle _handle; +}; + +// ============================================================================ +// [asmjit::AutoLock] +// ============================================================================ + +//! \internal +//! +//! Scoped lock. +struct AutoLock { + ASMJIT_NONCOPYABLE(AutoLock) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE AutoLock(Lock& target) noexcept : _target(target) { _target.lock(); } + ASMJIT_INLINE ~AutoLock() noexcept { _target.unlock(); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Reference to the `Lock`. + Lock& _target; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_OSUTILS_H diff --git a/src/asmjit/base/podvector.cpp b/src/asmjit/base/podvector.cpp deleted file mode 100644 index 8e02035..0000000 --- a/src/asmjit/base/podvector.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Export] -#define ASMJIT_EXPORTS - -// [Dependencies] -#include "../base/podvector.h" -#include "../base/utils.h" - -// [Api-Begin] -#include "../apibegin.h" - -namespace asmjit { - -// ============================================================================ -// [asmjit::PodVectorBase - NullData] -// ============================================================================ - -const PodVectorBase::Data PodVectorBase::_nullData = { 0, 0 }; - -static ASMJIT_INLINE bool isDataStatic(PodVectorBase* self, PodVectorBase::Data* d) noexcept { - return (void*)(self + 1) == (void*)d; -} - -// ============================================================================ -// [asmjit::PodVectorBase - Reset] -// ============================================================================ - -//! Clear vector data and free internal buffer. -void PodVectorBase::reset(bool releaseMemory) noexcept { - Data* d = _d; - if (d == &_nullData) - return; - - if (releaseMemory && !isDataStatic(this, d)) { - ASMJIT_FREE(d); - _d = const_cast(&_nullData); - return; - } - - d->length = 0; -} - -// ============================================================================ -// [asmjit::PodVectorBase - Helpers] -// ============================================================================ - -Error PodVectorBase::_grow(size_t n, size_t sizeOfT) noexcept { - Data* d = _d; - - size_t threshold = kMemAllocGrowMax / sizeOfT; - size_t capacity = d->capacity; - size_t after = d->length; - - if (IntTraits::maxValue() - n < after) - return kErrorNoHeapMemory; - - after += n; - - if (capacity >= after) - return kErrorOk; - - // PodVector is used as a linear array for some data structures used by - // AsmJit code generation. The purpose of this agressive growing schema - // is to minimize memory reallocations, because AsmJit code generation - // classes live short life and will be freed or reused soon. - if (capacity < 32) - capacity = 32; - else if (capacity < 128) - capacity = 128; - else if (capacity < 512) - capacity = 512; - - while (capacity < after) { - if (capacity < threshold) - capacity *= 2; - else - capacity += threshold; - } - - return _reserve(capacity, sizeOfT); -} - -Error PodVectorBase::_reserve(size_t n, size_t sizeOfT) noexcept { - Data* d = _d; - - if (d->capacity >= n) - return kErrorOk; - - size_t nBytes = sizeof(Data) + n * sizeOfT; - if (ASMJIT_UNLIKELY(nBytes < n)) - return kErrorNoHeapMemory; - - if (d == &_nullData) { - d = static_cast(ASMJIT_ALLOC(nBytes)); - if (ASMJIT_UNLIKELY(d == nullptr)) - return kErrorNoHeapMemory; - d->length = 0; - } - else { - if (isDataStatic(this, d)) { - Data* oldD = d; - - d = static_cast(ASMJIT_ALLOC(nBytes)); - if (ASMJIT_UNLIKELY(d == nullptr)) - return kErrorNoHeapMemory; - - size_t len = oldD->length; - d->length = len; - ::memcpy(d->getData(), oldD->getData(), len * sizeOfT); - } - else { - d = static_cast(ASMJIT_REALLOC(d, nBytes)); - if (ASMJIT_UNLIKELY(d == nullptr)) - return kErrorNoHeapMemory; - } - } - - d->capacity = n; - _d = d; - - return kErrorOk; -} - -} // asmjit namespace - -// [Api-End] -#include "../apiend.h" diff --git a/src/asmjit/base/podvector.h b/src/asmjit/base/podvector.h deleted file mode 100644 index ff7efe5..0000000 --- a/src/asmjit/base/podvector.h +++ /dev/null @@ -1,281 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Guard] -#ifndef _ASMJIT_BASE_PODVECTOR_H -#define _ASMJIT_BASE_PODVECTOR_H - -// [Dependencies] -#include "../base/globals.h" - -// [Api-Begin] -#include "../apibegin.h" - -namespace asmjit { - -//! \addtogroup asmjit_base -//! \{ - -// ============================================================================ -// [asmjit::PodVectorBase] -// ============================================================================ - -//! \internal -class PodVectorBase { - public: - // -------------------------------------------------------------------------- - // [Data] - // -------------------------------------------------------------------------- - - //! \internal - struct Data { - //! Get data. - ASMJIT_INLINE void* getData() const noexcept { - return static_cast(const_cast(this + 1)); - } - - //! Capacity of the vector. - size_t capacity; - //! Length of the vector. - size_t length; - }; - - static ASMJIT_API const Data _nullData; - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new instance of `PodVectorBase`. - ASMJIT_INLINE PodVectorBase() noexcept : _d(const_cast(&_nullData)) {} - //! Destroy the `PodVectorBase` and its data. - ASMJIT_INLINE ~PodVectorBase() noexcept { reset(true); } - -protected: - explicit ASMJIT_INLINE PodVectorBase(Data* d) noexcept : _d(d) {} - - // -------------------------------------------------------------------------- - // [Reset] - // -------------------------------------------------------------------------- - -public: - //! Reset the vector data and set its `length` to zero. - //! - //! If `releaseMemory` is true the vector buffer will be released to the - //! system. - ASMJIT_API void reset(bool releaseMemory = false) noexcept; - - // -------------------------------------------------------------------------- - // [Grow / Reserve] - // -------------------------------------------------------------------------- - -protected: - ASMJIT_API Error _grow(size_t n, size_t sizeOfT) noexcept; - ASMJIT_API Error _reserve(size_t n, size_t sizeOfT) noexcept; - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - -public: - Data* _d; -}; - -// ============================================================================ -// [asmjit::PodVector] -// ============================================================================ - -//! Template used to store and manage array of POD data. -//! -//! This template has these adventages over other vector<> templates: -//! - Non-copyable (designed to be non-copyable, we want it) -//! - No copy-on-write (some implementations of stl can use it) -//! - Optimized for working only with POD types -//! - Uses ASMJIT_... memory management macros -template -class PodVector : public PodVectorBase { - public: - ASMJIT_NO_COPY(PodVector) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new instance of `PodVector`. - ASMJIT_INLINE PodVector() noexcept {} - //! Destroy the `PodVector` and its data. - ASMJIT_INLINE ~PodVector() noexcept {} - -protected: - explicit ASMJIT_INLINE PodVector(Data* d) noexcept : PodVectorBase(d) {} - - // -------------------------------------------------------------------------- - // [Data] - // -------------------------------------------------------------------------- - -public: - //! Get whether the vector is empty. - ASMJIT_INLINE bool isEmpty() const noexcept { return _d->length == 0; } - //! Get length. - ASMJIT_INLINE size_t getLength() const noexcept { return _d->length; } - //! Get capacity. - ASMJIT_INLINE size_t getCapacity() const noexcept { return _d->capacity; } - //! Get data. - ASMJIT_INLINE T* getData() noexcept { return static_cast(_d->getData()); } - //! \overload - ASMJIT_INLINE const T* getData() const noexcept { return static_cast(_d->getData()); } - - // -------------------------------------------------------------------------- - // [Grow / Reserve] - // -------------------------------------------------------------------------- - - //! Called to grow the buffer to fit at least `n` elements more. - ASMJIT_INLINE Error _grow(size_t n) noexcept { return PodVectorBase::_grow(n, sizeof(T)); } - //! Realloc internal array to fit at least `n` items. - ASMJIT_INLINE Error _reserve(size_t n) noexcept { return PodVectorBase::_reserve(n, sizeof(T)); } - - // -------------------------------------------------------------------------- - // [Ops] - // -------------------------------------------------------------------------- - - //! Prepend `item` to vector. - Error prepend(const T& item) noexcept { - Data* d = _d; - - if (d->length == d->capacity) { - ASMJIT_PROPAGATE_ERROR(_grow(1)); - _d = d; - } - - ::memmove(static_cast(d->getData()) + 1, d->getData(), d->length * sizeof(T)); - ::memcpy(d->getData(), &item, sizeof(T)); - - d->length++; - return kErrorOk; - } - - //! Insert an `item` at the `index`. - Error insert(size_t index, const T& item) noexcept { - Data* d = _d; - ASMJIT_ASSERT(index <= d->length); - - if (d->length == d->capacity) { - ASMJIT_PROPAGATE_ERROR(_grow(1)); - d = _d; - } - - T* dst = static_cast(d->getData()) + index; - ::memmove(dst + 1, dst, d->length - index); - ::memcpy(dst, &item, sizeof(T)); - - d->length++; - return kErrorOk; - } - - //! Append `item` to vector. - Error append(const T& item) noexcept { - Data* d = _d; - - if (d->length == d->capacity) { - ASMJIT_PROPAGATE_ERROR(_grow(1)); - d = _d; - } - - ::memcpy(static_cast(d->getData()) + d->length, &item, sizeof(T)); - - d->length++; - return kErrorOk; - } - - //! Get index of `val` or `kInvalidIndex` if not found. - size_t indexOf(const T& val) const noexcept { - Data* d = _d; - - const T* data = static_cast(d->getData()); - size_t len = d->length; - - for (size_t i = 0; i < len; i++) - if (data[i] == val) - return i; - - return kInvalidIndex; - } - - //! Remove item at index `i`. - void removeAt(size_t i) noexcept { - Data* d = _d; - ASMJIT_ASSERT(i < d->length); - - T* data = static_cast(d->getData()) + i; - d->length--; - ::memmove(data, data + 1, d->length - i); - } - - //! Swap this pod-vector with `other`. - void swap(PodVector& other) noexcept { - T* otherData = other._d; - other._d = _d; - _d = otherData; - } - - //! Get item at index `i`. - ASMJIT_INLINE T& operator[](size_t i) noexcept { - ASMJIT_ASSERT(i < getLength()); - return getData()[i]; - } - - //! Get item at index `i`. - ASMJIT_INLINE const T& operator[](size_t i) const noexcept { - ASMJIT_ASSERT(i < getLength()); - return getData()[i]; - } -}; - -// ============================================================================ -// [asmjit::PodVectorTmp] -// ============================================================================ - -template -class PodVectorTmp : public PodVector { - public: - ASMJIT_NO_COPY(PodVectorTmp) - - // -------------------------------------------------------------------------- - // [StaticData] - // -------------------------------------------------------------------------- - - struct StaticData : public PodVectorBase::Data { - char data[sizeof(T) * N]; - }; - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a new instance of `PodVectorTmp`. - ASMJIT_INLINE PodVectorTmp() noexcept : PodVector(&_staticData) { - _staticData.capacity = N; - _staticData.length = 0; - } - //! Destroy the `PodVectorTmp` and its data. - ASMJIT_INLINE ~PodVectorTmp() noexcept {} - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - StaticData _staticData; -}; - -//! \} - -} // asmjit namespace - -// [Api-End] -#include "../apiend.h" - -// [Guard] -#endif // _ASMJIT_BASE_PODVECTOR_H diff --git a/src/asmjit/base/regalloc.cpp b/src/asmjit/base/regalloc.cpp new file mode 100644 index 0000000..92699a4 --- /dev/null +++ b/src/asmjit/base/regalloc.cpp @@ -0,0 +1,599 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Export] +#define ASMJIT_EXPORTS + +// [Guard] +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/regalloc_p.h" +#include "../base/utils.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +// ============================================================================ +// [asmjit::RAPass - Construction / Destruction] +// ============================================================================ + +RAPass::RAPass() noexcept : + CBPass("RA"), + _varMapToVaListOffset(0) {} +RAPass::~RAPass() noexcept {} + +// ============================================================================ +// [asmjit::RAPass - Interface] +// ============================================================================ + +Error RAPass::process(Zone* zone) noexcept { + _zone = zone; + _heap.reset(zone); + _emitComments = (cb()->getGlobalOptions() & CodeEmitter::kOptionLoggingEnabled) != 0; + + Error err = kErrorOk; + CBNode* node = cc()->getFirstNode(); + if (!node) return err; + + do { + if (node->getType() == CBNode::kNodeFunc) { + CCFunc* func = static_cast(node); + node = func->getEnd(); + + err = compile(func); + if (err) break; + } + + // Find a function by skipping all nodes that are not `kNodeFunc`. + do { + node = node->getNext(); + } while (node && node->getType() != CBNode::kNodeFunc); + } while (node); + + _heap.reset(nullptr); + _zone = nullptr; + return err; +} + +Error RAPass::compile(CCFunc* func) noexcept { + ASMJIT_PROPAGATE(prepare(func)); + + Error err; + do { + err = fetch(); + if (err) break; + + err = removeUnreachableCode(); + if (err) break; + + err = livenessAnalysis(); + if (err) break; + +#if !defined(ASMJIT_DISABLE_LOGGING) + if (cc()->getGlobalOptions() & CodeEmitter::kOptionLoggingEnabled) { + err = annotate(); + if (err) break; + } +#endif // !ASMJIT_DISABLE_LOGGING + + err = translate(); + } while (false); + + cleanup(); + + // We alter the compiler cursor, because it doesn't make sense to reference + // it after compilation - some nodes may disappear and it's forbidden to add + // new code after the compilation is done. + cc()->_setCursor(nullptr); + return err; +} + +Error RAPass::prepare(CCFunc* func) noexcept { + CBNode* end = func->getEnd(); + + _func = func; + _stop = end->getNext(); + _extraBlock = end; + + _unreachableList.reset(); + _returningList.reset(); + _jccList.reset(); + _contextVd.reset(); + + _memVarCells = nullptr; + _memStackCells = nullptr; + + _mem1ByteVarsUsed = 0; + _mem2ByteVarsUsed = 0; + _mem4ByteVarsUsed = 0; + _mem8ByteVarsUsed = 0; + _mem16ByteVarsUsed = 0; + _mem32ByteVarsUsed = 0; + _mem64ByteVarsUsed = 0; + _memStackCellsUsed = 0; + + _memMaxAlign = 0; + _memVarTotal = 0; + _memStackTotal = 0; + _memAllTotal = 0; + _annotationLength = 12; + + return kErrorOk; +} + +void RAPass::cleanup() noexcept { + VirtReg** virtArray = _contextVd.getData(); + size_t virtCount = _contextVd.getLength(); + + for (size_t i = 0; i < virtCount; i++) { + VirtReg* vreg = virtArray[i]; + vreg->_raId = kInvalidValue; + vreg->resetPhysId(); + } + + _contextVd.reset(); + _extraBlock = nullptr; +} + +// ============================================================================ +// [asmjit::RAPass - Mem] +// ============================================================================ + +static ASMJIT_INLINE uint32_t RAGetDefaultAlignment(uint32_t size) { + if (size > 32) + return 64; + else if (size > 16) + return 32; + else if (size > 8) + return 16; + else if (size > 4) + return 8; + else if (size > 2) + return 4; + else if (size > 1) + return 2; + else + return 1; +} + +RACell* RAPass::_newVarCell(VirtReg* vreg) { + ASMJIT_ASSERT(vreg->_memCell == nullptr); + + RACell* cell; + uint32_t size = vreg->getSize(); + + if (vreg->isStack()) { + cell = _newStackCell(size, vreg->getAlignment()); + if (ASMJIT_UNLIKELY(!cell)) return nullptr; + } + else { + cell = static_cast(_zone->alloc(sizeof(RACell))); + if (!cell) goto _NoMemory; + + cell->next = _memVarCells; + cell->offset = 0; + cell->size = size; + cell->alignment = size; + + _memVarCells = cell; + _memMaxAlign = std::max(_memMaxAlign, size); + _memVarTotal += size; + + switch (size) { + case 1: _mem1ByteVarsUsed++ ; break; + case 2: _mem2ByteVarsUsed++ ; break; + case 4: _mem4ByteVarsUsed++ ; break; + case 8: _mem8ByteVarsUsed++ ; break; + case 16: _mem16ByteVarsUsed++; break; + case 32: _mem32ByteVarsUsed++; break; + case 64: _mem64ByteVarsUsed++; break; + + default: + ASMJIT_NOT_REACHED(); + } + } + + vreg->_memCell = cell; + return cell; + +_NoMemory: + cc()->setLastError(DebugUtils::errored(kErrorNoHeapMemory)); + return nullptr; +} + +RACell* RAPass::_newStackCell(uint32_t size, uint32_t alignment) { + RACell* cell = static_cast(_zone->alloc(sizeof(RACell))); + if (ASMJIT_UNLIKELY(!cell)) return nullptr; + + if (alignment == 0) + alignment = RAGetDefaultAlignment(size); + + if (alignment > 64) + alignment = 64; + + ASMJIT_ASSERT(Utils::isPowerOf2(alignment)); + size = Utils::alignTo(size, alignment); + + // Insert it sorted according to the alignment and size. + { + RACell** pPrev = &_memStackCells; + RACell* cur = *pPrev; + + while (cur && ((cur->alignment > alignment) || (cur->alignment == alignment && cur->size > size))) { + pPrev = &cur->next; + cur = *pPrev; + } + + cell->next = cur; + cell->offset = 0; + cell->size = size; + cell->alignment = alignment; + + *pPrev = cell; + _memStackCellsUsed++; + + _memMaxAlign = std::max(_memMaxAlign, alignment); + _memStackTotal += size; + } + + return cell; +} + +Error RAPass::resolveCellOffsets() { + RACell* varCell = _memVarCells; + RACell* stackCell = _memStackCells; + + uint32_t stackAlignment = 0; + if (stackCell) stackAlignment = stackCell->alignment; + + uint32_t pos64 = 0; + uint32_t pos32 = pos64 + _mem64ByteVarsUsed * 64; + uint32_t pos16 = pos32 + _mem32ByteVarsUsed * 32; + uint32_t pos8 = pos16 + _mem16ByteVarsUsed * 16; + uint32_t pos4 = pos8 + _mem8ByteVarsUsed * 8 ; + uint32_t pos2 = pos4 + _mem4ByteVarsUsed * 4 ; + uint32_t pos1 = pos2 + _mem2ByteVarsUsed * 2 ; + + // Assign home slots. + while (varCell) { + uint32_t size = varCell->size; + uint32_t offset = 0; + + switch (size) { + case 1: offset = pos1 ; pos1 += 1 ; break; + case 2: offset = pos2 ; pos2 += 2 ; break; + case 4: offset = pos4 ; pos4 += 4 ; break; + case 8: offset = pos8 ; pos8 += 8 ; break; + case 16: offset = pos16; pos16 += 16; break; + case 32: offset = pos32; pos32 += 32; break; + case 64: offset = pos64; pos64 += 64; break; + + default: + ASMJIT_NOT_REACHED(); + } + + varCell->offset = static_cast(offset); + varCell = varCell->next; + } + + // Assign stack slots. + uint32_t stackPos = pos1 + _mem1ByteVarsUsed; + while (stackCell) { + uint32_t size = stackCell->size; + uint32_t alignment = stackCell->alignment; + ASMJIT_ASSERT(alignment != 0 && Utils::isPowerOf2(alignment)); + + stackPos = Utils::alignTo(stackPos, alignment); + stackCell->offset = stackPos; + stackCell = stackCell->next; + + stackPos += size; + } + + _memAllTotal = stackPos; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::RAPass - RemoveUnreachableCode] +// ============================================================================ + +Error RAPass::removeUnreachableCode() { + ZoneList::Link* link = _unreachableList.getFirst(); + CBNode* stop = getStop(); + + while (link) { + CBNode* node = link->getValue(); + if (node && node->getPrev() && node != stop) { + // Locate all unreachable nodes. + CBNode* first = node; + do { + if (node->hasPassData()) break; + node = node->getNext(); + } while (node != stop); + + // Remove unreachable nodes that are neither informative nor directives. + if (node != first) { + CBNode* end = node; + node = first; + + // NOTE: The strategy is as follows: + // 1. The algorithm removes everything until it finds a first label. + // 2. After the first label is found it removes only removable nodes. + bool removeEverything = true; + do { + CBNode* next = node->getNext(); + bool remove = node->isRemovable(); + + if (!remove) { + if (node->isLabel()) + removeEverything = false; + remove = removeEverything; + } + + if (remove) + cc()->removeNode(node); + + node = next; + } while (node != end); + } + } + + link = link->getNext(); + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::RAPass - Liveness Analysis] +// ============================================================================ + +//! \internal +struct LivenessTarget { + LivenessTarget* prev; //!< Previous target. + CBLabel* node; //!< Target node. + CBJump* from; //!< Jumped from. +}; + +Error RAPass::livenessAnalysis() { + uint32_t bLen = static_cast( + ((_contextVd.getLength() + RABits::kEntityBits - 1) / RABits::kEntityBits)); + + // No variables. + if (bLen == 0) + return kErrorOk; + + CCFunc* func = getFunc(); + CBJump* from = nullptr; + + LivenessTarget* ltCur = nullptr; + LivenessTarget* ltUnused = nullptr; + + ZoneList::Link* retPtr = _returningList.getFirst(); + ASMJIT_ASSERT(retPtr != nullptr); + + CBNode* node = retPtr->getValue(); + RAData* wd; + + size_t varMapToVaListOffset = _varMapToVaListOffset; + RABits* bCur = newBits(bLen); + if (ASMJIT_UNLIKELY(!bCur)) goto NoMem; + + // Allocate bits for code visited first time. +Visit: + for (;;) { + wd = node->getPassData(); + if (wd->liveness) { + if (bCur->_addBitsDelSource(wd->liveness, bCur, bLen)) + goto Patch; + else + goto Done; + } + + RABits* bTmp = copyBits(bCur, bLen); + if (!bTmp) goto NoMem; + + wd = node->getPassData(); + wd->liveness = bTmp; + + uint32_t tiedTotal = wd->tiedTotal; + TiedReg* tiedArray = reinterpret_cast(((uint8_t*)wd) + varMapToVaListOffset); + + for (uint32_t i = 0; i < tiedTotal; i++) { + TiedReg* tied = &tiedArray[i]; + VirtReg* vreg = tied->vreg; + + uint32_t flags = tied->flags; + uint32_t raId = vreg->_raId; + + if ((flags & TiedReg::kWAll) && !(flags & TiedReg::kRAll)) { + // Write-Only. + bTmp->setBit(raId); + bCur->delBit(raId); + } + else { + // Read-Only or Read/Write. + bTmp->setBit(raId); + bCur->setBit(raId); + } + } + + if (node->getType() == CBNode::kNodeLabel) + goto Target; + + if (node == func) + goto Done; + + ASMJIT_ASSERT(node->getPrev()); + node = node->getPrev(); + } + + // Patch already generated liveness bits. +Patch: + for (;;) { + ASMJIT_ASSERT(node->hasPassData()); + ASMJIT_ASSERT(node->getPassData()->liveness != nullptr); + + RABits* bNode = node->getPassData()->liveness; + if (!bNode->_addBitsDelSource(bCur, bLen)) goto Done; + if (node->getType() == CBNode::kNodeLabel) goto Target; + + if (node == func) goto Done; + node = node->getPrev(); + } + +Target: + if (static_cast(node)->getNumRefs() != 0) { + // Push a new LivenessTarget onto the stack if needed. + if (!ltCur || ltCur->node != node) { + // Allocate a new LivenessTarget object (from pool or zone). + LivenessTarget* ltTmp = ltUnused; + + if (ltTmp) { + ltUnused = ltUnused->prev; + } + else { + ltTmp = _zone->allocT( + sizeof(LivenessTarget) - sizeof(RABits) + bLen * sizeof(uintptr_t)); + if (!ltTmp) goto NoMem; + } + + // Initialize and make current - ltTmp->from will be set later on. + ltTmp->prev = ltCur; + ltTmp->node = static_cast(node); + ltCur = ltTmp; + + from = static_cast(node)->getFrom(); + ASMJIT_ASSERT(from != nullptr); + } + else { + from = ltCur->from; + goto JumpNext; + } + + // Visit/Patch. + do { + ltCur->from = from; + bCur->copyBits(node->getPassData()->liveness, bLen); + + if (!from->getPassData()->liveness) { + node = from; + goto Visit; + } + + // Issue #25: Moved 'JumpNext' here since it's important to patch + // code again if there are more live variables than before. +JumpNext: + if (bCur->delBits(from->getPassData()->liveness, bLen)) { + node = from; + goto Patch; + } + + from = from->getJumpNext(); + } while (from); + + // Pop the current LivenessTarget from the stack. + { + LivenessTarget* ltTmp = ltCur; + ltCur = ltCur->prev; + ltTmp->prev = ltUnused; + ltUnused = ltTmp; + } + } + + bCur->copyBits(node->getPassData()->liveness, bLen); + node = node->getPrev(); + if (node->isJmp() || !node->hasPassData()) goto Done; + + wd = node->getPassData(); + if (!wd->liveness) goto Visit; + if (bCur->delBits(wd->liveness, bLen)) goto Patch; + +Done: + if (ltCur) { + node = ltCur->node; + from = ltCur->from; + + goto JumpNext; + } + + retPtr = retPtr->getNext(); + if (retPtr) { + node = retPtr->getValue(); + goto Visit; + } + + return kErrorOk; + +NoMem: + return DebugUtils::errored(kErrorNoHeapMemory); +} + +// ============================================================================ +// [asmjit::RAPass - Annotate] +// ============================================================================ + +Error RAPass::formatInlineComment(StringBuilder& dst, CBNode* node) { +#if !defined(ASMJIT_DISABLE_LOGGING) + RAData* wd = node->getPassData(); + + if (node->hasInlineComment()) + dst.appendString(node->getInlineComment()); + + if (wd && wd->liveness) { + if (dst.getLength() < _annotationLength) + dst.appendChars(' ', _annotationLength - dst.getLength()); + + uint32_t vdCount = static_cast(_contextVd.getLength()); + size_t offset = dst.getLength() + 1; + + dst.appendChar('['); + dst.appendChars(' ', vdCount); + dst.appendChar(']'); + RABits* liveness = wd->liveness; + + uint32_t i; + for (i = 0; i < vdCount; i++) { + if (liveness->getBit(i)) + dst.getData()[offset + i] = '.'; + } + + uint32_t tiedTotal = wd->tiedTotal; + TiedReg* tiedArray = reinterpret_cast(((uint8_t*)wd) + _varMapToVaListOffset); + + for (i = 0; i < tiedTotal; i++) { + TiedReg* tied = &tiedArray[i]; + VirtReg* vreg = tied->vreg; + uint32_t flags = tied->flags; + + char c = 'u'; + if ( (flags & TiedReg::kRAll) && !(flags & TiedReg::kWAll)) c = 'r'; + if (!(flags & TiedReg::kRAll) && (flags & TiedReg::kWAll)) c = 'w'; + if ( (flags & TiedReg::kRAll) && (flags & TiedReg::kWAll)) c = 'x'; + // Uppercase if unused. + if ( (flags & TiedReg::kUnuse)) c -= 'a' - 'A'; + + ASMJIT_ASSERT(offset + vreg->_raId < dst.getLength()); + dst._data[offset + vreg->_raId] = c; + } + } +#endif // !ASMJIT_DISABLE_LOGGING + + return kErrorOk; +} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER diff --git a/src/asmjit/base/regalloc_p.h b/src/asmjit/base/regalloc_p.h new file mode 100644 index 0000000..d953bf4 --- /dev/null +++ b/src/asmjit/base/regalloc_p.h @@ -0,0 +1,574 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_REGALLOC_P_H +#define _ASMJIT_BASE_REGALLOC_P_H + +#include "../asmjit_build.h" +#if !defined(ASMJIT_DISABLE_COMPILER) + +// [Dependencies] +#include "../base/codecompiler.h" +#include "../base/zone.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::TiedReg] +// ============================================================================ + +//! Tied register (CodeCompiler) +//! +//! Tied register is used to describe one ore more register operands that share +//! the same virtual register. Tied register contains all the data that is +//! essential for register allocation. +struct TiedReg { + //! Flags. + ASMJIT_ENUM(Flags) { + kRReg = 0x00000001U, //!< Register read. + kWReg = 0x00000002U, //!< Register write. + kXReg = 0x00000003U, //!< Register read-write. + + kRMem = 0x00000004U, //!< Memory read. + kWMem = 0x00000008U, //!< Memory write. + kXMem = 0x0000000CU, //!< Memory read-write. + + kRDecide = 0x00000010U, //!< RA can decide between reg/mem read. + kWDecide = 0x00000020U, //!< RA can decide between reg/mem write. + kXDecide = 0x00000030U, //!< RA can decide between reg/mem read-write. + + kRFunc = 0x00000100U, //!< Function argument passed in register. + kWFunc = 0x00000200U, //!< Function return value passed into register. + kXFunc = 0x00000300U, //!< Function argument and return value. + kRCall = 0x00000400U, //!< Function call operand. + + kSpill = 0x00000800U, //!< Variable should be spilled. + kUnuse = 0x00001000U, //!< Variable should be unused at the end of the instruction/node. + + kRAll = kRReg | kRMem | kRDecide | kRFunc | kRCall, //!< All in-flags. + kWAll = kWReg | kWMem | kWDecide | kWFunc, //!< All out-flags. + + kRDone = 0x00400000U, //!< Already allocated on the input. + kWDone = 0x00800000U, //!< Already allocated on the output. + + kX86GpbLo = 0x10000000U, + kX86GpbHi = 0x20000000U, + kX86Fld4 = 0x40000000U, + kX86Fld8 = 0x80000000U + }; + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void init(VirtReg* vreg, uint32_t flags = 0, uint32_t inRegs = 0, uint32_t allocableRegs = 0) noexcept { + this->vreg = vreg; + this->flags = flags; + this->refCount = 0; + this->inPhysId = Globals::kInvalidRegId; + this->outPhysId = Globals::kInvalidRegId; + this->reserved = 0; + this->inRegs = inRegs; + this->allocableRegs = allocableRegs; + } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get whether the variable has to be allocated in a specific input register. + ASMJIT_INLINE uint32_t hasInPhysId() const { return inPhysId != Globals::kInvalidRegId; } + //! Get whether the variable has to be allocated in a specific output register. + ASMJIT_INLINE uint32_t hasOutPhysId() const { return outPhysId != Globals::kInvalidRegId; } + + //! Set the input register index. + ASMJIT_INLINE void setInPhysId(uint32_t index) { inPhysId = static_cast(index); } + //! Set the output register index. + ASMJIT_INLINE void setOutPhysId(uint32_t index) { outPhysId = static_cast(index); } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE TiedReg& operator=(const TiedReg& other) { + ::memcpy(this, &other, sizeof(TiedReg)); + return *this; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Pointer to the associated \ref VirtReg. + VirtReg* vreg; + //! Tied flags. + uint32_t flags; + + union { + struct { + //! How many times the variable is used by the instruction/node. + uint8_t refCount; + //! Input register index or `kInvalidReg` if it's not given. + //! + //! Even if the input register index is not given (i.e. it may by any + //! register), register allocator should assign an index that will be + //! used to persist a variable into this specific index. It's helpful + //! in situations where one variable has to be allocated in multiple + //! registers to determine the register which will be persistent. + uint8_t inPhysId; + //! Output register index or `kInvalidReg` if it's not given. + //! + //! Typically `kInvalidReg` if variable is only used on input. + uint8_t outPhysId; + //! \internal + uint8_t reserved; + }; + + //! \internal + //! + //! Packed data #0. + uint32_t packed; + }; + + //! Mandatory input registers. + //! + //! Mandatory input registers are required by the instruction even if + //! there are duplicates. This schema allows us to allocate one variable + //! in one or more register when needed. Required mostly by instructions + //! that have implicit register operands (imul, cpuid, ...) and function + //! call. + uint32_t inRegs; + + //! Allocable input registers. + //! + //! Optional input registers is a mask of all allocable registers for a given + //! variable where we have to pick one of them. This mask is usually not used + //! when _inRegs is set. If both masks are used then the register + //! allocator tries first to find an intersection between these and allocates + //! an extra slot if not found. + uint32_t allocableRegs; +}; + +// ============================================================================ +// [asmjit::RABits] +// ============================================================================ + +//! Fixed size bit-array. +//! +//! Used by variable liveness analysis. +struct RABits { + // -------------------------------------------------------------------------- + // [Enums] + // -------------------------------------------------------------------------- + + enum { + kEntitySize = static_cast(sizeof(uintptr_t)), + kEntityBits = kEntitySize * 8 + }; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE uintptr_t getBit(uint32_t index) const noexcept { + return (data[index / kEntityBits] >> (index % kEntityBits)) & 1; + } + + ASMJIT_INLINE void setBit(uint32_t index) noexcept { + data[index / kEntityBits] |= static_cast(1) << (index % kEntityBits); + } + + ASMJIT_INLINE void delBit(uint32_t index) noexcept { + data[index / kEntityBits] &= ~(static_cast(1) << (index % kEntityBits)); + } + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + //! Copy bits from `s0`, returns `true` if at least one bit is set in `s0`. + ASMJIT_INLINE bool copyBits(const RABits* s0, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool addBits(const RABits* s0, uint32_t len) noexcept { + return addBits(this, s0, len); + } + + ASMJIT_INLINE bool addBits(const RABits* s0, const RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i] | s1->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool andBits(const RABits* s1, uint32_t len) noexcept { + return andBits(this, s1, len); + } + + ASMJIT_INLINE bool andBits(const RABits* s0, const RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i] & s1->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool delBits(const RABits* s1, uint32_t len) noexcept { + return delBits(this, s1, len); + } + + ASMJIT_INLINE bool delBits(const RABits* s0, const RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t t = s0->data[i] & ~s1->data[i]; + data[i] = t; + r |= t; + } + return r != 0; + } + + ASMJIT_INLINE bool _addBitsDelSource(RABits* s1, uint32_t len) noexcept { + return _addBitsDelSource(this, s1, len); + } + + ASMJIT_INLINE bool _addBitsDelSource(const RABits* s0, RABits* s1, uint32_t len) noexcept { + uintptr_t r = 0; + for (uint32_t i = 0; i < len; i++) { + uintptr_t a = s0->data[i]; + uintptr_t b = s1->data[i]; + + this->data[i] = a | b; + b &= ~a; + + s1->data[i] = b; + r |= b; + } + return r != 0; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + uintptr_t data[1]; +}; + +// ============================================================================ +// [asmjit::RACell] +// ============================================================================ + +//! Register allocator's (RA) memory cell. +struct RACell { + RACell* next; //!< Next active cell. + int32_t offset; //!< Cell offset, relative to base-offset. + uint32_t size; //!< Cell size. + uint32_t alignment; //!< Cell alignment. +}; + +// ============================================================================ +// [asmjit::RAData] +// ============================================================================ + +//! Register allocator's (RA) data associated with each \ref CBNode. +struct RAData { + ASMJIT_INLINE RAData(uint32_t tiedTotal) noexcept + : liveness(nullptr), + state(nullptr), + tiedTotal(tiedTotal) {} + + RABits* liveness; //!< Liveness bits (populated by liveness-analysis). + RAState* state; //!< Optional saved \ref RAState. + uint32_t tiedTotal; //!< Total count of \ref TiedReg regs. +}; + +// ============================================================================ +// [asmjit::RAState] +// ============================================================================ + +//! Variables' state. +struct RAState {}; + +// ============================================================================ +// [asmjit::RAPass] +// ============================================================================ + +//! \internal +//! +//! Register allocator pipeline used by \ref CodeCompiler. +struct RAPass : public CBPass { +public: + ASMJIT_NONCOPYABLE(RAPass) + + typedef void (ASMJIT_CDECL* TraceNodeFunc)(RAPass* self, CBNode* node_, const char* prefix); + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + RAPass() noexcept; + virtual ~RAPass() noexcept; + + // -------------------------------------------------------------------------- + // [Interface] + // -------------------------------------------------------------------------- + + virtual Error process(Zone* zone) noexcept override; + + //! Run the register allocator for a given function `func`. + virtual Error compile(CCFunc* func) noexcept; + + //! Called by `compile()` to prepare the register allocator to process the + //! given function. It should reset and set-up everything (i.e. no states + //! from a previous compilation should prevail). + virtual Error prepare(CCFunc* func) noexcept; + + //! Called after `compile()` to clean everything up, no matter if it + //! succeeded or failed. + virtual void cleanup() noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the associated `CodeCompiler`. + ASMJIT_INLINE CodeCompiler* cc() const noexcept { return static_cast(_cb); } + + //! Get function. + ASMJIT_INLINE CCFunc* getFunc() const noexcept { return _func; } + //! Get stop node. + ASMJIT_INLINE CBNode* getStop() const noexcept { return _stop; } + + //! Get extra block. + ASMJIT_INLINE CBNode* getExtraBlock() const noexcept { return _extraBlock; } + //! Set extra block. + ASMJIT_INLINE void setExtraBlock(CBNode* node) noexcept { _extraBlock = node; } + + // -------------------------------------------------------------------------- + // [State] + // -------------------------------------------------------------------------- + + //! Get current state. + ASMJIT_INLINE RAState* getState() const { return _state; } + + //! Load current state from `target` state. + virtual void loadState(RAState* src) = 0; + + //! Save current state, returning new `RAState` instance. + virtual RAState* saveState() = 0; + + //! Change the current state to `target` state. + virtual void switchState(RAState* src) = 0; + + //! Change the current state to the intersection of two states `a` and `b`. + virtual void intersectStates(RAState* a, RAState* b) = 0; + + // -------------------------------------------------------------------------- + // [Context] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Error assignRAId(VirtReg* vreg) noexcept { + // Likely as a single virtual register would be mostly used more than once, + // this means that each virtual register will hit one bad case (doesn't + // have id) and then all likely cases. + if (ASMJIT_LIKELY(vreg->_raId != kInvalidValue)) return kErrorOk; + + uint32_t raId = static_cast(_contextVd.getLength()); + ASMJIT_PROPAGATE(_contextVd.append(&_heap, vreg)); + + vreg->_raId = raId; + return kErrorOk; + } + + // -------------------------------------------------------------------------- + // [Mem] + // -------------------------------------------------------------------------- + + RACell* _newVarCell(VirtReg* vreg); + RACell* _newStackCell(uint32_t size, uint32_t alignment); + + ASMJIT_INLINE RACell* getVarCell(VirtReg* vreg) { + RACell* cell = vreg->getMemCell(); + return cell ? cell : _newVarCell(vreg); + } + + virtual Error resolveCellOffsets(); + + // -------------------------------------------------------------------------- + // [Bits] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE RABits* newBits(uint32_t len) { + return static_cast( + _zone->allocZeroed(static_cast(len) * RABits::kEntitySize)); + } + + ASMJIT_INLINE RABits* copyBits(const RABits* src, uint32_t len) { + return static_cast( + _zone->dup(src, static_cast(len) * RABits::kEntitySize)); + } + + // -------------------------------------------------------------------------- + // [Fetch] + // -------------------------------------------------------------------------- + + //! Fetch. + //! + //! Fetch iterates over all nodes and gathers information about all variables + //! used. The process generates information required by register allocator, + //! variable liveness analysis and translator. + virtual Error fetch() = 0; + + // -------------------------------------------------------------------------- + // [Unreachable Code] + // -------------------------------------------------------------------------- + + //! Add unreachable-flow data to the unreachable flow list. + ASMJIT_INLINE Error addUnreachableNode(CBNode* node) { + ZoneList::Link* link = _zone->allocT::Link>(); + if (!link) return DebugUtils::errored(kErrorNoHeapMemory); + + link->setValue(node); + _unreachableList.append(link); + + return kErrorOk; + } + + //! Remove unreachable code. + virtual Error removeUnreachableCode(); + + // -------------------------------------------------------------------------- + // [Code-Flow] + // -------------------------------------------------------------------------- + + //! Add returning node (i.e. node that returns and where liveness analysis + //! should start). + ASMJIT_INLINE Error addReturningNode(CBNode* node) { + ZoneList::Link* link = _zone->allocT::Link>(); + if (!link) return DebugUtils::errored(kErrorNoHeapMemory); + + link->setValue(node); + _returningList.append(link); + + return kErrorOk; + } + + //! Add jump-flow data to the jcc flow list. + ASMJIT_INLINE Error addJccNode(CBNode* node) { + ZoneList::Link* link = _zone->allocT::Link>(); + if (!link) return DebugUtils::errored(kErrorNoHeapMemory); + + link->setValue(node); + _jccList.append(link); + + return kErrorOk; + } + + // -------------------------------------------------------------------------- + // [Analyze] + // -------------------------------------------------------------------------- + + //! Perform variable liveness analysis. + //! + //! Analysis phase iterates over nodes in reverse order and generates a bit + //! array describing variables that are alive at every node in the function. + //! When the analysis start all variables are assumed dead. When a read or + //! read/write operations of a variable is detected the variable becomes + //! alive; when only write operation is detected the variable becomes dead. + //! + //! When a label is found all jumps to that label are followed and analysis + //! repeats until all variables are resolved. + virtual Error livenessAnalysis(); + + // -------------------------------------------------------------------------- + // [Annotate] + // -------------------------------------------------------------------------- + + virtual Error annotate() = 0; + virtual Error formatInlineComment(StringBuilder& dst, CBNode* node); + + // -------------------------------------------------------------------------- + // [Translate] + // -------------------------------------------------------------------------- + + //! Translate code by allocating registers and handling state changes. + virtual Error translate() = 0; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Zone* _zone; //!< Zone passed to `process()`. + ZoneHeap _heap; //!< ZoneHeap that uses `_zone`. + + CCFunc* _func; //!< Function being processed. + CBNode* _stop; //!< Stop node. + CBNode* _extraBlock; //!< Node that is used to insert extra code after the function body. + + //! \internal + //! + //! Offset (how many bytes to add) to `VarMap` to get `TiedReg` array. Used + //! by liveness analysis shared across all backends. This is needed because + //! `VarMap` is a base class for a specialized version that liveness analysis + //! doesn't use, it just needs `TiedReg` array. + uint32_t _varMapToVaListOffset; + + uint8_t _emitComments; //!< Whether to emit comments. + + ZoneList _unreachableList; //!< Unreachable nodes. + ZoneList _returningList; //!< Returning nodes. + ZoneList _jccList; //!< Jump nodes. + + ZoneVector _contextVd; //!< All variables used by the current function. + RACell* _memVarCells; //!< Memory used to spill variables. + RACell* _memStackCells; //!< Memory used to allocate memory on the stack. + + uint32_t _mem1ByteVarsUsed; //!< Count of 1-byte cells. + uint32_t _mem2ByteVarsUsed; //!< Count of 2-byte cells. + uint32_t _mem4ByteVarsUsed; //!< Count of 4-byte cells. + uint32_t _mem8ByteVarsUsed; //!< Count of 8-byte cells. + uint32_t _mem16ByteVarsUsed; //!< Count of 16-byte cells. + uint32_t _mem32ByteVarsUsed; //!< Count of 32-byte cells. + uint32_t _mem64ByteVarsUsed; //!< Count of 64-byte cells. + uint32_t _memStackCellsUsed; //!< Count of stack memory cells. + + uint32_t _memMaxAlign; //!< Maximum memory alignment used by the function. + uint32_t _memVarTotal; //!< Count of bytes used by variables. + uint32_t _memStackTotal; //!< Count of bytes used by stack. + uint32_t _memAllTotal; //!< Count of bytes used by variables and stack after alignment. + + uint32_t _annotationLength; //!< Default length of an annotated instruction. + RAState* _state; //!< Current RA state. +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // !ASMJIT_DISABLE_COMPILER +#endif // _ASMJIT_BASE_REGALLOC_P_H diff --git a/src/asmjit/base/runtime.cpp b/src/asmjit/base/runtime.cpp index ee9aa49..f074885 100644 --- a/src/asmjit/base/runtime.cpp +++ b/src/asmjit/base/runtime.cpp @@ -9,52 +9,15 @@ // [Dependencies] #include "../base/assembler.h" +#include "../base/cpuinfo.h" #include "../base/runtime.h" -// TODO: Rename this, or make call conv independent of CompilerFunc. -#include "../base/compilerfunc.h" - // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { -// ============================================================================ -// [asmjit::Runtime - Utilities] -// ============================================================================ - -static ASMJIT_INLINE uint32_t hostStackAlignment() noexcept { - // By default a pointer-size stack alignment is assumed. - uint32_t alignment = sizeof(intptr_t); - - // ARM & ARM64 - // ----------- - // - // - 32-bit ARM requires stack to be aligned to 8 bytes. - // - 64-bit ARM requires stack to be aligned to 16 bytes. -#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 - alignment = ASMJIT_ARCH_ARM32 ? 8 : 16; -#endif - - // X86 & X64 - // --------- - // - // - 32-bit X86 requires stack to be aligned to 4 bytes. Modern Linux, APPLE - // and UNIX guarantees 16-byte stack alignment even in 32-bit, but I'm - // not sure about all other UNIX operating systems, because 16-byte alignment - // is addition to an older specification. - // - 64-bit X86 requires stack to be aligned to 16 bytes. -#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 - int modernOS = ASMJIT_OS_LINUX || // Linux & ANDROID. - ASMJIT_OS_MAC || // OSX and iOS. - ASMJIT_OS_BSD; // BSD variants. - alignment = ASMJIT_ARCH_X64 || modernOS ? 16 : 4; -#endif - - return alignment; -} - -static ASMJIT_INLINE void hostFlushInstructionCache(void* p, size_t size) noexcept { +static ASMJIT_INLINE void hostFlushInstructionCache(const void* p, size_t size) noexcept { // Only useful on non-x86 architectures. #if !ASMJIT_ARCH_X86 && !ASMJIT_ARCH_X64 # if ASMJIT_OS_WINDOWS @@ -67,22 +30,46 @@ static ASMJIT_INLINE void hostFlushInstructionCache(void* p, size_t size) noexce #endif // !ASMJIT_ARCH_X86 && !ASMJIT_ARCH_X64 } +static ASMJIT_INLINE uint32_t hostDetectNaturalStackAlignment() noexcept { + // Alignment is assumed to match the pointer-size by default. + uint32_t alignment = sizeof(intptr_t); + + // X86 & X64 + // --------- + // + // - 32-bit X86 requires stack to be aligned to 4 bytes. Modern Linux, Mac + // and UNIX guarantees 16-byte stack alignment even on 32-bit. I'm not + // sure about all other UNIX operating systems, because 16-byte alignment + //! is addition to an older specification. + // - 64-bit X86 requires stack to be aligned to at least 16 bytes. +#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 + int kIsModernOS = ASMJIT_OS_LINUX || // Linux & ANDROID. + ASMJIT_OS_MAC || // OSX and iOS. + ASMJIT_OS_BSD ; // BSD variants. + alignment = ASMJIT_ARCH_X64 || kIsModernOS ? 16 : 4; +#endif + + // ARM32 & ARM64 + // ------------- + // + // - 32-bit ARM requires stack to be aligned to 8 bytes. + // - 64-bit ARM requires stack to be aligned to 16 bytes. +#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64 + alignment = ASMJIT_ARCH_ARM32 ? 8 : 16; +#endif + + return alignment; +} + + // ============================================================================ // [asmjit::Runtime - Construction / Destruction] // ============================================================================ Runtime::Runtime() noexcept - : _runtimeType(kTypeNone), - _allocType(kVMemAllocFreeable), - _cpuInfo(), - _stackAlignment(0), - _cdeclConv(kCallConvNone), - _stdCallConv(kCallConvNone), - _baseAddress(kNoBaseAddress), - _sizeLimit(0) { - - ::memset(_reserved, 0, sizeof(_reserved)); -} + : _codeInfo(), + _runtimeType(kRuntimeNone), + _allocType(VMemMgr::kAllocFreeable) {} Runtime::~Runtime() noexcept {} // ============================================================================ @@ -90,12 +77,14 @@ Runtime::~Runtime() noexcept {} // ============================================================================ HostRuntime::HostRuntime() noexcept { - _runtimeType = kTypeJit; - _cpuInfo = CpuInfo::getHost(); + _runtimeType = kRuntimeJit; - _stackAlignment = hostStackAlignment(); - _cdeclConv = kCallConvHostCDecl; - _stdCallConv = kCallConvHostStdCall; + // Setup the CodeInfo of this Runtime. + _codeInfo._archInfo = CpuInfo::getHost().getArchInfo(); + _codeInfo._stackAlignment = static_cast(hostDetectNaturalStackAlignment()); + _codeInfo._cdeclCallConv = CallConv::kIdHostCDecl; + _codeInfo._stdCallConv = CallConv::kIdHostStdCall; + _codeInfo._fastCallConv = CallConv::kIdHostFastCall; } HostRuntime::~HostRuntime() noexcept {} @@ -103,66 +92,10 @@ HostRuntime::~HostRuntime() noexcept {} // [asmjit::HostRuntime - Interface] // ============================================================================ -void HostRuntime::flush(void* p, size_t size) noexcept { +void HostRuntime::flush(const void* p, size_t size) noexcept { hostFlushInstructionCache(p, size); } -// ============================================================================ -// [asmjit::StaticRuntime - Construction / Destruction] -// ============================================================================ - -StaticRuntime::StaticRuntime(void* baseAddress, size_t sizeLimit) noexcept { - _sizeLimit = sizeLimit; - _baseAddress = static_cast((uintptr_t)baseAddress); -} -StaticRuntime::~StaticRuntime() noexcept {} - -// ============================================================================ -// [asmjit::StaticRuntime - Interface] -// ============================================================================ - -Error StaticRuntime::add(void** dst, Assembler* assembler) noexcept { - size_t codeSize = assembler->getCodeSize(); - size_t sizeLimit = _sizeLimit; - - if (codeSize == 0) { - *dst = nullptr; - return kErrorNoCodeGenerated; - } - - if (sizeLimit != 0 && sizeLimit < codeSize) { - *dst = nullptr; - return kErrorCodeTooLarge; - } - - Ptr baseAddress = _baseAddress; - uint8_t* p = static_cast((void*)static_cast(baseAddress)); - - // Since the base address is known the `relocSize` returned should be equal - // to `codeSize`. It's better to fail if they don't match instead of passsing - // silently. - size_t relocSize = assembler->relocCode(p, baseAddress); - if (relocSize == 0 || codeSize != relocSize) { - *dst = nullptr; - return kErrorInvalidState; - } - - _baseAddress += codeSize; - if (sizeLimit) - sizeLimit -= codeSize; - - flush(p, codeSize); - *dst = p; - - return kErrorOk; -} - -Error StaticRuntime::release(void* p) noexcept { - // There is nothing to release as `StaticRuntime` doesn't manage any memory. - ASMJIT_UNUSED(p); - return kErrorOk; -} - // ============================================================================ // [asmjit::JitRuntime - Construction / Destruction] // ============================================================================ @@ -174,25 +107,25 @@ JitRuntime::~JitRuntime() noexcept {} // [asmjit::JitRuntime - Interface] // ============================================================================ -Error JitRuntime::add(void** dst, Assembler* assembler) noexcept { - size_t codeSize = assembler->getCodeSize(); - if (codeSize == 0) { +Error JitRuntime::_add(void** dst, CodeHolder* code) noexcept { + size_t codeSize = code->getCodeSize(); + if (ASMJIT_UNLIKELY(codeSize == 0)) { *dst = nullptr; - return kErrorNoCodeGenerated; + return DebugUtils::errored(kErrorNoCodeGenerated); } void* p = _memMgr.alloc(codeSize, getAllocType()); - if (p == nullptr) { + if (ASMJIT_UNLIKELY(!p)) { *dst = nullptr; - return kErrorNoVirtualMemory; + return DebugUtils::errored(kErrorNoVirtualMemory); } // Relocate the code and release the unused memory back to `VMemMgr`. - size_t relocSize = assembler->relocCode(p); - if (relocSize == 0) { + size_t relocSize = code->relocate(p); + if (ASMJIT_UNLIKELY(relocSize == 0)) { *dst = nullptr; _memMgr.release(p); - return kErrorInvalidState; + return DebugUtils::errored(kErrorInvalidState); } if (relocSize < codeSize) @@ -204,11 +137,11 @@ Error JitRuntime::add(void** dst, Assembler* assembler) noexcept { return kErrorOk; } -Error JitRuntime::release(void* p) noexcept { +Error JitRuntime::_release(void* p) noexcept { return _memMgr.release(p); } } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/runtime.h b/src/asmjit/base/runtime.h index 9239a30..730b6a8 100644 --- a/src/asmjit/base/runtime.h +++ b/src/asmjit/base/runtime.h @@ -9,11 +9,11 @@ #define _ASMJIT_BASE_RUNTIME_H // [Dependencies] -#include "../base/cpuinfo.h" +#include "../base/codeholder.h" #include "../base/vmem.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { @@ -21,8 +21,7 @@ namespace asmjit { // [Forward Declarations] // ============================================================================ -class Assembler; -class CpuInfo; +class CodeHolder; //! \addtogroup asmjit_base //! \{ @@ -33,17 +32,13 @@ class CpuInfo; //! Base runtime. class ASMJIT_VIRTAPI Runtime { - public: - ASMJIT_NO_COPY(Runtime) +public: + ASMJIT_NONCOPYABLE(Runtime) - // -------------------------------------------------------------------------- - // [asmjit::RuntimeType] - // -------------------------------------------------------------------------- - - ASMJIT_ENUM(Type) { - kTypeNone = 0, - kTypeJit = 1, - kTypeRemote = 2 + ASMJIT_ENUM(RuntimeType) { + kRuntimeNone = 0, + kRuntimeJit = 1, + kRuntimeRemote = 2 }; // -------------------------------------------------------------------------- @@ -59,83 +54,66 @@ class ASMJIT_VIRTAPI Runtime { // [Accessors] // -------------------------------------------------------------------------- + //! Get CodeInfo of this runtime. + //! + //! CodeInfo can be used to setup a CodeHolder in case you plan to generate a + //! code compatible and executable by this Runtime. + ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; } + + //! Get the Runtime's architecture type, see \ref ArchInfo::Type. + ASMJIT_INLINE uint32_t getArchType() const noexcept { return _codeInfo.getArchType(); } + //! Get the Runtime's architecture sub-type, see \ref ArchInfo::SubType. + ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _codeInfo.getArchSubType(); } + //! Get the runtime type, see \ref Type. ASMJIT_INLINE uint32_t getRuntimeType() const noexcept { return _runtimeType; } - //! Get stack alignment of the target. - ASMJIT_INLINE uint32_t getStackAlignment() const noexcept { return _stackAlignment; } - - //! Get the CDECL calling convention conforming to the runtime's ABI. - //! - //! NOTE: This is a default calling convention used by the runtime's target. - ASMJIT_INLINE uint32_t getCdeclConv() const noexcept { return _cdeclConv; } - //! Get the STDCALL calling convention conforming to the runtime's ABI. - //! - //! NOTE: STDCALL calling convention is only used by 32-bit x86 target. On - //! all other targets it's mapped to CDECL and calling `getStdcallConv()` will - //! return the same as `getCdeclConv()`. - ASMJIT_INLINE uint32_t getStdCallConv() const noexcept { return _stdCallConv; } - - //! Get CPU information. - ASMJIT_INLINE const CpuInfo& getCpuInfo() const noexcept { return _cpuInfo; } - //! Set CPU information. - ASMJIT_INLINE void setCpuInfo(const CpuInfo& ci) noexcept { _cpuInfo = ci; } - - //! Get whether the runtime has a base address. - ASMJIT_INLINE bool hasBaseAddress() const noexcept { return _baseAddress != kNoBaseAddress; } - //! Get the base address. - ASMJIT_INLINE Ptr getBaseAddress() const noexcept { return _baseAddress; } - // -------------------------------------------------------------------------- // [Interface] // -------------------------------------------------------------------------- - //! Allocate a memory needed for a code generated by `assembler` and + // NOTE: To allow passing function pointers to `add()` and `release()` the + // virtual methods are prefixed with `_` and called from templates. + + template + ASMJIT_INLINE Error add(Func* dst, CodeHolder* code) noexcept { + return _add(Internal::ptr_cast(dst), code); + } + + template + ASMJIT_INLINE Error release(Func dst) noexcept { + return _release(Internal::ptr_cast(dst)); + } + + //! Allocate a memory needed for a code stored in the \ref CodeHolder and //! relocate it to the target location. //! //! The beginning of the memory allocated for the function is returned in - //! `dst`. Returns Status code as \ref ErrorCode, on failure `dst` is set to - //! `nullptr`. - virtual Error add(void** dst, Assembler* assembler) noexcept = 0; + //! `dst`. If failed the \ref Error code is returned and `dst` is set to null + //! (this means that you don't have to set it to null before calling `add()`). + virtual Error _add(void** dst, CodeHolder* code) noexcept = 0; - //! Release memory allocated by `add`. - virtual Error release(void* p) noexcept = 0; + //! Release `p` allocated by `add()`. + virtual Error _release(void* p) noexcept = 0; // -------------------------------------------------------------------------- // [Members] // -------------------------------------------------------------------------- - //! Type of the runtime. - uint8_t _runtimeType; - //! Type of the allocation. - uint8_t _allocType; - - //! Runtime's stack alignment. - uint8_t _stackAlignment; - //! CDECL calling convention conforming to runtime ABI. - uint8_t _cdeclConv; - //! STDCALL calling convention conforming to runtime ABI. - uint8_t _stdCallConv; - //! \internal - uint8_t _reserved[3]; - - //! Runtime CPU information. - CpuInfo _cpuInfo; - - //! Base address (-1 means no base address). - Ptr _baseAddress; - //! Maximum size of the code that can be added to the runtime (0=unlimited). - size_t _sizeLimit; + CodeInfo _codeInfo; //!< Basic information about the Runtime's code. + uint8_t _runtimeType; //!< Type of the runtime. + uint8_t _allocType; //!< Type of the allocator the Runtime uses. + uint8_t _reserved[6]; //!< \internal }; // ============================================================================ // [asmjit::HostRuntime] // ============================================================================ -//! Base runtime for JIT code generation. +//! Runtime designed to be used in the same process the code is generated in. class ASMJIT_VIRTAPI HostRuntime : public Runtime { - public: - ASMJIT_NO_COPY(HostRuntime) +public: + ASMJIT_NONCOPYABLE(HostRuntime) // -------------------------------------------------------------------------- // [Construction / Destruction] @@ -154,70 +132,24 @@ class ASMJIT_VIRTAPI HostRuntime : public Runtime { //! //! This member function is called after the code has been copied to the //! destination buffer. It is only useful for JIT code generation as it - //! causes a flush of the processor cache. + //! causes a flush of the processor's cache. //! //! Flushing is basically a NOP under X86/X64, but is needed by architectures - //! that do not have a transparent instruction cache. + //! that do not have a transparent instruction cache like ARM. //! //! This function can also be overridden to improve compatibility with tools //! such as Valgrind, however, it's not an official part of AsmJit. - ASMJIT_API virtual void flush(void* p, size_t size) noexcept; -}; - -// ============================================================================ -// [asmjit::StaticRuntime] -// ============================================================================ - -//! JIT static runtime. -//! -//! JIT static runtime can be used to generate code to a memory location that -//! is known. -class ASMJIT_VIRTAPI StaticRuntime : public HostRuntime { - public: - ASMJIT_NO_COPY(StaticRuntime) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - //! Create a `StaticRuntime` instance. - //! - //! The `address` specifies a fixed target address, which will be used as a - //! base address for relocation, and `sizeLimit` specifies the maximum size - //! of a code that can be copied to it. If there is no limit `sizeLimit` - //! should be zero. - ASMJIT_API StaticRuntime(void* baseAddress, size_t sizeLimit = 0) noexcept; - //! Destroy the `StaticRuntime` instance. - ASMJIT_API virtual ~StaticRuntime() noexcept; - - // -------------------------------------------------------------------------- - // [Accessors] - // -------------------------------------------------------------------------- - - //! Get the base address. - ASMJIT_INLINE Ptr getBaseAddress() const noexcept { return _baseAddress; } - - //! Get the maximum size of the code that can be relocated/stored in the target. - //! - //! Returns zero if unlimited. - ASMJIT_INLINE size_t getSizeLimit() const noexcept { return _sizeLimit; } - - // -------------------------------------------------------------------------- - // [Interface] - // -------------------------------------------------------------------------- - - ASMJIT_API virtual Error add(void** dst, Assembler* assembler) noexcept; - ASMJIT_API virtual Error release(void* p) noexcept; + ASMJIT_API virtual void flush(const void* p, size_t size) noexcept; }; // ============================================================================ // [asmjit::JitRuntime] // ============================================================================ -//! JIT runtime. +//! Runtime designed to store and execute code generated at runtime (JIT). class ASMJIT_VIRTAPI JitRuntime : public HostRuntime { - public: - ASMJIT_NO_COPY(JitRuntime) +public: + ASMJIT_NONCOPYABLE(JitRuntime) // -------------------------------------------------------------------------- // [Construction / Destruction] @@ -244,8 +176,8 @@ class ASMJIT_VIRTAPI JitRuntime : public HostRuntime { // [Interface] // -------------------------------------------------------------------------- - ASMJIT_API virtual Error add(void** dst, Assembler* assembler) noexcept; - ASMJIT_API virtual Error release(void* p) noexcept; + ASMJIT_API Error _add(void** dst, CodeHolder* code) noexcept override; + ASMJIT_API Error _release(void* p) noexcept override; // -------------------------------------------------------------------------- // [Members] @@ -260,7 +192,7 @@ class ASMJIT_VIRTAPI JitRuntime : public HostRuntime { } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" // [Guard] #endif // _ASMJIT_BASE_RUNTIME_H diff --git a/src/asmjit/base/vectypes.h b/src/asmjit/base/simdtypes.h similarity index 68% rename from src/asmjit/base/vectypes.h rename to src/asmjit/base/simdtypes.h index 89c3176..5c1c75a 100644 --- a/src/asmjit/base/vectypes.h +++ b/src/asmjit/base/simdtypes.h @@ -5,14 +5,14 @@ // Zlib - See LICENSE.md file in the package. // [Guard] -#ifndef _ASMJIT_BASE_VECTYPES_H -#define _ASMJIT_BASE_VECTYPES_H +#ifndef _ASMJIT_BASE_SIMDTYPES_H +#define _ASMJIT_BASE_SIMDTYPES_H // [Dependencies] #include "../base/globals.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { @@ -20,135 +20,135 @@ namespace asmjit { //! \{ // ============================================================================ -// [asmjit::Vec64] +// [asmjit::Data64] // ============================================================================ -//! 64-bit vector register data. -union Vec64 { +//! 64-bit data useful for creating SIMD constants. +union Data64 { // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- //! Set all eight 8-bit signed integers. - static ASMJIT_INLINE Vec64 fromSB(int8_t x0) noexcept { - Vec64 self; - self.setSB(x0); + static ASMJIT_INLINE Data64 fromI8(int8_t x0) noexcept { + Data64 self; + self.setI8(x0); return self; } //! Set all eight 8-bit unsigned integers. - static ASMJIT_INLINE Vec64 fromUB(uint8_t x0) noexcept { - Vec64 self; - self.setUB(x0); + static ASMJIT_INLINE Data64 fromU8(uint8_t x0) noexcept { + Data64 self; + self.setU8(x0); return self; } //! Set all eight 8-bit signed integers. - static ASMJIT_INLINE Vec64 fromSB( + static ASMJIT_INLINE Data64 fromI8( int8_t x0, int8_t x1, int8_t x2, int8_t x3, int8_t x4, int8_t x5, int8_t x6, int8_t x7) noexcept { - Vec64 self; - self.setSB(x0, x1, x2, x3, x4, x5, x6, x7); + Data64 self; + self.setI8(x0, x1, x2, x3, x4, x5, x6, x7); return self; } //! Set all eight 8-bit unsigned integers. - static ASMJIT_INLINE Vec64 fromUB( + static ASMJIT_INLINE Data64 fromU8( uint8_t x0, uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, uint8_t x5, uint8_t x6, uint8_t x7) noexcept { - Vec64 self; - self.setUB(x0, x1, x2, x3, x4, x5, x6, x7); + Data64 self; + self.setU8(x0, x1, x2, x3, x4, x5, x6, x7); return self; } //! Set all four 16-bit signed integers. - static ASMJIT_INLINE Vec64 fromSW(int16_t x0) noexcept { - Vec64 self; - self.setSW(x0); + static ASMJIT_INLINE Data64 fromI16(int16_t x0) noexcept { + Data64 self; + self.setI16(x0); return self; } //! Set all four 16-bit unsigned integers. - static ASMJIT_INLINE Vec64 fromUW(uint16_t x0) noexcept { - Vec64 self; - self.setUW(x0); + static ASMJIT_INLINE Data64 fromU16(uint16_t x0) noexcept { + Data64 self; + self.setU16(x0); return self; } //! Set all four 16-bit signed integers. - static ASMJIT_INLINE Vec64 fromSW(int16_t x0, int16_t x1, int16_t x2, int16_t x3) noexcept { - Vec64 self; - self.setSW(x0, x1, x2, x3); + static ASMJIT_INLINE Data64 fromI16(int16_t x0, int16_t x1, int16_t x2, int16_t x3) noexcept { + Data64 self; + self.setI16(x0, x1, x2, x3); return self; } //! Set all four 16-bit unsigned integers. - static ASMJIT_INLINE Vec64 fromUW(uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3) noexcept { - Vec64 self; - self.setUW(x0, x1, x2, x3); + static ASMJIT_INLINE Data64 fromU16(uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3) noexcept { + Data64 self; + self.setU16(x0, x1, x2, x3); return self; } //! Set all two 32-bit signed integers. - static ASMJIT_INLINE Vec64 fromSD(int32_t x0) noexcept { - Vec64 self; - self.setSD(x0); + static ASMJIT_INLINE Data64 fromI32(int32_t x0) noexcept { + Data64 self; + self.setI32(x0); return self; } //! Set all two 32-bit unsigned integers. - static ASMJIT_INLINE Vec64 fromUD(uint32_t x0) noexcept { - Vec64 self; - self.setUD(x0); + static ASMJIT_INLINE Data64 fromU32(uint32_t x0) noexcept { + Data64 self; + self.setU32(x0); return self; } //! Set all two 32-bit signed integers. - static ASMJIT_INLINE Vec64 fromSD(int32_t x0, int32_t x1) noexcept { - Vec64 self; - self.setSD(x0, x1); + static ASMJIT_INLINE Data64 fromI32(int32_t x0, int32_t x1) noexcept { + Data64 self; + self.setI32(x0, x1); return self; } //! Set all two 32-bit unsigned integers. - static ASMJIT_INLINE Vec64 fromUD(uint32_t x0, uint32_t x1) noexcept { - Vec64 self; - self.setUD(x0, x1); + static ASMJIT_INLINE Data64 fromU32(uint32_t x0, uint32_t x1) noexcept { + Data64 self; + self.setU32(x0, x1); return self; } //! Set 64-bit signed integer. - static ASMJIT_INLINE Vec64 fromSQ(int64_t x0) noexcept { - Vec64 self; - self.setSQ(x0); + static ASMJIT_INLINE Data64 fromI64(int64_t x0) noexcept { + Data64 self; + self.setI64(x0); return self; } //! Set 64-bit unsigned integer. - static ASMJIT_INLINE Vec64 fromUQ(uint64_t x0) noexcept { - Vec64 self; - self.setUQ(x0); + static ASMJIT_INLINE Data64 fromU64(uint64_t x0) noexcept { + Data64 self; + self.setU64(x0); return self; } //! Set all two SP-FP values. - static ASMJIT_INLINE Vec64 fromSF(float x0) noexcept { - Vec64 self; - self.setSF(x0); + static ASMJIT_INLINE Data64 fromF32(float x0) noexcept { + Data64 self; + self.setF32(x0); return self; } //! Set all two SP-FP values. - static ASMJIT_INLINE Vec64 fromSF(float x0, float x1) noexcept { - Vec64 self; - self.setSF(x0, x1); + static ASMJIT_INLINE Data64 fromF32(float x0, float x1) noexcept { + Data64 self; + self.setF32(x0, x1); return self; } //! Set all two SP-FP values. - static ASMJIT_INLINE Vec64 fromDF(double x0) noexcept { - Vec64 self; - self.setDF(x0); + static ASMJIT_INLINE Data64 fromF64(double x0) noexcept { + Data64 self; + self.setF64(x0); return self; } @@ -157,12 +157,12 @@ union Vec64 { // -------------------------------------------------------------------------- //! Set all eight 8-bit signed integers. - ASMJIT_INLINE void setSB(int8_t x0) noexcept { - setUB(static_cast(x0)); + ASMJIT_INLINE void setI8(int8_t x0) noexcept { + setU8(static_cast(x0)); } //! Set all eight 8-bit unsigned integers. - ASMJIT_INLINE void setUB(uint8_t x0) noexcept { + ASMJIT_INLINE void setU8(uint8_t x0) noexcept { if (ASMJIT_ARCH_64BIT) { uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0101010101010101); uq[0] = xq; @@ -175,7 +175,7 @@ union Vec64 { } //! Set all eight 8-bit signed integers. - ASMJIT_INLINE void setSB( + ASMJIT_INLINE void setI8( int8_t x0, int8_t x1, int8_t x2, int8_t x3, int8_t x4, int8_t x5, int8_t x6, int8_t x7) noexcept { sb[0] = x0; sb[1] = x1; sb[2] = x2; sb[3] = x3; @@ -183,7 +183,7 @@ union Vec64 { } //! Set all eight 8-bit unsigned integers. - ASMJIT_INLINE void setUB( + ASMJIT_INLINE void setU8( uint8_t x0, uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, uint8_t x5, uint8_t x6, uint8_t x7) noexcept { ub[0] = x0; ub[1] = x1; ub[2] = x2; ub[3] = x3; @@ -191,12 +191,12 @@ union Vec64 { } //! Set all four 16-bit signed integers. - ASMJIT_INLINE void setSW(int16_t x0) noexcept { - setUW(static_cast(x0)); + ASMJIT_INLINE void setI16(int16_t x0) noexcept { + setU16(static_cast(x0)); } //! Set all four 16-bit unsigned integers. - ASMJIT_INLINE void setUW(uint16_t x0) noexcept { + ASMJIT_INLINE void setU16(uint16_t x0) noexcept { if (ASMJIT_ARCH_64BIT) { uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0001000100010001); uq[0] = xq; @@ -209,57 +209,57 @@ union Vec64 { } //! Set all four 16-bit signed integers. - ASMJIT_INLINE void setSW(int16_t x0, int16_t x1, int16_t x2, int16_t x3) noexcept { + ASMJIT_INLINE void setI16(int16_t x0, int16_t x1, int16_t x2, int16_t x3) noexcept { sw[0] = x0; sw[1] = x1; sw[2] = x2; sw[3] = x3; } //! Set all four 16-bit unsigned integers. - ASMJIT_INLINE void setUW(uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3) noexcept { + ASMJIT_INLINE void setU16(uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3) noexcept { uw[0] = x0; uw[1] = x1; uw[2] = x2; uw[3] = x3; } //! Set all two 32-bit signed integers. - ASMJIT_INLINE void setSD(int32_t x0) noexcept { + ASMJIT_INLINE void setI32(int32_t x0) noexcept { sd[0] = x0; sd[1] = x0; } //! Set all two 32-bit unsigned integers. - ASMJIT_INLINE void setUD(uint32_t x0) noexcept { + ASMJIT_INLINE void setU32(uint32_t x0) noexcept { ud[0] = x0; ud[1] = x0; } //! Set all two 32-bit signed integers. - ASMJIT_INLINE void setSD(int32_t x0, int32_t x1) noexcept { + ASMJIT_INLINE void setI32(int32_t x0, int32_t x1) noexcept { sd[0] = x0; sd[1] = x1; } //! Set all two 32-bit unsigned integers. - ASMJIT_INLINE void setUD(uint32_t x0, uint32_t x1) noexcept { + ASMJIT_INLINE void setU32(uint32_t x0, uint32_t x1) noexcept { ud[0] = x0; ud[1] = x1; } //! Set 64-bit signed integer. - ASMJIT_INLINE void setSQ(int64_t x0) noexcept { + ASMJIT_INLINE void setI64(int64_t x0) noexcept { sq[0] = x0; } //! Set 64-bit unsigned integer. - ASMJIT_INLINE void setUQ(uint64_t x0) noexcept { + ASMJIT_INLINE void setU64(uint64_t x0) noexcept { uq[0] = x0; } //! Set all two SP-FP values. - ASMJIT_INLINE void setSF(float x0) noexcept { + ASMJIT_INLINE void setF32(float x0) noexcept { sf[0] = x0; sf[1] = x0; } //! Set all two SP-FP values. - ASMJIT_INLINE void setSF(float x0, float x1) noexcept { + ASMJIT_INLINE void setF32(float x0, float x1) noexcept { sf[0] = x0; sf[1] = x1; } //! Set all two SP-FP values. - ASMJIT_INLINE void setDF(double x0) noexcept { + ASMJIT_INLINE void setF64(double x0) noexcept { df[0] = x0; } @@ -291,166 +291,166 @@ union Vec64 { }; // ============================================================================ -// [asmjit::Vec128] +// [asmjit::Data128] // ============================================================================ -//! 128-bit vector register data. -union Vec128 { +//! 128-bit data useful for creating SIMD constants. +union Data128 { // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- //! Set all sixteen 8-bit signed integers. - static ASMJIT_INLINE Vec128 fromSB(int8_t x0) noexcept { - Vec128 self; - self.setSB(x0); + static ASMJIT_INLINE Data128 fromI8(int8_t x0) noexcept { + Data128 self; + self.setI8(x0); return self; } //! Set all sixteen 8-bit unsigned integers. - static ASMJIT_INLINE Vec128 fromUB(uint8_t x0) noexcept { - Vec128 self; - self.setUB(x0); + static ASMJIT_INLINE Data128 fromU8(uint8_t x0) noexcept { + Data128 self; + self.setU8(x0); return self; } //! Set all sixteen 8-bit signed integers. - static ASMJIT_INLINE Vec128 fromSB( + static ASMJIT_INLINE Data128 fromI8( int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 , int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 , int8_t x8 , int8_t x9 , int8_t x10, int8_t x11, int8_t x12, int8_t x13, int8_t x14, int8_t x15) noexcept { - Vec128 self; - self.setSB(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); + Data128 self; + self.setI8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); return self; } //! Set all sixteen 8-bit unsigned integers. - static ASMJIT_INLINE Vec128 fromUB( + static ASMJIT_INLINE Data128 fromU8( uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 , uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 , uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11, uint8_t x12, uint8_t x13, uint8_t x14, uint8_t x15) noexcept { - Vec128 self; - self.setUB(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); + Data128 self; + self.setU8(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); return self; } //! Set all eight 16-bit signed integers. - static ASMJIT_INLINE Vec128 fromSW(int16_t x0) noexcept { - Vec128 self; - self.setSW(x0); + static ASMJIT_INLINE Data128 fromI16(int16_t x0) noexcept { + Data128 self; + self.setI16(x0); return self; } //! Set all eight 16-bit unsigned integers. - static ASMJIT_INLINE Vec128 fromUW(uint16_t x0) noexcept { - Vec128 self; - self.setUW(x0); + static ASMJIT_INLINE Data128 fromU16(uint16_t x0) noexcept { + Data128 self; + self.setU16(x0); return self; } //! Set all eight 16-bit signed integers. - static ASMJIT_INLINE Vec128 fromSW( + static ASMJIT_INLINE Data128 fromI16( int16_t x0, int16_t x1, int16_t x2, int16_t x3, int16_t x4, int16_t x5, int16_t x6, int16_t x7) noexcept { - Vec128 self; - self.setSW(x0, x1, x2, x3, x4, x5, x6, x7); + Data128 self; + self.setI16(x0, x1, x2, x3, x4, x5, x6, x7); return self; } //! Set all eight 16-bit unsigned integers. - static ASMJIT_INLINE Vec128 fromUW( + static ASMJIT_INLINE Data128 fromU16( uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3, uint16_t x4, uint16_t x5, uint16_t x6, uint16_t x7) noexcept { - Vec128 self; - self.setUW(x0, x1, x2, x3, x4, x5, x6, x7); + Data128 self; + self.setU16(x0, x1, x2, x3, x4, x5, x6, x7); return self; } //! Set all four 32-bit signed integers. - static ASMJIT_INLINE Vec128 fromSD(int32_t x0) noexcept { - Vec128 self; - self.setSD(x0); + static ASMJIT_INLINE Data128 fromI32(int32_t x0) noexcept { + Data128 self; + self.setI32(x0); return self; } //! Set all four 32-bit unsigned integers. - static ASMJIT_INLINE Vec128 fromUD(uint32_t x0) noexcept { - Vec128 self; - self.setUD(x0); + static ASMJIT_INLINE Data128 fromU32(uint32_t x0) noexcept { + Data128 self; + self.setU32(x0); return self; } //! Set all four 32-bit signed integers. - static ASMJIT_INLINE Vec128 fromSD(int32_t x0, int32_t x1, int32_t x2, int32_t x3) noexcept { - Vec128 self; - self.setSD(x0, x1, x2, x3); + static ASMJIT_INLINE Data128 fromI32(int32_t x0, int32_t x1, int32_t x2, int32_t x3) noexcept { + Data128 self; + self.setI32(x0, x1, x2, x3); return self; } //! Set all four 32-bit unsigned integers. - static ASMJIT_INLINE Vec128 fromUD(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) noexcept { - Vec128 self; - self.setUD(x0, x1, x2, x3); + static ASMJIT_INLINE Data128 fromU32(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) noexcept { + Data128 self; + self.setU32(x0, x1, x2, x3); return self; } //! Set all two 64-bit signed integers. - static ASMJIT_INLINE Vec128 fromSQ(int64_t x0) noexcept { - Vec128 self; - self.setSQ(x0); + static ASMJIT_INLINE Data128 fromI64(int64_t x0) noexcept { + Data128 self; + self.setI64(x0); return self; } //! Set all two 64-bit unsigned integers. - static ASMJIT_INLINE Vec128 fromUQ(uint64_t x0) noexcept { - Vec128 self; - self.setUQ(x0); + static ASMJIT_INLINE Data128 fromU64(uint64_t x0) noexcept { + Data128 self; + self.setU64(x0); return self; } //! Set all two 64-bit signed integers. - static ASMJIT_INLINE Vec128 fromSQ(int64_t x0, int64_t x1) noexcept { - Vec128 self; - self.setSQ(x0, x1); + static ASMJIT_INLINE Data128 fromI64(int64_t x0, int64_t x1) noexcept { + Data128 self; + self.setI64(x0, x1); return self; } //! Set all two 64-bit unsigned integers. - static ASMJIT_INLINE Vec128 fromUQ(uint64_t x0, uint64_t x1) noexcept { - Vec128 self; - self.setUQ(x0, x1); + static ASMJIT_INLINE Data128 fromU64(uint64_t x0, uint64_t x1) noexcept { + Data128 self; + self.setU64(x0, x1); return self; } //! Set all four SP-FP floats. - static ASMJIT_INLINE Vec128 fromSF(float x0) noexcept { - Vec128 self; - self.setSF(x0); + static ASMJIT_INLINE Data128 fromF32(float x0) noexcept { + Data128 self; + self.setF32(x0); return self; } //! Set all four SP-FP floats. - static ASMJIT_INLINE Vec128 fromSF(float x0, float x1, float x2, float x3) noexcept { - Vec128 self; - self.setSF(x0, x1, x2, x3); + static ASMJIT_INLINE Data128 fromF32(float x0, float x1, float x2, float x3) noexcept { + Data128 self; + self.setF32(x0, x1, x2, x3); return self; } //! Set all two DP-FP floats. - static ASMJIT_INLINE Vec128 fromDF(double x0) noexcept { - Vec128 self; - self.setDF(x0); + static ASMJIT_INLINE Data128 fromF64(double x0) noexcept { + Data128 self; + self.setF64(x0); return self; } //! Set all two DP-FP floats. - static ASMJIT_INLINE Vec128 fromDF(double x0, double x1) noexcept { - Vec128 self; - self.setDF(x0, x1); + static ASMJIT_INLINE Data128 fromF64(double x0, double x1) noexcept { + Data128 self; + self.setF64(x0, x1); return self; } @@ -459,12 +459,12 @@ union Vec128 { // -------------------------------------------------------------------------- //! Set all sixteen 8-bit signed integers. - ASMJIT_INLINE void setSB(int8_t x0) noexcept { - setUB(static_cast(x0)); + ASMJIT_INLINE void setI8(int8_t x0) noexcept { + setU8(static_cast(x0)); } //! Set all sixteen 8-bit unsigned integers. - ASMJIT_INLINE void setUB(uint8_t x0) noexcept { + ASMJIT_INLINE void setU8(uint8_t x0) noexcept { if (ASMJIT_ARCH_64BIT) { uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0101010101010101); uq[0] = xq; @@ -480,7 +480,7 @@ union Vec128 { } //! Set all sixteen 8-bit signed integers. - ASMJIT_INLINE void setSB( + ASMJIT_INLINE void setI8( int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 , int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 , int8_t x8 , int8_t x9 , int8_t x10, int8_t x11, @@ -493,7 +493,7 @@ union Vec128 { } //! Set all sixteen 8-bit unsigned integers. - ASMJIT_INLINE void setUB( + ASMJIT_INLINE void setU8( uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 , uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 , uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11, @@ -506,12 +506,12 @@ union Vec128 { } //! Set all eight 16-bit signed integers. - ASMJIT_INLINE void setSW(int16_t x0) noexcept { - setUW(static_cast(x0)); + ASMJIT_INLINE void setI16(int16_t x0) noexcept { + setU16(static_cast(x0)); } //! Set all eight 16-bit unsigned integers. - ASMJIT_INLINE void setUW(uint16_t x0) noexcept { + ASMJIT_INLINE void setU16(uint16_t x0) noexcept { if (ASMJIT_ARCH_64BIT) { uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0001000100010001); uq[0] = xq; @@ -527,7 +527,7 @@ union Vec128 { } //! Set all eight 16-bit signed integers. - ASMJIT_INLINE void setSW( + ASMJIT_INLINE void setI16( int16_t x0, int16_t x1, int16_t x2, int16_t x3, int16_t x4, int16_t x5, int16_t x6, int16_t x7) noexcept { sw[0] = x0; sw[1] = x1; sw[2] = x2; sw[3] = x3; @@ -535,7 +535,7 @@ union Vec128 { } //! Set all eight 16-bit unsigned integers. - ASMJIT_INLINE void setUW( + ASMJIT_INLINE void setU16( uint16_t x0, uint16_t x1, uint16_t x2, uint16_t x3, uint16_t x4, uint16_t x5, uint16_t x6, uint16_t x7) noexcept { uw[0] = x0; uw[1] = x1; uw[2] = x2; uw[3] = x3; @@ -543,12 +543,12 @@ union Vec128 { } //! Set all four 32-bit signed integers. - ASMJIT_INLINE void setSD(int32_t x0) noexcept { - setUD(static_cast(x0)); + ASMJIT_INLINE void setI32(int32_t x0) noexcept { + setU32(static_cast(x0)); } //! Set all four 32-bit unsigned integers. - ASMJIT_INLINE void setUD(uint32_t x0) noexcept { + ASMJIT_INLINE void setU32(uint32_t x0) noexcept { if (ASMJIT_ARCH_64BIT) { uint64_t t = (static_cast(x0) << 32) + x0; uq[0] = t; @@ -563,52 +563,52 @@ union Vec128 { } //! Set all four 32-bit signed integers. - ASMJIT_INLINE void setSD(int32_t x0, int32_t x1, int32_t x2, int32_t x3) noexcept { + ASMJIT_INLINE void setI32(int32_t x0, int32_t x1, int32_t x2, int32_t x3) noexcept { sd[0] = x0; sd[1] = x1; sd[2] = x2; sd[3] = x3; } //! Set all four 32-bit unsigned integers. - ASMJIT_INLINE void setUD(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) noexcept { + ASMJIT_INLINE void setU32(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) noexcept { ud[0] = x0; ud[1] = x1; ud[2] = x2; ud[3] = x3; } //! Set all two 64-bit signed integers. - ASMJIT_INLINE void setSQ(int64_t x0) noexcept { + ASMJIT_INLINE void setI64(int64_t x0) noexcept { sq[0] = x0; sq[1] = x0; } //! Set all two 64-bit unsigned integers. - ASMJIT_INLINE void setUQ(uint64_t x0) noexcept { + ASMJIT_INLINE void setU64(uint64_t x0) noexcept { uq[0] = x0; uq[1] = x0; } //! Set all two 64-bit signed integers. - ASMJIT_INLINE void setSQ(int64_t x0, int64_t x1) noexcept { + ASMJIT_INLINE void setI64(int64_t x0, int64_t x1) noexcept { sq[0] = x0; sq[1] = x1; } //! Set all two 64-bit unsigned integers. - ASMJIT_INLINE void setUQ(uint64_t x0, uint64_t x1) noexcept { + ASMJIT_INLINE void setU64(uint64_t x0, uint64_t x1) noexcept { uq[0] = x0; uq[1] = x1; } //! Set all four SP-FP floats. - ASMJIT_INLINE void setSF(float x0) noexcept { + ASMJIT_INLINE void setF32(float x0) noexcept { sf[0] = x0; sf[1] = x0; sf[2] = x0; sf[3] = x0; } //! Set all four SP-FP floats. - ASMJIT_INLINE void setSF(float x0, float x1, float x2, float x3) noexcept { + ASMJIT_INLINE void setF32(float x0, float x1, float x2, float x3) noexcept { sf[0] = x0; sf[1] = x1; sf[2] = x2; sf[3] = x3; } //! Set all two DP-FP floats. - ASMJIT_INLINE void setDF(double x0) noexcept { + ASMJIT_INLINE void setF64(double x0) noexcept { df[0] = x0; df[1] = x0; } //! Set all two DP-FP floats. - ASMJIT_INLINE void setDF(double x0, double x1) noexcept { + ASMJIT_INLINE void setF64(double x0, double x1) noexcept { df[0] = x0; df[1] = x1; } @@ -640,31 +640,31 @@ union Vec128 { }; // ============================================================================ -// [asmjit::Vec256] +// [asmjit::Data256] // ============================================================================ -//! 256-bit vector register data. -union Vec256 { +//! 256-bit data useful for creating SIMD constants. +union Data256 { // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- //! Set all thirty two 8-bit signed integers. - static ASMJIT_INLINE Vec256 fromSB(int8_t x0) noexcept { - Vec256 self; - self.setSB(x0); + static ASMJIT_INLINE Data256 fromI8(int8_t x0) noexcept { + Data256 self; + self.setI8(x0); return self; } //! Set all thirty two 8-bit unsigned integers. - static ASMJIT_INLINE Vec256 fromUB(uint8_t x0) noexcept { - Vec256 self; - self.setUB(x0); + static ASMJIT_INLINE Data256 fromU8(uint8_t x0) noexcept { + Data256 self; + self.setU8(x0); return self; } //! Set all thirty two 8-bit signed integers. - static ASMJIT_INLINE Vec256 fromSB( + static ASMJIT_INLINE Data256 fromI8( int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 , int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 , int8_t x8 , int8_t x9 , int8_t x10, int8_t x11, @@ -674,15 +674,15 @@ union Vec256 { int8_t x24, int8_t x25, int8_t x26, int8_t x27, int8_t x28, int8_t x29, int8_t x30, int8_t x31) noexcept { - Vec256 self; - self.setSB( + Data256 self; + self.setI8( x0, x1 , x2 , x3 , x4 , x5 , x6 , x7 , x8 , x9 , x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31); return self; } //! Set all thirty two 8-bit unsigned integers. - static ASMJIT_INLINE Vec256 fromUB( + static ASMJIT_INLINE Data256 fromU8( uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 , uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 , uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11, @@ -692,137 +692,137 @@ union Vec256 { uint8_t x24, uint8_t x25, uint8_t x26, uint8_t x27, uint8_t x28, uint8_t x29, uint8_t x30, uint8_t x31) noexcept { - Vec256 self; - self.setUB( + Data256 self; + self.setU8( x0, x1 , x2 , x3 , x4 , x5 , x6 , x7 , x8 , x9 , x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31); return self; } //! Set all sixteen 16-bit signed integers. - static ASMJIT_INLINE Vec256 fromSW(int16_t x0) noexcept { - Vec256 self; - self.setSW(x0); + static ASMJIT_INLINE Data256 fromI16(int16_t x0) noexcept { + Data256 self; + self.setI16(x0); return self; } //! Set all sixteen 16-bit unsigned integers. - static ASMJIT_INLINE Vec256 fromUW(uint16_t x0) noexcept { - Vec256 self; - self.setUW(x0); + static ASMJIT_INLINE Data256 fromU16(uint16_t x0) noexcept { + Data256 self; + self.setU16(x0); return self; } //! Set all sixteen 16-bit signed integers. - static ASMJIT_INLINE Vec256 fromSW( + static ASMJIT_INLINE Data256 fromI16( int16_t x0, int16_t x1, int16_t x2 , int16_t x3 , int16_t x4 , int16_t x5 , int16_t x6 , int16_t x7 , int16_t x8, int16_t x9, int16_t x10, int16_t x11, int16_t x12, int16_t x13, int16_t x14, int16_t x15) noexcept { - Vec256 self; - self.setSW(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); + Data256 self; + self.setI16(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); return self; } //! Set all sixteen 16-bit unsigned integers. - static ASMJIT_INLINE Vec256 fromUW( + static ASMJIT_INLINE Data256 fromU16( uint16_t x0, uint16_t x1, uint16_t x2 , uint16_t x3 , uint16_t x4 , uint16_t x5 , uint16_t x6 , uint16_t x7 , uint16_t x8, uint16_t x9, uint16_t x10, uint16_t x11, uint16_t x12, uint16_t x13, uint16_t x14, uint16_t x15) noexcept { - Vec256 self; - self.setUW(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); + Data256 self; + self.setU16(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15); return self; } //! Set all eight 32-bit signed integers. - static ASMJIT_INLINE Vec256 fromSD(int32_t x0) noexcept { - Vec256 self; - self.setSD(x0); + static ASMJIT_INLINE Data256 fromI32(int32_t x0) noexcept { + Data256 self; + self.setI32(x0); return self; } //! Set all eight 32-bit unsigned integers. - static ASMJIT_INLINE Vec256 fromUD(uint32_t x0) noexcept { - Vec256 self; - self.setUD(x0); + static ASMJIT_INLINE Data256 fromU32(uint32_t x0) noexcept { + Data256 self; + self.setU32(x0); return self; } //! Set all eight 32-bit signed integers. - static ASMJIT_INLINE Vec256 fromSD( + static ASMJIT_INLINE Data256 fromI32( int32_t x0, int32_t x1, int32_t x2, int32_t x3, int32_t x4, int32_t x5, int32_t x6, int32_t x7) noexcept { - Vec256 self; - self.setSD(x0, x1, x2, x3, x4, x5, x6, x7); + Data256 self; + self.setI32(x0, x1, x2, x3, x4, x5, x6, x7); return self; } //! Set all eight 32-bit unsigned integers. - static ASMJIT_INLINE Vec256 fromUD( + static ASMJIT_INLINE Data256 fromU32( uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7) noexcept { - Vec256 self; - self.setUD(x0, x1, x2, x3, x4, x5, x6, x7); + Data256 self; + self.setU32(x0, x1, x2, x3, x4, x5, x6, x7); return self; } //! Set all four 64-bit signed integers. - static ASMJIT_INLINE Vec256 fromSQ(int64_t x0) noexcept { - Vec256 self; - self.setSQ(x0); + static ASMJIT_INLINE Data256 fromI64(int64_t x0) noexcept { + Data256 self; + self.setI64(x0); return self; } //! Set all four 64-bit unsigned integers. - static ASMJIT_INLINE Vec256 fromUQ(uint64_t x0) noexcept { - Vec256 self; - self.setUQ(x0); + static ASMJIT_INLINE Data256 fromU64(uint64_t x0) noexcept { + Data256 self; + self.setU64(x0); return self; } //! Set all four 64-bit signed integers. - static ASMJIT_INLINE Vec256 fromSQ(int64_t x0, int64_t x1, int64_t x2, int64_t x3) noexcept { - Vec256 self; - self.setSQ(x0, x1, x2, x3); + static ASMJIT_INLINE Data256 fromI64(int64_t x0, int64_t x1, int64_t x2, int64_t x3) noexcept { + Data256 self; + self.setI64(x0, x1, x2, x3); return self; } //! Set all four 64-bit unsigned integers. - static ASMJIT_INLINE Vec256 fromUQ(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3) noexcept { - Vec256 self; - self.setUQ(x0, x1, x2, x3); + static ASMJIT_INLINE Data256 fromU64(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3) noexcept { + Data256 self; + self.setU64(x0, x1, x2, x3); return self; } //! Set all eight SP-FP floats. - static ASMJIT_INLINE Vec256 fromSF(float x0) noexcept { - Vec256 self; - self.setSF(x0); + static ASMJIT_INLINE Data256 fromF32(float x0) noexcept { + Data256 self; + self.setF32(x0); return self; } //! Set all eight SP-FP floats. - static ASMJIT_INLINE Vec256 fromSF( + static ASMJIT_INLINE Data256 fromF32( float x0, float x1, float x2, float x3, float x4, float x5, float x6, float x7) noexcept { - Vec256 self; - self.setSF(x0, x1, x2, x3, x4, x5, x6, x7); + Data256 self; + self.setF32(x0, x1, x2, x3, x4, x5, x6, x7); return self; } //! Set all four DP-FP floats. - static ASMJIT_INLINE Vec256 fromDF(double x0) noexcept { - Vec256 self; - self.setDF(x0); + static ASMJIT_INLINE Data256 fromF64(double x0) noexcept { + Data256 self; + self.setF64(x0); return self; } //! Set all four DP-FP floats. - static ASMJIT_INLINE Vec256 fromDF(double x0, double x1, double x2, double x3) noexcept { - Vec256 self; - self.setDF(x0, x1, x2, x3); + static ASMJIT_INLINE Data256 fromF64(double x0, double x1, double x2, double x3) noexcept { + Data256 self; + self.setF64(x0, x1, x2, x3); return self; } @@ -831,21 +831,21 @@ union Vec256 { // -------------------------------------------------------------------------- //! Set all thirty two 8-bit signed integers. - ASMJIT_INLINE void setSB(int8_t x0) noexcept { - setUB(static_cast(x0)); + ASMJIT_INLINE void setI8(int8_t x0) noexcept { + setU8(static_cast(x0)); } //! Set all thirty two 8-bit unsigned integers. - ASMJIT_INLINE void setUB(uint8_t x0) noexcept { + ASMJIT_INLINE void setU8(uint8_t x0) noexcept { if (ASMJIT_ARCH_64BIT) { - uint64_t xq = static_cast(x0)* ASMJIT_UINT64_C(0x0101010101010101); + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0101010101010101); uq[0] = xq; uq[1] = xq; uq[2] = xq; uq[3] = xq; } else { - uint32_t xd = static_cast(x0)* static_cast(0x01010101U); + uint32_t xd = static_cast(x0) * static_cast(0x01010101U); ud[0] = xd; ud[1] = xd; ud[2] = xd; @@ -858,7 +858,7 @@ union Vec256 { } //! Set all thirty two 8-bit signed integers. - ASMJIT_INLINE void setSB( + ASMJIT_INLINE void setI8( int8_t x0 , int8_t x1 , int8_t x2 , int8_t x3 , int8_t x4 , int8_t x5 , int8_t x6 , int8_t x7 , int8_t x8 , int8_t x9 , int8_t x10, int8_t x11, @@ -879,7 +879,7 @@ union Vec256 { } //! Set all thirty two 8-bit unsigned integers. - ASMJIT_INLINE void setUB( + ASMJIT_INLINE void setU8( uint8_t x0 , uint8_t x1 , uint8_t x2 , uint8_t x3 , uint8_t x4 , uint8_t x5 , uint8_t x6 , uint8_t x7 , uint8_t x8 , uint8_t x9 , uint8_t x10, uint8_t x11, @@ -900,21 +900,21 @@ union Vec256 { } //! Set all sixteen 16-bit signed integers. - ASMJIT_INLINE void setSW(int16_t x0) noexcept { - setUW(static_cast(x0)); + ASMJIT_INLINE void setI16(int16_t x0) noexcept { + setU16(static_cast(x0)); } //! Set all eight 16-bit unsigned integers. - ASMJIT_INLINE void setUW(uint16_t x0) noexcept { + ASMJIT_INLINE void setU16(uint16_t x0) noexcept { if (ASMJIT_ARCH_64BIT) { - uint64_t xq = static_cast(x0)* ASMJIT_UINT64_C(0x0001000100010001); + uint64_t xq = static_cast(x0) * ASMJIT_UINT64_C(0x0001000100010001); uq[0] = xq; uq[1] = xq; uq[2] = xq; uq[3] = xq; } else { - uint32_t xd = static_cast(x0)* static_cast(0x00010001U); + uint32_t xd = static_cast(x0) * static_cast(0x00010001U); ud[0] = xd; ud[1] = xd; ud[2] = xd; @@ -927,7 +927,7 @@ union Vec256 { } //! Set all sixteen 16-bit signed integers. - ASMJIT_INLINE void setSW( + ASMJIT_INLINE void setI16( int16_t x0, int16_t x1, int16_t x2 , int16_t x3 , int16_t x4 , int16_t x5 , int16_t x6 , int16_t x7, int16_t x8, int16_t x9, int16_t x10, int16_t x11, int16_t x12, int16_t x13, int16_t x14, int16_t x15) noexcept { @@ -938,7 +938,7 @@ union Vec256 { } //! Set all sixteen 16-bit unsigned integers. - ASMJIT_INLINE void setUW( + ASMJIT_INLINE void setU16( uint16_t x0, uint16_t x1, uint16_t x2 , uint16_t x3 , uint16_t x4 , uint16_t x5 , uint16_t x6 , uint16_t x7, uint16_t x8, uint16_t x9, uint16_t x10, uint16_t x11, uint16_t x12, uint16_t x13, uint16_t x14, uint16_t x15) noexcept { @@ -949,12 +949,12 @@ union Vec256 { } //! Set all eight 32-bit signed integers. - ASMJIT_INLINE void setSD(int32_t x0) noexcept { - setUD(static_cast(x0)); + ASMJIT_INLINE void setI32(int32_t x0) noexcept { + setU32(static_cast(x0)); } //! Set all eight 32-bit unsigned integers. - ASMJIT_INLINE void setUD(uint32_t x0) noexcept { + ASMJIT_INLINE void setU32(uint32_t x0) noexcept { if (ASMJIT_ARCH_64BIT) { uint64_t xq = (static_cast(x0) << 32) + x0; uq[0] = xq; @@ -975,7 +975,7 @@ union Vec256 { } //! Set all eight 32-bit signed integers. - ASMJIT_INLINE void setSD( + ASMJIT_INLINE void setI32( int32_t x0, int32_t x1, int32_t x2, int32_t x3, int32_t x4, int32_t x5, int32_t x6, int32_t x7) noexcept { @@ -984,7 +984,7 @@ union Vec256 { } //! Set all eight 32-bit unsigned integers. - ASMJIT_INLINE void setUD( + ASMJIT_INLINE void setU32( uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7) noexcept { @@ -993,33 +993,33 @@ union Vec256 { } //! Set all four 64-bit signed integers. - ASMJIT_INLINE void setSQ(int64_t x0) noexcept { + ASMJIT_INLINE void setI64(int64_t x0) noexcept { sq[0] = x0; sq[1] = x0; sq[2] = x0; sq[3] = x0; } //! Set all four 64-bit unsigned integers. - ASMJIT_INLINE void setUQ(uint64_t x0) noexcept { + ASMJIT_INLINE void setU64(uint64_t x0) noexcept { uq[0] = x0; uq[1] = x0; uq[2] = x0; uq[3] = x0; } //! Set all four 64-bit signed integers. - ASMJIT_INLINE void setSQ(int64_t x0, int64_t x1, int64_t x2, int64_t x3) noexcept { + ASMJIT_INLINE void setI64(int64_t x0, int64_t x1, int64_t x2, int64_t x3) noexcept { sq[0] = x0; sq[1] = x1; sq[2] = x2; sq[3] = x3; } //! Set all four 64-bit unsigned integers. - ASMJIT_INLINE void setUQ(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3) noexcept { + ASMJIT_INLINE void setU64(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3) noexcept { uq[0] = x0; uq[1] = x1; uq[2] = x2; uq[3] = x3; } //! Set all eight SP-FP floats. - ASMJIT_INLINE void setSF(float x0) noexcept { + ASMJIT_INLINE void setF32(float x0) noexcept { sf[0] = x0; sf[1] = x0; sf[2] = x0; sf[3] = x0; sf[4] = x0; sf[5] = x0; sf[6] = x0; sf[7] = x0; } //! Set all eight SP-FP floats. - ASMJIT_INLINE void setSF( + ASMJIT_INLINE void setF32( float x0, float x1, float x2, float x3, float x4, float x5, float x6, float x7) noexcept { @@ -1028,12 +1028,12 @@ union Vec256 { } //! Set all four DP-FP floats. - ASMJIT_INLINE void setDF(double x0) noexcept { + ASMJIT_INLINE void setF64(double x0) noexcept { df[0] = x0; df[1] = x0; df[2] = x0; df[3] = x0; } //! Set all four DP-FP floats. - ASMJIT_INLINE void setDF(double x0, double x1, double x2, double x3) noexcept { + ASMJIT_INLINE void setF64(double x0, double x1, double x2, double x3) noexcept { df[0] = x0; df[1] = x1; df[2] = x2; df[3] = x3; } @@ -1069,7 +1069,7 @@ union Vec256 { } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" // [Guard] -#endif // _ASMJIT_BASE_VECTYPES_H +#endif // _ASMJIT_BASE_SIMDTYPES_H diff --git a/src/asmjit/base/containers.cpp b/src/asmjit/base/string.cpp similarity index 73% rename from src/asmjit/base/containers.cpp rename to src/asmjit/base/string.cpp index 3242a0f..4d0a837 100644 --- a/src/asmjit/base/containers.cpp +++ b/src/asmjit/base/string.cpp @@ -8,11 +8,11 @@ #define ASMJIT_EXPORTS // [Dependencies] -#include "../base/containers.h" +#include "../base/string.h" #include "../base/utils.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { @@ -31,20 +31,16 @@ StringBuilder::StringBuilder() noexcept StringBuilder::~StringBuilder() noexcept { if (_canFree) - ASMJIT_FREE(_data); + Internal::releaseMemory(_data); } // ============================================================================ // [asmjit::StringBuilder - Prepare / Reserve] // ============================================================================ -char* StringBuilder::prepare(uint32_t op, size_t len) noexcept { - // -------------------------------------------------------------------------- - // [Set] - // -------------------------------------------------------------------------- - +ASMJIT_FAVOR_SIZE char* StringBuilder::prepare(uint32_t op, size_t len) noexcept { if (op == kStringOpSet) { - // We don't care here, but we can't return a NULL pointer since it indicates + // We don't care here, but we can't return a null pointer since it indicates // failure in memory allocation. if (len == 0) { if (_data != StringBuilder_empty) @@ -62,14 +58,14 @@ char* StringBuilder::prepare(uint32_t op, size_t len) noexcept { if (to < 256 - sizeof(intptr_t)) to = 256 - sizeof(intptr_t); - char* newData = static_cast(ASMJIT_ALLOC(to + sizeof(intptr_t))); - if (newData == nullptr) { + char* newData = static_cast(Internal::allocMemory(to + sizeof(intptr_t))); + if (!newData) { clear(); return nullptr; } if (_canFree) - ASMJIT_FREE(_data); + Internal::releaseMemory(_data); _data = newData; _capacity = to + sizeof(intptr_t) - 1; @@ -82,14 +78,9 @@ char* StringBuilder::prepare(uint32_t op, size_t len) noexcept { ASMJIT_ASSERT(_length <= _capacity); return _data; } - - // -------------------------------------------------------------------------- - // [Append] - // -------------------------------------------------------------------------- - else { - // We don't care here, but we can't return a nullptr pointer since it indicates - // failure in memory allocation. + // We don't care here, but we can't return a null pointer since it indicates + // failure of memory allocation. if (len == 0) return _data + _length; @@ -114,14 +105,12 @@ char* StringBuilder::prepare(uint32_t op, size_t len) noexcept { } to = Utils::alignTo(to, sizeof(intptr_t)); - char* newData = static_cast(ASMJIT_ALLOC(to + sizeof(intptr_t))); - - if (newData == nullptr) - return nullptr; + char* newData = static_cast(Internal::allocMemory(to + sizeof(intptr_t))); + if (!newData) return nullptr; ::memcpy(newData, _data, _length); if (_canFree) - ASMJIT_FREE(_data); + Internal::releaseMemory(_data); _data = newData; _capacity = to + sizeof(intptr_t) - 1; @@ -137,27 +126,27 @@ char* StringBuilder::prepare(uint32_t op, size_t len) noexcept { } } -bool StringBuilder::reserve(size_t to) noexcept { +ASMJIT_FAVOR_SIZE Error StringBuilder::reserve(size_t to) noexcept { if (_capacity >= to) - return true; + return kErrorOk; if (to >= IntTraits::maxValue() - sizeof(intptr_t) * 2) - return false; + return DebugUtils::errored(kErrorNoHeapMemory); to = Utils::alignTo(to, sizeof(intptr_t)); + char* newData = static_cast(Internal::allocMemory(to + sizeof(intptr_t))); - char* newData = static_cast(ASMJIT_ALLOC(to + sizeof(intptr_t))); - if (newData == nullptr) - return false; + if (!newData) + return DebugUtils::errored(kErrorNoHeapMemory); ::memcpy(newData, _data, _length + 1); if (_canFree) - ASMJIT_FREE(_data); + Internal::releaseMemory(_data); _data = newData; _capacity = to + sizeof(intptr_t) - 1; _canFree = true; - return true; + return kErrorOk; } // ============================================================================ @@ -174,39 +163,36 @@ void StringBuilder::clear() noexcept { // [asmjit::StringBuilder - Methods] // ============================================================================ -bool StringBuilder::_opString(uint32_t op, const char* str, size_t len) noexcept { - if (len == kInvalidIndex) - len = str != nullptr ? ::strlen(str) : static_cast(0); +Error StringBuilder::_opString(uint32_t op, const char* str, size_t len) noexcept { + if (len == Globals::kInvalidIndex) + len = str ? ::strlen(str) : static_cast(0); char* p = prepare(op, len); - if (p == nullptr) - return false; + if (!p) return DebugUtils::errored(kErrorNoHeapMemory); ::memcpy(p, str, len); - return true; + return kErrorOk; } -bool StringBuilder::_opChar(uint32_t op, char c) noexcept { +Error StringBuilder::_opChar(uint32_t op, char c) noexcept { char* p = prepare(op, 1); - if (p == nullptr) - return false; + if (!p) return DebugUtils::errored(kErrorNoHeapMemory); *p = c; - return true; + return kErrorOk; } -bool StringBuilder::_opChars(uint32_t op, char c, size_t len) noexcept { - char* p = prepare(op, len); - if (p == nullptr) - return false; +Error StringBuilder::_opChars(uint32_t op, char c, size_t n) noexcept { + char* p = prepare(op, n); + if (!p) return DebugUtils::errored(kErrorNoHeapMemory); - ::memset(p, c, len); - return true; + ::memset(p, c, n); + return kErrorOk; } static const char StringBuilder_numbers[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -bool StringBuilder::_opNumber(uint32_t op, uint64_t i, uint32_t base, size_t width, uint32_t flags) noexcept { +Error StringBuilder::_opNumber(uint32_t op, uint64_t i, uint32_t base, size_t width, uint32_t flags) noexcept { if (base < 2 || base > 36) base = 10; @@ -282,8 +268,8 @@ bool StringBuilder::_opNumber(uint32_t op, uint64_t i, uint32_t base, size_t wid size_t prefixLength = (size_t)(buf + ASMJIT_ARRAY_SIZE(buf) - p) - numberLength; char* data = prepare(op, prefixLength + width + numberLength); - if (data == nullptr) - return false; + if (!data) + return DebugUtils::errored(kErrorNoHeapMemory); ::memcpy(data, p, prefixLength); data += prefixLength; @@ -292,28 +278,24 @@ bool StringBuilder::_opNumber(uint32_t op, uint64_t i, uint32_t base, size_t wid data += width; ::memcpy(data, p + prefixLength, numberLength); - return true; + return kErrorOk; } -bool StringBuilder::_opHex(uint32_t op, const void* data, size_t len) noexcept { - if (len >= IntTraits::maxValue() / 2) - return false; +Error StringBuilder::_opHex(uint32_t op, const void* data, size_t len) noexcept { + char* dst; - char* dst = prepare(op, len * 2); - if (dst == nullptr) - return false; + if (len >= IntTraits::maxValue() / 2 || !(dst = prepare(op, len * 2))) + return DebugUtils::errored(kErrorNoHeapMemory);; const char* src = static_cast(data); - for (size_t i = 0; i < len; i++, dst += 2, src += 1) - { + for (size_t i = 0; i < len; i++, dst += 2, src++) { dst[0] = StringBuilder_numbers[(src[0] >> 4) & 0xF]; dst[1] = StringBuilder_numbers[(src[0] ) & 0xF]; } - - return true; + return kErrorOk; } -bool StringBuilder::_opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept { +Error StringBuilder::_opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept { char buf[1024]; vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap); @@ -322,7 +304,7 @@ bool StringBuilder::_opVFormat(uint32_t op, const char* fmt, va_list ap) noexcep return _opString(op, buf); } -bool StringBuilder::setFormat(const char* fmt, ...) noexcept { +Error StringBuilder::setFormat(const char* fmt, ...) noexcept { bool result; va_list ap; @@ -333,7 +315,7 @@ bool StringBuilder::setFormat(const char* fmt, ...) noexcept { return result; } -bool StringBuilder::appendFormat(const char* fmt, ...) noexcept { +Error StringBuilder::appendFormat(const char* fmt, ...) noexcept { bool result; va_list ap; @@ -351,19 +333,16 @@ bool StringBuilder::eq(const char* str, size_t len) const noexcept { size_t aLength = _length; size_t bLength = len; - if (bLength == kInvalidIndex) { + if (bLength == Globals::kInvalidIndex) { size_t i; - for (i = 0; i < aLength; i++) { + for (i = 0; i < aLength; i++) if (aData[i] != bData[i] || bData[i] == 0) return false; - } - return bData[i] == 0; } else { if (aLength != bLength) return false; - return ::memcmp(aData, bData, aLength) == 0; } } @@ -371,4 +350,4 @@ bool StringBuilder::eq(const char* str, size_t len) const noexcept { } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/string.h b/src/asmjit/base/string.h new file mode 100644 index 0000000..3621a99 --- /dev/null +++ b/src/asmjit/base/string.h @@ -0,0 +1,289 @@ +// [AsmJit] +// Complete x86/x64 JIT and Remote Assembler for C++. +// +// [License] +// Zlib - See LICENSE.md file in the package. + +// [Guard] +#ifndef _ASMJIT_BASE_STRING_H +#define _ASMJIT_BASE_STRING_H + +// [Dependencies] +#include "../base/globals.h" + +// [Api-Begin] +#include "../asmjit_apibegin.h" + +namespace asmjit { + +//! \addtogroup asmjit_base +//! \{ + +// ============================================================================ +// [asmjit::SmallString] +// ============================================================================ + +//! Small string is a template that helps to create strings that can be either +//! statically allocated if they are small, or externally allocated in case +//! their length exceed the limit. The `WholeSize` represents the size of the +//! whole `SmallString` structure, based on that size the maximum size of the +//! internal buffer is determined. +template +class SmallString { +public: + enum { kMaxEmbeddedLength = WholeSize - 5 }; + + ASMJIT_INLINE SmallString() noexcept { reset(); } + ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); } + + ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } + ASMJIT_INLINE bool isEmbedded() const noexcept { return _length <= kMaxEmbeddedLength; } + ASMJIT_INLINE bool mustEmbed(size_t len) const noexcept { return len <= kMaxEmbeddedLength; } + + ASMJIT_INLINE uint32_t getLength() const noexcept { return _length; } + ASMJIT_INLINE char* getData() const noexcept { + return _length <= kMaxEmbeddedLength ? const_cast(_embedded) : _external[1]; + } + + ASMJIT_INLINE void setEmbedded(const char* data, size_t len) noexcept { + ASMJIT_ASSERT(len <= kMaxEmbeddedLength); + + _length = static_cast(len); + ::memcpy(_embedded, data, len); + _embedded[len] = '\0'; + } + + ASMJIT_INLINE void setExternal(const char* data, size_t len) noexcept { + ASMJIT_ASSERT(len > kMaxEmbeddedLength); + ASMJIT_ASSERT(len <= ~static_cast(0)); + + _length = static_cast(len); + _external[1] = const_cast(data); + } + + union { + struct { + uint32_t _length; + char _embedded[WholeSize - 4]; + }; + char* _external[2]; + }; +}; + +// ============================================================================ +// [asmjit::StringBuilder] +// ============================================================================ + +//! String builder. +//! +//! String builder was designed to be able to build a string using append like +//! operation to append numbers, other strings, or signle characters. It can +//! allocate it's own buffer or use a buffer created on the stack. +//! +//! String builder contains method specific to AsmJit functionality, used for +//! logging or HTML output. +class StringBuilder { +public: + ASMJIT_NONCOPYABLE(StringBuilder) + + //! \internal + //! + //! String operation. + ASMJIT_ENUM(OpType) { + kStringOpSet = 0, //!< Replace the current string by a given content. + kStringOpAppend = 1 //!< Append a given content to the current string. + }; + + //! \internal + //! + //! String format flags. + ASMJIT_ENUM(StringFormatFlags) { + kStringFormatShowSign = 0x00000001, + kStringFormatShowSpace = 0x00000002, + kStringFormatAlternate = 0x00000004, + kStringFormatSigned = 0x80000000 + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_API StringBuilder() noexcept; + ASMJIT_API ~StringBuilder() noexcept; + + ASMJIT_INLINE StringBuilder(const _NoInit&) noexcept {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get string builder capacity. + ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } + //! Get length. + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + + //! Get null-terminated string data. + ASMJIT_INLINE char* getData() noexcept { return _data; } + //! Get null-terminated string data (const). + ASMJIT_INLINE const char* getData() const noexcept { return _data; } + + // -------------------------------------------------------------------------- + // [Prepare / Reserve] + // -------------------------------------------------------------------------- + + //! Prepare to set/append. + ASMJIT_API char* prepare(uint32_t op, size_t len) noexcept; + + //! Reserve `to` bytes in string builder. + ASMJIT_API Error reserve(size_t to) noexcept; + + // -------------------------------------------------------------------------- + // [Clear] + // -------------------------------------------------------------------------- + + //! Clear the content in String builder. + ASMJIT_API void clear() noexcept; + + // -------------------------------------------------------------------------- + // [Op] + // -------------------------------------------------------------------------- + + ASMJIT_API Error _opString(uint32_t op, const char* str, size_t len = Globals::kInvalidIndex) noexcept; + ASMJIT_API Error _opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept; + ASMJIT_API Error _opChar(uint32_t op, char c) noexcept; + ASMJIT_API Error _opChars(uint32_t op, char c, size_t n) noexcept; + ASMJIT_API Error _opNumber(uint32_t op, uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept; + ASMJIT_API Error _opHex(uint32_t op, const void* data, size_t len) noexcept; + + // -------------------------------------------------------------------------- + // [Set] + // -------------------------------------------------------------------------- + + //! Replace the current string with `str` having `len` characters (or `kInvalidIndex` if it's null terminated). + ASMJIT_INLINE Error setString(const char* str, size_t len = Globals::kInvalidIndex) noexcept { return _opString(kStringOpSet, str, len); } + //! Replace the current content by a formatted string `fmt`. + ASMJIT_API Error setFormat(const char* fmt, ...) noexcept; + //! Replace the current content by a formatted string `fmt` (va_list version). + ASMJIT_INLINE Error setFormatVA(const char* fmt, va_list ap) noexcept { return _opVFormat(kStringOpSet, fmt, ap); } + + //! Replace the current content by a single `c` character. + ASMJIT_INLINE Error setChar(char c) noexcept { return _opChar(kStringOpSet, c); } + //! Replace the current content by `c` character `n` times. + ASMJIT_INLINE Error setChars(char c, size_t n) noexcept { return _opChars(kStringOpSet, c, n); } + + //! Replace the current content by a formatted integer `i` (signed). + ASMJIT_INLINE Error setInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpSet, i, base, width, flags | kStringFormatSigned); + } + + //! Replace the current content by a formatted integer `i` (unsigned). + ASMJIT_INLINE Error setUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpSet, i, base, width, flags); + } + + //! Replace the current content by the given `data` converted to a HEX string. + ASMJIT_INLINE Error setHex(const void* data, size_t len) noexcept { + return _opHex(kStringOpSet, data, len); + } + + // -------------------------------------------------------------------------- + // [Append] + // -------------------------------------------------------------------------- + + //! Append string `str` having `len` characters (or `kInvalidIndex` if it's null terminated). + ASMJIT_INLINE Error appendString(const char* str, size_t len = Globals::kInvalidIndex) noexcept { return _opString(kStringOpAppend, str, len); } + //! Append a formatted string `fmt`. + ASMJIT_API Error appendFormat(const char* fmt, ...) noexcept; + //! Append a formatted string `fmt` (va_list version). + ASMJIT_INLINE Error appendFormatVA(const char* fmt, va_list ap) noexcept { return _opVFormat(kStringOpAppend, fmt, ap); } + + //! Append a single `c` character. + ASMJIT_INLINE Error appendChar(char c) noexcept { return _opChar(kStringOpAppend, c); } + //! Append `c` character `n` times. + ASMJIT_INLINE Error appendChars(char c, size_t n) noexcept { return _opChars(kStringOpAppend, c, n); } + + //! Append `i`. + ASMJIT_INLINE Error appendInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpAppend, static_cast(i), base, width, flags | kStringFormatSigned); + } + + //! Append `i`. + ASMJIT_INLINE Error appendUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept { + return _opNumber(kStringOpAppend, i, base, width, flags); + } + + //! Append the given `data` converted to a HEX string. + ASMJIT_INLINE Error appendHex(const void* data, size_t len) noexcept { + return _opHex(kStringOpAppend, data, len); + } + + // -------------------------------------------------------------------------- + // [Eq] + // -------------------------------------------------------------------------- + + //! Check for equality with other `str` of length `len`. + ASMJIT_API bool eq(const char* str, size_t len = Globals::kInvalidIndex) const noexcept; + //! Check for equality with `other`. + ASMJIT_INLINE bool eq(const StringBuilder& other) const noexcept { return eq(other._data); } + + // -------------------------------------------------------------------------- + // [Operator Overload] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool operator==(const StringBuilder& other) const noexcept { return eq(other); } + ASMJIT_INLINE bool operator!=(const StringBuilder& other) const noexcept { return !eq(other); } + + ASMJIT_INLINE bool operator==(const char* str) const noexcept { return eq(str); } + ASMJIT_INLINE bool operator!=(const char* str) const noexcept { return !eq(str); } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + char* _data; //!< String data. + size_t _length; //!< String length. + size_t _capacity; //!< String capacity. + size_t _canFree; //!< If the string data can be freed. +}; + +// ============================================================================ +// [asmjit::StringBuilderTmp] +// ============================================================================ + +//! Temporary string builder, has statically allocated `N` bytes. +template +class StringBuilderTmp : public StringBuilder { +public: + ASMJIT_NONCOPYABLE(StringBuilderTmp) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE StringBuilderTmp() noexcept : StringBuilder(NoInit) { + _data = _embeddedData; + _data[0] = 0; + + _length = 0; + _capacity = N; + _canFree = false; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + //! Embedded data. + char _embeddedData[static_cast( + N + 1 + sizeof(intptr_t)) & ~static_cast(sizeof(intptr_t) - 1)]; +}; + +//! \} + +} // asmjit namespace + +// [Api-End] +#include "../asmjit_apiend.h" + +// [Guard] +#endif // _ASMJIT_BASE_STRING_H diff --git a/src/asmjit/base/utils.cpp b/src/asmjit/base/utils.cpp index cc21188..91e0170 100644 --- a/src/asmjit/base/utils.cpp +++ b/src/asmjit/base/utils.cpp @@ -10,115 +10,11 @@ // [Dependencies] #include "../base/utils.h" -#if ASMJIT_OS_POSIX -# include -# include -#endif // ASMJIT_OS_POSIX - -#if ASMJIT_OS_MAC -# include -#endif // ASMJIT_OS_MAC - -#if ASMJIT_OS_WINDOWS -# if defined(_MSC_VER) && _MSC_VER >= 1400 -# include -# else -# define _InterlockedCompareExchange InterlockedCompareExchange -# endif // _MSC_VER -#endif // ASMJIT_OS_WINDOWS - // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { -// ============================================================================ -// [asmjit::CpuTicks - Windows] -// ============================================================================ - -#if ASMJIT_OS_WINDOWS -static volatile uint32_t Utils_hiResTicks; -static volatile double Utils_hiResFreq; - -uint32_t Utils::getTickCount() noexcept { - do { - uint32_t hiResOk = Utils_hiResTicks; - - if (hiResOk == 1) { - LARGE_INTEGER now; - if (!::QueryPerformanceCounter(&now)) - break; - return (int64_t)(double(now.QuadPart) / Utils_hiResFreq); - } - - if (hiResOk == 0) { - LARGE_INTEGER qpf; - if (!::QueryPerformanceFrequency(&qpf)) { - _InterlockedCompareExchange((LONG*)&Utils_hiResTicks, 0xFFFFFFFF, 0); - break; - } - - LARGE_INTEGER now; - if (!::QueryPerformanceCounter(&now)) { - _InterlockedCompareExchange((LONG*)&Utils_hiResTicks, 0xFFFFFFFF, 0); - break; - } - - double freqDouble = double(qpf.QuadPart) / 1000.0; - Utils_hiResFreq = freqDouble; - _InterlockedCompareExchange((LONG*)&Utils_hiResTicks, 1, 0); - - return static_cast( - static_cast(double(now.QuadPart) / freqDouble) & 0xFFFFFFFF); - } - } while (0); - - // Bail to a less precise GetTickCount(). - return ::GetTickCount(); -} - -// ============================================================================ -// [asmjit::CpuTicks - Mac] -// ============================================================================ - -#elif ASMJIT_OS_MAC -static mach_timebase_info_data_t CpuTicks_machTime; - -uint32_t Utils::getTickCount() noexcept { - // Initialize the first time CpuTicks::now() is called (See Apple's QA1398). - if (CpuTicks_machTime.denom == 0) { - if (mach_timebase_info(&CpuTicks_machTime) != KERN_SUCCESS) - return 0; - } - - // mach_absolute_time() returns nanoseconds, we need just milliseconds. - uint64_t t = mach_absolute_time() / 1000000; - - t = t * CpuTicks_machTime.numer / CpuTicks_machTime.denom; - return static_cast(t & 0xFFFFFFFFU); -} - -// ============================================================================ -// [asmjit::CpuTicks - Posix] -// ============================================================================ - -#else -uint32_t Utils::getTickCount() noexcept { -#if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 - struct timespec ts; - - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) - return 0; - - uint64_t t = (uint64_t(ts.tv_sec ) * 1000) + (uint64_t(ts.tv_nsec) / 1000000); - return static_cast(t & 0xFFFFFFFFU); -#else // _POSIX_MONOTONIC_CLOCK -#error "[asmjit] Utils::getTickCount() is not implemented for your target OS." - return 0; -#endif // _POSIX_MONOTONIC_CLOCK -} -#endif // ASMJIT_OS - // ============================================================================ // [asmjit::Utils - Unit] // ============================================================================ @@ -127,93 +23,84 @@ uint32_t Utils::getTickCount() noexcept { UNIT(base_utils) { uint32_t i; - INFO("IntTraits<>."); - EXPECT(IntTraits::kIsSigned,"IntTraits should report signed."); - EXPECT(IntTraits::kIsSigned, "IntTraits should report signed."); - EXPECT(IntTraits::kIsSigned, "IntTraits should report signed."); - EXPECT(IntTraits::kIsSigned, "IntTraits should report signed."); + INFO("IntTraits<>"); + EXPECT(IntTraits::kIsSigned,"IntTraits should report signed"); + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed"); + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed"); + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed"); - EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned."); - EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned."); - EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned."); - EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned."); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned"); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned"); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned"); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned"); - EXPECT(IntTraits::kIsSigned, "IntTraits should report signed."); - EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned."); + EXPECT(IntTraits::kIsSigned, "IntTraits should report signed"); + EXPECT(IntTraits::kIsUnsigned, "IntTraits should report unsigned"); - EXPECT(IntTraits::kIsIntPtr, "IntTraits should report intptr_t type."); - EXPECT(IntTraits::kIsIntPtr, "IntTraits should report intptr_t type."); + EXPECT(IntTraits::kIsIntPtr, "IntTraits should report intptr_t type"); + EXPECT(IntTraits::kIsIntPtr, "IntTraits should report intptr_t type"); - INFO("Utils::iMin()/iMax()."); - EXPECT(Utils::iMin( 0, -1) == -1, "Utils::iMin should return a minimum value."); - EXPECT(Utils::iMin(-1, -2) == -2, "Utils::iMin should return a minimum value."); - EXPECT(Utils::iMin( 1, 2) == 1, "Utils::iMin should return a minimum value."); + INFO("Utils::inInterval()"); + EXPECT(Utils::inInterval(11 , 10, 20) == true , "Utils::inInterval should return true if inside"); + EXPECT(Utils::inInterval(101, 10, 20) == false, "Utils::inInterval should return false if outside"); - EXPECT(Utils::iMax( 0, -1) == 0, "Utils::iMax should return a maximum value."); - EXPECT(Utils::iMax(-1, -2) == -1, "Utils::iMax should return a maximum value."); - EXPECT(Utils::iMax( 1, 2) == 2, "Utils::iMax should return a maximum value."); + INFO("Utils::isInt8()"); + EXPECT(Utils::isInt8(-128) == true , "Utils::isInt8<> should return true if inside"); + EXPECT(Utils::isInt8( 127) == true , "Utils::isInt8<> should return true if inside"); + EXPECT(Utils::isInt8(-129) == false, "Utils::isInt8<> should return false if outside"); + EXPECT(Utils::isInt8( 128) == false, "Utils::isInt8<> should return false if outside"); - INFO("Utils::inInterval()."); - EXPECT(Utils::inInterval(11 , 10, 20) == true , "Utils::inInterval should return true if inside."); - EXPECT(Utils::inInterval(101, 10, 20) == false, "Utils::inInterval should return false if outside."); + INFO("Utils::isInt16()"); + EXPECT(Utils::isInt16(-32768) == true , "Utils::isInt16<> should return true if inside"); + EXPECT(Utils::isInt16( 32767) == true , "Utils::isInt16<> should return true if inside"); + EXPECT(Utils::isInt16(-32769) == false, "Utils::isInt16<> should return false if outside"); + EXPECT(Utils::isInt16( 32768) == false, "Utils::isInt16<> should return false if outside"); - INFO("Utils::isInt8()."); - EXPECT(Utils::isInt8(-128) == true , "Utils::isInt8<> should return true if inside."); - EXPECT(Utils::isInt8( 127) == true , "Utils::isInt8<> should return true if inside."); - EXPECT(Utils::isInt8(-129) == false, "Utils::isInt8<> should return false if outside."); - EXPECT(Utils::isInt8( 128) == false, "Utils::isInt8<> should return false if outside."); + INFO("Utils::isInt32()"); + EXPECT(Utils::isInt32( 2147483647 ) == true, "Utils::isInt32 should return true if inside"); + EXPECT(Utils::isInt32(-2147483647 - 1) == true, "Utils::isInt32 should return true if inside"); + EXPECT(Utils::isInt32(ASMJIT_UINT64_C(2147483648)) == false, "Utils::isInt32 should return false if outside"); + EXPECT(Utils::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == false, "Utils::isInt32 should return false if outside"); + EXPECT(Utils::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false, "Utils::isInt32 should return false if outside"); - INFO("Utils::isInt16()."); - EXPECT(Utils::isInt16(-32768) == true , "Utils::isInt16<> should return true if inside."); - EXPECT(Utils::isInt16( 32767) == true , "Utils::isInt16<> should return true if inside."); - EXPECT(Utils::isInt16(-32769) == false, "Utils::isInt16<> should return false if outside."); - EXPECT(Utils::isInt16( 32768) == false, "Utils::isInt16<> should return false if outside."); + INFO("Utils::isUInt8()"); + EXPECT(Utils::isUInt8(0) == true , "Utils::isUInt8<> should return true if inside"); + EXPECT(Utils::isUInt8(255) == true , "Utils::isUInt8<> should return true if inside"); + EXPECT(Utils::isUInt8(256) == false, "Utils::isUInt8<> should return false if outside"); + EXPECT(Utils::isUInt8(-1) == false, "Utils::isUInt8<> should return false if negative"); - INFO("Utils::isInt32()."); - EXPECT(Utils::isInt32( 2147483647 ) == true, "Utils::isInt32 should return true if inside."); - EXPECT(Utils::isInt32(-2147483647 - 1) == true, "Utils::isInt32 should return true if inside."); - EXPECT(Utils::isInt32(ASMJIT_UINT64_C(2147483648)) == false, "Utils::isInt32 should return false if outside."); - EXPECT(Utils::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == false, "Utils::isInt32 should return false if outside."); - EXPECT(Utils::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false, "Utils::isInt32 should return false if outside."); + INFO("Utils::isUInt12()"); + EXPECT(Utils::isUInt12(0) == true , "Utils::isUInt12<> should return true if inside"); + EXPECT(Utils::isUInt12(4095) == true , "Utils::isUInt12<> should return true if inside"); + EXPECT(Utils::isUInt12(4096) == false, "Utils::isUInt12<> should return false if outside"); + EXPECT(Utils::isUInt12(-1) == false, "Utils::isUInt12<> should return false if negative"); - INFO("Utils::isUInt8()."); - EXPECT(Utils::isUInt8(0) == true , "Utils::isUInt8<> should return true if inside."); - EXPECT(Utils::isUInt8(255) == true , "Utils::isUInt8<> should return true if inside."); - EXPECT(Utils::isUInt8(256) == false, "Utils::isUInt8<> should return false if outside."); - EXPECT(Utils::isUInt8(-1) == false, "Utils::isUInt8<> should return false if negative."); + INFO("Utils::isUInt16()"); + EXPECT(Utils::isUInt16(0) == true , "Utils::isUInt16<> should return true if inside"); + EXPECT(Utils::isUInt16(65535) == true , "Utils::isUInt16<> should return true if inside"); + EXPECT(Utils::isUInt16(65536) == false, "Utils::isUInt16<> should return false if outside"); + EXPECT(Utils::isUInt16(-1) == false, "Utils::isUInt16<> should return false if negative"); - INFO("Utils::isUInt12()."); - EXPECT(Utils::isUInt12(0) == true , "Utils::isUInt12<> should return true if inside."); - EXPECT(Utils::isUInt12(4095) == true , "Utils::isUInt12<> should return true if inside."); - EXPECT(Utils::isUInt12(4096) == false, "Utils::isUInt12<> should return false if outside."); - EXPECT(Utils::isUInt12(-1) == false, "Utils::isUInt12<> should return false if negative."); + INFO("Utils::isUInt32()"); + EXPECT(Utils::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == true, "Utils::isUInt32 should return true if inside"); + EXPECT(Utils::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false, "Utils::isUInt32 should return false if outside"); + EXPECT(Utils::isUInt32(-1) == false, "Utils::isUInt32 should return false if negative"); - INFO("Utils::isUInt16()."); - EXPECT(Utils::isUInt16(0) == true , "Utils::isUInt16<> should return true if inside."); - EXPECT(Utils::isUInt16(65535) == true , "Utils::isUInt16<> should return true if inside."); - EXPECT(Utils::isUInt16(65536) == false, "Utils::isUInt16<> should return false if outside."); - EXPECT(Utils::isUInt16(-1) == false, "Utils::isUInt16<> should return false if negative."); - - INFO("Utils::isUInt32()."); - EXPECT(Utils::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == true, "Utils::isUInt32 should return true if inside."); - EXPECT(Utils::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false, "Utils::isUInt32 should return false if outside."); - EXPECT(Utils::isUInt32(-1) == false, "Utils::isUInt32 should return false if negative."); - - INFO("Utils::isPower2()."); + INFO("Utils::isPower2()"); for (i = 0; i < 64; i++) { EXPECT(Utils::isPowerOf2(static_cast(1) << i) == true, - "Utils::isPower2() didn't report power of 2."); + "Utils::isPower2() didn't report power of 2"); EXPECT(Utils::isPowerOf2((static_cast(1) << i) ^ 0x001101) == false, - "Utils::isPower2() didn't report not power of 2."); + "Utils::isPower2() didn't report not power of 2"); } - INFO("Utils::mask()."); + INFO("Utils::mask()"); for (i = 0; i < 32; i++) { EXPECT(Utils::mask(i) == (1 << i), - "Utils::mask(%u) should return %X.", i, (1 << i)); + "Utils::mask(%u) should return %X", i, (1 << i)); } - INFO("Utils::bits()."); + INFO("Utils::bits()"); for (i = 0; i < 32; i++) { uint32_t expectedBits = 0; @@ -221,19 +108,19 @@ UNIT(base_utils) { expectedBits |= static_cast(1) << b; EXPECT(Utils::bits(i) == expectedBits, - "Utils::bits(%u) should return %X.", i, expectedBits); + "Utils::bits(%u) should return %X", i, expectedBits); } - INFO("Utils::hasBit()."); + INFO("Utils::hasBit()"); for (i = 0; i < 32; i++) { EXPECT(Utils::hasBit((1 << i), i) == true, - "Utils::hasBit(%X, %u) should return true.", (1 << i), i); + "Utils::hasBit(%X, %u) should return true", (1 << i), i); } - INFO("Utils::bitCount()."); + INFO("Utils::bitCount()"); for (i = 0; i < 32; i++) { EXPECT(Utils::bitCount((1 << i)) == 1, - "Utils::bitCount(%X) should return true.", (1 << i)); + "Utils::bitCount(%X) should return true", (1 << i)); } EXPECT(Utils::bitCount(0x000000F0) == 4, ""); EXPECT(Utils::bitCount(0x10101010) == 4, ""); @@ -241,40 +128,40 @@ UNIT(base_utils) { EXPECT(Utils::bitCount(0xFFFFFFF7) == 31, ""); EXPECT(Utils::bitCount(0x7FFFFFFF) == 31, ""); - INFO("Utils::findFirstBit()."); + INFO("Utils::findFirstBit()"); for (i = 0; i < 32; i++) { EXPECT(Utils::findFirstBit((1 << i)) == i, - "Utils::findFirstBit(%X) should return %u.", (1 << i), i); + "Utils::findFirstBit(%X) should return %u", (1 << i), i); } - INFO("Utils::keepNOnesFromRight()."); + INFO("Utils::keepNOnesFromRight()"); EXPECT(Utils::keepNOnesFromRight(0xF, 1) == 0x1, ""); EXPECT(Utils::keepNOnesFromRight(0xF, 2) == 0x3, ""); EXPECT(Utils::keepNOnesFromRight(0xF, 3) == 0x7, ""); EXPECT(Utils::keepNOnesFromRight(0x5, 2) == 0x5, ""); EXPECT(Utils::keepNOnesFromRight(0xD, 2) == 0x5, ""); - INFO("Utils::isAligned()."); + INFO("Utils::isAligned()"); EXPECT(Utils::isAligned(0xFFFF, 4) == false, ""); EXPECT(Utils::isAligned(0xFFF4, 4) == true , ""); EXPECT(Utils::isAligned(0xFFF8, 8) == true , ""); EXPECT(Utils::isAligned(0xFFF0, 16) == true , ""); - INFO("Utils::alignTo()."); + INFO("Utils::alignTo()"); EXPECT(Utils::alignTo(0xFFFF, 4) == 0x10000, ""); EXPECT(Utils::alignTo(0xFFF4, 4) == 0x0FFF4, ""); EXPECT(Utils::alignTo(0xFFF8, 8) == 0x0FFF8, ""); EXPECT(Utils::alignTo(0xFFF0, 16) == 0x0FFF0, ""); EXPECT(Utils::alignTo(0xFFF0, 32) == 0x10000, ""); - INFO("Utils::alignToPowerOf2()."); + INFO("Utils::alignToPowerOf2()"); EXPECT(Utils::alignToPowerOf2(0xFFFF) == 0x10000, ""); EXPECT(Utils::alignToPowerOf2(0xF123) == 0x10000, ""); EXPECT(Utils::alignToPowerOf2(0x0F00) == 0x01000, ""); EXPECT(Utils::alignToPowerOf2(0x0100) == 0x00100, ""); EXPECT(Utils::alignToPowerOf2(0x1001) == 0x02000, ""); - INFO("Utils::alignDiff()."); + INFO("Utils::alignDiff()"); EXPECT(Utils::alignDiff(0xFFFF, 4) == 1, ""); EXPECT(Utils::alignDiff(0xFFF4, 4) == 0, ""); EXPECT(Utils::alignDiff(0xFFF8, 8) == 0, ""); @@ -286,4 +173,4 @@ UNIT(base_utils) { } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/utils.h b/src/asmjit/base/utils.h index 5c850bf..4568e92 100644 --- a/src/asmjit/base/utils.h +++ b/src/asmjit/base/utils.h @@ -16,7 +16,7 @@ #endif // ASMJIT_OS_WINDOWS // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { @@ -30,9 +30,7 @@ namespace asmjit { //! \internal //! \{ template -struct IntTraitsPrivate { - // Let it fail if not specialized! -}; +struct IntTraitsPrivate {}; // Let it fail if not specialized! template<> struct IntTraitsPrivate<1, 0> { typedef int IntType; typedef int8_t SignedType; typedef uint8_t UnsignedType; }; template<> struct IntTraitsPrivate<1, 1> { typedef int IntType; typedef int8_t SignedType; typedef uint8_t UnsignedType; }; @@ -50,15 +48,13 @@ template<> struct IntTraitsPrivate<8, 1> { typedef int64_t IntType; typedef int6 template struct IntTraits { enum { - kIsSigned = static_cast(~static_cast(0)) < static_cast(0), + kIsSigned = static_cast(~static_cast(0)) < static_cast(0), kIsUnsigned = !kIsSigned, - - kIs8Bit = sizeof(T) == 1, - kIs16Bit = sizeof(T) == 2, - kIs32Bit = sizeof(T) == 4, - kIs64Bit = sizeof(T) == 8, - - kIsIntPtr = sizeof(T) == sizeof(intptr_t) + kIs8Bit = sizeof(T) == 1, + kIs16Bit = sizeof(T) == 2, + kIs32Bit = sizeof(T) == 4, + kIs64Bit = sizeof(T) == 8, + kIsIntPtr = sizeof(T) == sizeof(intptr_t) }; typedef typename IntTraitsPrivate::IntType IntType; @@ -67,18 +63,12 @@ struct IntTraits { //! Get a minimum value of `T`. static ASMJIT_INLINE T minValue() noexcept { - if (kIsSigned) - return static_cast((~static_cast(0) >> 1) + static_cast(1)); - else - return static_cast(0); + return kIsSigned ? T((~static_cast(0) >> 1) + static_cast(1)) : T(0); } //! Get a maximum value of `T`. static ASMJIT_INLINE T maxValue() noexcept { - if (kIsSigned) - return static_cast(~static_cast(0) >> 1); - else - return ~static_cast(0); + return kIsSigned ? T(~static_cast(0) >> 1) : ~T(0); } }; @@ -120,17 +110,9 @@ struct Utils { // [Pack / Unpack] // -------------------------------------------------------------------------- - //! Pack two 8-bit integer and one 16-bit integer into a 32-bit integer as it - //! is an array of `{b0,b1,w2}`. - static ASMJIT_INLINE uint32_t pack32_2x8_1x16(uint32_t b0, uint32_t b1, uint32_t w2) noexcept { - return ASMJIT_ARCH_LE ? b0 + (b1 << 8) + (w2 << 16) - : (b0 << 24) + (b1 << 16) + w2; - } - //! Pack four 8-bit integer into a 32-bit integer as it is an array of `{b0,b1,b2,b3}`. static ASMJIT_INLINE uint32_t pack32_4x8(uint32_t b0, uint32_t b1, uint32_t b2, uint32_t b3) noexcept { - return ASMJIT_ARCH_LE ? b0 + (b1 << 8) + (b2 << 16) + (b3 << 24) - : (b0 << 24) + (b1 << 16) + (b2 << 8) + b3; + return ASMJIT_PACK32_4x8(b0, b1, b2, b3); } //! Pack two 32-bit integer into a 64-bit integer as it is an array of `{u0,u1}`. @@ -151,19 +133,41 @@ struct Utils { } // -------------------------------------------------------------------------- - // [Min/Max] + // [Lower/Upper] // -------------------------------------------------------------------------- - // Some environments declare `min()` and `max()` as preprocessor macros so it - // was decided to use different names to prevent such collision. - - //! Get minimum value of `a` and `b`. template - static ASMJIT_INLINE T iMin(const T& a, const T& b) noexcept { return a < b ? a : b; } - - //! Get maximum value of `a` and `b`. + static ASMJIT_INLINE T toLower(T c) noexcept { return c ^ (static_cast(c >= T('A') && c <= T('Z')) << 5); } template - static ASMJIT_INLINE T iMax(const T& a, const T& b) noexcept { return a > b ? a : b; } + static ASMJIT_INLINE T toUpper(T c) noexcept { return c ^ (static_cast(c >= T('a') && c <= T('z')) << 5); } + + // -------------------------------------------------------------------------- + // [Hash] + // -------------------------------------------------------------------------- + + // \internal + static ASMJIT_INLINE uint32_t hashRound(uint32_t hash, uint32_t c) noexcept { return hash * 65599 + c; } + + // Get a hash of the given string `str` of `len` length. Length must be valid + // as this function doesn't check for a null terminator and allows it in the + // middle of the string. + static ASMJIT_INLINE uint32_t hashString(const char* str, size_t len) noexcept { + uint32_t hVal = 0; + for (uint32_t i = 0; i < len; i++) + hVal = hashRound(hVal, str[i]); + return hVal; + } + + // -------------------------------------------------------------------------- + // [Swap] + // -------------------------------------------------------------------------- + + template + static ASMJIT_INLINE void swap(T& a, T& b) noexcept { + T tmp = a; + a = b; + b = tmp; + } // -------------------------------------------------------------------------- // [InInterval] @@ -179,9 +183,9 @@ struct Utils { // [AsInt] // -------------------------------------------------------------------------- - //! Map an integer `x` of type `T` to an `int` or `int64_t`, depending on the - //! type. Used internally by AsmJit to dispatch an argument that can be an - //! arbitrary integer type into a function that accepts either `int` or + //! Map an integer `x` of type `T` to `int` or `int64_t` depending on the + //! type. Used internally by AsmJit to dispatch arguments that can be of + //! arbitrary integer type into a function argument that is either `int` or //! `int64_t`. template static ASMJIT_INLINE typename IntTraits::IntType asInt(T x) noexcept { @@ -467,15 +471,22 @@ struct Utils { // [Alignment] // -------------------------------------------------------------------------- - template - static ASMJIT_INLINE bool isAligned(T base, T alignment) noexcept { - return (base % alignment) == 0; + template + static ASMJIT_INLINE bool isAligned(X base, Y alignment) noexcept { + typedef typename IntTraitsPrivate::UnsignedType U; + return ((U)base % (U)alignment) == 0; } - //! Align `base` to `alignment`. - template - static ASMJIT_INLINE T alignTo(T base, T alignment) noexcept { - return (base + (alignment - 1)) & ~(alignment - 1); + template + static ASMJIT_INLINE X alignTo(X x, Y alignment) noexcept { + typedef typename IntTraitsPrivate::UnsignedType U; + return (X)( ((U)x + (U)(alignment - 1)) & ~(static_cast(alignment) - 1) ); + } + + //! Get delta required to align `base` to `alignment`. + template + static ASMJIT_INLINE X alignDiff(X base, Y alignment) noexcept { + return alignTo(base, alignment) - base; } template @@ -505,12 +516,6 @@ struct Utils { return base + 1; } - //! Get delta required to align `base` to `alignment`. - template - static ASMJIT_INLINE T alignDiff(T base, T alignment) noexcept { - return alignTo(base, alignment) - base; - } - // -------------------------------------------------------------------------- // [String] // -------------------------------------------------------------------------- @@ -523,6 +528,33 @@ struct Utils { return i; } + static ASMJIT_INLINE const char* findPackedString(const char* p, uint32_t id) noexcept { + uint32_t i = 0; + while (i < id) { + while (p[0]) + p++; + p++; + i++; + } + return p; + } + + //! \internal + //! + //! Compare two instruction names. + //! + //! `a` is a null terminated instruction name from `???InstDB::nameData[]` table. + //! `b` is a non-null terminated instruction name passed to `???Inst::getIdByName()`. + static ASMJIT_INLINE int cmpInstName(const char* a, const char* b, size_t len) noexcept { + for (size_t i = 0; i < len; i++) { + int c = static_cast(static_cast(a[i])) - + static_cast(static_cast(b[i])) ; + if (c != 0) return c; + } + + return static_cast(a[len]); + } + // -------------------------------------------------------------------------- // [BSwap] // -------------------------------------------------------------------------- @@ -968,13 +1000,6 @@ struct Utils { static ASMJIT_INLINE void writeI64a(void* p, int64_t x) noexcept { writeI64x<8>(p, x); } static ASMJIT_INLINE void writeI64u(void* p, int64_t x) noexcept { writeI64x<0>(p, x); } - - // -------------------------------------------------------------------------- - // [GetTickCount] - // -------------------------------------------------------------------------- - - //! Get the current CPU tick count, used for benchmarking (1ms resolution). - static ASMJIT_API uint32_t getTickCount() noexcept; }; // ============================================================================ @@ -1237,104 +1262,30 @@ union UInt64 { // [Members] // -------------------------------------------------------------------------- - //! 64-bit unsigned value. - uint64_t u64; + int8_t i8[8]; //!< 8-bit signed integer (8x). + uint8_t u8[8]; //!< 8-bit unsigned integer (8x). - uint32_t u32[2]; - uint16_t u16[4]; - uint8_t u8[8]; + int16_t i16[4]; //!< 16-bit signed integer (4x). + uint16_t u16[4]; //!< 16-bit unsigned integer (4x). + + int32_t i32[2]; //!< 32-bit signed integer (2x). + uint32_t u32[2]; //!< 32-bit unsigned integer (2x). + + int64_t i64; //!< 64-bit signed integer. + uint64_t u64; //!< 64-bit unsigned integer. + + float f32[2]; //!< 32-bit floating point (2x). + double f64; //!< 64-bit floating point. - struct { #if ASMJIT_ARCH_LE - uint32_t lo, hi; + struct { float f32Lo, f32Hi; }; + struct { int32_t i32Lo, i32Hi; }; + struct { uint32_t u32Lo, u32Hi; }; #else - uint32_t hi, lo; + struct { float f32Hi, f32Lo; }; + struct { int32_t i32Hi, i32Lo; }; + struct { uint32_t u32Hi, u32Lo; }; #endif // ASMJIT_ARCH_LE - }; -}; - -// ============================================================================ -// [asmjit::Lock] -// ============================================================================ - -//! \internal -//! -//! Lock. -struct Lock { - ASMJIT_NO_COPY(Lock) - - // -------------------------------------------------------------------------- - // [Windows] - // -------------------------------------------------------------------------- - -#if ASMJIT_OS_WINDOWS - typedef CRITICAL_SECTION Handle; - - //! Create a new `Lock` instance. - ASMJIT_INLINE Lock() noexcept { InitializeCriticalSection(&_handle); } - //! Destroy the `Lock` instance. - ASMJIT_INLINE ~Lock() noexcept { DeleteCriticalSection(&_handle); } - - //! Lock. - ASMJIT_INLINE void lock() noexcept { EnterCriticalSection(&_handle); } - //! Unlock. - ASMJIT_INLINE void unlock() noexcept { LeaveCriticalSection(&_handle); } -#endif // ASMJIT_OS_WINDOWS - - // -------------------------------------------------------------------------- - // [Posix] - // -------------------------------------------------------------------------- - -#if ASMJIT_OS_POSIX - typedef pthread_mutex_t Handle; - - //! Create a new `Lock` instance. - ASMJIT_INLINE Lock() noexcept { pthread_mutex_init(&_handle, nullptr); } - //! Destroy the `Lock` instance. - ASMJIT_INLINE ~Lock() noexcept { pthread_mutex_destroy(&_handle); } - - //! Lock. - ASMJIT_INLINE void lock() noexcept { pthread_mutex_lock(&_handle); } - //! Unlock. - ASMJIT_INLINE void unlock() noexcept { pthread_mutex_unlock(&_handle); } -#endif // ASMJIT_OS_POSIX - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Native handle. - Handle _handle; -}; - -// ============================================================================ -// [asmjit::AutoLock] -// ============================================================================ - -//! \internal -//! -//! Scoped lock. -struct AutoLock { - ASMJIT_NO_COPY(AutoLock) - - // -------------------------------------------------------------------------- - // [Construction / Destruction] - // -------------------------------------------------------------------------- - - ASMJIT_INLINE AutoLock(Lock& target) noexcept : _target(target) { - _target.lock(); - } - - ASMJIT_INLINE ~AutoLock() noexcept { - _target.unlock(); - } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- - - //! Reference to the `Lock`. - Lock& _target; }; //! \} @@ -1342,7 +1293,7 @@ struct AutoLock { } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" // [Guard] #endif // _ASMJIT_BASE_UTILS_H diff --git a/src/asmjit/base/vmem.cpp b/src/asmjit/base/vmem.cpp index 7a77516..553032a 100644 --- a/src/asmjit/base/vmem.cpp +++ b/src/asmjit/base/vmem.cpp @@ -8,33 +8,26 @@ #define ASMJIT_EXPORTS // [Dependencies] -#include "../base/globals.h" +#include "../base/osutils.h" +#include "../base/utils.h" #include "../base/vmem.h" -#if ASMJIT_OS_POSIX -# include -# include -# include -#endif // ASMJIT_OS_POSIX - // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" // This file contains implementation of virtual memory management for AsmJit -// library. The initial concept is to keep this implementation simple but -// efficient. There are several goals I decided to write implementation myself. -// -// Goals: +// library. There are several goals I decided to write implementation myself: // // - Granularity of allocated blocks is different than granularity for a typical -// C malloc. It is at least 64-bytes so Assembler/Compiler can guarantee the -// alignment required. Alignment requirements can grow in the future, but at -// the moment 64 bytes is safe (we may jump to 128 bytes if necessary or make -// it configurable). +// C malloc. It is at least 64-bytes so CodeEmitter can guarantee the alignment +// up to 64 bytes, which is the size of a cache-line and it's also required by +// AVX-512 aligned loads and stores. Alignment requirements can grow in the future, +// but at the moment 64 bytes is safe (we may jump to 128 bytes if necessary or +// make it configurable). // // - Keep memory manager information outside of the allocated virtual memory -// pages, because these pages allow executing of machine code and there should -// not be data required to keep track of these blocks. Another reason is that +// pages, because these pages allow machine code execution and there should +// be not data required to keep track of these blocks. Another reason is that // some environments (i.e. iOS) allow to generate and run JIT code, but this // code has to be set to [Executable, but not Writable]. // @@ -44,7 +37,7 @@ // information related to allocated and unused blocks of memory. The size of // a block is described by `MemNode::density`. Count of blocks is stored in // `MemNode::blocks`. For example if density is 64 and count of blocks is 20, -// memory node contains 64*20 bytes of memory and smallest possible allocation +// memory node contains 64*20 bytes of memory and the smallest possible allocation // (and also alignment) is 64 bytes. So density is also related to memory // alignment. Binary trees (RB) are used to enable fast lookup into all addresses // allocated by memory manager instance. This is used mainly by `VMemPrivate::release()`. @@ -61,162 +54,6 @@ namespace asmjit { -// ============================================================================ -// [asmjit::VMemUtil - Windows] -// ============================================================================ - -// Windows specific implementation using `VirtualAllocEx` and `VirtualFree`. -#if ASMJIT_OS_WINDOWS -struct VMemLocal { - // AsmJit allows to pass a `nullptr` handle to `VMemUtil`. This function is - // just a convenient way to convert such handle to the current process one. - ASMJIT_INLINE HANDLE getSafeProcessHandle(HANDLE hParam) const noexcept { - return hParam != nullptr ? hParam : hProcess; - } - - size_t pageSize; - size_t pageGranularity; - HANDLE hProcess; -}; -static VMemLocal vMemLocal; - -static const VMemLocal& vMemGet() noexcept { - VMemLocal& vMem = vMemLocal; - - if (!vMem.hProcess) { - SYSTEM_INFO info; - ::GetSystemInfo(&info); - - vMem.pageSize = Utils::alignToPowerOf2(info.dwPageSize); - vMem.pageGranularity = info.dwAllocationGranularity; - - vMem.hProcess = ::GetCurrentProcess(); - } - - return vMem; -}; - -size_t VMemUtil::getPageSize() noexcept { - const VMemLocal& vMem = vMemGet(); - return vMem.pageSize; -} - -size_t VMemUtil::getPageGranularity() noexcept { - const VMemLocal& vMem = vMemGet(); - return vMem.pageGranularity; -} - -void* VMemUtil::alloc(size_t length, size_t* allocated, uint32_t flags) noexcept { - return allocProcessMemory(static_cast(0), length, allocated, flags); -} - -void* VMemUtil::allocProcessMemory(HANDLE hProcess, size_t length, size_t* allocated, uint32_t flags) noexcept { - if (length == 0) - return nullptr; - - const VMemLocal& vMem = vMemGet(); - hProcess = vMem.getSafeProcessHandle(hProcess); - - // VirtualAlloc rounds allocated size to a page size automatically. - size_t mSize = Utils::alignTo(length, vMem.pageSize); - - // Windows XP SP2 / Vista allow Data Excution Prevention (DEP). - DWORD protectFlags = 0; - - if (flags & kVMemFlagExecutable) - protectFlags |= (flags & kVMemFlagWritable) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; - else - protectFlags |= (flags & kVMemFlagWritable) ? PAGE_READWRITE : PAGE_READONLY; - - LPVOID mBase = ::VirtualAllocEx(hProcess, nullptr, mSize, MEM_COMMIT | MEM_RESERVE, protectFlags); - if (mBase == nullptr) - return nullptr; - - ASMJIT_ASSERT(Utils::isAligned( - reinterpret_cast(mBase), vMem.pageSize)); - - if (allocated != nullptr) - *allocated = mSize; - return mBase; -} - -Error VMemUtil::release(void* addr, size_t length) noexcept { - return releaseProcessMemory(static_cast(0), addr, length); -} - -Error VMemUtil::releaseProcessMemory(HANDLE hProcess, void* addr, size_t /* length */) noexcept { - hProcess = vMemGet().getSafeProcessHandle(hProcess); - if (!::VirtualFreeEx(hProcess, addr, 0, MEM_RELEASE)) - return kErrorInvalidState; - return kErrorOk; -} -#endif // ASMJIT_OS_WINDOWS - -// ============================================================================ -// [asmjit::VMemUtil - Posix] -// ============================================================================ - -// Posix specific implementation using `mmap` and `munmap`. -#if ASMJIT_OS_POSIX - -// MacOS uses MAP_ANON instead of MAP_ANONYMOUS. -#if !defined(MAP_ANONYMOUS) -# define MAP_ANONYMOUS MAP_ANON -#endif // MAP_ANONYMOUS - -struct VMemLocal { - size_t pageSize; - size_t pageGranularity; -}; -static VMemLocal vMemLocal; - -static const VMemLocal& vMemGet() noexcept { - VMemLocal& vMem = vMemLocal; - - if (!vMem.pageSize) { - size_t pageSize = ::getpagesize(); - vMem.pageSize = pageSize; - vMem.pageGranularity = Utils::iMax(pageSize, 65536); - } - - return vMem; -}; - -size_t VMemUtil::getPageSize() noexcept { - const VMemLocal& vMem = vMemGet(); - return vMem.pageSize; -} - -size_t VMemUtil::getPageGranularity() noexcept { - const VMemLocal& vMem = vMemGet(); - return vMem.pageGranularity; -} - -void* VMemUtil::alloc(size_t length, size_t* allocated, uint32_t flags) noexcept { - const VMemLocal& vMem = vMemGet(); - size_t msize = Utils::alignTo(length, vMem.pageSize); - int protection = PROT_READ; - - if (flags & kVMemFlagWritable ) protection |= PROT_WRITE; - if (flags & kVMemFlagExecutable) protection |= PROT_EXEC; - - void* mbase = ::mmap(nullptr, msize, protection, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (mbase == MAP_FAILED) - return nullptr; - - if (allocated != nullptr) - *allocated = msize; - return mbase; -} - -Error VMemUtil::release(void* addr, size_t length) noexcept { - if (::munmap(addr, length) != 0) - return kErrorInvalidState; - - return kErrorOk; -} -#endif // ASMJIT_OS_POSIX - // ============================================================================ // [asmjit::VMemMgr - BitOps] // ============================================================================ @@ -225,9 +62,7 @@ Error VMemUtil::release(void* addr, size_t length) noexcept { #define M_MOD(x, y) ((x) % (y)) //! \internal -enum { - kBitsPerEntity = (sizeof(size_t) * 8) -}; +enum { kBitsPerEntity = (sizeof(size_t) * 8) }; //! \internal //! @@ -278,33 +113,29 @@ struct VMemMgr::RbNode { // Implementation is based on article by Julienne Walker (Public Domain), // including C code and original comments. Thanks for the excellent article. - // Left[0] and right[1] nodes. - RbNode* node[2]; - // Virtual memory address. - uint8_t* mem; - // Whether the node is RED. - uint32_t red; + RbNode* node[2]; //!< Left[0] and right[1] nodes. + uint8_t* mem; //!< Virtual memory address. + uint32_t red; //!< Node color (red vs. black). }; //! \internal //! -//! Get whether the node is red (nullptr or node with red flag). +//! Get if the node is red (nullptr or node with red flag). static ASMJIT_INLINE bool rbIsRed(RbNode* node) noexcept { - return node != nullptr && node->red; + return node && node->red; } //! \internal //! //! Check whether the RB tree is valid. static int rbAssert(RbNode* root) noexcept { - if (root == nullptr) - return 1; + if (!root) return 1; RbNode* ln = root->node[0]; RbNode* rn = root->node[1]; // Red violation. - ASMJIT_ASSERT( !(rbIsRed(root) && (rbIsRed(ln) || rbIsRed(rn))) ); + ASMJIT_ASSERT(!(rbIsRed(root) && (rbIsRed(ln) || rbIsRed(rn)))); int lh = rbAssert(ln); int rh = rbAssert(rn); @@ -314,7 +145,7 @@ static int rbAssert(RbNode* root) noexcept { ASMJIT_ASSERT(rn == nullptr || rn->mem > root->mem); // Black violation. - ASMJIT_ASSERT( !(lh != 0 && rh != 0 && lh != rh) ); + ASMJIT_ASSERT(!(lh != 0 && rh != 0 && lh != rh)); // Only count black links. if (lh != 0 && rh != 0) @@ -351,16 +182,7 @@ static ASMJIT_INLINE RbNode* rbRotateDouble(RbNode* root, int dir) noexcept { // ============================================================================ struct VMemMgr::MemNode : public RbNode { - // -------------------------------------------------------------------------- - // [Helpers] - // -------------------------------------------------------------------------- - - // Get available space. - ASMJIT_INLINE size_t getAvailable() const noexcept { - return size - used; - } - - ASMJIT_INLINE void fillData(MemNode* other) noexcept { + ASMJIT_INLINE void init(MemNode* other) noexcept { mem = other->mem; size = other->size; @@ -373,9 +195,8 @@ struct VMemMgr::MemNode : public RbNode { baCont = other->baCont; } - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- + // Get available space. + ASMJIT_INLINE size_t getAvailable() const noexcept { return size - used; } MemNode* prev; // Prev node in list. MemNode* next; // Next node in list. @@ -398,18 +219,8 @@ struct VMemMgr::MemNode : public RbNode { //! //! Permanent node. struct VMemMgr::PermanentNode { - // -------------------------------------------------------------------------- - // [Helpers] - // -------------------------------------------------------------------------- - //! Get available space. - ASMJIT_INLINE size_t getAvailable() const noexcept { - return size - used; - } - - // -------------------------------------------------------------------------- - // [Members] - // -------------------------------------------------------------------------- + ASMJIT_INLINE size_t getAvailable() const noexcept { return size - used; } PermanentNode* prev; // Pointer to prev chunk or nullptr. uint8_t* mem; // Base pointer (virtual memory address). @@ -425,11 +236,11 @@ struct VMemMgr::PermanentNode { //! //! Helper to avoid `#ifdef`s in the code. ASMJIT_INLINE uint8_t* vMemMgrAllocVMem(VMemMgr* self, size_t size, size_t* vSize) noexcept { - uint32_t flags = kVMemFlagWritable | kVMemFlagExecutable; + uint32_t flags = OSUtils::kVMWritable | OSUtils::kVMExecutable; #if !ASMJIT_OS_WINDOWS - return static_cast(VMemUtil::alloc(size, vSize, flags)); + return static_cast(OSUtils::allocVirtualMemory(size, vSize, flags)); #else - return static_cast(VMemUtil::allocProcessMemory(self->_hProcess, size, vSize, flags)); + return static_cast(OSUtils::allocProcessMemory(self->_hProcess, size, vSize, flags)); #endif } @@ -438,9 +249,9 @@ ASMJIT_INLINE uint8_t* vMemMgrAllocVMem(VMemMgr* self, size_t size, size_t* vSiz //! Helper to avoid `#ifdef`s in the code. ASMJIT_INLINE Error vMemMgrReleaseVMem(VMemMgr* self, void* p, size_t vSize) noexcept { #if !ASMJIT_OS_WINDOWS - return VMemUtil::release(p, vSize); + return OSUtils::releaseVirtualMemory(p, vSize); #else - return VMemUtil::releaseProcessMemory(self->_hProcess, p, vSize); + return OSUtils::releaseProcessMemory(self->_hProcess, p, vSize); #endif } @@ -459,22 +270,19 @@ static bool vMemMgrCheckTree(VMemMgr* self) noexcept { static MemNode* vMemMgrCreateNode(VMemMgr* self, size_t size, size_t density) noexcept { size_t vSize; uint8_t* vmem = vMemMgrAllocVMem(self, size, &vSize); - - // Out of memory. - if (vmem == nullptr) - return nullptr; + if (!vmem) return nullptr; size_t blocks = (vSize / density); size_t bsize = (((blocks + 7) >> 3) + sizeof(size_t) - 1) & ~(size_t)(sizeof(size_t) - 1); - MemNode* node = static_cast(ASMJIT_ALLOC(sizeof(MemNode))); - uint8_t* data = static_cast(ASMJIT_ALLOC(bsize * 2)); + MemNode* node = static_cast(Internal::allocMemory(sizeof(MemNode))); + uint8_t* data = static_cast(Internal::allocMemory(bsize * 2)); // Out of memory. - if (node == nullptr || data == nullptr) { + if (!node || !data) { vMemMgrReleaseVMem(self, vmem, vSize); - if (node) ASMJIT_FREE(node); - if (data) ASMJIT_FREE(data); + if (node) Internal::releaseMemory(node); + if (data) Internal::releaseMemory(data); return nullptr; } @@ -502,7 +310,7 @@ static MemNode* vMemMgrCreateNode(VMemMgr* self, size_t size, size_t density) no } static void vMemMgrInsertNode(VMemMgr* self, MemNode* node) noexcept { - if (self->_root == nullptr) { + if (!self->_root) { // Empty tree case. self->_root = node; } @@ -523,7 +331,7 @@ static void vMemMgrInsertNode(VMemMgr* self, MemNode* node) noexcept { // Search down the tree. for (;;) { - if (q == nullptr) { + if (!q) { // Insert new node at the bottom. q = node; p->node[dir] = node; @@ -549,8 +357,7 @@ static void vMemMgrInsertNode(VMemMgr* self, MemNode* node) noexcept { dir = q->mem < node->mem; // Update helpers. - if (g != nullptr) - t = g; + if (g) t = g; g = p; p = q; @@ -567,7 +374,7 @@ static void vMemMgrInsertNode(VMemMgr* self, MemNode* node) noexcept { // Link with others. node->prev = self->_last; - if (self->_first == nullptr) { + if (!self->_first) { self->_first = node; self->_last = node; self->_optimal = node; @@ -602,7 +409,7 @@ static MemNode* vMemMgrRemoveNode(VMemMgr* self, MemNode* node) noexcept { q->node[1] = self->_root; // Search and push a red down. - while (q->node[dir] != nullptr) { + while (q->node[dir]) { int last = dir; // Update helpers. @@ -623,7 +430,7 @@ static MemNode* vMemMgrRemoveNode(VMemMgr* self, MemNode* node) noexcept { else if (!rbIsRed(q->node[!dir])) { RbNode* s = p->node[!last]; - if (s != nullptr) { + if (s) { if (!rbIsRed(s->node[!last]) && !rbIsRed(s->node[last])) { // Color flip. p->red = 0; @@ -655,15 +462,14 @@ static MemNode* vMemMgrRemoveNode(VMemMgr* self, MemNode* node) noexcept { if (f != q) { ASMJIT_ASSERT(f != &head); - static_cast(f)->fillData(static_cast(q)); + static_cast(f)->init(static_cast(q)); } p->node[p->node[1] == q] = q->node[q->node[0] == nullptr]; // Update root and make it black. self->_root = static_cast(head.node[1]); - if (self->_root != nullptr) - self->_root->red = 0; + if (self->_root) self->_root->red = 0; // Unlink. MemNode* next = static_cast(q)->next; @@ -687,7 +493,7 @@ static MemNode* vMemMgrRemoveNode(VMemMgr* self, MemNode* node) noexcept { static MemNode* vMemMgrFindNodeByPtr(VMemMgr* self, uint8_t* mem) noexcept { MemNode* node = self->_root; - while (node != nullptr) { + while (node) { uint8_t* nodeMem = node->mem; // Go left. @@ -723,23 +529,16 @@ static void* vMemMgrAllocPermanent(VMemMgr* self, size_t vSize) noexcept { node = node->prev; // Or allocate new node. - if (node == nullptr) { + if (!node) { size_t nodeSize = permanentNodeSize; + if (nodeSize < vSize) nodeSize = vSize; - if (nodeSize < vSize) - nodeSize = vSize; - - node = static_cast(ASMJIT_ALLOC(sizeof(PermanentNode))); - - // Out of memory. - if (node == nullptr) - return nullptr; + node = static_cast(Internal::allocMemory(sizeof(PermanentNode))); + if (!node) return nullptr; node->mem = vMemMgrAllocVMem(self, nodeSize, &node->size); - - // Out of memory. - if (node->mem == nullptr) { - ASMJIT_FREE(node); + if (!node->mem) { + Internal::releaseMemory(node); return nullptr; } @@ -848,12 +647,10 @@ static void* vMemMgrAllocFreeable(VMemMgr* self, size_t vSize) noexcept { // allocate a new one. { size_t blockSize = self->_blockSize; - if (blockSize < vSize) - blockSize = vSize; + if (blockSize < vSize) blockSize = vSize; node = vMemMgrCreateNode(self, blockSize, self->_blockDensity); - if (node == nullptr) - return nullptr; + if (!node) return nullptr; // Update binary tree. vMemMgrInsertNode(self, node); @@ -894,14 +691,14 @@ L_Found: static void vMemMgrReset(VMemMgr* self, bool keepVirtualMemory) noexcept { MemNode* node = self->_first; - while (node != nullptr) { + while (node) { MemNode* next = node->next; if (!keepVirtualMemory) vMemMgrReleaseVMem(self, node->mem, node->size); - ASMJIT_FREE(node->baUsed); - ASMJIT_FREE(node); + Internal::releaseMemory(node->baUsed); + Internal::releaseMemory(node); node = next; } @@ -920,13 +717,18 @@ static void vMemMgrReset(VMemMgr* self, bool keepVirtualMemory) noexcept { // ============================================================================ #if !ASMJIT_OS_WINDOWS -VMemMgr::VMemMgr() noexcept +VMemMgr::VMemMgr() noexcept { #else -VMemMgr::VMemMgr(HANDLE hProcess) noexcept - : _hProcess(vMemGet().getSafeProcessHandle(hProcess)) +VMemMgr::VMemMgr(HANDLE hProcess) noexcept { +#endif + + VMemInfo vm = OSUtils::getVirtualMemoryInfo(); + +#if ASMJIT_OS_WINDOWS + _hProcess = hProcess ? hProcess : vm.hCurrentProcess; #endif // ASMJIT_OS_WINDOWS -{ - _blockSize = VMemUtil::getPageGranularity(); + + _blockSize = vm.pageGranularity; _blockDensity = 64; _allocatedBytes = 0; @@ -949,7 +751,7 @@ VMemMgr::~VMemMgr() noexcept { PermanentNode* node = _permanent; while (node) { PermanentNode* prev = node->prev; - ASMJIT_FREE(node); + Internal::releaseMemory(node); node = prev; } } @@ -967,21 +769,18 @@ void VMemMgr::reset() noexcept { // ============================================================================ void* VMemMgr::alloc(size_t size, uint32_t type) noexcept { - if (type == kVMemAllocPermanent) + if (type == kAllocPermanent) return vMemMgrAllocPermanent(this, size); else return vMemMgrAllocFreeable(this, size); } Error VMemMgr::release(void* p) noexcept { - if (p == nullptr) - return kErrorOk; + if (!p) return kErrorOk; AutoLock locked(_lock); MemNode* node = vMemMgrFindNodeByPtr(this, static_cast(p)); - - if (node == nullptr) - return kErrorInvalidArgument; + if (!node) return DebugUtils::errored(kErrorInvalidArgument); size_t offset = (size_t)((uint8_t*)p - (uint8_t*)node->mem); size_t bitpos = M_DIV(offset, node->density); @@ -1043,7 +842,7 @@ Error VMemMgr::release(void* p) noexcept { // Free memory associated with node (this memory is not accessed // anymore so it's safe). vMemMgrReleaseVMem(this, node->mem, node->size); - ASMJIT_FREE(node->baUsed); + Internal::releaseMemory(node->baUsed); node->baUsed = nullptr; node->baCont = nullptr; @@ -1053,7 +852,7 @@ Error VMemMgr::release(void* p) noexcept { // Remove node. This function can return different node than // passed into, but data is copied into previous node if needed. - ASMJIT_FREE(vMemMgrRemoveNode(this, node)); + Internal::releaseMemory(vMemMgrRemoveNode(this, node)); ASMJIT_ASSERT(vMemMgrCheckTree(this)); } @@ -1061,17 +860,13 @@ Error VMemMgr::release(void* p) noexcept { } Error VMemMgr::shrink(void* p, size_t used) noexcept { - if (p == nullptr) - return kErrorOk; - + if (!p) return kErrorOk; if (used == 0) return release(p); AutoLock locked(_lock); - MemNode* node = vMemMgrFindNodeByPtr(this, (uint8_t*)p); - if (node == nullptr) - return kErrorInvalidArgument; + if (!node) return DebugUtils::errored(kErrorInvalidArgument); size_t offset = (size_t)((uint8_t*)p - (uint8_t*)node->mem); size_t bitpos = M_DIV(offset, node->density); @@ -1195,13 +990,13 @@ UNIT(base_vmem) { int i; int kCount = 200000; - INFO("Memory alloc/free test - %d allocations.", static_cast(kCount)); + INFO("Memory alloc/free test - %d allocations", static_cast(kCount)); - void** a = (void**)ASMJIT_ALLOC(sizeof(void*) * kCount); - void** b = (void**)ASMJIT_ALLOC(sizeof(void*) * kCount); + void** a = (void**)Internal::allocMemory(sizeof(void*) * kCount); + void** b = (void**)Internal::allocMemory(sizeof(void*) * kCount); EXPECT(a != nullptr && b != nullptr, - "Couldn't allocate %u bytes on heap.", kCount * 2); + "Couldn't allocate %u bytes on heap", kCount * 2); INFO("Allocating virtual memory..."); for (i = 0; i < kCount; i++) { @@ -1217,21 +1012,21 @@ UNIT(base_vmem) { INFO("Freeing virtual memory..."); for (i = 0; i < kCount; i++) { EXPECT(memmgr.release(a[i]) == kErrorOk, - "Failed to free %p.", b[i]); + "Failed to free %p", b[i]); } VMemTest_stats(memmgr); - INFO("Verified alloc/free test - %d allocations.", static_cast(kCount)); + INFO("Verified alloc/free test - %d allocations", static_cast(kCount)); for (i = 0; i < kCount; i++) { int r = (rand() % 1000) + 4; a[i] = memmgr.alloc(r); EXPECT(a[i] != nullptr, - "Couldn't allocate %d bytes of virtual memory.", r); + "Couldn't allocate %d bytes of virtual memory", r); - b[i] = ASMJIT_ALLOC(r); + b[i] = Internal::allocMemory(r); EXPECT(b[i] != nullptr, - "Couldn't allocate %d bytes on heap.", r); + "Couldn't allocate %d bytes on heap", r); VMemTest_fill(a[i], b[i], r); } @@ -1244,22 +1039,22 @@ UNIT(base_vmem) { for (i = 0; i < kCount / 2; i++) { VMemTest_verify(a[i], b[i]); EXPECT(memmgr.release(a[i]) == kErrorOk, - "Failed to free %p.", a[i]); - ASMJIT_FREE(b[i]); + "Failed to free %p", a[i]); + Internal::releaseMemory(b[i]); } VMemTest_stats(memmgr); - INFO("Alloc again."); + INFO("Alloc again"); for (i = 0; i < kCount / 2; i++) { int r = (rand() % 1000) + 4; a[i] = memmgr.alloc(r); EXPECT(a[i] != nullptr, - "Couldn't allocate %d bytes of virtual memory.", r); + "Couldn't allocate %d bytes of virtual memory", r); - b[i] = ASMJIT_ALLOC(r); + b[i] = Internal::allocMemory(r); EXPECT(b[i] != nullptr, - "Couldn't allocate %d bytes on heap."); + "Couldn't allocate %d bytes on heap"); VMemTest_fill(a[i], b[i], r); } @@ -1269,13 +1064,13 @@ UNIT(base_vmem) { for (i = 0; i < kCount; i++) { VMemTest_verify(a[i], b[i]); EXPECT(memmgr.release(a[i]) == kErrorOk, - "Failed to free %p.", a[i]); - ASMJIT_FREE(b[i]); + "Failed to free %p", a[i]); + Internal::releaseMemory(b[i]); } VMemTest_stats(memmgr); - ASMJIT_FREE(a); - ASMJIT_FREE(b); + Internal::releaseMemory(a); + Internal::releaseMemory(b); } #endif // ASMJIT_TEST diff --git a/src/asmjit/base/vmem.h b/src/asmjit/base/vmem.h index b36031e..6a1a513 100644 --- a/src/asmjit/base/vmem.h +++ b/src/asmjit/base/vmem.h @@ -9,80 +9,17 @@ #define _ASMJIT_BASE_VMEM_H // [Dependencies] -#include "../base/utils.h" +#include "../base/globals.h" +#include "../base/osutils.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { //! \addtogroup asmjit_base //! \{ -// ============================================================================ -// [asmjit::VMemAllocType] -// ============================================================================ - -//! Type of virtual memory allocation, see `VMemMgr::alloc()`. -ASMJIT_ENUM(VMemAllocType) { - //! Normal memory allocation, has to be freed by `VMemMgr::release()`. - kVMemAllocFreeable = 0, - //! Allocate permanent memory, can't be freed. - kVMemAllocPermanent = 1 -}; - -// ============================================================================ -// [asmjit::VMemFlags] -// ============================================================================ - -//! Type of virtual memory allocation, see `VMemMgr::alloc()`. -ASMJIT_ENUM(VMemFlags) { - //! Memory is writable. - kVMemFlagWritable = 0x00000001, - //! Memory is executable. - kVMemFlagExecutable = 0x00000002 -}; - -// ============================================================================ -// [asmjit::VMemUtil] -// ============================================================================ - -//! Virtual memory utilities. -//! -//! Defines functions that provide facility to allocate and free memory that is -//! executable in a platform independent manner. If both the processor and host -//! operating system support data-execution-prevention then the only way how to -//! run machine code is to allocate it to a memory that has marked as executable. -//! VMemUtil is just unified interface to platform dependent APIs. -//! -//! `VirtualAlloc()` function is used on Windows operating system and `mmap()` -//! on POSIX. `VirtualAlloc()` and `mmap()` documentation provide a detailed -//! overview on how to use a platform specific APIs. -struct VMemUtil { - //! Get a size/alignment of a single virtual memory page. - static ASMJIT_API size_t getPageSize() noexcept; - - //! Get a recommended granularity for a single `alloc` call. - static ASMJIT_API size_t getPageGranularity() noexcept; - - //! Allocate virtual memory. - //! - //! Pages are readable/writeable, but they are not guaranteed to be - //! executable unless 'canExecute' is true. Returns the address of - //! allocated memory, or `nullptr` on failure. - static ASMJIT_API void* alloc(size_t length, size_t* allocated, uint32_t flags) noexcept; - //! Free memory allocated by `alloc()`. - static ASMJIT_API Error release(void* addr, size_t length) noexcept; - -#if ASMJIT_OS_WINDOWS - //! Allocate virtual memory of `hProcess` (Windows only). - static ASMJIT_API void* allocProcessMemory(HANDLE hProcess, size_t length, size_t* allocated, uint32_t flags) noexcept; - - //! Release virtual memory of `hProcess` (Windows only). - static ASMJIT_API Error releaseProcessMemory(HANDLE hProcess, void* addr, size_t length) noexcept; -#endif // ASMJIT_OS_WINDOWS -}; - // ============================================================================ // [asmjit::VMemMgr] // ============================================================================ @@ -90,7 +27,15 @@ struct VMemUtil { //! Reference implementation of memory manager that uses `VMemUtil` to allocate //! chunks of virtual memory and bit arrays to manage it. class VMemMgr { - public: +public: + //! Type of virtual memory allocation, see `VMemMgr::alloc()`. + ASMJIT_ENUM(AllocType) { + //! Normal memory allocation, has to be freed by `VMemMgr::release()`. + kAllocFreeable = 0, + //! Allocate permanent memory, can't be freed. + kAllocPermanent = 1 + }; + // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- @@ -123,30 +68,19 @@ class VMemMgr { #if ASMJIT_OS_WINDOWS //! Get the handle of the process memory manager is bound to. - ASMJIT_INLINE HANDLE getProcessHandle() const noexcept { - return _hProcess; - } + ASMJIT_INLINE HANDLE getProcessHandle() const noexcept { return _hProcess; } #endif // ASMJIT_OS_WINDOWS //! Get how many bytes are currently allocated. - ASMJIT_INLINE size_t getAllocatedBytes() const noexcept { - return _allocatedBytes; - } - + ASMJIT_INLINE size_t getAllocatedBytes() const noexcept { return _allocatedBytes; } //! Get how many bytes are currently used. - ASMJIT_INLINE size_t getUsedBytes() const noexcept { - return _usedBytes; - } + ASMJIT_INLINE size_t getUsedBytes() const noexcept { return _usedBytes; } //! Get whether to keep allocated memory after the `VMemMgr` is destroyed. //! //! \sa \ref setKeepVirtualMemory. - ASMJIT_INLINE bool getKeepVirtualMemory() const noexcept { - return _keepVirtualMemory; - } - - //! Set whether to keep allocated memory after memory manager is - //! destroyed. + ASMJIT_INLINE bool getKeepVirtualMemory() const noexcept { return _keepVirtualMemory; } + //! Set whether to keep allocated memory after the memory manager is destroyed. //! //! This method is usable when patching code of remote process. You need to //! allocate process memory, store generated assembler into it and patch the @@ -154,12 +88,10 @@ class VMemMgr { //! VMemMgr destructor. After destruction all internal //! structures are freed, only the process virtual memory remains. //! - //! NOTE: Memory allocated with kVMemAllocPermanent is always kept. + //! NOTE: Memory allocated with kAllocPermanent is always kept. //! //! \sa \ref getKeepVirtualMemory. - ASMJIT_INLINE void setKeepVirtualMemory(bool keepVirtualMemory) noexcept { - _keepVirtualMemory = keepVirtualMemory; - } + ASMJIT_INLINE void setKeepVirtualMemory(bool val) noexcept { _keepVirtualMemory = val; } // -------------------------------------------------------------------------- // [Alloc / Release] @@ -170,11 +102,9 @@ class VMemMgr { //! Note that if you are implementing your own virtual memory manager then you //! can quitly ignore type of allocation. This is mainly for AsmJit to memory //! manager that allocated memory will be never freed. - ASMJIT_API void* alloc(size_t size, uint32_t type = kVMemAllocFreeable) noexcept; - + ASMJIT_API void* alloc(size_t size, uint32_t type = kAllocFreeable) noexcept; //! Free previously allocated memory at a given `address`. ASMJIT_API Error release(void* p) noexcept; - //! Free extra memory allocated with `p`. ASMJIT_API Error shrink(void* p, size_t used) noexcept; @@ -183,25 +113,16 @@ class VMemMgr { // -------------------------------------------------------------------------- #if ASMJIT_OS_WINDOWS - //! Process passed to `VirtualAllocEx` and `VirtualFree`. - HANDLE _hProcess; + HANDLE _hProcess; //!< Process passed to `VirtualAllocEx` and `VirtualFree`. #endif // ASMJIT_OS_WINDOWS + Lock _lock; //!< Lock to enable thread-safe functionality. - //! Lock to enable thread-safe functionality. - Lock _lock; + size_t _blockSize; //!< Default block size. + size_t _blockDensity; //!< Default block density. + bool _keepVirtualMemory; //!< Keep virtual memory after destroyed. - //! Default block size. - size_t _blockSize; - //! Default block density. - size_t _blockDensity; - - // Whether to keep virtual memory after destroy. - bool _keepVirtualMemory; - - //! How many bytes are currently allocated. - size_t _allocatedBytes; - //! How many bytes are currently used. - size_t _usedBytes; + size_t _allocatedBytes; //!< How many bytes are currently allocated. + size_t _usedBytes; //!< How many bytes are currently used. //! \internal //! \{ @@ -227,7 +148,7 @@ class VMemMgr { } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" // [Guard] #endif // _ASMJIT_BASE_VMEM_H diff --git a/src/asmjit/base/zone.cpp b/src/asmjit/base/zone.cpp index bef1a8a..6dd535d 100644 --- a/src/asmjit/base/zone.cpp +++ b/src/asmjit/base/zone.cpp @@ -10,26 +10,39 @@ // [Dependencies] #include "../base/utils.h" #include "../base/zone.h" -#include // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { //! Zero size block used by `Zone` that doesn't have any memory allocated. -static const Zone::Block Zone_zeroBlock = { - nullptr, nullptr, nullptr, nullptr, { 0 } -}; +static const Zone::Block Zone_zeroBlock = { nullptr, nullptr, 0, { 0 } }; + +static ASMJIT_INLINE uint32_t Zone_getAlignmentOffsetFromAlignment(uint32_t x) noexcept { + switch (x) { + default: return 0; + case 0 : return 0; + case 1 : return 0; + case 2 : return 1; + case 4 : return 2; + case 8 : return 3; + case 16: return 4; + case 32: return 5; + case 64: return 6; + } +} // ============================================================================ // [asmjit::Zone - Construction / Destruction] // ============================================================================ -Zone::Zone(size_t blockSize) noexcept { - _block = const_cast(&Zone_zeroBlock); - _blockSize = blockSize; -} +Zone::Zone(uint32_t blockSize, uint32_t blockAlignment) noexcept + : _ptr(nullptr), + _end(nullptr), + _block(const_cast(&Zone_zeroBlock)), + _blockSize(blockSize), + _blockAlignmentShift(Zone_getAlignmentOffsetFromAlignment(blockAlignment)) {} Zone::~Zone() noexcept { reset(true); @@ -52,24 +65,27 @@ void Zone::reset(bool releaseMemory) noexcept { Block* next = cur->next; do { Block* prev = cur->prev; - ASMJIT_FREE(cur); + Internal::releaseMemory(cur); cur = prev; - } while (cur != nullptr); + } while (cur); cur = next; - while (cur != nullptr) { + while (cur) { next = cur->next; - ASMJIT_FREE(cur); + Internal::releaseMemory(cur); cur = next; } + _ptr = nullptr; + _end = nullptr; _block = const_cast(&Zone_zeroBlock); } else { - while (cur->prev != nullptr) + while (cur->prev) cur = cur->prev; - cur->pos = cur->data; + _ptr = cur->data; + _end = _ptr + cur->size; _block = cur; } } @@ -80,35 +96,47 @@ void Zone::reset(bool releaseMemory) noexcept { void* Zone::_alloc(size_t size) noexcept { Block* curBlock = _block; - size_t blockSize = Utils::iMax(_blockSize, size); + uint8_t* p; + + size_t blockSize = std::max(_blockSize, size); + size_t blockAlignment = getBlockAlignment(); // The `_alloc()` method can only be called if there is not enough space // in the current block, see `alloc()` implementation for more details. - ASMJIT_ASSERT(curBlock == &Zone_zeroBlock || curBlock->getRemainingSize() < size); + ASMJIT_ASSERT(curBlock == &Zone_zeroBlock || getRemainingSize() < size); - // If the `Zone` has been reset the current block doesn't have to be the + // If the `Zone` has been cleared the current block doesn't have to be the // last one. Check if there is a block that can be used instead of allocating // a new one. If there is a `next` block it's completely unused, we don't have // to check for remaining bytes. Block* next = curBlock->next; - if (next != nullptr && next->getBlockSize() >= size) { - next->pos = next->data + size; + if (next && next->size >= size) { + p = Utils::alignTo(next->data, blockAlignment); + _block = next; - return static_cast(next->data); + _ptr = p + size; + _end = next->data + next->size; + + return static_cast(p); } // Prevent arithmetic overflow. - if (blockSize > ~static_cast(0) - sizeof(Block)) + if (ASMJIT_UNLIKELY(blockSize > (~static_cast(0) - sizeof(Block) - blockAlignment))) return nullptr; - Block* newBlock = static_cast(ASMJIT_ALLOC(sizeof(Block) - sizeof(void*) + blockSize)); - if (newBlock == nullptr) + blockSize += blockAlignment; + Block* newBlock = static_cast(Internal::allocMemory(sizeof(Block) + blockSize)); + + if (ASMJIT_UNLIKELY(!newBlock)) return nullptr; - newBlock->pos = newBlock->data + size; - newBlock->end = newBlock->data + blockSize; + // Align the pointer to `blockAlignment` and adjust the size of this block + // accordingly. It's the same as using `blockAlignment - Utils::alignDiff()`, + // just written differently. + p = Utils::alignTo(newBlock->data, blockAlignment); newBlock->prev = nullptr; newBlock->next = nullptr; + newBlock->size = blockSize; if (curBlock != &Zone_zeroBlock) { newBlock->prev = curBlock; @@ -117,62 +145,40 @@ void* Zone::_alloc(size_t size) noexcept { // Does only happen if there is a next block, but the requested memory // can't fit into it. In this case a new buffer is allocated and inserted // between the current block and the next one. - if (next != nullptr) { + if (next) { newBlock->next = next; next->prev = newBlock; } } _block = newBlock; - return static_cast(newBlock->data); + _ptr = p + size; + _end = newBlock->data + blockSize; + + return static_cast(p); } void* Zone::allocZeroed(size_t size) noexcept { void* p = alloc(size); - if (p != nullptr) - ::memset(p, 0, size); - return p; + if (ASMJIT_UNLIKELY(!p)) return p; + return ::memset(p, 0, size); } -void* Zone::dup(const void* data, size_t size) noexcept { - if (data == nullptr) - return nullptr; +void* Zone::dup(const void* data, size_t size, bool nullTerminate) noexcept { + if (ASMJIT_UNLIKELY(!data || !size)) return nullptr; - if (size == 0) - return nullptr; - - void* m = alloc(size); - if (m == nullptr) - return nullptr; + ASMJIT_ASSERT(size != IntTraits::maxValue()); + uint8_t* m = allocT(size + nullTerminate); + if (ASMJIT_UNLIKELY(!m)) return nullptr; ::memcpy(m, data, size); - return m; -} + if (nullTerminate) m[size] = '\0'; -char* Zone::sdup(const char* str) noexcept { - if (str == nullptr) - return nullptr; - - size_t len = ::strlen(str); - if (len == 0) - return nullptr; - - // Include NULL terminator and limit string length. - if (++len > 256) - len = 256; - - char* m = static_cast(alloc(len)); - if (m == nullptr) - return nullptr; - - ::memcpy(m, str, len); - m[len - 1] = '\0'; - return m; + return static_cast(m); } char* Zone::sformat(const char* fmt, ...) noexcept { - if (fmt == nullptr) - return nullptr; + if (ASMJIT_UNLIKELY(!fmt)) return nullptr; char buf[512]; size_t len; @@ -187,7 +193,770 @@ char* Zone::sformat(const char* fmt, ...) noexcept { return static_cast(dup(buf, len)); } +// ============================================================================ +// [asmjit::ZoneHeap - Helpers] +// ============================================================================ + +static bool ZoneHeap_hasDynamicBlock(ZoneHeap* self, ZoneHeap::DynamicBlock* block) noexcept { + ZoneHeap::DynamicBlock* cur = self->_dynamicBlocks; + while (cur) { + if (cur == block) + return true; + cur = cur->next; + } + return false; +} + +// ============================================================================ +// [asmjit::ZoneHeap - Init / Reset] +// ============================================================================ + +void ZoneHeap::reset(Zone* zone) noexcept { + // Free dynamic blocks. + DynamicBlock* block = _dynamicBlocks; + while (block) { + DynamicBlock* next = block->next; + Internal::releaseMemory(block); + block = next; + } + + // Zero the entire class and initialize to the given `zone`. + ::memset(this, 0, sizeof(*this)); + _zone = zone; +} + +// ============================================================================ +// [asmjit::ZoneHeap - Alloc / Release] +// ============================================================================ + +void* ZoneHeap::_alloc(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + + // We use our memory pool only if the requested block is of a reasonable size. + uint32_t slot; + if (_getSlotIndex(size, slot, allocatedSize)) { + // Slot reuse. + uint8_t* p = reinterpret_cast(_slots[slot]); + size = allocatedSize; + + if (p) { + _slots[slot] = reinterpret_cast(p)->next; + //printf("ALLOCATED %p of size %d (SLOT %d)\n", p, int(size), slot); + return p; + } + + // So use Zone to allocate a new chunk for us. But before we use it, we + // check if there is enough room for the new chunk in zone, and if not, + // we redistribute the remaining memory in Zone's current block into slots. + Zone* zone = _zone; + p = Utils::alignTo(zone->getCursor(), kBlockAlignment); + size_t remain = (p <= zone->getEnd()) ? (size_t)(zone->getEnd() - p) : size_t(0); + + if (ASMJIT_LIKELY(remain >= size)) { + zone->setCursor(p + size); + //printf("ALLOCATED %p of size %d (SLOT %d)\n", p, int(size), slot); + return p; + } + else { + // Distribute the remaining memory to suitable slots. + if (remain >= kLoGranularity) { + do { + size_t distSize = std::min(remain, kLoMaxSize); + uint32_t distSlot = static_cast((distSize - kLoGranularity) / kLoGranularity); + ASMJIT_ASSERT(distSlot < kLoCount); + + reinterpret_cast(p)->next = _slots[distSlot]; + _slots[distSlot] = reinterpret_cast(p); + + p += distSize; + remain -= distSize; + } while (remain >= kLoGranularity); + zone->setCursor(p); + } + + p = static_cast(zone->_alloc(size)); + if (ASMJIT_UNLIKELY(!p)) { + allocatedSize = 0; + return nullptr; + } + + //printf("ALLOCATED %p of size %d (SLOT %d)\n", p, int(size), slot); + return p; + } + } + else { + // Allocate a dynamic block. + size_t overhead = sizeof(DynamicBlock) + sizeof(DynamicBlock*) + kBlockAlignment; + + // Handle a possible overflow. + if (ASMJIT_UNLIKELY(overhead >= ~static_cast(0) - size)) + return nullptr; + + void* p = Internal::allocMemory(size + overhead); + if (ASMJIT_UNLIKELY(!p)) { + allocatedSize = 0; + return nullptr; + } + + // Link as first in `_dynamicBlocks` double-linked list. + DynamicBlock* block = static_cast(p); + DynamicBlock* next = _dynamicBlocks; + + if (next) + next->prev = block; + + block->prev = nullptr; + block->next = next; + _dynamicBlocks = block; + + // Align the pointer to the guaranteed alignment and store `DynamicBlock` + // at the end of the memory block, so `_releaseDynamic()` can find it. + p = Utils::alignTo(static_cast(p) + sizeof(DynamicBlock) + sizeof(DynamicBlock*), kBlockAlignment); + reinterpret_cast(p)[-1] = block; + + allocatedSize = size; + //printf("ALLOCATED DYNAMIC %p of size %d\n", p, int(size)); + return p; + } +} + +void* ZoneHeap::_allocZeroed(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + + void* p = _alloc(size, allocatedSize); + if (ASMJIT_UNLIKELY(!p)) return p; + return ::memset(p, 0, allocatedSize); +} + +void ZoneHeap::_releaseDynamic(void* p, size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + //printf("RELEASING DYNAMIC %p of size %d\n", p, int(size)); + + // Pointer to `DynamicBlock` is stored at [-1]. + DynamicBlock* block = reinterpret_cast(p)[-1]; + ASMJIT_ASSERT(ZoneHeap_hasDynamicBlock(this, block)); + + // Unlink and free. + DynamicBlock* prev = block->prev; + DynamicBlock* next = block->next; + + if (prev) + prev->next = next; + else + _dynamicBlocks = next; + + if (next) + next->prev = prev; + + Internal::releaseMemory(block); +} + +// ============================================================================ +// [asmjit::ZoneVectorBase - Helpers] +// ============================================================================ + +Error ZoneVectorBase::_grow(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept { + size_t threshold = Globals::kAllocThreshold / sizeOfT; + size_t capacity = _capacity; + size_t after = _length; + + if (ASMJIT_UNLIKELY(IntTraits::maxValue() - n < after)) + return DebugUtils::errored(kErrorNoHeapMemory); + + after += n; + if (capacity >= after) + return kErrorOk; + + // ZoneVector is used as an array to hold short-lived data structures used + // during code generation. The growing strategy is simple - use small capacity + // at the beginning (very good for ZoneHeap) and then grow quicker to prevent + // successive reallocations. + if (capacity < 4) + capacity = 4; + else if (capacity < 8) + capacity = 8; + else if (capacity < 16) + capacity = 16; + else if (capacity < 64) + capacity = 64; + else if (capacity < 256) + capacity = 256; + + while (capacity < after) { + if (capacity < threshold) + capacity *= 2; + else + capacity += threshold; + } + + return _reserve(heap, sizeOfT, capacity); +} + +Error ZoneVectorBase::_reserve(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept { + size_t oldCapacity = _capacity; + if (oldCapacity >= n) return kErrorOk; + + size_t nBytes = n * sizeOfT; + if (ASMJIT_UNLIKELY(nBytes < n)) + return DebugUtils::errored(kErrorNoHeapMemory); + + size_t allocatedBytes; + uint8_t* newData = static_cast(heap->alloc(nBytes, allocatedBytes)); + + if (ASMJIT_UNLIKELY(!newData)) + return DebugUtils::errored(kErrorNoHeapMemory); + + void* oldData = _data; + if (_length) + ::memcpy(newData, oldData, _length * sizeOfT); + + if (oldData) + heap->release(oldData, oldCapacity * sizeOfT); + + _capacity = allocatedBytes / sizeOfT; + ASMJIT_ASSERT(_capacity >= n); + + _data = newData; + return kErrorOk; +} + +Error ZoneVectorBase::_resize(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept { + size_t length = _length; + if (_capacity < n) { + ASMJIT_PROPAGATE(_grow(heap, sizeOfT, n - length)); + ASMJIT_ASSERT(_capacity >= n); + } + + if (length < n) + ::memset(static_cast(_data) + length * sizeOfT, 0, (n - length) * sizeOfT); + + _length = n; + return kErrorOk; +} + +// ============================================================================ +// [asmjit::ZoneBitVector - Ops] +// ============================================================================ + +Error ZoneBitVector::_resize(ZoneHeap* heap, size_t newLength, size_t idealCapacity, bool newBitsValue) noexcept { + ASMJIT_ASSERT(idealCapacity >= newLength); + + if (newLength <= _length) { + // The size after the resize is lesser than or equal to the current length. + size_t idx = newLength / kBitsPerWord; + size_t bit = newLength % kBitsPerWord; + + // Just set all bits outside of the new length in the last word to zero. + // There is a case that there are not bits to set if `bit` is zero. This + // happens when `newLength` is a multiply of `kBitsPerWord` like 64, 128, + // and so on. In that case don't change anything as that would mean settings + // bits outside of the `_length`. + if (bit) + _data[idx] &= (static_cast(1) << bit) - 1U; + + _length = newLength; + return kErrorOk; + } + + size_t oldLength = _length; + BitWord* data = _data; + + if (newLength > _capacity) { + // Realloc needed... Calculate the minimum capacity (in bytes) requied. + size_t minimumCapacityInBits = Utils::alignTo(idealCapacity, kBitsPerWord); + size_t allocatedCapacity; + + if (ASMJIT_UNLIKELY(minimumCapacityInBits < newLength)) + return DebugUtils::errored(kErrorNoHeapMemory); + + // Normalize to bytes. + size_t minimumCapacity = minimumCapacityInBits / 8; + BitWord* newData = static_cast(heap->alloc(minimumCapacity, allocatedCapacity)); + + if (ASMJIT_UNLIKELY(!newData)) + return DebugUtils::errored(kErrorNoHeapMemory); + + // `allocatedCapacity` now contains number in bytes, we need bits. + size_t allocatedCapacityInBits = allocatedCapacity * 8; + + // Arithmetic overflow should normally not happen. If it happens we just + // change the `allocatedCapacityInBits` to the `minimumCapacityInBits` as + // this value is still safe to be used to call `_heap->release(...)`. + if (ASMJIT_UNLIKELY(allocatedCapacityInBits < allocatedCapacity)) + allocatedCapacityInBits = minimumCapacityInBits; + + if (oldLength) + ::memcpy(newData, data, _wordsPerBits(oldLength)); + + if (data) + heap->release(data, _capacity / 8); + data = newData; + + _data = data; + _capacity = allocatedCapacityInBits; + } + + // Start (of the old length) and end (of the new length) bits + size_t idx = oldLength / kBitsPerWord; + size_t startBit = oldLength % kBitsPerWord; + size_t endBit = newLength % kBitsPerWord; + + // Set new bits to either 0 or 1. The `pattern` is used to set multiple + // bits per bit-word and contains either all zeros or all ones. + BitWord pattern = _patternFromBit(newBitsValue); + + // First initialize the last bit-word of the old length. + if (startBit) { + size_t nBits = 0; + + if (idx == (newLength / kBitsPerWord)) { + // The number of bit-words is the same after the resize. In that case + // we need to set only bits necessary in the current last bit-word. + ASMJIT_ASSERT(startBit < endBit); + nBits = endBit - startBit; + } + else { + // There is be more bit-words after the resize. In that case we don't + // have to be extra careful about the last bit-word of the old length. + nBits = kBitsPerWord - startBit; + } + + data[idx++] |= pattern << nBits; + } + + // Initialize all bit-words after the last bit-word of the old length. + size_t endIdx = _wordsPerBits(newLength); + endIdx -= static_cast(endIdx * kBitsPerWord == newLength); + + while (idx <= endIdx) + data[idx++] = pattern; + + // Clear unused bits of the last bit-word. + if (endBit) + data[endIdx] &= (static_cast(1) << endBit) - 1; + + _length = newLength; + return kErrorOk; +} + +Error ZoneBitVector::_append(ZoneHeap* heap, bool value) noexcept { + size_t kThreshold = Globals::kAllocThreshold * 8; + size_t newLength = _length + 1; + size_t idealCapacity = _capacity; + + if (idealCapacity < 128) + idealCapacity = 128; + else if (idealCapacity <= kThreshold) + idealCapacity *= 2; + else + idealCapacity += kThreshold; + + if (ASMJIT_UNLIKELY(idealCapacity < _capacity)) { + // It's technically impossible that `_length + 1` overflows. + idealCapacity = newLength; + ASMJIT_ASSERT(idealCapacity > _capacity); + } + + return _resize(heap, newLength, idealCapacity, value); +} + +Error ZoneBitVector::fill(size_t from, size_t to, bool value) noexcept { + if (ASMJIT_UNLIKELY(from >= to)) { + if (from > to) + return DebugUtils::errored(kErrorInvalidArgument); + else + return kErrorOk; + } + + ASMJIT_ASSERT(from <= _length); + ASMJIT_ASSERT(to <= _length); + + // This is very similar to `ZoneBitVector::_fill()`, however, since we + // actually set bits that are already part of the container we need to + // special case filiing to zeros and ones. + size_t idx = from / kBitsPerWord; + size_t startBit = from % kBitsPerWord; + + size_t endIdx = to / kBitsPerWord; + size_t endBit = to % kBitsPerWord; + + BitWord* data = _data; + ASMJIT_ASSERT(data != nullptr); + + // Special case for non-zero `startBit`. + if (startBit) { + if (idx == endIdx) { + ASMJIT_ASSERT(startBit < endBit); + + size_t nBits = endBit - startBit; + BitWord mask = ((static_cast(1) << nBits) - 1) << startBit; + + if (value) + data[idx] |= mask; + else + data[idx] &= ~mask; + return kErrorOk; + } + else { + BitWord mask = (static_cast(0) - 1) << startBit; + + if (value) + data[idx++] |= mask; + else + data[idx++] &= ~mask; + } + } + + // Fill all bits in case there is a gap between the current `idx` and `endIdx`. + if (idx < endIdx) { + BitWord pattern = _patternFromBit(value); + do { + data[idx++] = pattern; + } while (idx < endIdx); + } + + // Special case for non-zero `endBit`. + if (endBit) { + BitWord mask = ((static_cast(1) << endBit) - 1); + if (value) + data[endIdx] |= mask; + else + data[endIdx] &= ~mask; + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::ZoneStackBase - Init / Reset] +// ============================================================================ + +Error ZoneStackBase::_init(ZoneHeap* heap, size_t middleIndex) noexcept { + ZoneHeap* oldHeap = _heap; + + if (oldHeap) { + Block* block = _block[kSideLeft]; + while (block) { + Block* next = block->getNext(); + oldHeap->release(block, kBlockSize); + block = next; + } + + _heap = nullptr; + _block[kSideLeft] = nullptr; + _block[kSideRight] = nullptr; + } + + + if (heap) { + Block* block = static_cast(heap->alloc(kBlockSize)); + if (ASMJIT_UNLIKELY(!block)) + return DebugUtils::errored(kErrorNoHeapMemory); + + block->_link[kSideLeft] = nullptr; + block->_link[kSideRight] = nullptr; + block->_start = (uint8_t*)block + middleIndex; + block->_end = (uint8_t*)block + middleIndex; + + _heap = heap; + _block[kSideLeft] = block; + _block[kSideRight] = block; + } + + return kErrorOk; +} + +// ============================================================================ +// [asmjit::ZoneStackBase - Ops] +// ============================================================================ + +Error ZoneStackBase::_prepareBlock(uint32_t side, size_t initialIndex) noexcept { + ASMJIT_ASSERT(isInitialized()); + + Block* prev = _block[side]; + ASMJIT_ASSERT(!prev->isEmpty()); + + Block* block = _heap->allocT(kBlockSize); + if (ASMJIT_UNLIKELY(!block)) + return DebugUtils::errored(kErrorNoHeapMemory); + + block->_link[ side] = nullptr; + block->_link[!side] = prev; + block->_start = (uint8_t*)block + initialIndex; + block->_end = (uint8_t*)block + initialIndex; + + prev->_link[side] = block; + _block[side] = block; + + return kErrorOk; +} + +void ZoneStackBase::_cleanupBlock(uint32_t side, size_t middleIndex) noexcept { + Block* block = _block[side]; + ASMJIT_ASSERT(block->isEmpty()); + + Block* prev = block->_link[!side]; + if (prev) { + ASMJIT_ASSERT(prev->_link[side] == block); + _heap->release(block, kBlockSize); + + prev->_link[side] = nullptr; + _block[side] = prev; + } + else if (_block[!side] == prev && prev->isEmpty()) { + // If the container becomes empty center both pointers in the remaining block. + prev->_start = (uint8_t*)prev + middleIndex; + prev->_end = (uint8_t*)prev + middleIndex; + } +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Utilities] +// ============================================================================ + +static uint32_t ZoneHash_getClosestPrime(uint32_t x) noexcept { + static const uint32_t primeTable[] = { + 23, 53, 193, 389, 769, 1543, 3079, 6151, 12289, 24593 + }; + + size_t i = 0; + uint32_t p; + + do { + if ((p = primeTable[i]) > x) + break; + } while (++i < ASMJIT_ARRAY_SIZE(primeTable)); + + return p; +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Reset] +// ============================================================================ + +void ZoneHashBase::reset(ZoneHeap* heap) noexcept { + ZoneHashNode** oldData = _data; + if (oldData != _embedded) + _heap->release(oldData, _bucketsCount * sizeof(ZoneHashNode*)); + + _heap = heap; + _size = 0; + _bucketsCount = 1; + _bucketsGrow = 1; + _data = _embedded; + _embedded[0] = nullptr; +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Rehash] +// ============================================================================ + +void ZoneHashBase::_rehash(uint32_t newCount) noexcept { + ASMJIT_ASSERT(isInitialized()); + + ZoneHashNode** oldData = _data; + ZoneHashNode** newData = reinterpret_cast( + _heap->allocZeroed(static_cast(newCount) * sizeof(ZoneHashNode*))); + + // We can still store nodes into the table, but it will degrade. + if (ASMJIT_UNLIKELY(newData == nullptr)) + return; + + uint32_t i; + uint32_t oldCount = _bucketsCount; + + for (i = 0; i < oldCount; i++) { + ZoneHashNode* node = oldData[i]; + while (node) { + ZoneHashNode* next = node->_hashNext; + uint32_t hMod = node->_hVal % newCount; + + node->_hashNext = newData[hMod]; + newData[hMod] = node; + + node = next; + } + } + + // 90% is the maximum occupancy, can't overflow since the maximum capacity + // is limited to the last prime number stored in the prime table. + if (oldData != _embedded) + _heap->release(oldData, _bucketsCount * sizeof(ZoneHashNode*)); + + _bucketsCount = newCount; + _bucketsGrow = newCount * 9 / 10; + + _data = newData; +} + +// ============================================================================ +// [asmjit::ZoneHashBase - Ops] +// ============================================================================ + +ZoneHashNode* ZoneHashBase::_put(ZoneHashNode* node) noexcept { + uint32_t hMod = node->_hVal % _bucketsCount; + ZoneHashNode* next = _data[hMod]; + + node->_hashNext = next; + _data[hMod] = node; + + if (++_size >= _bucketsGrow && next) { + uint32_t newCapacity = ZoneHash_getClosestPrime(_bucketsCount); + if (newCapacity != _bucketsCount) + _rehash(newCapacity); + } + + return node; +} + +ZoneHashNode* ZoneHashBase::_del(ZoneHashNode* node) noexcept { + uint32_t hMod = node->_hVal % _bucketsCount; + + ZoneHashNode** pPrev = &_data[hMod]; + ZoneHashNode* p = *pPrev; + + while (p) { + if (p == node) { + *pPrev = p->_hashNext; + return node; + } + + pPrev = &p->_hashNext; + p = *pPrev; + } + + return nullptr; +} + +// ============================================================================ +// [asmjit::Zone - Test] +// ============================================================================ + +#if defined(ASMJIT_TEST) +UNIT(base_zonevector) { + Zone zone(8096 - Zone::kZoneOverhead); + ZoneHeap heap(&zone); + + int i; + int kMax = 100000; + + ZoneVector vec; + + INFO("ZoneVector basic tests"); + EXPECT(vec.append(&heap, 0) == kErrorOk); + EXPECT(vec.isEmpty() == false); + EXPECT(vec.getLength() == 1); + EXPECT(vec.getCapacity() >= 1); + EXPECT(vec.indexOf(0) == 0); + EXPECT(vec.indexOf(-11) == Globals::kInvalidIndex); + + vec.clear(); + EXPECT(vec.isEmpty()); + EXPECT(vec.getLength() == 0); + EXPECT(vec.indexOf(0) == Globals::kInvalidIndex); + + for (i = 0; i < kMax; i++) { + EXPECT(vec.append(&heap, i) == kErrorOk); + } + EXPECT(vec.isEmpty() == false); + EXPECT(vec.getLength() == static_cast(kMax)); + EXPECT(vec.indexOf(kMax - 1) == static_cast(kMax - 1)); +} + +UNIT(base_ZoneBitVector) { + Zone zone(8096 - Zone::kZoneOverhead); + ZoneHeap heap(&zone); + + size_t i, count; + size_t kMaxCount = 100; + + ZoneBitVector vec; + EXPECT(vec.isEmpty()); + EXPECT(vec.getLength() == 0); + + INFO("ZoneBitVector::resize()"); + for (count = 1; count < kMaxCount; count++) { + vec.clear(); + EXPECT(vec.resize(&heap, count, false) == kErrorOk); + EXPECT(vec.getLength() == count); + + for (i = 0; i < count; i++) + EXPECT(vec.getAt(i) == false); + + vec.clear(); + EXPECT(vec.resize(&heap, count, true) == kErrorOk); + EXPECT(vec.getLength() == count); + + for (i = 0; i < count; i++) + EXPECT(vec.getAt(i) == true); + } + + INFO("ZoneBitVector::fill()"); + for (count = 1; count < kMaxCount; count += 2) { + vec.clear(); + EXPECT(vec.resize(&heap, count) == kErrorOk); + EXPECT(vec.getLength() == count); + + for (i = 0; i < (count + 1) / 2; i++) { + bool value = static_cast(i & 1); + EXPECT(vec.fill(i, count - i, value) == kErrorOk); + } + + for (i = 0; i < count; i++) { + EXPECT(vec.getAt(i) == static_cast(i & 1)); + } + } +} + +UNIT(base_zonestack) { + Zone zone(8096 - Zone::kZoneOverhead); + ZoneHeap heap(&zone); + ZoneStack stack; + + INFO("ZoneStack contains %d elements per one Block", ZoneStack::kNumBlockItems); + + EXPECT(stack.init(&heap) == kErrorOk); + EXPECT(stack.isEmpty(), "Stack must be empty after `init()`"); + + EXPECT(stack.append(42) == kErrorOk); + EXPECT(!stack.isEmpty() , "Stack must not be empty after an item has been appended"); + EXPECT(stack.pop() == 42, "Stack.pop() must return the item that has been appended last"); + EXPECT(stack.isEmpty() , "Stack must be empty after the last element has been removed"); + + EXPECT(stack.prepend(43) == kErrorOk); + EXPECT(!stack.isEmpty() , "Stack must not be empty after an item has been prepended"); + EXPECT(stack.popFirst() == 43, "Stack.popFirst() must return the item that has been prepended last"); + EXPECT(stack.isEmpty() , "Stack must be empty after the last element has been removed"); + + int i; + int iMin =-100; + int iMax = 100000; + + INFO("Adding items from %d to %d to the stack", iMin, iMax); + for (i = 1; i <= iMax; i++) stack.append(i); + for (i = 0; i >= iMin; i--) stack.prepend(i); + + INFO("Validating popFirst()"); + for (i = iMin; i <= iMax; i++) { + int item = stack.popFirst(); + EXPECT(i == item, "Item '%d' didn't match the item '%d' popped", i, item); + } + EXPECT(stack.isEmpty()); + + INFO("Adding items from %d to %d to the stack", iMin, iMax); + for (i = 0; i >= iMin; i--) stack.prepend(i); + for (i = 1; i <= iMax; i++) stack.append(i); + + INFO("Validating pop()"); + for (i = iMax; i >= iMin; i--) { + int item = stack.pop(); + EXPECT(i == item, "Item '%d' didn't match the item '%d' popped", i, item); + } + EXPECT(stack.isEmpty()); +} +#endif // ASMJIT_TEST + } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" diff --git a/src/asmjit/base/zone.h b/src/asmjit/base/zone.h index 9bbb50c..5a461a2 100644 --- a/src/asmjit/base/zone.h +++ b/src/asmjit/base/zone.h @@ -9,10 +9,10 @@ #define _ASMJIT_BASE_ZONE_H // [Dependencies] -#include "../base/globals.h" +#include "../base/utils.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { @@ -23,60 +23,31 @@ namespace asmjit { // [asmjit::Zone] // ============================================================================ -//! Zone memory allocator. +//! Memory zone. //! //! Zone is an incremental memory allocator that allocates memory by simply //! incrementing a pointer. It allocates blocks of memory by using standard -//! C library `malloc/free`, but divides these blocks into smaller segments -//! requirested by calling `Zone::alloc()` and friends. +//! C `malloc`, but divides these blocks into smaller segments requested by +//! calling `Zone::alloc()` and friends. //! -//! Zone memory allocators are designed to allocate data of short lifetime. The -//! data used by `Assembler` and `Compiler` has a very short lifetime, thus, is -//! allocated by `Zone`. The advantage is that `Zone` can free all of the data -//! allocated at once by calling `reset()` or by `Zone` destructor. +//! Zone has no function to release the allocated memory. It has to be released +//! all at once by calling `reset()`. If you need a more friendly allocator that +//! also supports `release()`, consider using \ref Zone with \ref ZoneHeap. class Zone { - public: +public: //! \internal //! //! A single block of memory. struct Block { - // ------------------------------------------------------------------------ - // [Accessors] - // ------------------------------------------------------------------------ - - //! Get the size of the block. - ASMJIT_INLINE size_t getBlockSize() const noexcept { - return (size_t)(end - data); - } - - //! Get count of remaining bytes in the block. - ASMJIT_INLINE size_t getRemainingSize() const noexcept { - return (size_t)(end - pos); - } - - // ------------------------------------------------------------------------ - // [Members] - // ------------------------------------------------------------------------ - - //! Current data pointer (pointer to the first available byte). - uint8_t* pos; - //! End data pointer (pointer to the first invalid byte). - uint8_t* end; - - //! Link to the previous block. - Block* prev; - //! Link to the next block. - Block* next; - - //! Data. - uint8_t data[sizeof(void*)]; + Block* prev; //!< Link to the previous block. + Block* next; //!< Link to the next block. + size_t size; //!< Size of the block. + uint8_t data[sizeof(void*)]; //!< Data. }; enum { //! Zone allocator overhead. - kZoneOverhead = - kMemAllocOverhead - + static_cast(sizeof(Block) - sizeof(void*)) + kZoneOverhead = Globals::kAllocOverhead + static_cast(sizeof(Block)) }; // -------------------------------------------------------------------------- @@ -92,8 +63,8 @@ class Zone { //! //! It's not required, but it's good practice to set `blockSize` to a //! reasonable value that depends on the usage of `Zone`. Greater block sizes - //! are generally safer and performs better than unreasonably low values. - ASMJIT_API Zone(size_t blockSize) noexcept; + //! are generally safer and perform better than unreasonably low values. + ASMJIT_API Zone(uint32_t blockSize, uint32_t blockAlignment = 0) noexcept; //! Destroy the `Zone` instance. //! @@ -115,8 +86,26 @@ class Zone { // -------------------------------------------------------------------------- //! Get the default block size. - ASMJIT_INLINE size_t getBlockSize() const noexcept { - return _blockSize; + ASMJIT_INLINE uint32_t getBlockSize() const noexcept { return _blockSize; } + //! Get the default block alignment. + ASMJIT_INLINE uint32_t getBlockAlignment() const noexcept { return (uint32_t)1 << _blockAlignmentShift; } + //! Get remaining size of the current block. + ASMJIT_INLINE size_t getRemainingSize() const noexcept { return (size_t)(_end - _ptr); } + + //! Get the current zone cursor (dangerous). + //! + //! This is a function that can be used to get exclusive access to the current + //! block's memory buffer. + ASMJIT_INLINE uint8_t* getCursor() noexcept { return _ptr; } + //! Get the end of the current zone block, only useful if you use `getCursor()`. + ASMJIT_INLINE uint8_t* getEnd() noexcept { return _end; } + + //! Set the current zone cursor to `p` (must match the current block). + //! + //! This is a counterpart of `getZoneCursor()`. + ASMJIT_INLINE void setCursor(uint8_t* p) noexcept { + ASMJIT_ASSERT(p >= _ptr && p <= _end); + _ptr = p; } // -------------------------------------------------------------------------- @@ -140,7 +129,7 @@ class Zone { //! // Create your objects using zone object allocating, for example: //! Object* obj = static_cast( zone.alloc(sizeof(Object)) ); // - //! if (obj == nullptr) { + //! if (!obj) { //! // Handle out of memory error. //! } //! @@ -156,18 +145,28 @@ class Zone { //! zone.reset(); //! ~~~ ASMJIT_INLINE void* alloc(size_t size) noexcept { - Block* cur = _block; + uint8_t* ptr = _ptr; + size_t remainingBytes = (size_t)(_end - ptr); - uint8_t* ptr = cur->pos; - size_t remainingBytes = (size_t)(cur->end - ptr); - - if (remainingBytes < size) + if (ASMJIT_UNLIKELY(remainingBytes < size)) return _alloc(size); - cur->pos += size; - ASMJIT_ASSERT(cur->pos <= cur->end); + _ptr += size; + ASMJIT_ASSERT(_ptr <= _end); - return (void*)ptr; + return static_cast(ptr); + } + + //! Allocate `size` bytes without any checks. + //! + //! Can only be called if `getRemainingSize()` returns size at least equal + //! to `size`. + ASMJIT_INLINE void* allocNoCheck(size_t size) noexcept { + ASMJIT_ASSERT((size_t)(_end - _ptr) >= size); + + uint8_t* ptr = _ptr; + _ptr += size; + return static_cast(ptr); } //! Allocate `size` bytes of zeroed memory. @@ -181,20 +180,40 @@ class Zone { return static_cast(alloc(size)); } + //! Like `allocNoCheck()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocNoCheckT(size_t size = sizeof(T)) noexcept { + return static_cast(allocNoCheck(size)); + } + //! Like `allocZeroed()`, but the return pointer is casted to `T*`. template ASMJIT_INLINE T* allocZeroedT(size_t size = sizeof(T)) noexcept { return static_cast(allocZeroed(size)); } + //! Like `new(std::nothrow) T(...)`, but allocated by `Zone`. + template + ASMJIT_INLINE T* newT() noexcept { + void* p = alloc(sizeof(T)); + if (ASMJIT_UNLIKELY(!p)) + return nullptr; + return new(p) T(); + } + //! Like `new(std::nothrow) T(...)`, but allocated by `Zone`. + template + ASMJIT_INLINE T* newT(P1 p1) noexcept { + void* p = alloc(sizeof(T)); + if (ASMJIT_UNLIKELY(!p)) + return nullptr; + return new(p) T(p1); + } + //! \internal ASMJIT_API void* _alloc(size_t size) noexcept; //! Helper to duplicate data. - ASMJIT_API void* dup(const void* data, size_t size) noexcept; - - //! Helper to duplicate string. - ASMJIT_API char* sdup(const char* str) noexcept; + ASMJIT_API void* dup(const void* data, size_t size, bool nullTerminate = false) noexcept; //! Helper to duplicate formatted string, maximum length is 256 bytes. ASMJIT_API char* sformat(const char* str, ...) noexcept; @@ -203,10 +222,1100 @@ class Zone { // [Members] // -------------------------------------------------------------------------- - //! The current block. - Block* _block; - //! Default block size. - size_t _blockSize; + uint8_t* _ptr; //!< Pointer in the current block's buffer. + uint8_t* _end; //!< End of the current block's buffer. + Block* _block; //!< Current block. + +#if ASMJIT_ARCH_64BIT + uint32_t _blockSize; //!< Default size of a newly allocated block. + uint32_t _blockAlignmentShift; //!< Minimum alignment of each block. +#else + uint32_t _blockSize : 29; //!< Default size of a newly allocated block. + uint32_t _blockAlignmentShift : 3; //!< Minimum alignment of each block. +#endif +}; + +// ============================================================================ +// [asmjit::ZoneHeap] +// ============================================================================ + +//! Zone-based memory allocator that uses an existing \ref Zone and provides +//! a `release()` functionality on top of it. It uses \ref Zone only for chunks +//! that can be pooled, and uses libc `malloc()` for chunks that are large. +//! +//! The advantage of ZoneHeap is that it can allocate small chunks of memory +//! really fast, and these chunks, when released, will be reused by consecutive +//! calls to `alloc()`. Also, since ZoneHeap uses \ref Zone, you can turn any +//! \ref Zone into a \ref ZoneHeap, and use it in your \ref Pass when necessary. +//! +//! ZoneHeap is used by AsmJit containers to make containers having only +//! few elements fast (and lightweight) and to allow them to grow and use +//! dynamic blocks when require more storage. +class ZoneHeap { + ASMJIT_NONCOPYABLE(ZoneHeap) + + enum { + // In short, we pool chunks of these sizes: + // [32, 64, 96, 128, 192, 256, 320, 384, 448, 512] + + //! How many bytes per a low granularity pool (has to be at least 16). + kLoGranularity = 32, + //! Number of slots of a low granularity pool. + kLoCount = 4, + //! Maximum size of a block that can be allocated in a low granularity pool. + kLoMaxSize = kLoGranularity * kLoCount, + + //! How many bytes per a high granularity pool. + kHiGranularity = 64, + //! Number of slots of a high granularity pool. + kHiCount = 6, + //! Maximum size of a block that can be allocated in a high granularity pool. + kHiMaxSize = kLoMaxSize + kHiGranularity * kHiCount, + + //! Alignment of every pointer returned by `alloc()`. + kBlockAlignment = kLoGranularity + }; + + //! Single-linked list used to store unused chunks. + struct Slot { + //! Link to a next slot in a single-linked list. + Slot* next; + }; + + //! A block of memory that has been allocated dynamically and is not part of + //! block-list used by the allocator. This is used to keep track of all these + //! blocks so they can be freed by `reset()` if not freed explicitly. + struct DynamicBlock { + DynamicBlock* prev; + DynamicBlock* next; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new `ZoneHeap`. + //! + //! NOTE: To use it, you must first `init()` it. + ASMJIT_INLINE ZoneHeap() noexcept { + ::memset(this, 0, sizeof(*this)); + } + //! Create a new `ZoneHeap` initialized to use `zone`. + explicit ASMJIT_INLINE ZoneHeap(Zone* zone) noexcept { + ::memset(this, 0, sizeof(*this)); + _zone = zone; + } + //! Destroy the `ZoneHeap`. + ASMJIT_INLINE ~ZoneHeap() noexcept { reset(); } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + //! Get if the `ZoneHeap` is initialized (i.e. has `Zone`). + ASMJIT_INLINE bool isInitialized() const noexcept { return _zone != nullptr; } + + //! Convenience method to initialize the `ZoneHeap` with `zone`. + //! + //! It's the same as calling `reset(zone)`. + ASMJIT_INLINE void init(Zone* zone) noexcept { reset(zone); } + + //! Reset this `ZoneHeap` and also forget about the current `Zone` which + //! is attached (if any). Reset optionally attaches a new `zone` passed, or + //! keeps the `ZoneHeap` in an uninitialized state, if `zone` is null. + ASMJIT_API void reset(Zone* zone = nullptr) noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get the `Zone` the `ZoneHeap` is using, or null if it's not initialized. + ASMJIT_INLINE Zone* getZone() const noexcept { return _zone; } + + // -------------------------------------------------------------------------- + // [Utilities] + // -------------------------------------------------------------------------- + + //! \internal + //! + //! Get the slot index to be used for `size`. Returns `true` if a valid slot + //! has been written to `slot` and `allocatedSize` has been filled with slot + //! exact size (`allocatedSize` can be equal or slightly greater than `size`). + static ASMJIT_INLINE bool _getSlotIndex(size_t size, uint32_t& slot) noexcept { + ASMJIT_ASSERT(size > 0); + if (size > kHiMaxSize) + return false; + + if (size <= kLoMaxSize) + slot = static_cast((size - 1) / kLoGranularity); + else + slot = static_cast((size - kLoMaxSize - 1) / kHiGranularity) + kLoCount; + + return true; + } + + //! \overload + static ASMJIT_INLINE bool _getSlotIndex(size_t size, uint32_t& slot, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(size > 0); + if (size > kHiMaxSize) + return false; + + if (size <= kLoMaxSize) { + slot = static_cast((size - 1) / kLoGranularity); + allocatedSize = Utils::alignTo(size, kLoGranularity); + } + else { + slot = static_cast((size - kLoMaxSize - 1) / kHiGranularity) + kLoCount; + allocatedSize = Utils::alignTo(size, kHiGranularity); + } + + return true; + } + + // -------------------------------------------------------------------------- + // [Alloc / Release] + // -------------------------------------------------------------------------- + + ASMJIT_API void* _alloc(size_t size, size_t& allocatedSize) noexcept; + ASMJIT_API void* _allocZeroed(size_t size, size_t& allocatedSize) noexcept; + ASMJIT_API void _releaseDynamic(void* p, size_t size) noexcept; + + //! Allocate `size` bytes of memory, ideally from an available pool. + //! + //! NOTE: `size` can't be zero, it will assert in debug mode in such case. + ASMJIT_INLINE void* alloc(size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + size_t allocatedSize; + return _alloc(size, allocatedSize); + } + + //! Like `alloc(size)`, but provides a second argument `allocatedSize` that + //! provides a way to know how big the block returned actually is. This is + //! useful for containers to prevent growing too early. + ASMJIT_INLINE void* alloc(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + return _alloc(size, allocatedSize); + } + + //! Like `alloc()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocT(size_t size = sizeof(T)) noexcept { + return static_cast(alloc(size)); + } + + //! Like `alloc(size)`, but returns zeroed memory. + ASMJIT_INLINE void* allocZeroed(size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + + size_t allocatedSize; + return _allocZeroed(size, allocatedSize); + } + + //! Like `alloc(size, allocatedSize)`, but returns zeroed memory. + ASMJIT_INLINE void* allocZeroed(size_t size, size_t& allocatedSize) noexcept { + ASMJIT_ASSERT(isInitialized()); + + return _allocZeroed(size, allocatedSize); + } + + //! Like `allocZeroed()`, but the return pointer is casted to `T*`. + template + ASMJIT_INLINE T* allocZeroedT(size_t size = sizeof(T)) noexcept { + return static_cast(allocZeroed(size)); + } + + //! Release the memory previously allocated by `alloc()`. The `size` argument + //! has to be the same as used to call `alloc()` or `allocatedSize` returned + //! by `alloc()`. + ASMJIT_INLINE void release(void* p, size_t size) noexcept { + ASMJIT_ASSERT(isInitialized()); + + ASMJIT_ASSERT(p != nullptr); + ASMJIT_ASSERT(size != 0); + + uint32_t slot; + if (_getSlotIndex(size, slot)) { + //printf("RELEASING %p of size %d (SLOT %u)\n", p, int(size), slot); + static_cast(p)->next = static_cast(_slots[slot]); + _slots[slot] = static_cast(p); + } + else { + _releaseDynamic(p, size); + } + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Zone* _zone; //!< Zone used to allocate memory that fits into slots. + Slot* _slots[kLoCount + kHiCount]; //!< Indexed slots containing released memory. + DynamicBlock* _dynamicBlocks; //!< Dynamic blocks for larger allocations (no slots). +}; + +// ============================================================================ +// [asmjit::ZoneList] +// ============================================================================ + +//! \internal +template +class ZoneList { +public: + ASMJIT_NONCOPYABLE(ZoneList) + + // -------------------------------------------------------------------------- + // [Link] + // -------------------------------------------------------------------------- + + //! ZoneList node. + struct Link { + //! Get next node. + ASMJIT_INLINE Link* getNext() const noexcept { return _next; } + //! Get value. + ASMJIT_INLINE T getValue() const noexcept { return _value; } + //! Set value to `value`. + ASMJIT_INLINE void setValue(const T& value) noexcept { _value = value; } + + Link* _next; + T _value; + }; + + // -------------------------------------------------------------------------- + // [Appender] + // -------------------------------------------------------------------------- + + //! Specialized appender that takes advantage of ZoneList structure. You must + //! initialize it and then call done(). + struct Appender { + ASMJIT_INLINE Appender(ZoneList& list) noexcept { init(list); } + + ASMJIT_INLINE void init(ZoneList& list) noexcept { + pPrev = &list._first; + } + + ASMJIT_INLINE void done(ZoneList& list) noexcept { + list._last = *pPrev; + *pPrev = nullptr; + } + + ASMJIT_INLINE void append(Link* node) noexcept { + *pPrev = node; + pPrev = &node->_next; + } + + Link** pPrev; + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ZoneList() noexcept : _first(nullptr), _last(nullptr) {} + ASMJIT_INLINE ~ZoneList() noexcept {} + + // -------------------------------------------------------------------------- + // [Data] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isEmpty() const noexcept { return _first != nullptr; } + ASMJIT_INLINE Link* getFirst() const noexcept { return _first; } + ASMJIT_INLINE Link* getLast() const noexcept { return _last; } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void reset() noexcept { + _first = nullptr; + _last = nullptr; + } + + ASMJIT_INLINE void prepend(Link* link) noexcept { + link->_next = _first; + if (!_first) _last = link; + _first = link; + } + + ASMJIT_INLINE void append(Link* link) noexcept { + link->_next = nullptr; + if (!_first) + _first = link; + else + _last->_next = link; + _last = link; + } + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + Link* _first; + Link* _last; +}; + +// ============================================================================ +// [asmjit::ZoneVectorBase] +// ============================================================================ + +//! \internal +class ZoneVectorBase { +public: + ASMJIT_NONCOPYABLE(ZoneVectorBase) + +protected: + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new instance of `ZoneVectorBase`. + explicit ASMJIT_INLINE ZoneVectorBase() noexcept + : _data(nullptr), + _length(0), + _capacity(0) {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + +public: + //! Get if the vector is empty. + ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } + //! Get vector length. + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + //! Get vector capacity. + ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + //! Makes the vector empty (won't change the capacity or data pointer). + ASMJIT_INLINE void clear() noexcept { _length = 0; } + //! Reset the vector data and set its `length` to zero. + ASMJIT_INLINE void reset() noexcept { + _data = nullptr; + _length = 0; + _capacity = 0; + } + + //! Truncate the vector to at most `n` items. + ASMJIT_INLINE void truncate(size_t n) noexcept { + _length = std::min(_length, n); + } + + // -------------------------------------------------------------------------- + // [Memory Management] + // -------------------------------------------------------------------------- + +protected: + ASMJIT_INLINE void _release(ZoneHeap* heap, size_t sizeOfT) noexcept { + if (_data != nullptr) { + heap->release(_data, _capacity * sizeOfT); + reset(); + } + } + + ASMJIT_API Error _grow(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept; + ASMJIT_API Error _resize(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept; + ASMJIT_API Error _reserve(ZoneHeap* heap, size_t sizeOfT, size_t n) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + +public: + void* _data; //!< Vector data. + size_t _length; //!< Length of the vector. + size_t _capacity; //!< Capacity of the vector. +}; + +// ============================================================================ +// [asmjit::ZoneVector] +// ============================================================================ + +//! Template used to store and manage array of Zone allocated data. +//! +//! This template has these advantages over other std::vector<>: +//! - Always non-copyable (designed to be non-copyable, we want it). +//! - No copy-on-write (some implementations of STL can use it). +//! - Optimized for working only with POD types. +//! - Uses ZoneHeap, thus small vectors are basically for free. +template +class ZoneVector : public ZoneVectorBase { +public: + ASMJIT_NONCOPYABLE(ZoneVector) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + //! Create a new instance of `ZoneVector`. + explicit ASMJIT_INLINE ZoneVector() noexcept : ZoneVectorBase() {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get data. + ASMJIT_INLINE T* getData() noexcept { return static_cast(_data); } + //! \overload + ASMJIT_INLINE const T* getData() const noexcept { return static_cast(_data); } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + //! Prepend `item` to the vector. + Error prepend(ZoneHeap* heap, const T& item) noexcept { + if (ASMJIT_UNLIKELY(_length == _capacity)) + ASMJIT_PROPAGATE(grow(heap, 1)); + + ::memmove(static_cast(_data) + 1, _data, _length * sizeof(T)); + ::memcpy(_data, &item, sizeof(T)); + + _length++; + return kErrorOk; + } + + //! Insert an `item` at the specified `index`. + Error insert(ZoneHeap* heap, size_t index, const T& item) noexcept { + ASMJIT_ASSERT(index <= _length); + + if (ASMJIT_UNLIKELY(_length == _capacity)) + ASMJIT_PROPAGATE(grow(heap, 1)); + + T* dst = static_cast(_data) + index; + ::memmove(dst + 1, dst, _length - index); + ::memcpy(dst, &item, sizeof(T)); + + _length++; + return kErrorOk; + } + + //! Append `item` to the vector. + Error append(ZoneHeap* heap, const T& item) noexcept { + if (ASMJIT_UNLIKELY(_length == _capacity)) + ASMJIT_PROPAGATE(grow(heap, 1)); + + ::memcpy(static_cast(_data) + _length, &item, sizeof(T)); + + _length++; + return kErrorOk; + } + + Error concat(ZoneHeap* heap, const ZoneVector& other) noexcept { + size_t count = other._length; + if (_capacity - _length < count) + ASMJIT_PROPAGATE(grow(heap, count)); + + ::memcpy(static_cast(_data) + _length, other._data, count * sizeof(T)); + + _length += count; + return kErrorOk; + } + + //! Prepend `item` to the vector (unsafe case). + //! + //! Can only be used together with `willGrow()`. If `willGrow(N)` returns + //! `kErrorOk` then N elements can be added to the vector without checking + //! if there is a place for them. Used mostly internally. + ASMJIT_INLINE void prependUnsafe(const T& item) noexcept { + ASMJIT_ASSERT(_length < _capacity); + T* data = static_cast(_data); + + if (_length) + ::memmove(data + 1, data, _length * sizeof(T)); + + ::memcpy(data, &item, sizeof(T)); + _length++; + } + + //! Append `item` to the vector (unsafe case). + //! + //! Can only be used together with `willGrow()`. If `willGrow(N)` returns + //! `kErrorOk` then N elements can be added to the vector without checking + //! if there is a place for them. Used mostly internally. + ASMJIT_INLINE void appendUnsafe(const T& item) noexcept { + ASMJIT_ASSERT(_length < _capacity); + + ::memcpy(static_cast(_data) + _length, &item, sizeof(T)); + _length++; + } + + //! Concatenate all items of `other` at the end of the vector. + ASMJIT_INLINE void concatUnsafe(const ZoneVector& other) noexcept { + size_t count = other._length; + ASMJIT_ASSERT(_capacity - _length >= count); + + ::memcpy(static_cast(_data) + _length, other._data, count * sizeof(T)); + _length += count; + } + + //! Get index of `val` or `kInvalidIndex` if not found. + ASMJIT_INLINE size_t indexOf(const T& val) const noexcept { + const T* data = static_cast(_data); + size_t length = _length; + + for (size_t i = 0; i < length; i++) + if (data[i] == val) + return i; + + return Globals::kInvalidIndex; + } + + //! Get whether the vector contains `val`. + ASMJIT_INLINE bool contains(const T& val) const noexcept { + return indexOf(val) != Globals::kInvalidIndex; + } + + //! Remove item at index `i`. + ASMJIT_INLINE void removeAt(size_t i) noexcept { + ASMJIT_ASSERT(i < _length); + + T* data = static_cast(_data) + i; + _length--; + ::memmove(data, data + 1, _length - i); + } + + //! Swap this pod-vector with `other`. + ASMJIT_INLINE void swap(ZoneVector& other) noexcept { + Utils::swap(_length, other._length); + Utils::swap(_capacity, other._capacity); + Utils::swap(_data, other._data); + } + + //! Get item at index `i` (const). + ASMJIT_INLINE const T& getAt(size_t i) const noexcept { + ASMJIT_ASSERT(i < _length); + return getData()[i]; + } + + //! Get item at index `i`. + ASMJIT_INLINE T& operator[](size_t i) noexcept { + ASMJIT_ASSERT(i < _length); + return getData()[i]; + } + + //! Get item at index `i`. + ASMJIT_INLINE const T& operator[](size_t i) const noexcept { + ASMJIT_ASSERT(i < _length); + return getData()[i]; + } + + // -------------------------------------------------------------------------- + // [Memory Management] + // -------------------------------------------------------------------------- + + //! Release the memory held by `ZoneVector` back to the `heap`. + ASMJIT_INLINE void release(ZoneHeap* heap) noexcept { _release(heap, sizeof(T)); } + + //! Called to grow the buffer to fit at least `n` elements more. + ASMJIT_INLINE Error grow(ZoneHeap* heap, size_t n) noexcept { return ZoneVectorBase::_grow(heap, sizeof(T), n); } + + //! Resize the vector to hold `n` elements. + //! + //! If `n` is greater than the current length then the additional elements' + //! content will be initialized to zero. If `n` is less than the current + //! length then the vector will be truncated to exactly `n` elements. + ASMJIT_INLINE Error resize(ZoneHeap* heap, size_t n) noexcept { return ZoneVectorBase::_resize(heap, sizeof(T), n); } + + //! Realloc internal array to fit at least `n` items. + ASMJIT_INLINE Error reserve(ZoneHeap* heap, size_t n) noexcept { return ZoneVectorBase::_reserve(heap, sizeof(T), n); } + + ASMJIT_INLINE Error willGrow(ZoneHeap* heap, size_t n = 1) noexcept { + return _capacity - _length < n ? grow(heap, n) : static_cast(kErrorOk); + } +}; + +// ============================================================================ +// [asmjit::ZoneBitVector] +// ============================================================================ + +class ZoneBitVector { +public: + ASMJIT_NONCOPYABLE(ZoneBitVector) + + //! Storage used to store a pack of bits (should by compatible with a machine word). + typedef uintptr_t BitWord; + enum { kBitsPerWord = static_cast(sizeof(BitWord)) * 8 }; + + static ASMJIT_INLINE size_t _wordsPerBits(size_t nBits) noexcept { + return ((nBits + kBitsPerWord) / kBitsPerWord) - 1; + } + + // Return all bits zero if 0 and all bits set if 1. + static ASMJIT_INLINE BitWord _patternFromBit(bool bit) noexcept { + BitWord bitAsWord = static_cast(bit); + ASMJIT_ASSERT(bitAsWord == 0 || bitAsWord == 1); + return static_cast(0) - bitAsWord; + } + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + explicit ASMJIT_INLINE ZoneBitVector() noexcept : + _data(nullptr), + _length(0), + _capacity(0) {} + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get if the bit-vector is empty (has no bits). + ASMJIT_INLINE bool isEmpty() const noexcept { return _length == 0; } + //! Get a length of this bit-vector (in bits). + ASMJIT_INLINE size_t getLength() const noexcept { return _length; } + //! Get a capacity of this bit-vector (in bits). + ASMJIT_INLINE size_t getCapacity() const noexcept { return _capacity; } + + //! Get data. + ASMJIT_INLINE BitWord* getData() noexcept { return _data; } + //! \overload + ASMJIT_INLINE const BitWord* getData() const noexcept { return _data; } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void clear() noexcept { + _length = 0; + } + + ASMJIT_INLINE void reset() noexcept { + _data = nullptr; + _length = 0; + _capacity = 0; + } + + ASMJIT_INLINE void truncate(size_t newLength) noexcept { + _length = std::min(_length, newLength); + _clearUnusedBits(); + } + + ASMJIT_INLINE bool getAt(size_t index) const noexcept { + ASMJIT_ASSERT(index < _length); + + size_t idx = index / kBitsPerWord; + size_t bit = index % kBitsPerWord; + return static_cast((_data[idx] >> bit) & 1); + } + + ASMJIT_INLINE void setAt(size_t index, bool value) noexcept { + ASMJIT_ASSERT(index < _length); + + size_t idx = index / kBitsPerWord; + size_t bit = index % kBitsPerWord; + if (value) + _data[idx] |= static_cast(1) << bit; + else + _data[idx] &= ~(static_cast(1) << bit); + } + + ASMJIT_INLINE void toggleAt(size_t index) noexcept { + ASMJIT_ASSERT(index < _length); + + size_t idx = index / kBitsPerWord; + size_t bit = index % kBitsPerWord; + _data[idx] ^= static_cast(1) << bit; + } + + ASMJIT_INLINE Error append(ZoneHeap* heap, bool value) noexcept { + size_t index = _length; + if (ASMJIT_UNLIKELY(index >= _capacity)) + return _append(heap, value); + + size_t idx = index / kBitsPerWord; + size_t bit = index % kBitsPerWord; + + if (bit == 0) + _data[idx] = static_cast(value) << bit; + else + _data[idx] |= static_cast(value) << bit; + + _length++; + return kErrorOk; + } + + ASMJIT_API Error fill(size_t fromIndex, size_t toIndex, bool value) noexcept; + + ASMJIT_INLINE void and_(const ZoneBitVector& other) noexcept { + BitWord* dst = _data; + const BitWord* src = other._data; + + size_t numWords = (std::min(_length, other._length) + kBitsPerWord - 1) / kBitsPerWord; + for (size_t i = 0; i < numWords; i++) + dst[i] = dst[i] & src[i]; + _clearUnusedBits(); + } + + ASMJIT_INLINE void andNot(const ZoneBitVector& other) noexcept { + BitWord* dst = _data; + const BitWord* src = other._data; + + size_t numWords = (std::min(_length, other._length) + kBitsPerWord - 1) / kBitsPerWord; + for (size_t i = 0; i < numWords; i++) + dst[i] = dst[i] & ~src[i]; + _clearUnusedBits(); + } + + ASMJIT_INLINE void or_(const ZoneBitVector& other) noexcept { + BitWord* dst = _data; + const BitWord* src = other._data; + + size_t numWords = (std::min(_length, other._length) + kBitsPerWord - 1) / kBitsPerWord; + for (size_t i = 0; i < numWords; i++) + dst[i] = dst[i] | src[i]; + _clearUnusedBits(); + } + + ASMJIT_INLINE void _clearUnusedBits() noexcept { + size_t idx = _length / kBitsPerWord; + size_t bit = _length % kBitsPerWord; + + if (!bit) return; + _data[idx] &= (static_cast(1) << bit) - 1U; + } + + // -------------------------------------------------------------------------- + // [Memory Management] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE void release(ZoneHeap* heap) noexcept { + if (_data != nullptr) { + heap->release(_data, _capacity / 8); + reset(); + } + } + + ASMJIT_INLINE Error resize(ZoneHeap* heap, size_t newLength, bool newBitsValue = false) noexcept { + return _resize(heap, newLength, newLength, newBitsValue); + } + + ASMJIT_API Error _resize(ZoneHeap* heap, size_t newLength, size_t idealCapacity, bool newBitsValue) noexcept; + ASMJIT_API Error _append(ZoneHeap* heap, bool value) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + BitWord* _data; //!< Bits. + size_t _length; //!< Length of the bit-vector (in bits). + size_t _capacity; //!< Capacity of the bit-vector (in bits). +}; + +// ============================================================================ +// [asmjit::ZoneStackBase] +// ============================================================================ + +class ZoneStackBase { +public: + enum Side { + kSideLeft = 0, + kSideRight = 1 + }; + + enum { + kBlockSize = ZoneHeap::kHiMaxSize + }; + + struct Block { + ASMJIT_INLINE Block* getPrev() const noexcept { return _link[kSideLeft]; } + ASMJIT_INLINE void setPrev(Block* block) noexcept { _link[kSideLeft] = block; } + + ASMJIT_INLINE Block* getNext() const noexcept { return _link[kSideRight]; } + ASMJIT_INLINE void setNext(Block* block) noexcept { _link[kSideRight] = block; } + + template + ASMJIT_INLINE T* getStart() const noexcept { return static_cast(_start); } + template + ASMJIT_INLINE void setStart(T* start) noexcept { _start = static_cast(start); } + + template + ASMJIT_INLINE T* getEnd() const noexcept { return static_cast(_end); } + template + ASMJIT_INLINE void setEnd(T* end) noexcept { _end = static_cast(end); } + + ASMJIT_INLINE bool isEmpty() const noexcept { return _start == _end; } + + template + ASMJIT_INLINE T* getData() const noexcept { + return static_cast(static_cast((uint8_t*)this + sizeof(Block))); + } + + template + ASMJIT_INLINE bool canPrepend() const noexcept { + return _start > getData(); + } + + template + ASMJIT_INLINE bool canAppend() const noexcept { + size_t kNumBlockItems = (kBlockSize - sizeof(Block)) / sizeof(T); + size_t kBlockEnd = sizeof(Block) + kNumBlockItems * sizeof(T); + return (uintptr_t)_end - (uintptr_t)this < kBlockEnd; + } + + Block* _link[2]; //!< Next and previous blocks. + void* _start; //!< Pointer to the start of the array. + void* _end; //!< Pointer to the end of the array. + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ZoneStackBase() noexcept { + _heap = nullptr; + _block[0] = nullptr; + _block[1] = nullptr; + } + ASMJIT_INLINE ~ZoneStackBase() noexcept { reset(); } + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { return _heap != nullptr; } + ASMJIT_API Error _init(ZoneHeap* heap, size_t middleIndex) noexcept; + ASMJIT_INLINE Error reset() noexcept { return _init(nullptr, 0); } + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get a `ZoneHeap` attached to this container. + ASMJIT_INLINE ZoneHeap* getHeap() const noexcept { return _heap; } + + ASMJIT_INLINE bool isEmpty() const noexcept { + ASMJIT_ASSERT(isInitialized()); + return _block[0] == _block[1] && _block[0]->isEmpty(); + } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_API Error _prepareBlock(uint32_t side, size_t initialIndex) noexcept; + ASMJIT_API void _cleanupBlock(uint32_t side, size_t middleIndex) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ZoneHeap* _heap; //!< ZoneHeap used to allocate data. + Block* _block[2]; //!< First and last blocks. +}; + +// ============================================================================ +// [asmjit::ZoneStack] +// ============================================================================ + +template +class ZoneStack : public ZoneStackBase { +public: + enum { + kNumBlockItems = static_cast((kBlockSize - sizeof(Block)) / sizeof(T)), + kStartBlockIndex = static_cast(sizeof(Block)), + kMidBlockIndex = static_cast(kStartBlockIndex + (kNumBlockItems / 2) * sizeof(T)), + kEndBlockIndex = static_cast(kStartBlockIndex + kNumBlockItems * sizeof(T)) + }; + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ZoneStack() noexcept {} + ASMJIT_INLINE ~ZoneStack() noexcept {} + + // -------------------------------------------------------------------------- + // [Init / Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Error init(ZoneHeap* heap) noexcept { return _init(heap, kMidBlockIndex); } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE Error prepend(T item) noexcept { + ASMJIT_ASSERT(isInitialized()); + Block* block = _block[kSideLeft]; + + if (!block->canPrepend()) { + ASMJIT_PROPAGATE(_prepareBlock(kSideLeft, kEndBlockIndex)); + block = _block[kSideLeft]; + } + + T* ptr = block->getStart() - 1; + ASMJIT_ASSERT(ptr >= block->getData() && ptr < block->getData() + kNumBlockItems); + *ptr = item; + block->setStart(ptr); + return kErrorOk; + } + + ASMJIT_INLINE Error append(T item) noexcept { + ASMJIT_ASSERT(isInitialized()); + Block* block = _block[kSideRight]; + + if (!block->canAppend()) { + ASMJIT_PROPAGATE(_prepareBlock(kSideRight, kStartBlockIndex)); + block = _block[kSideRight]; + } + + T* ptr = block->getEnd(); + ASMJIT_ASSERT(ptr >= block->getData() && ptr < block->getData() + kNumBlockItems); + + *ptr++ = item; + block->setEnd(ptr); + return kErrorOk; + } + + ASMJIT_INLINE T popFirst() noexcept { + ASMJIT_ASSERT(isInitialized()); + ASMJIT_ASSERT(!isEmpty()); + + Block* block = _block[kSideLeft]; + ASMJIT_ASSERT(!block->isEmpty()); + + T* ptr = block->getStart(); + T item = *ptr++; + + block->setStart(ptr); + if (block->isEmpty()) + _cleanupBlock(kSideLeft, kMidBlockIndex); + + return item; + } + + ASMJIT_INLINE T pop() noexcept { + ASMJIT_ASSERT(isInitialized()); + ASMJIT_ASSERT(!isEmpty()); + + Block* block = _block[kSideRight]; + ASMJIT_ASSERT(!block->isEmpty()); + + T* ptr = block->getEnd(); + T item = *--ptr; + + block->setEnd(ptr); + if (block->isEmpty()) + _cleanupBlock(kSideRight, kMidBlockIndex); + + return item; + } +}; + +// ============================================================================ +// [asmjit::ZoneHashNode] +// ============================================================================ + +//! Node used by \ref ZoneHash<> template. +//! +//! You must provide function `bool eq(const Key& key)` in order to make +//! `ZoneHash::get()` working. +class ZoneHashNode { +public: + ASMJIT_INLINE ZoneHashNode(uint32_t hVal = 0) noexcept + : _hashNext(nullptr), + _hVal(hVal) {} + + //! Next node in the chain, null if it terminates the chain. + ZoneHashNode* _hashNext; + //! Key hash. + uint32_t _hVal; + //! Should be used by Node that inherits ZoneHashNode, it aligns ZoneHashNode. + uint32_t _customData; +}; + +// ============================================================================ +// [asmjit::ZoneHashBase] +// ============================================================================ + +class ZoneHashBase { +public: + ASMJIT_NONCOPYABLE(ZoneHashBase) + + // -------------------------------------------------------------------------- + // [Construction / Destruction] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE ZoneHashBase(ZoneHeap* heap) noexcept { + _heap = heap; + _size = 0; + _bucketsCount = 1; + _bucketsGrow = 1; + _data = _embedded; + _embedded[0] = nullptr; + } + ASMJIT_INLINE ~ZoneHashBase() noexcept { reset(nullptr); } + + // -------------------------------------------------------------------------- + // [Reset] + // -------------------------------------------------------------------------- + + ASMJIT_INLINE bool isInitialized() const noexcept { return _heap != nullptr; } + ASMJIT_API void reset(ZoneHeap* heap) noexcept; + + // -------------------------------------------------------------------------- + // [Accessors] + // -------------------------------------------------------------------------- + + //! Get a `ZoneHeap` attached to this container. + ASMJIT_INLINE ZoneHeap* getHeap() const noexcept { return _heap; } + + ASMJIT_INLINE size_t getSize() const noexcept { return _size; } + + // -------------------------------------------------------------------------- + // [Ops] + // -------------------------------------------------------------------------- + + ASMJIT_API void _rehash(uint32_t newCount) noexcept; + ASMJIT_API ZoneHashNode* _put(ZoneHashNode* node) noexcept; + ASMJIT_API ZoneHashNode* _del(ZoneHashNode* node) noexcept; + + // -------------------------------------------------------------------------- + // [Members] + // -------------------------------------------------------------------------- + + ZoneHeap* _heap; //!< ZoneHeap used to allocate data. + size_t _size; //!< Count of records inserted into the hash table. + uint32_t _bucketsCount; //!< Count of hash buckets. + uint32_t _bucketsGrow; //!< When buckets array should grow. + + ZoneHashNode** _data; //!< Buckets data. + ZoneHashNode* _embedded[1]; //!< Embedded data, used by empty hash tables. +}; + +// ============================================================================ +// [asmjit::ZoneHash] +// ============================================================================ + +//! Low-level hash table specialized for storing string keys and POD values. +//! +//! This hash table allows duplicates to be inserted (the API is so low +//! level that it's up to you if you allow it or not, as you should first +//! `get()` the node and then modify it or insert a new node by using `put()`, +//! depending on the intention). +template +class ZoneHash : public ZoneHashBase { +public: + explicit ASMJIT_INLINE ZoneHash(ZoneHeap* heap = nullptr) noexcept + : ZoneHashBase(heap) {} + ASMJIT_INLINE ~ZoneHash() noexcept {} + + template + ASMJIT_INLINE Node* get(const Key& key) const noexcept { + uint32_t hMod = key.hVal % _bucketsCount; + Node* node = static_cast(_data[hMod]); + + while (node && !key.matches(node)) + node = static_cast(node->_hashNext); + return node; + } + + ASMJIT_INLINE Node* put(Node* node) noexcept { return static_cast(_put(node)); } + ASMJIT_INLINE Node* del(Node* node) noexcept { return static_cast(_del(node)); } }; //! \} @@ -214,7 +1323,7 @@ class Zone { } // asmjit namespace // [Api-End] -#include "../apiend.h" +#include "../asmjit_apiend.h" // [Guard] #endif // _ASMJIT_BASE_ZONE_H diff --git a/src/asmjit/host.h b/src/asmjit/host.h deleted file mode 100644 index d99ca9d..0000000 --- a/src/asmjit/host.h +++ /dev/null @@ -1,53 +0,0 @@ -// [AsmJit] -// Complete x86/x64 JIT and Remote Assembler for C++. -// -// [License] -// Zlib - See LICENSE.md file in the package. - -// [Guard] -#ifndef _ASMJIT_HOST_H -#define _ASMJIT_HOST_H - -// [Dependencies] -#include "./base.h" - -// [X86 / X64] -#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 -#include "./x86.h" - -namespace asmjit { - -// Define `asmjit::host` namespace wrapping `asmjit::x86`. -namespace host { using namespace ::asmjit::x86; } - -// Define host assembler. -typedef X86Assembler HostAssembler; - -// Define host operands. -typedef X86GpReg GpReg; -typedef X86FpReg FpReg; -typedef X86MmReg MmReg; -typedef X86XmmReg XmmReg; -typedef X86YmmReg YmmReg; -typedef X86SegReg SegReg; -typedef X86Mem Mem; - -// Define host compiler and related. -#if !defined(ASMJIT_DISABLE_COMPILER) -typedef X86Compiler HostCompiler; -typedef X86CallNode HostCallNode; -typedef X86FuncDecl HostFuncDecl; -typedef X86FuncNode HostFuncNode; - -typedef X86GpVar GpVar; -typedef X86MmVar MmVar; -typedef X86XmmVar XmmVar; -typedef X86YmmVar YmmVar; -#endif // !ASMJIT_DISABLE_COMPILER - -} // asmjit namespace - -#endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 - -// [Guard] -#endif // _ASMJIT_HOST_H diff --git a/src/asmjit/x86.h b/src/asmjit/x86.h index 4d9e796..e850534 100644 --- a/src/asmjit/x86.h +++ b/src/asmjit/x86.h @@ -12,9 +12,11 @@ #include "./base.h" #include "./x86/x86assembler.h" +#include "./x86/x86builder.h" #include "./x86/x86compiler.h" -#include "./x86/x86compilerfunc.h" +#include "./x86/x86emitter.h" #include "./x86/x86inst.h" +#include "./x86/x86misc.h" #include "./x86/x86operand.h" // [Guard] diff --git a/src/asmjit/x86/x86assembler.cpp b/src/asmjit/x86/x86assembler.cpp index 105e8bb..b0ac57c 100644 --- a/src/asmjit/x86/x86assembler.cpp +++ b/src/asmjit/x86/x86assembler.cpp @@ -8,28 +8,35 @@ #define ASMJIT_EXPORTS // [Guard] -#include "../build.h" -#if defined(ASMJIT_BUILD_X86) || defined(ASMJIT_BUILD_X64) +#include "../asmjit_build.h" +#if defined(ASMJIT_BUILD_X86) // [Dependencies] -#include "../base/containers.h" #include "../base/cpuinfo.h" -#include "../base/logger.h" -#include "../base/runtime.h" +#include "../base/logging.h" +#include "../base/misc_p.h" #include "../base/utils.h" -#include "../base/vmem.h" #include "../x86/x86assembler.h" +#include "../x86/x86logging_p.h" // [Api-Begin] -#include "../apibegin.h" +#include "../asmjit_apibegin.h" namespace asmjit { // ============================================================================ -// [Constants] +// [FastUInt8] // ============================================================================ -enum { kX86RexNoRexMask = kX86InstOptionRex | _kX86InstOptionNoRex }; +#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64 +typedef unsigned char FastUInt8; +#else +typedef unsigned int FastUInt8; +#endif + +// ============================================================================ +// [Constants] +// ============================================================================ //! \internal //! @@ -65,28 +72,28 @@ enum X86Byte { //! - `[3]` - Payload2 or `P[23:16]` - `[z L' L b V' a a a]`. //! //! Groups: - //! - `P[ 1: 0]` - EXT: VEX.mmmmm, only lowest 2 bits used. - //! - `P[ 3: 2]` - ___: Must be 0. - //! - `P[ 4]` - REG: EVEX.R'. - //! - `P[ 5]` - REG: EVEX.B. - //! - `P[ 6]` - REG: EVEX.X. - //! - `P[ 7]` - REG: EVEX.R. - //! - `P[ 9: 8]` - EXT: VEX.pp. - //! - `P[ 10]` - ___: Must be 1. - //! - `P[14:11]` - REG: 2nd SRC vector register (4 bits). - //! - `P[ 15]` - EXT: VEX.W. - //! - `P[18:16]` - REG: K registers k0...k7 (Merging/Zeroing Vector Ops). - //! - `P[ 19]` - REG: 2nd SRC vector register (Hi bit). - //! - `P[ 20]` - EXT: Broadcast/Static-Rounding/SAE bit. - //! - `P[22.21]` - EXT: Vector Length/Rounding Control. - //! - `P[ 23]` - EXT: Destination result behavior (Merging/Zeroing Vector Ops). + //! - `P[ 1: 0]` - OPCODE: EVEX.mmmmm, only lowest 2 bits [1:0] used. + //! - `P[ 3: 2]` - ______: Must be 0. + //! - `P[ 4]` - REG-ID: EVEX.R' - 5th bit of 'RRRRR'. + //! - `P[ 5]` - REG-ID: EVEX.B - 4th bit of 'BBBBB'. + //! - `P[ 6]` - REG-ID: EVEX.X - 5th bit of 'BBBBB' or 4th bit of 'XXXX' (with SIB). + //! - `P[ 7]` - REG-ID: EVEX.R - 4th bit of 'RRRRR'. + //! - `P[ 9: 8]` - OPCODE: EVEX.pp. + //! - `P[ 10]` - ______: Must be 1. + //! - `P[14:11]` - REG-ID: 4 bits of 'VVVV'. + //! - `P[ 15]` - OPCODE: EVEX.W. + //! - `P[18:16]` - REG-ID: K register k0...k7 (Merging/Zeroing Vector Ops). + //! - `P[ 19]` - REG-ID: 5th bit of 'VVVVV'. + //! - `P[ 20]` - OPCODE: Broadcast/Rounding Control/SAE bit. + //! - `P[22.21]` - OPCODE: Vector Length (L' and L) / Rounding Control. + //! - `P[ 23]` - OPCODE: Zeroing/Merging. kX86ByteEvex = 0x62 }; -// AsmJit specific (used to encode VVVV field in XOP/VEX). -enum VexVVVV { - kVexVVVVShift = 12, - kVexVVVVMask = 0xF << kVexVVVVShift +// AsmJit specific (used to encode VVVVV field in XOP/VEX/EVEX). +enum VexVVVVV { + kVexVVVVVShift = 7, + kVexVVVVVMask = 0x1F << kVexVVVVVShift }; //! \internal @@ -99,342 +106,4238 @@ struct X86OpCodeMM { //! \internal //! -//! Mandatory prefixes used to encode [66, F3, F2] and [9B]. -static const uint8_t x86OpCodePP[8] = { - 0x00, 0x66, 0xF3, 0xF2, 0x00, 0x00, 0x00, 0x9B -}; +//! Mandatory prefixes used to encode legacy [66, F3, F2] or [9B] byte. +static const uint8_t x86OpCodePP[8] = { 0x00, 0x66, 0xF3, 0xF2, 0x00, 0x00, 0x00, 0x9B }; //! \internal //! //! Instruction 2-byte/3-byte opcode prefix data. static const X86OpCodeMM x86OpCodeMM[] = { - { 0, { 0x00, 0x00, 0 } }, - { 1, { 0x0F, 0x00, 0 } }, - { 2, { 0x0F, 0x38, 0 } }, - { 2, { 0x0F, 0x3A, 0 } }, - { 0, { 0x00, 0x00, 0 } }, - { 0, { 0x00, 0x00, 0 } }, - { 0, { 0x00, 0x00, 0 } }, - { 0, { 0x00, 0x00, 0 } }, - { 0, { 0x00, 0x00, 0 } }, - { 0, { 0x00, 0x00, 0 } }, - { 0, { 0x00, 0x00, 0 } }, - { 0, { 0x00, 0x00, 0 } }, - { 0, { 0x00, 0x00, 0 } }, - { 0, { 0x00, 0x00, 0 } }, - { 0, { 0x00, 0x00, 0 } }, - { 2, { 0x0F, 0x01, 0 } } + { 0, { 0x00, 0x00, 0 } }, // #00 (0b0000). + { 1, { 0x0F, 0x00, 0 } }, // #01 (0b0001). + { 2, { 0x0F, 0x38, 0 } }, // #02 (0b0010). + { 2, { 0x0F, 0x3A, 0 } }, // #03 (0b0011). + { 2, { 0x0F, 0x01, 0 } }, // #04 (0b0100). + { 0, { 0x00, 0x00, 0 } }, // #05 (0b0101). + { 0, { 0x00, 0x00, 0 } }, // #06 (0b0110). + { 0, { 0x00, 0x00, 0 } }, // #07 (0b0111). + { 0, { 0x00, 0x00, 0 } }, // #08 (0b1000). + { 0, { 0x00, 0x00, 0 } }, // #09 (0b1001). + { 0, { 0x00, 0x00, 0 } }, // #0A (0b1010). + { 0, { 0x00, 0x00, 0 } }, // #0B (0b1011). + { 0, { 0x00, 0x00, 0 } }, // #0C (0b1100). + { 0, { 0x00, 0x00, 0 } }, // #0D (0b1101). + { 0, { 0x00, 0x00, 0 } }, // #0E (0b1110). + { 0, { 0x00, 0x00, 0 } } // #0F (0b1111). }; -static const uint8_t x86SegmentPrefix[8] = { 0x00, 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65 }; -static const uint8_t x86OpCodePushSeg[8] = { 0x00, 0x06, 0x0E, 0x16, 0x1E, 0xA0, 0xA8 }; -static const uint8_t x86OpCodePopSeg[8] = { 0x00, 0x07, 0x00, 0x17, 0x1F, 0xA1, 0xA9 }; +static const uint8_t x86SegmentPrefix[8] = { 0x00, 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x00 }; +static const uint8_t x86OpCodePushSeg[8] = { 0x00, 0x06, 0x0E, 0x16, 0x1E, 0xA0, 0xA8, 0x00 }; +static const uint8_t x86OpCodePopSeg[8] = { 0x00, 0x07, 0x00, 0x17, 0x1F, 0xA1, 0xA9, 0x00 }; // ============================================================================ -// [Utils] +// [asmjit::X86MemInfo | X86VEXPrefix | X86LLByRegType | X86CDisp8Table] // ============================================================================ -static ASMJIT_INLINE uint32_t x86RexFromOpCodeAndOptions(uint32_t opCode, uint32_t options) { - uint32_t rex = (opCode >> (kX86InstOpCode_W_Shift - 3)); - ASMJIT_ASSERT((rex & ~static_cast(0x08)) == 0); +//! \internal +//! +//! Memory operand's info bits. +//! +//! A lookup table that contains various information based on the BASE and INDEX +//! information of a memory operand. This is much better and safer than playing +//! with IFs in the code and can check for errors must faster and better. +enum X86MemInfo_Enum { + kX86MemInfo_0 = 0x00, - return rex + (options & kX86RexNoRexMask); + kX86MemInfo_BaseGp = 0x01, //!< Has BASE reg, REX.B can be 1, compatible with REX.B byte. + kX86MemInfo_Index = 0x02, //!< Has INDEX reg, REX.X can be 1, compatible with REX.X byte. + + kX86MemInfo_BaseLabel = 0x10, //!< Base is Label. + kX86MemInfo_BaseRip = 0x20, //!< Base is RIP. + + kX86MemInfo_67H_X86 = 0x40, //!< Address-size override in 32-bit mode. + kX86MemInfo_67H_X64 = 0x80, //!< Address-size override in 64-bit mode. + kX86MemInfo_67H_Mask = 0xC0 //!< Contains all address-size override bits. +}; + +template +struct X86MemInfo_T { + enum { + B = (X ) & 0x1F, + I = (X >> 5) & 0x1F, + + kBase = ((B >= X86Reg::kRegGpw && B <= X86Reg::kRegGpq ) ? kX86MemInfo_BaseGp : + (B == X86Reg::kRegRip ) ? kX86MemInfo_BaseRip : + (B == Label::kLabelTag ) ? kX86MemInfo_BaseLabel : 0), + + kIndex = ((I >= X86Reg::kRegGpw && I <= X86Reg::kRegGpq ) ? kX86MemInfo_Index : + (I >= X86Reg::kRegXmm && I <= X86Reg::kRegZmm ) ? kX86MemInfo_Index : 0), + + k67H = ((B == X86Reg::kRegGpw && I == X86Reg::kRegNone) ? kX86MemInfo_67H_X86 : + (B == X86Reg::kRegGpd && I == X86Reg::kRegNone) ? kX86MemInfo_67H_X64 : + (B == X86Reg::kRegNone && I == X86Reg::kRegGpw ) ? kX86MemInfo_67H_X86 : + (B == X86Reg::kRegNone && I == X86Reg::kRegGpd ) ? kX86MemInfo_67H_X64 : + (B == X86Reg::kRegGpw && I == X86Reg::kRegGpw ) ? kX86MemInfo_67H_X86 : + (B == X86Reg::kRegGpd && I == X86Reg::kRegGpd ) ? kX86MemInfo_67H_X64 : + (B == X86Reg::kRegGpw && I == X86Reg::kRegXmm ) ? kX86MemInfo_67H_X86 : + (B == X86Reg::kRegGpd && I == X86Reg::kRegXmm ) ? kX86MemInfo_67H_X64 : + (B == X86Reg::kRegGpw && I == X86Reg::kRegYmm ) ? kX86MemInfo_67H_X86 : + (B == X86Reg::kRegGpd && I == X86Reg::kRegYmm ) ? kX86MemInfo_67H_X64 : + (B == X86Reg::kRegGpw && I == X86Reg::kRegZmm ) ? kX86MemInfo_67H_X86 : + (B == X86Reg::kRegGpd && I == X86Reg::kRegZmm ) ? kX86MemInfo_67H_X64 : + (B == Label::kLabelTag && I == X86Reg::kRegGpw ) ? kX86MemInfo_67H_X86 : + (B == Label::kLabelTag && I == X86Reg::kRegGpd ) ? kX86MemInfo_67H_X64 : 0), + + kValue = kBase | kIndex | k67H | 0x04 | 0x08 + }; +}; + +// The result stored in the LUT is a combination of +// - 67H - Address override prefix - depends on BASE+INDEX register types and +// the target architecture. +// - REX - A possible combination of REX.[B|X|R|W] bits in REX prefix where +// REX.B and REX.X are possibly masked out, but REX.R and REX.W are +// kept as is. +static const uint8_t x86MemInfo[] = { ASMJIT_TABLE_T_1024(X86MemInfo_T, kValue, 0) }; + +// VEX3 or XOP xor bits applied to the opcode before emitted. The index to this +// table is 'mmmmm' value, which contains all we need. This is only used by a +// 3 BYTE VEX and XOP prefixes, 2 BYTE VEX prefix is handled differently. The +// idea is to minimize the difference between VEX3 vs XOP when encoding VEX +// or XOP instruction. This should minimize the code required to emit such +// instructions and should also make it faster as we don't need any branch to +// decide between VEX3 vs XOP. +// ____ ___ +// [_OPCODE_|WvvvvLpp|RXBmmmmm|VEX3_XOP] +template +struct X86VEXPrefix_T { + enum { kValue = ((X & 0x08) ? kX86ByteXop3 : kX86ByteVex3) | (0xF << 19) | (0x7 << 13) }; +}; +static const uint32_t x86VEXPrefix[] = { ASMJIT_TABLE_T_16(X86VEXPrefix_T, kValue, 0) }; + +// Table that contains LL opcode field addressed by a register size / 16. It's +// used to propagate L.256 or L.512 when YMM or ZMM registers are used, +// respectively. +template +struct X86LLBySizeDiv16_T { + enum { + kValue = (X & (64 >> 4)) ? X86Inst::kOpCode_LL_512 : + (X & (32 >> 4)) ? X86Inst::kOpCode_LL_256 : 0 + }; +}; +static const uint32_t x86LLBySizeDiv16[] = { ASMJIT_TABLE_T_16(X86LLBySizeDiv16_T, kValue, 0) }; + +// Table that contains LL opcode field addressed by a register size / 16. It's +// used to propagate L.256 or L.512 when YMM or ZMM registers are used, +// respectively. +template +struct X86LLByRegType_T { + enum { + kValue = X == X86Reg::kRegZmm ? X86Inst::kOpCode_LL_512 : + X == X86Reg::kRegYmm ? X86Inst::kOpCode_LL_256 : 0 + }; +}; +static const uint32_t x86LLByRegType[] = { ASMJIT_TABLE_T_16(X86LLByRegType_T, kValue, 0) }; + +// Table that contains a scale (shift left) based on 'TTWLL' field and +// the instruction's tuple-type (TT) field. The scale is then applied to +// the BASE-N stored in each opcode to calculate the final compressed +// displacement used by all EVEX encoded instructions. +template +struct X86CDisp8SHL_T { + enum { + TT = (((X) >> 3) << X86Inst::kOpCode_CDTT_Shift), + LL = (((X) >> 0) & 0x3), + W = (((X) >> 2) & 0x1), + + kValue = (TT == X86Inst::kOpCode_CDTT_None ? ((LL==0) ? 0 : (LL==1) ? 0 : 0 ) : + TT == X86Inst::kOpCode_CDTT_ByLL ? ((LL==0) ? 0 : (LL==1) ? 1 : 2 ) : + TT == X86Inst::kOpCode_CDTT_T1W ? ((LL==0) ? W : (LL==1) ? 1+W : 2+W) : + TT == X86Inst::kOpCode_CDTT_DUP ? ((LL==0) ? 0 : (LL==1) ? 2 : 3 ) : 0 ) << X86Inst::kOpCode_CDSHL_Shift + }; +}; +static const uint32_t x86CDisp8SHL[] = { ASMJIT_TABLE_T_32(X86CDisp8SHL_T, kValue, 0) }; + +// Table that contains MOD byte of a 16-bit [BASE + disp] address. +// 0xFF == Invalid. +static const uint8_t x86Mod16BaseTable[8] = { + 0xFF, // AX -> N/A. + 0xFF, // CX -> N/A. + 0xFF, // DX -> N/A. + 0x07, // BX -> 111. + 0xFF, // SP -> N/A. + 0x06, // BP -> 110. + 0x04, // SI -> 100. + 0x05 // DI -> 101. +}; + +// Table that contains MOD byte of a 16-bit [BASE + INDEX + disp] combination. +// 0xFF == Invalid. +template +struct X86Mod16BaseIndexTable_T { + enum { + B = X >> 3, + I = X & 0x7, + + kValue = ((B == X86Gp::kIdBx && I == X86Gp::kIdSi) || (B == X86Gp::kIdSi && I == X86Gp::kIdBx)) ? 0x00 : + ((B == X86Gp::kIdBx && I == X86Gp::kIdDi) || (B == X86Gp::kIdDi && I == X86Gp::kIdBx)) ? 0x01 : + ((B == X86Gp::kIdBp && I == X86Gp::kIdSi) || (B == X86Gp::kIdSi && I == X86Gp::kIdBp)) ? 0x02 : + ((B == X86Gp::kIdBp && I == X86Gp::kIdDi) || (B == X86Gp::kIdDi && I == X86Gp::kIdBp)) ? 0x03 : 0xFF + }; +}; +static const uint8_t x86Mod16BaseIndexTable[] = { ASMJIT_TABLE_T_64(X86Mod16BaseIndexTable_T, kValue, 0) }; + +// ============================================================================ +// [asmjit::X86Assembler - Helpers] +// ============================================================================ + +static ASMJIT_INLINE bool x86IsJmpOrCall(uint32_t instId) noexcept { + return instId == X86Inst::kIdJmp || + instId == X86Inst::kIdCall; } -static ASMJIT_INLINE bool x86RexIsInvalid(uint32_t rex) { - return rex >= _kX86InstOptionNoRex; +static ASMJIT_INLINE bool x86IsImplicitMem(const Operand_& op, uint32_t base) noexcept { + return op.isMem() && op.as().getBaseId() == base; } -//! Encode ModR/M. -static ASMJIT_INLINE uint32_t x86EncodeMod(uint32_t m, uint32_t o, uint32_t rm) { +static ASMJIT_INLINE int64_t x86SignExtend32To64(int64_t imm) noexcept { + return static_cast(static_cast(imm & 0xFFFFFFFF)); +} + +//! Get `O` field of `opCode`. +static ASMJIT_INLINE uint32_t x86ExtractO(uint32_t opCode) noexcept { + return (opCode >> X86Inst::kOpCode_O_Shift) & 0x07; +} + +static ASMJIT_INLINE uint32_t x86ExtractREX(uint32_t opCode, uint32_t options) noexcept { + // kOpCode_REX was designed in a way that when shifted there will be no bytes + // set except REX.[B|X|R|W]. The returned value forms a real REX prefix byte. + // This case is tested by `X86Inst.cpp`. + return (opCode | options) >> X86Inst::kOpCode_REX_Shift; +} + +//! Combine `regId` and `vvvvvId` into a single value (used by AVX and AVX-512). +static ASMJIT_INLINE uint32_t x86PackRegAndVvvvv(uint32_t regId, uint32_t vvvvvId) noexcept { + return regId + (vvvvvId << kVexVVVVVShift); +} + +static ASMJIT_INLINE uint32_t x86OpCodeLByVMem(const Operand_& op) noexcept { + return x86LLByRegType[static_cast(op).getIndexType()]; +} + +static ASMJIT_INLINE uint32_t x86OpCodeLBySize(uint32_t size) noexcept { + return x86LLBySizeDiv16[size / 16]; +} + +static ASMJIT_INLINE uint32_t x86ExtractLLMM(uint32_t opCode, uint32_t options) noexcept { + uint32_t x = opCode & (X86Inst::kOpCode_LL_Mask | X86Inst::kOpCode_MM_Mask); + uint32_t y = options & X86Inst::kOptionVex3; + return (x | y) >> X86Inst::kOpCode_MM_Shift; +} + +//! Encode MOD byte. +static ASMJIT_INLINE uint32_t x86EncodeMod(uint32_t m, uint32_t o, uint32_t rm) noexcept { ASMJIT_ASSERT(m <= 3); ASMJIT_ASSERT(o <= 7); ASMJIT_ASSERT(rm <= 7); return (m << 6) + (o << 3) + rm; } -//! Encode SIB. -static ASMJIT_INLINE uint32_t x86EncodeSib(uint32_t s, uint32_t i, uint32_t b) { +//! Encode SIB byte. +static ASMJIT_INLINE uint32_t x86EncodeSib(uint32_t s, uint32_t i, uint32_t b) noexcept { ASMJIT_ASSERT(s <= 3); ASMJIT_ASSERT(i <= 7); ASMJIT_ASSERT(b <= 7); return (s << 6) + (i << 3) + b; } -//! Get if the given pointers `a` and `b` can be encoded by using relative -//! displacement, which fits into a signed 32-bit integer. -static ASMJIT_INLINE bool x64IsRelative(Ptr a, Ptr b) { - SignedPtr diff = static_cast(a) - static_cast(b); - return Utils::isInt32(diff); -} - -//! Cast `reg` to `X86Reg` and get the register index. -static ASMJIT_INLINE uint32_t x86OpReg(const Operand* reg) { - return static_cast(reg)->getRegIndex(); -} - -//! Cast `mem` to `X86Mem` and return it. -static ASMJIT_INLINE const X86Mem* x86OpMem(const Operand* mem) { - return static_cast(mem); -} - -//! Combine `regIndex` and `vvvvIndex` into single value (used by AVX and AVX-512). -static ASMJIT_INLINE uint32_t x86RegAndVvvv(uint32_t regIndex, uint32_t vvvvIndex) { - return regIndex + (vvvvIndex << kVexVVVVShift); -} - -//! Get `O` field of `opCode`. -static ASMJIT_INLINE uint32_t x86ExtractO(uint32_t opCode) { - return (opCode >> kX86InstOpCode_O_Shift) & 0x07; -} - -static ASMJIT_INLINE bool x86IsGpq(const Operand* op) { return op->isRegType(kX86RegTypeGpq); } -static ASMJIT_INLINE bool x86IsGpq(const X86Reg* reg) { return reg->isGpq(); } - -static ASMJIT_INLINE bool x86IsXmm(const Operand* op) { return op->isRegType(kX86RegTypeXmm); } -static ASMJIT_INLINE bool x86IsXmm(const X86Reg* reg) { return reg->isXmm(); } - -static ASMJIT_INLINE bool x86IsYmm(const Operand* op) { return op->isRegType(kX86RegTypeYmm); } -static ASMJIT_INLINE bool x86IsYmm(const X86Reg* reg) { return reg->isYmm(); } - -static ASMJIT_INLINE bool x86IsZmm(const Operand* op) { return op->isRegType(kX86RegTypeZmm); } -static ASMJIT_INLINE bool x86IsZmm(const X86Reg* reg) { return reg->isZmm(); } - -// ============================================================================ -// [Macros] -// ============================================================================ - -#define ENC_OPS(op0, op1, op2) \ - ((Operand::kType##op0) + ((Operand::kType##op1) << 3) + ((Operand::kType##op2) << 6)) - -#define ADD_66H_P(exp) \ - do { \ - opCode |= (static_cast(exp) << kX86InstOpCode_PP_Shift); \ - } while (0) - -#define ADD_66H_P_BY_SIZE(sz) \ - do { \ - opCode |= (static_cast(sz) & 0x02) << (kX86InstOpCode_PP_Shift - 1); \ - } while (0) - -#define ADD_REX_W(exp) \ - do { \ - if (Arch == kArchX64) \ - opCode |= static_cast(exp) << kX86InstOpCode_W_Shift; \ - } while (0) - -#define ADD_REX_W_BY_SIZE(sz) \ - do { \ - if (Arch == kArchX64 && (sz) == 8) \ - opCode |= kX86InstOpCode_W; \ - } while (0) - -#define ADD_VEX_W(exp) \ - do { \ - opCode |= static_cast(exp) << kX86InstOpCode_W_Shift; \ - } while (0) - -#define ADD_VEX_L(exp) \ - do { \ - opCode |= static_cast(exp) << kX86InstOpCode_L_Shift; \ - } while (0) - -#define EMIT_BYTE(_Val_) \ - do { \ - cursor[0] = static_cast((_Val_) & 0xFF); \ - cursor += 1; \ - } while (0) - -#define EMIT_WORD(_Val_) \ - do { \ - Utils::writeU16uLE(cursor, static_cast(_Val_)); \ - cursor += 2; \ - } while (0) - -#define EMIT_DWORD(_Val_) \ - do { \ - Utils::writeU32uLE(cursor, static_cast(_Val_)); \ - cursor += 4; \ - } while (0) - -#define EMIT_QWORD(_Val_) \ - do { \ - Utils::writeU64uLE(cursor, static_cast(_Val_)); \ - cursor += 8; \ - } while (0) - -#define EMIT_PP(_Val_) \ - do { \ - uint32_t ppIndex = ((_Val_) >> kX86InstOpCode_PP_Shift) & (kX86InstOpCode_PP_Mask >> kX86InstOpCode_PP_Shift); \ - uint8_t ppCode = x86OpCodePP[ppIndex]; \ - \ - if (!ppIndex) \ - break; \ - \ - cursor[0] = ppCode; \ - cursor++; \ - } while (0) - -#define EMIT_MM(_Val_) \ - do { \ - uint32_t mmIndex = ((_Val_) >> kX86InstOpCode_MM_Shift) & (kX86InstOpCode_MM_Mask >> kX86InstOpCode_MM_Shift); \ - const X86OpCodeMM& mmCode = x86OpCodeMM[mmIndex]; \ - \ - if (!mmIndex) \ - break; \ - \ - cursor[0] = mmCode.data[0]; \ - cursor[1] = mmCode.data[1]; \ - cursor += mmCode.len; \ - } while (0) - // ============================================================================ // [asmjit::X86Assembler - Construction / Destruction] // ============================================================================ -X86Assembler::X86Assembler(Runtime* runtime, uint32_t arch) - : Assembler(runtime), - zax(NoInit), - zcx(NoInit), - zdx(NoInit), - zbx(NoInit), - zsp(NoInit), - zbp(NoInit), - zsi(NoInit), - zdi(NoInit) { - - ASMJIT_ASSERT(arch == kArchX86 || arch == kArchX64); - _setArch(arch); +X86Assembler::X86Assembler(CodeHolder* code) noexcept : Assembler() { + if (code) + code->attach(this); } - -X86Assembler::~X86Assembler() {} +X86Assembler::~X86Assembler() noexcept {} // ============================================================================ -// [asmjit::X86Assembler - Arch] +// [asmjit::X86Assembler - Events] // ============================================================================ -Error X86Assembler::_setArch(uint32_t arch) { -#if defined(ASMJIT_BUILD_X86) - if (arch == kArchX86) { - _arch = arch; - _regSize = 4; +Error X86Assembler::onAttach(CodeHolder* code) noexcept { + uint32_t archType = code->getArchType(); + if (!ArchInfo::isX86Family(archType)) + return DebugUtils::errored(kErrorInvalidArch); - _regCount.reset(); - _regCount._gp = 8; - _regCount._mm = 8; - _regCount._k = 8; - _regCount._xyz = 8; - ::memcpy(&zax, &x86RegData.gpd, sizeof(Operand) * 8); + ASMJIT_PROPAGATE(Base::onAttach(code)); - return kErrorOk; - } -#endif // ASMJIT_BUILD_X86 - -#if defined(ASMJIT_BUILD_X64) - if (arch == kArchX64) { - _arch = arch; - _regSize = 8; - - _regCount.reset(); - _regCount._gp = 16; - _regCount._mm = 8; - _regCount._k = 8; - _regCount._xyz = 16; - ::memcpy(&zax, &x86RegData.gpq, sizeof(Operand) * 8); - - return kErrorOk; - } -#endif // ASMJIT_BUILD_X64 - - return kErrorInvalidArgument; -} - -// ============================================================================ -// [asmjit::X86Assembler - Embed] -// ============================================================================ - -Error X86Assembler::embedLabel(const Label& op) { - ASMJIT_ASSERT(op.getId() != kInvalidValue); - uint32_t regSize = _regSize; - - if (getRemainingSpace() < regSize) - ASMJIT_PROPAGATE_ERROR(_grow(regSize)); - - uint8_t* cursor = getCursor(); - LabelData* label = getLabelData(op.getId()); - RelocData rd; - -#if !defined(ASMJIT_DISABLE_LOGGER) - if (_logger) - _logger->logFormat(Logger::kStyleData, regSize == 4 ? ".dd L%u\n" : ".dq L%u\n", op.getId()); -#endif // !ASMJIT_DISABLE_LOGGER - - rd.type = kRelocRelToAbs; - rd.size = regSize; - rd.from = static_cast(getOffset()); - rd.data = 0; - - if (label->offset != -1) { - // Bound label. - rd.data = static_cast(static_cast(label->offset)); + if (archType == ArchInfo::kTypeX86) { + // 32 bit architecture - X86. + _setAddressOverrideMask(kX86MemInfo_67H_X86); + _globalOptions |= X86Inst::_kOptionInvalidRex; + _nativeGpArray = x86OpData.gpd; } else { - // Non-bound label. Need to chain. - LabelLink* link = _newLabelLink(); - - link->prev = (LabelLink*)label->links; - link->offset = getOffset(); - link->displacement = 0; - link->relocId = _relocations.getLength(); - - label->links = link; + // 64 bit architecture - X64 or X32. + _setAddressOverrideMask(kX86MemInfo_67H_X64); + _nativeGpArray = x86OpData.gpq; } - if (_relocations.append(rd) != kErrorOk) - return setLastError(kErrorNoHeapMemory); - - // Emit dummy intptr_t (4 or 8 bytes; depends on the address size). - EMIT_DWORD(0); - if (regSize == 8) - EMIT_DWORD(0); - - setCursor(cursor); + _nativeGpReg = _nativeGpArray[0]; return kErrorOk; } +Error X86Assembler::onDetach(CodeHolder* code) noexcept { + return Base::onDetach(code); +} + +// ============================================================================ +// [asmjit::X86Assembler - Helpers] +// ============================================================================ + +#define EMIT_BYTE(VAL) \ + do { \ + cursor[0] = static_cast((VAL) & 0xFFU); \ + cursor += 1; \ + } while (0) + +#define EMIT_16(VAL) \ + do { \ + Utils::writeU16uLE(cursor, \ + static_cast((VAL) & 0xFFFFU)); \ + cursor += 2; \ + } while (0) + +#define EMIT_32(VAL) \ + do { \ + Utils::writeU32uLE(cursor, \ + static_cast((VAL) & 0xFFFFFFFFU)); \ + cursor += 4; \ + } while (0) + +#define ADD_66H_P(EXP) \ + do { \ + opCode |= (static_cast(EXP) << X86Inst::kOpCode_PP_Shift); \ + } while (0) + +#define ADD_66H_P_BY_SIZE(SIZE) \ + do { \ + opCode |= (static_cast((SIZE) & 0x02)) \ + << (X86Inst::kOpCode_PP_Shift - 1); \ + } while (0) + +#define ADD_REX_W(EXP) \ + do { \ + if (EXP) \ + opCode |= X86Inst::kOpCode_W; \ + } while (0) + +#define ADD_REX_W_BY_SIZE(SIZE) \ + do { \ + if ((SIZE) == 8) \ + opCode |= X86Inst::kOpCode_W; \ + } while (0) + +#define ADD_PREFIX_BY_SIZE(SIZE) \ + do { \ + ADD_66H_P_BY_SIZE(SIZE); \ + ADD_REX_W_BY_SIZE(SIZE); \ + } while (0) + +#define ADD_VEX_W(EXP) \ + do { \ + opCode |= static_cast(EXP) << X86Inst::kOpCode_W_Shift; \ + } while (0) + +#define EMIT_PP(OPCODE) \ + do { \ + uint32_t ppIndex = \ + ((OPCODE ) >> X86Inst::kOpCode_PP_Shift) & \ + (X86Inst::kOpCode_PP_FPUMask >> X86Inst::kOpCode_PP_Shift) ; \ + uint8_t ppCode = x86OpCodePP[ppIndex]; \ + \ + cursor[0] = ppCode; \ + cursor += ppIndex != 0; \ + } while (0) + +#define EMIT_MM_OP(OPCODE) \ + do { \ + uint32_t op = OPCODE & (0x00FF | X86Inst::kOpCode_MM_Mask); \ + \ + uint32_t mmIndex = op >> X86Inst::kOpCode_MM_Shift; \ + const X86OpCodeMM& mmCode = x86OpCodeMM[mmIndex]; \ + \ + if (mmIndex) { \ + cursor[0] = mmCode.data[0]; \ + cursor[1] = mmCode.data[1]; \ + cursor += mmCode.len; \ + } \ + \ + EMIT_BYTE(op); \ + } while (0) + +// If the operand is BPL|SPL|SIL|DIL|R8B-15B +// - Force REX prefix +// If the operand is AH|BH|CH|DH +// - patch its index from 0..3 to 4..7 as encoded by X86. +// - Disallow REX prefix. +#define FIXUP_GPB(REG_OP, REG_ID, ...) \ + do { \ + if (static_cast(REG_OP).isGpbLo()) { \ + options |= (REG_ID >= 4) ? X86Inst::kOptionRex : 0; \ + } \ + else { \ + ASMJIT_ASSERT(X86Reg::isGpbHi(REG_OP)); \ + options |= X86Inst::_kOptionInvalidRex; \ + REG_ID += 4; \ + } \ + } while (0) + +#define ENC_OPS1(OP0) ((Operand::kOp##OP0)) +#define ENC_OPS2(OP0, OP1) ((Operand::kOp##OP0) + ((Operand::kOp##OP1) << 3)) +#define ENC_OPS3(OP0, OP1, OP2) ((Operand::kOp##OP0) + ((Operand::kOp##OP1) << 3) + ((Operand::kOp##OP2) << 6)) +#define ENC_OPS4(OP0, OP1, OP2, OP3) ((Operand::kOp##OP0) + ((Operand::kOp##OP1) << 3) + ((Operand::kOp##OP2) << 6) + ((Operand::kOp##OP3) << 9)) + +// ============================================================================ +// [asmjit::X86Assembler - Emit] +// ============================================================================ + +Error X86Assembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) { + Error err; + + const Operand_* rmRel; // Memory operand or operand that holds Label|Imm. + uint32_t rmInfo; // Memory operand's info based on x86MemInfo. + uint32_t rbReg; // Memory base or modRM register. + uint32_t rxReg; // Memory index register. + uint32_t opReg; // ModR/M opcode or register id. + uint32_t opCode; // Instruction opcode. + + LabelEntry* label; // Label entry. + RelocEntry* re = nullptr; // Relocation entry. + int32_t relOffset; // Relative offset + FastUInt8 relSize = 0; // Relative size. + + int64_t imVal; // Immediate value (must be 64-bit). + FastUInt8 imLen = 0; // Immediate length. + + const uint32_t kSHR_W_PP = X86Inst::kOpCode_PP_Shift - 16; + const uint32_t kSHR_W_EW = X86Inst::kOpCode_EW_Shift - 23; + + uint8_t* cursor = _bufferPtr; + uint32_t options = static_cast(instId >= X86Inst::_kIdCount) | + static_cast((size_t)(_bufferEnd - cursor) < 16) | + getGlobalOptions() | getOptions(); + + const X86Inst* instData = X86InstDB::instData + instId; + const X86Inst::CommonData* commonData; + + // Handle failure and rare cases first. + const uint32_t kErrorsAndSpecialCases = + CodeEmitter::kOptionMaybeFailureCase | // Error and buffer check. + CodeEmitter::kOptionStrictValidation | // Strict validation. + X86Inst::kOptionRep | // REP/REPZ prefix. + X86Inst::kOptionRepnz | // REPNZ prefix. + X86Inst::kOptionLock ; // LOCK prefix. + + // Signature of the first 3 operands. + uint32_t isign3 = o0.getOp() + (o1.getOp() << 3) + (o2.getOp() << 6); + + if (ASMJIT_UNLIKELY(options & kErrorsAndSpecialCases)) { + // Don't do anything if we are in error state. + if (_lastError) return _lastError; + + if (options & CodeEmitter::kOptionMaybeFailureCase) { + // Unknown instruction. + if (ASMJIT_UNLIKELY(instId >= X86Inst::_kIdCount)) + goto InvalidArgument; + + // Grow request, happens rarely. + if ((size_t)(_bufferEnd - cursor) < 16) { + err = _code->growBuffer(&_section->_buffer, 16); + if (ASMJIT_UNLIKELY(err)) goto Failed; + + cursor = _bufferPtr; + } + } + + // Strict validation. +#if !defined(ASMJIT_DISABLE_VALIDATION) + if (options & CodeEmitter::kOptionStrictValidation) + ASMJIT_PROPAGATE(_validate(instId, o0, o1, o2, o3)); +#endif // !ASMJIT_DISABLE_VALIDATION + + uint32_t instFlags = instData->getFlags(); + + // LOCK prefix. + if (options & X86Inst::kOptionLock) { + if (ASMJIT_UNLIKELY(!(instFlags & X86Inst::kInstFlagLock))) + goto InvalidInstruction; + EMIT_BYTE(0xF0); + } + + // REP / REPNZ prefix. + if (options & (X86Inst::kOptionRep | X86Inst::kOptionRepnz)) { + if (ASMJIT_UNLIKELY(!(instFlags & (X86Inst::kInstFlagRep | X86Inst::kInstFlagRepnz)))) + goto InvalidInstruction; + + if (!_opExtra.isNone() && ASMJIT_UNLIKELY(!X86Reg::isGp(_opExtra, X86Gp::kIdCx))) + goto InvalidInstruction; + + EMIT_BYTE((options & X86Inst::kOptionRepnz) ? 0xF2 : 0xF3); + } + } + + // -------------------------------------------------------------------------- + // [Encoding Scope] + // -------------------------------------------------------------------------- + + opCode = instData->getMainOpCode(); + opReg = x86ExtractO(opCode); + commonData = &instData->getCommonData(); + + switch (instData->getEncodingType()) { + case X86Inst::kEncodingNone: + goto EmitDone; + + // ------------------------------------------------------------------------ + // [X86] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingX86Op: + goto EmitX86Op; + + case X86Inst::kEncodingX86Op_O: + rbReg = 0; + goto EmitX86R; + + case X86Inst::kEncodingX86Op_xAX: + if (isign3 == 0) + goto EmitX86Op; + + if (isign3 == ENC_OPS1(Reg) && o0.getId() == X86Gp::kIdAx) + goto EmitX86Op; + break; + + case X86Inst::kEncodingX86Op_xDX_xAX: + if (isign3 == 0) + goto EmitX86Op; + + if (isign3 == ENC_OPS2(Reg, Reg) && o0.getId() == X86Gp::kIdDx && + o1.getId() == X86Gp::kIdAx) + goto EmitX86Op; + break; + + case X86Inst::kEncodingX86Op_ZAX: + if (isign3 == 0) + goto EmitX86Op; + + rmRel = &o0; + if (isign3 == ENC_OPS1(Mem) && x86IsImplicitMem(o0, X86Gp::kIdAx)) + goto EmitX86OpImplicitMem; + + break; + + case X86Inst::kEncodingX86I_xAX: + // Implicit form. + if (isign3 == ENC_OPS1(Imm)) { + imVal = o0.as().getUInt8(); + imLen = 1; + goto EmitX86Op; + } + + // Explicit form. + if (isign3 == ENC_OPS2(Reg, Imm) && o0.getId() == X86Gp::kIdAx) { + imVal = o1.as().getUInt8(); + imLen = 1; + goto EmitX86Op; + } + break; + + case X86Inst::kEncodingX86M: + rbReg = o0.getId(); + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (isign3 == ENC_OPS1(Reg)) + goto EmitX86R; + + rmRel = &o0; + if (isign3 == ENC_OPS1(Mem)) + goto EmitX86M; + break; + + case X86Inst::kEncodingX86M_GPB_MulDiv: +CaseX86M_GPB_MulDiv: + // Explicit form? + if (isign3 > 0x7) { + // [AX] <- [AX] div|mul r8. + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (ASMJIT_UNLIKELY(!X86Reg::isGpw(o0, X86Gp::kIdAx) || !X86Reg::isGpb(o1))) + goto InvalidInstruction; + + rbReg = o1.getId(); + FIXUP_GPB(o1, rbReg); + goto EmitX86R; + } + + // [AX] <- [AX] div|mul m8. + if (isign3 == ENC_OPS2(Reg, Mem)) { + if (ASMJIT_UNLIKELY(!X86Reg::isGpw(o0, X86Gp::kIdAx))) + goto InvalidInstruction; + + rmRel = &o1; + goto EmitX86M; + } + + // [?DX:?AX] <- [?DX:?AX] div|mul r16|r32|r64 + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + if (ASMJIT_UNLIKELY(o0.getSize() != o1.getSize())) + goto InvalidInstruction; + rbReg = o2.getId(); + + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + + // [?DX:?AX] <- [?DX:?AX] div|mul m16|m32|m64 + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + if (ASMJIT_UNLIKELY(o0.getSize() != o1.getSize())) + goto InvalidInstruction; + rmRel = &o2; + + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + + goto InvalidInstruction; + } + + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingX86M_GPB: + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + goto EmitX86R; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS1(Mem)) { + if (ASMJIT_UNLIKELY(o0.getSize() == 0)) + goto AmbiguousOperandSize; + rmRel = &o0; + + opCode += o0.getSize() != 1; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86M_Only: + if (isign3 == ENC_OPS1(Mem)) { + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Rm: + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Arith: + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (o0.getSize() != o1.getSize()) + goto OperandSizeMismatch; + + opReg = o0.getId(); + rbReg = o1.getId(); + + if (o0.getSize() == 1) { + opCode += 2; + FIXUP_GPB(o0, opReg); + FIXUP_GPB(o1, rbReg); + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode -= 2; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + else { + opCode += 3; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode -= 2; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, opReg); + opCode += 2; + goto EmitX86M; + } + else { + opCode += 3; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + if (o1.getSize() == 1) { + FIXUP_GPB(o1, opReg); + goto EmitX86M; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o1.getSize()); + goto EmitX86M; + } + } + + // The remaining instructions use 0x80 opcode. + opCode = 0x80; + + if (isign3 == ENC_OPS2(Reg, Imm)) { + uint32_t size = o0.getSize(); + + rbReg = o0.getId(); + imVal = static_cast(o1).getInt64(); + + if (size == 1) { + FIXUP_GPB(o0, rbReg); + imLen = 1; + } + else { + if (size == 2) { + ADD_66H_P(1); + } + else if (size == 4) { + // Sign extend so isInt8 returns the right result. + imVal = x86SignExtend32To64(imVal); + } + else if (size == 8) { + // In 64-bit mode it's not possible to use 64-bit immediate. + if (Utils::isUInt32(imVal)) { + // Zero-extend `and` by using a 32-bit GPD destination instead of a 64-bit GPQ. + if (instId == X86Inst::kIdAnd) + size = 4; + else if (!Utils::isInt32(imVal)) + goto InvalidInstruction; + } + ADD_REX_W_BY_SIZE(size); + } + + imLen = std::min(size, 4); + if (Utils::isInt8(imVal) && !(options & X86Inst::kOptionLongForm)) + imLen = 1; + } + + // Alternate Form - AL, AX, EAX, RAX. + if (rbReg == 0 && (size == 1 || imLen != 1) && !(options & X86Inst::kOptionLongForm)) { + opCode &= X86Inst::kOpCode_PP_66 | X86Inst::kOpCode_W; + opCode |= ((opReg << 3) | (0x04 + (size != 1))); + imLen = std::min(size, 4); + goto EmitX86Op; + } + + opCode += size != 1 ? (imLen != 1 ? 1 : 3) : 0; + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Mem, Imm)) { + uint32_t memSize = o0.getSize(); + + if (ASMJIT_UNLIKELY(memSize == 0)) + goto AmbiguousOperandSize; + + imVal = static_cast(o1).getInt64(); + imLen = std::min(memSize, 4); + + // Sign extend so isInt8 returns the right result. + if (memSize == 4) + imVal = x86SignExtend32To64(imVal); + + if (Utils::isInt8(imVal) && !(options & X86Inst::kOptionLongForm)) + imLen = 1; + + opCode += memSize != 1 ? (imLen != 1 ? 1 : 3) : 0; + ADD_PREFIX_BY_SIZE(memSize); + + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Bswap: + if (isign3 == ENC_OPS1(Reg)) { + if (ASMJIT_UNLIKELY(o0.getSize() < 4)) + goto InvalidInstruction; + + opReg = o0.getId(); + ADD_REX_W_BY_SIZE(o0.getSize()); + goto EmitX86OpReg; + } + break; + + case X86Inst::kEncodingX86Bt: + if (isign3 == ENC_OPS2(Reg, Reg)) { + ADD_PREFIX_BY_SIZE(o1.getSize()); + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + ADD_PREFIX_BY_SIZE(o1.getSize()); + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + + // The remaining instructions use the secondary opcode/r. + imVal = static_cast(o1).getInt64(); + imLen = 1; + + opCode = commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (isign3 == ENC_OPS2(Reg, Imm)) { + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Mem, Imm)) { + if (ASMJIT_UNLIKELY(o0.getSize() == 0)) + goto AmbiguousOperandSize; + + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Call: + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + goto EmitX86R; + } + + rmRel = &o0; + if (isign3 == ENC_OPS1(Mem)) + goto EmitX86M; + + // Call with 32-bit displacement use 0xE8 opcode. Call with 8-bit + // displacement is not encodable so the alternative opcode field + // in X86DB must be zero. + opCode = 0xE8; + goto EmitJmpCall; + + case X86Inst::kEncodingX86Cmpxchg: { + // Convert explicit to implicit. + if (isign3 & (0x7 << 6)) { + if (!X86Reg::isGp(o2) || o2.getId() != X86Gp::kIdAx) + goto InvalidInstruction; + isign3 &= 0x3F; + } + + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (o0.getSize() != o1.getSize()) + goto OperandSizeMismatch; + + rbReg = o0.getId(); + opReg = o1.getId(); + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + FIXUP_GPB(o1, opReg); + goto EmitX86R; + } + else { + ADD_PREFIX_BY_SIZE(o0.getSize()); + opCode++; + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + if (o1.getSize() == 1) { + FIXUP_GPB(o0, opReg); + goto EmitX86M; + } + else { + ADD_PREFIX_BY_SIZE(o1.getSize()); + opCode++; + goto EmitX86M; + } + } + break; + } + + case X86Inst::kEncodingX86Crc: + opReg = o0.getId(); + + if (isign3 == ENC_OPS2(Reg, Reg)) { + rbReg = o1.getId(); + if (o1.getSize() == 1) { + FIXUP_GPB(o1, rbReg); + goto EmitX86R; + } + else { + // This seems to be the only exception of encoding 66F2 PP prefix. + if (o1.getSize() == 2) EMIT_BYTE(0x66); + + opCode++; + ADD_REX_W_BY_SIZE(o1.getSize()); + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + rmRel = &o1; + if (o1.getSize() == 0) + goto AmbiguousOperandSize; + + // This seems to be the only exception of encoding 66F2 PP prefix. + if (o1.getSize() == 2) EMIT_BYTE(0x66); + + opCode += o1.getSize() != 1; + ADD_REX_W_BY_SIZE(o1.getSize()); + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Enter: + if (isign3 == ENC_OPS2(Imm, Imm)) { + imVal = (static_cast(static_cast(o1).getUInt16()) << 0) | + (static_cast(static_cast(o0).getUInt8()) << 16) ; + imLen = 3; + goto EmitX86Op; + } + break; + + case X86Inst::kEncodingX86Imul: + // First process all forms distinct of `kEncodingX86M_OptB_MulDiv`. + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opCode = 0x6B; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (!Utils::isInt8(imVal) || (options & X86Inst::kOptionLongForm)) { + opCode -= 2; + imLen = o0.getSize() == 2 ? 2 : 4; + } + + opReg = o0.getId(); + rbReg = o1.getId(); + + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opCode = 0x6B; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + // Sign extend so isInt8 returns the right result. + if (o0.getSize() == 4) + imVal = x86SignExtend32To64(imVal); + + if (!Utils::isInt8(imVal) || (options & X86Inst::kOptionLongForm)) { + opCode -= 2; + imLen = o0.getSize() == 2 ? 2 : 4; + } + + opReg = o0.getId(); + rmRel = &o1; + + goto EmitX86M; + } + + if (isign3 == ENC_OPS2(Reg, Reg)) { + // Must be explicit 'ax, r8' form. + if (o1.getSize() == 1) + goto CaseX86M_GPB_MulDiv; + + if (o0.getSize() != o1.getSize()) + goto OperandSizeMismatch; + + opReg = o0.getId(); + rbReg = o1.getId(); + + opCode = X86Inst::kOpCode_MM_0F | 0xAF; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + // Must be explicit 'ax, m8' form. + if (o1.getSize() == 1) + goto CaseX86M_GPB_MulDiv; + + opReg = o0.getId(); + rmRel = &o1; + + opCode = X86Inst::kOpCode_MM_0F | 0xAF; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + + // Shorthand to imul 'reg, reg, imm'. + if (isign3 == ENC_OPS2(Reg, Imm)) { + opCode = 0x6B; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + imVal = static_cast(o1).getInt64(); + imLen = 1; + + // Sign extend so isInt8 returns the right result. + if (o0.getSize() == 4) + imVal = x86SignExtend32To64(imVal); + + if (!Utils::isInt8(imVal) || (options & X86Inst::kOptionLongForm)) { + opCode -= 2; + imLen = o0.getSize() == 2 ? 2 : 4; + } + + opReg = rbReg = o0.getId(); + goto EmitX86R; + } + + // Try implicit form. + goto CaseX86M_GPB_MulDiv; + + case X86Inst::kEncodingX86In: + if (isign3 == ENC_OPS2(Reg, Imm)) { + if (ASMJIT_UNLIKELY(o0.getId() != X86Gp::kIdAx)) + goto InvalidInstruction; + + imVal = o1.as().getUInt8(); + imLen = 1; + + opCode = commonData->getAltOpCode() + (o0.getSize() != 1); + ADD_66H_P_BY_SIZE(o0.getSize()); + goto EmitX86Op; + } + + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (ASMJIT_UNLIKELY(o0.getId() != X86Gp::kIdAx || o1.getId() != X86Gp::kIdDx)) + goto InvalidInstruction; + + opCode += o0.getSize() != 1; + ADD_66H_P_BY_SIZE(o0.getSize()); + goto EmitX86Op; + } + break; + + case X86Inst::kEncodingX86Ins: + if (isign3 == ENC_OPS2(Mem, Reg)) { + if (ASMJIT_UNLIKELY(!x86IsImplicitMem(o0, X86Gp::kIdDi) || o1.getId() != X86Gp::kIdDx)) + goto InvalidInstruction; + + uint32_t size = o0.getSize(); + if (ASMJIT_UNLIKELY(size == 0)) + goto AmbiguousOperandSize; + + rmRel = &o0; + opCode += (size != 1); + + ADD_66H_P_BY_SIZE(size); + goto EmitX86OpImplicitMem; + } + break; + + case X86Inst::kEncodingX86IncDec: + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + goto EmitX86R; + } + + if (is32Bit()) { + // INC r16|r32 is only encodable in 32-bit mode (collides with REX). + opCode = commonData->getAltOpCode() + (rbReg & 0x07); + ADD_66H_P_BY_SIZE(o0.getSize()); + goto EmitX86Op; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS1(Mem)) { + rmRel = &o0; + opCode += o0.getSize() != 1; + + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Int: + if (isign3 == ENC_OPS1(Imm)) { + imVal = static_cast(o0).getInt64(); + imLen = 1; + goto EmitX86Op; + } + break; + + case X86Inst::kEncodingX86Jcc: + if (_globalHints & CodeEmitter::kHintPredictedJumps) { + if (options & X86Inst::kOptionTaken) + EMIT_BYTE(0x3E); + if (options & X86Inst::kOptionNotTaken) + EMIT_BYTE(0x2E); + } + + rmRel = &o0; + goto EmitJmpCall; + + case X86Inst::kEncodingX86JecxzLoop: + rmRel = &o0; + // Explicit jecxz|loop [r|e]cx, dst + if (o0.isReg()) { + if (ASMJIT_UNLIKELY(!X86Reg::isGp(o0, X86Gp::kIdCx))) + goto InvalidInstruction; + + if ((is32Bit() && o0.getSize() == 2) || (is64Bit() && o0.getSize() == 4)) + EMIT_BYTE(0x67); + + rmRel = &o1; + } + goto EmitJmpCall; + + case X86Inst::kEncodingX86Jmp: + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + goto EmitX86R; + } + + rmRel = &o0; + if (isign3 == ENC_OPS1(Mem)) + goto EmitX86M; + + // Jump encoded with 32-bit displacement use 0xE9 opcode. Jump encoded + // with 8-bit displacement's opcode is stored as an alternative opcode. + opCode = 0xE9; + goto EmitJmpCall; + + case X86Inst::kEncodingX86Lea: + if (isign3 == ENC_OPS2(Reg, Mem)) { + ADD_PREFIX_BY_SIZE(o0.getSize()); + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Mov: + // Reg <- Reg + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + + // Asmjit uses segment registers indexed from 1 to 6, leaving zero as + // "no segment register used". We have to fix this (decrement the index + // of the register) when emitting MOV instructions which move to/from + // a segment register. The segment register is always `opReg`, because + // the MOV instruction uses either RM or MR encoding. + + // GP <- ?? + if (X86Reg::isGp(o0)) { + // GP <- GP + if (X86Reg::isGp(o1)) { + uint32_t size0 = o0.getSize(); + uint32_t size1 = o1.getSize(); + + if (size0 != size1) { + // We allow 'mov r64, r32' as it's basically zero-extend. + if (size0 == 8 && size1 == 4) + size0 = 4; // Zero extend, don't promote to 64-bit. + else + goto InvalidInstruction; + } + + if (size0 == 1) { + FIXUP_GPB(o0, opReg); + FIXUP_GPB(o1, rbReg); + opCode = 0x8A; + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode -= 2; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + else { + opCode = 0x8B; + ADD_PREFIX_BY_SIZE(size0); + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode -= 2; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + } + + opReg = rbReg; + rbReg = o0.getId(); + + // GP <- SEG + if (X86Reg::isSeg(o1)) { + opCode = 0x8C; + opReg--; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + + // GP <- CR + if (X86Reg::isCr(o1)) { + opCode = 0x20 | X86Inst::kOpCode_MM_0F; + goto EmitX86R; + } + + // GP <- DR + if (X86Reg::isDr(o1)) { + opCode = 0x21 | X86Inst::kOpCode_MM_0F; + goto EmitX86R; + } + } + else { + // ?? <- GP + if (!X86Reg::isGp(o1)) + goto InvalidInstruction; + + // SEG <- GP + if (X86Reg::isSeg(o0)) { + opCode = 0x8E; + opReg--; + ADD_PREFIX_BY_SIZE(o1.getSize()); + goto EmitX86R; + } + + // CR <- GP + if (X86Reg::isCr(o0)) { + opCode = 0x22 | X86Inst::kOpCode_MM_0F; + goto EmitX86R; + } + + // DR <- GP + if (X86Reg::isDr(o0)) { + opCode = 0x23 | X86Inst::kOpCode_MM_0F; + goto EmitX86R; + } + } + + goto InvalidInstruction; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + + // SEG <- Mem + if (X86Reg::isSeg(o0)) { + opCode = 0x8E; + opReg--; + ADD_PREFIX_BY_SIZE(o1.getSize()); + goto EmitX86M; + } + // Reg <- Mem + else { + if (o0.getSize() == 1) { + opCode = 0; + FIXUP_GPB(o0, opReg); + } + else { + opCode = 1; + ADD_PREFIX_BY_SIZE(o0.getSize()); + } + + // Handle a special form 'mov al|ax|eax|rax, [ptr64]' that doesn't use MOD. + if (o0.getId() == X86Gp::kIdAx && !rmRel->as().hasBaseOrIndex()) { + opCode += 0xA0; + imVal = rmRel->as().getOffset(); + imLen = getGpSize(); + goto EmitX86Op; + } + else { + opCode += 0x8A; + goto EmitX86M; + } + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + // Mem <- SEG + if (X86Reg::isSeg(o1)) { + opCode = 0x8C; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + // Mem <- Reg + else { + if (o1.getSize() == 1) { + opCode = 0; + FIXUP_GPB(o1, opReg); + } + else { + opCode = 1; + ADD_PREFIX_BY_SIZE(o1.getSize()); + } + + // Handle a special form 'mov [ptr64], al|ax|eax|rax' that doesn't use MOD. + if (!rmRel->as().hasBaseOrIndex() && o1.getId() == X86Gp::kIdAx) { + opCode += 0xA2; + imVal = rmRel->as().getOffset(); + imLen = getGpSize(); + goto EmitX86Op; + } + else { + opCode += 0x88; + goto EmitX86M; + } + } + } + + if (isign3 == ENC_OPS2(Reg, Imm)) { + opReg = o0.getId(); + imLen = o0.getSize(); + + if (imLen == 1) { + FIXUP_GPB(o0, opReg); + + imVal = static_cast(o1).getUInt8(); + opCode = 0xB0; + goto EmitX86OpReg; + } + else { + // 64-bit immediate in 64-bit mode is allowed. + imVal = static_cast(o1).getInt64(); + + // Optimize the instruction size by using a 32-bit immediate if possible. + if (imLen == 8 && !(options & X86Inst::kOptionLongForm)) { + if (Utils::isUInt32(imVal)) { + // Zero-extend by using a 32-bit GPD destination instead of a 64-bit GPQ. + imLen = 4; + } + else if (Utils::isInt32(imVal)) { + // Sign-extend, uses 'C7 /0' opcode. + rbReg = opReg; + + opCode = 0xC7 | X86Inst::kOpCode_W; + opReg = 0; + + imLen = 4; + goto EmitX86R; + } + } + + opCode = 0xB8; + ADD_PREFIX_BY_SIZE(imLen); + goto EmitX86OpReg; + } + } + + if (isign3 == ENC_OPS2(Mem, Imm)) { + uint32_t memSize = o0.getSize(); + + if (ASMJIT_UNLIKELY(memSize == 0)) + goto AmbiguousOperandSize; + + imVal = static_cast(o1).getInt64(); + imLen = std::min(memSize, 4); + + opCode = 0xC6 + (memSize != 1); + opReg = 0; + ADD_PREFIX_BY_SIZE(memSize); + + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86MovsxMovzx: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (o1.getSize() == 1) { + FIXUP_GPB(o1, rbReg); + goto EmitX86R; + } + else { + opCode++; + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opCode += o1.getSize() != 1; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Out: + if (isign3 == ENC_OPS2(Imm, Reg)) { + if (ASMJIT_UNLIKELY(o1.getId() != X86Gp::kIdAx)) + goto InvalidInstruction; + + imVal = o0.as().getUInt8(); + imLen = 1; + + opCode = commonData->getAltOpCode() + (o1.getSize() != 1); + ADD_66H_P_BY_SIZE(o1.getSize()); + goto EmitX86Op; + } + + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (ASMJIT_UNLIKELY(o0.getId() != X86Gp::kIdDx || o1.getId() != X86Gp::kIdAx)) + goto InvalidInstruction; + + opCode += o1.getSize() != 1; + ADD_66H_P_BY_SIZE(o1.getSize()); + goto EmitX86Op; + } + break; + + case X86Inst::kEncodingX86Outs: + if (isign3 == ENC_OPS2(Reg, Mem)) { + if (ASMJIT_UNLIKELY(o0.getId() != X86Gp::kIdDx), !x86IsImplicitMem(o1, X86Gp::kIdSi)) + goto InvalidInstruction; + + uint32_t size = o1.getSize(); + if (ASMJIT_UNLIKELY(size == 0)) + goto AmbiguousOperandSize; + + rmRel = &o1; + opCode += (size != 1); + + ADD_66H_P_BY_SIZE(size); + goto EmitX86OpImplicitMem; + } + break; + + case X86Inst::kEncodingX86Push: + if (isign3 == ENC_OPS1(Reg)) { + if (X86Reg::isSeg(o0)) { + uint32_t segment = o0.getId(); + if (ASMJIT_UNLIKELY(segment >= X86Seg::kIdCount)) + goto InvalidSegment; + + if (segment >= X86Seg::kIdFs) + EMIT_BYTE(0x0F); + + EMIT_BYTE(x86OpCodePushSeg[segment]); + goto EmitDone; + } + else { + goto CaseX86Pop_Gp; + } + } + + if (isign3 == ENC_OPS1(Imm)) { + imVal = static_cast(o0).getInt64(); + imLen = 4; + + if (Utils::isInt8(imVal) && !(options & X86Inst::kOptionLongForm)) + imLen = 1; + + opCode = imLen == 1 ? 0x6A : 0x68; + goto EmitX86Op; + } + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingX86Pop: + if (isign3 == ENC_OPS1(Reg)) { + if (X86Reg::isSeg(o0)) { + uint32_t segment = o0.getId(); + if (ASMJIT_UNLIKELY(segment == X86Seg::kIdCs || segment >= X86Seg::kIdCount)) + goto InvalidSegment; + + if (segment >= X86Seg::kIdFs) + EMIT_BYTE(0x0F); + + EMIT_BYTE(x86OpCodePopSeg[segment]); + goto EmitDone; + } + else { +CaseX86Pop_Gp: + // We allow 2 byte, 4 byte, and 8 byte register sizes, although PUSH + // and POP only allow 2 bytes or native size. On 64-bit we simply + // PUSH/POP 64-bit register even if 32-bit register was given. + if (ASMJIT_UNLIKELY(o0.getSize() < 2)) + goto InvalidInstruction; + + opCode = commonData->getAltOpCode(); + opReg = o0.getId(); + + ADD_66H_P_BY_SIZE(o0.getSize()); + goto EmitX86OpReg; + } + } + + if (isign3 == ENC_OPS1(Mem)) { + if (ASMJIT_UNLIKELY(o0.getSize() == 0)) + goto AmbiguousOperandSize; + + if (ASMJIT_UNLIKELY(o0.getSize() != 2 && o0.getSize() != getGpSize())) + goto InvalidInstruction; + + ADD_66H_P_BY_SIZE(o0.getSize()); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Ret: + if (isign3 == 0) { + // 'ret' without immediate, change C2 to C3. + opCode++; + goto EmitX86Op; + } + + if (isign3 == ENC_OPS1(Imm)) { + imVal = static_cast(o0).getInt64(); + if (imVal == 0 && !(options & X86Inst::kOptionLongForm)) { + // 'ret' without immediate, change C2 to C3. + opCode++; + goto EmitX86Op; + } + else { + imLen = 2; + goto EmitX86Op; + } + } + break; + + case X86Inst::kEncodingX86Rot: + if (o0.isReg()) { + rbReg = o0.getId(); + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + } + + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (ASMJIT_UNLIKELY(o1.getId() != X86Gp::kIdCx)) + goto InvalidInstruction; + + opCode += 2; + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Imm)) { + imVal = static_cast(o1).getInt64() & 0xFF; + imLen = 0; + + if (imVal == 1 && !(options & X86Inst::kOptionLongForm)) + goto EmitX86R; + + imLen = 1; + opCode -= 0x10; + goto EmitX86R; + } + } + else { + opCode += o0.getSize() != 1; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + if (isign3 == ENC_OPS2(Mem, Reg)) { + if (ASMJIT_UNLIKELY(o1.getId() != X86Gp::kIdCx)) + goto InvalidInstruction; + + opCode += 2; + rmRel = &o0; + goto EmitX86M; + } + + if (isign3 == ENC_OPS2(Mem, Imm)) { + if (ASMJIT_UNLIKELY(o0.getSize() == 0)) + goto AmbiguousOperandSize; + + imVal = static_cast(o1).getInt64() & 0xFF; + imLen = 0; + rmRel = &o0; + + if (imVal == 1 && !(options & X86Inst::kOptionLongForm)) + goto EmitX86M; + + imLen = 1; + opCode -= 0x10; + goto EmitX86M; + } + } + break; + + case X86Inst::kEncodingX86Set: + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS1(Mem)) { + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86ShldShrd: + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + ADD_PREFIX_BY_SIZE(o0.getSize()); + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Mem, Reg, Imm)) { + ADD_PREFIX_BY_SIZE(o1.getSize()); + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + + // The following instructions use opCode + 1. + opCode++; + + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + if (ASMJIT_UNLIKELY(o2.getId() != X86Gp::kIdCx)) + goto InvalidInstruction; + + ADD_PREFIX_BY_SIZE(o0.getSize()); + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Mem, Reg, Reg)) { + if (ASMJIT_UNLIKELY(o2.getId() != X86Gp::kIdCx)) + goto InvalidInstruction; + + ADD_PREFIX_BY_SIZE(o1.getSize()); + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86StrRm: + if (isign3 == ENC_OPS2(Reg, Mem)) { + rmRel = &o1; + if (ASMJIT_UNLIKELY(rmRel->as().getOffsetLo32() || !X86Reg::isGp(o0.as(), X86Gp::kIdAx))) + goto InvalidInstruction; + + uint32_t size = o0.getSize(); + if (o1.hasSize() && ASMJIT_UNLIKELY(o1.getSize() != size)) + goto OperandSizeMismatch; + + ADD_PREFIX_BY_SIZE(size); + opCode += static_cast(size != 1); + + goto EmitX86OpImplicitMem; + } + break; + + case X86Inst::kEncodingX86StrMr: + if (isign3 == ENC_OPS2(Mem, Reg)) { + rmRel = &o0; + if (ASMJIT_UNLIKELY(rmRel->as().getOffsetLo32() || !X86Reg::isGp(o1.as(), X86Gp::kIdAx))) + goto InvalidInstruction; + + uint32_t size = o1.getSize(); + if (o0.hasSize() && ASMJIT_UNLIKELY(o0.getSize() != size)) + goto OperandSizeMismatch; + + ADD_PREFIX_BY_SIZE(size); + opCode += static_cast(size != 1); + + goto EmitX86OpImplicitMem; + } + break; + + case X86Inst::kEncodingX86StrMm: + if (isign3 == ENC_OPS2(Mem, Mem)) { + if (ASMJIT_UNLIKELY(o0.as().getBaseIndexType() != + o1.as().getBaseIndexType())) + goto InvalidInstruction; + + rmRel = &o1; + if (ASMJIT_UNLIKELY(o0.as().hasOffset())) + goto InvalidInstruction; + + uint32_t size = o1.getSize(); + if (ASMJIT_UNLIKELY(size == 0)) + goto AmbiguousOperandSize; + + if (ASMJIT_UNLIKELY(o0.getSize() != size)) + goto OperandSizeMismatch; + + ADD_PREFIX_BY_SIZE(size); + opCode += static_cast(size != 1); + + goto EmitX86OpImplicitMem; + } + break; + + case X86Inst::kEncodingX86Test: + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (o0.getSize() != o1.getSize()) + goto OperandSizeMismatch; + + rbReg = o0.getId(); + opReg = o1.getId(); + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + FIXUP_GPB(o1, opReg); + goto EmitX86R; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + if (o1.getSize() == 1) { + FIXUP_GPB(o1, opReg); + goto EmitX86M; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o1.getSize()); + goto EmitX86M; + } + } + + // The following instructions use the secondary opcode. + opCode = commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + + if (isign3 == ENC_OPS2(Reg, Imm)) { + rbReg = o0.getId(); + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + + imVal = static_cast(o1).getUInt8(); + imLen = 1; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + imVal = static_cast(o1).getInt64(); + imLen = std::min(o0.getSize(), 4); + } + + // Alternate Form - AL, AX, EAX, RAX. + if (o0.getId() == 0 && !(options & X86Inst::kOptionLongForm)) { + opCode &= X86Inst::kOpCode_PP_66 | X86Inst::kOpCode_W; + opCode |= 0xA8 + (o0.getSize() != 1); + goto EmitX86Op; + } + + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Mem, Imm)) { + if (ASMJIT_UNLIKELY(o0.getSize() == 0)) + goto AmbiguousOperandSize; + + imVal = static_cast(o1).getInt64(); + imLen = std::min(o0.getSize(), 4); + + opCode += (o0.getSize() != 1); + ADD_PREFIX_BY_SIZE(o0.getSize()); + + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Xchg: + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, opReg); + goto EmitX86M; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + goto EmitX86M; + } + } + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingX86Xadd: + if (isign3 == ENC_OPS2(Reg, Reg)) { + rbReg = o0.getId(); + opReg = o1.getId(); + + if (o0.getSize() != o1.getSize()) + goto OperandSizeMismatch; + + if (o0.getSize() == 1) { + FIXUP_GPB(o0, rbReg); + FIXUP_GPB(o1, opReg); + goto EmitX86R; + } + else { + opCode++; + ADD_PREFIX_BY_SIZE(o0.getSize()); + + // Special opcode for 'xchg ?ax, reg'. + if (instId == X86Inst::kIdXchg && (opReg == 0 || rbReg == 0)) { + opCode &= X86Inst::kOpCode_PP_66 | X86Inst::kOpCode_W; + opCode |= 0x90; + // One of `xchg a, b` or `xchg b, a` is AX/EAX/RAX. + opReg += rbReg; + goto EmitX86OpReg; + } + else { + goto EmitX86R; + } + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opCode += o1.getSize() != 1; + ADD_PREFIX_BY_SIZE(o1.getSize()); + + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingX86Fence: + rbReg = 0; + goto EmitX86R; + + // ------------------------------------------------------------------------ + // [FPU] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingFpuOp: + goto EmitFpuOp; + + case X86Inst::kEncodingFpuArith: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + + // We switch to the alternative opcode if the first operand is zero. + if (opReg == 0) { +CaseFpuArith_Reg: + opCode = ((0xD8 << X86Inst::kOpCode_FPU_2B_Shift) ) + + ((opCode >> X86Inst::kOpCode_FPU_2B_Shift) & 0xFF) + rbReg; + goto EmitFpuOp; + } + else if (rbReg == 0) { + rbReg = opReg; + opCode = ((0xDC << X86Inst::kOpCode_FPU_2B_Shift) ) + + ((opCode ) & 0xFF) + rbReg; + goto EmitFpuOp; + } + else { + goto InvalidInstruction; + } + } + + if (isign3 == ENC_OPS1(Mem)) { +CaseFpuArith_Mem: + // 0xD8/0xDC, depends on the size of the memory operand; opReg is valid. + opCode = (o0.getSize() == 4) ? 0xD8 : 0xDC; + // Clear compressed displacement before going to EmitX86M. + opCode &= ~static_cast(X86Inst::kOpCode_CDSHL_Mask); + + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingFpuCom: + if (isign3 == 0) { + rbReg = 1; + goto CaseFpuArith_Reg; + } + + if (isign3 == ENC_OPS1(Reg)) { + rbReg = o0.getId(); + goto CaseFpuArith_Reg; + } + + if (isign3 == ENC_OPS1(Mem)) { + goto CaseFpuArith_Mem; + } + break; + + case X86Inst::kEncodingFpuFldFst: + if (isign3 == ENC_OPS1(Mem)) { + rmRel = &o0; + + if (o0.getSize() == 4 && commonData->hasFlag(X86Inst::kInstFlagFPU_M4)) { + goto EmitX86M; + } + + if (o0.getSize() == 8 && commonData->hasFlag(X86Inst::kInstFlagFPU_M8)) { + opCode += 4; + goto EmitX86M; + } + + if (o0.getSize() == 10 && commonData->hasFlag(X86Inst::kInstFlagFPU_M10)) { + opCode = commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + goto EmitX86M; + } + } + + if (isign3 == ENC_OPS1(Reg)) { + if (instId == X86Inst::kIdFld ) { opCode = (0xD9 << X86Inst::kOpCode_FPU_2B_Shift) + 0xC0 + o0.getId(); goto EmitFpuOp; } + if (instId == X86Inst::kIdFst ) { opCode = (0xDD << X86Inst::kOpCode_FPU_2B_Shift) + 0xD0 + o0.getId(); goto EmitFpuOp; } + if (instId == X86Inst::kIdFstp) { opCode = (0xDD << X86Inst::kOpCode_FPU_2B_Shift) + 0xD8 + o0.getId(); goto EmitFpuOp; } + } + break; + + case X86Inst::kEncodingFpuM: + if (isign3 == ENC_OPS1(Mem)) { + // Clear compressed displacement before going to EmitX86M. + opCode &= ~static_cast(X86Inst::kOpCode_CDSHL_Mask); + + rmRel = &o0; + if (o0.getSize() == 2 && commonData->hasFlag(X86Inst::kInstFlagFPU_M2)) { + opCode += 4; + goto EmitX86M; + } + + if (o0.getSize() == 4 && commonData->hasFlag(X86Inst::kInstFlagFPU_M4)) { + goto EmitX86M; + } + + if (o0.getSize() == 8 && commonData->hasFlag(X86Inst::kInstFlagFPU_M8)) { + opCode = commonData->getAltOpCode() & ~static_cast(X86Inst::kOpCode_CDSHL_Mask); + opReg = x86ExtractO(opCode); + goto EmitX86M; + } + } + break; + + case X86Inst::kEncodingFpuRDef: + if (isign3 == 0) { + opCode += 1; + goto EmitFpuOp; + } + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingFpuR: + if (isign3 == ENC_OPS1(Reg)) { + opCode += o0.getId(); + goto EmitFpuOp; + } + break; + + case X86Inst::kEncodingFpuStsw: + if (isign3 == ENC_OPS1(Reg)) { + if (ASMJIT_UNLIKELY(o0.getId() != X86Gp::kIdAx)) + goto InvalidInstruction; + + opCode = commonData->getAltOpCode(); + goto EmitFpuOp; + } + + if (isign3 == ENC_OPS1(Mem)) { + // Clear compressed displacement before going to EmitX86M. + opCode &= ~static_cast(X86Inst::kOpCode_CDSHL_Mask); + + rmRel = &o0; + goto EmitX86M; + } + break; + + // ------------------------------------------------------------------------ + // [Ext] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingExtPextrw: + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + ADD_66H_P(X86Reg::isXmm(o1)); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Mem, Reg, Imm)) { + // Secondary opcode of 'pextrw' instruction (SSE4.1). + opCode = commonData->getAltOpCode(); + ADD_66H_P(X86Reg::isXmm(o1)); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtExtract: + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + ADD_66H_P(X86Reg::isXmm(o1)); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Mem, Reg, Imm)) { + ADD_66H_P(X86Reg::isXmm(o1)); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtMov: + // GP|MMX|XMM <- GP|MMX|XMM + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + + if (!(options & X86Inst::kOptionModMR) || !commonData->hasAltOpCode()) + goto EmitX86R; + + opCode = commonData->getAltOpCode(); + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + + // GP|MMX|XMM <- Mem + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + + // The following instruction uses opCode[1]. + opCode = commonData->getAltOpCode(); + + // Mem <- GP|MMX|XMM + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtMovnti: + if (isign3 == ENC_OPS2(Mem, Reg)) { + ADD_REX_W(X86Reg::isGpq(o1)); + + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtMovbe: + if (isign3 == ENC_OPS2(Reg, Mem)) { + if (o0.getSize() == 1) + goto InvalidInstruction; + + ADD_PREFIX_BY_SIZE(o0.getSize()); + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + + // The following instruction uses the secondary opcode. + opCode = commonData->getAltOpCode(); + + if (isign3 == ENC_OPS2(Mem, Reg)) { + if (o1.getSize() == 1) + goto InvalidInstruction; + + ADD_PREFIX_BY_SIZE(o1.getSize()); + opReg = o1.getId(); + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtMovd: +CaseExtMovd: + opReg = o0.getId(); + ADD_66H_P(X86Reg::isXmm(o0)); + + // MMX/XMM <- Gp + if (isign3 == ENC_OPS2(Reg, Reg) && X86Reg::isGp(o1)) { + rbReg = o1.getId(); + goto EmitX86R; + } + + // MMX/XMM <- Mem + if (isign3 == ENC_OPS2(Reg, Mem)) { + rmRel = &o1; + goto EmitX86M; + } + + // The following instructions use the secondary opcode. + opCode &= X86Inst::kOpCode_W; + opCode |= commonData->getAltOpCode(); + opReg = o1.getId(); + ADD_66H_P(X86Reg::isXmm(o1)); + + // GP <- MMX/XMM + if (isign3 == ENC_OPS2(Reg, Reg) && X86Reg::isGp(o0)) { + rbReg = o0.getId(); + goto EmitX86R; + } + + // Mem <- MMX/XMM + if (isign3 == ENC_OPS2(Mem, Reg)) { + rmRel = &o0; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtMovq: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + + // MMX <- MMX + if (X86Reg::isMm(o0) && X86Reg::isMm(o1)) { + opCode = X86Inst::kOpCode_PP_00 | X86Inst::kOpCode_MM_0F | 0x6F; + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode += 0x10; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + + // XMM <- XMM + if (X86Reg::isXmm(o0) && X86Reg::isXmm(o1)) { + opCode = X86Inst::kOpCode_PP_F3 | X86Inst::kOpCode_MM_0F | 0x7E; + + if (!(options & X86Inst::kOptionModMR)) + goto EmitX86R; + + opCode = X86Inst::kOpCode_PP_66 | X86Inst::kOpCode_MM_0F | 0xD6; + Utils::swap(opReg, rbReg); + goto EmitX86R; + } + + // MMX <- XMM (MOVDQ2Q) + if (X86Reg::isMm(o0) && X86Reg::isXmm(o1)) { + opCode = X86Inst::kOpCode_PP_F2 | X86Inst::kOpCode_MM_0F | 0xD6; + goto EmitX86R; + } + + // XMM <- MMX (MOVQ2DQ) + if (X86Reg::isXmm(o0) && X86Reg::isMm(o1)) { + opCode = X86Inst::kOpCode_PP_F3 | X86Inst::kOpCode_MM_0F | 0xD6; + goto EmitX86R; + } + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + + // MMX <- Mem + if (X86Reg::isMm(o0)) { + opCode = X86Inst::kOpCode_PP_00 | X86Inst::kOpCode_MM_0F | 0x6F; + goto EmitX86M; + } + + // XMM <- Mem + if (X86Reg::isXmm(o0)) { + opCode = X86Inst::kOpCode_PP_F3 | X86Inst::kOpCode_MM_0F | 0x7E; + goto EmitX86M; + } + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + // Mem <- MMX + if (X86Reg::isMm(o1)) { + opCode = X86Inst::kOpCode_PP_00 | X86Inst::kOpCode_MM_0F | 0x7F; + goto EmitX86M; + } + + // Mem <- XMM + if (X86Reg::isXmm(o1)) { + opCode = X86Inst::kOpCode_PP_66 | X86Inst::kOpCode_MM_0F | 0xD6; + goto EmitX86M; + } + } + + // MOVQ in other case is simply a MOVD instruction promoted to 64-bit. + opCode |= X86Inst::kOpCode_W; + goto CaseExtMovd; + + case X86Inst::kEncodingExtRm_XMM0: + if (ASMJIT_UNLIKELY(!o2.isNone() && !X86Reg::isXmm(o2, 0))) + goto InvalidInstruction; + + isign3 &= 0x3F; + goto CaseExtRm; + + case X86Inst::kEncodingExtRm_ZDI: + if (ASMJIT_UNLIKELY(!o2.isNone() && !x86IsImplicitMem(o2, X86Gp::kIdDi))) + goto InvalidInstruction; + + isign3 &= 0x3F; + goto CaseExtRm; + + case X86Inst::kEncodingExtRm_Wx: + ADD_REX_W(X86Reg::isGpq(o0) || o1.getSize() == 8); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingExtRm: +CaseExtRm: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtRm_P: + if (isign3 == ENC_OPS2(Reg, Reg)) { + ADD_66H_P(X86Reg::isXmm(o0) | X86Reg::isXmm(o1)); + + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + ADD_66H_P(X86Reg::isXmm(o0)); + + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtRmRi: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + + // The following instruction uses the secondary opcode. + opCode = commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + + if (isign3 == ENC_OPS2(Reg, Imm)) { + imVal = static_cast(o1).getInt64(); + imLen = 1; + + rbReg = o0.getId(); + goto EmitX86R; + } + break; + + case X86Inst::kEncodingExtRmRi_P: + if (isign3 == ENC_OPS2(Reg, Reg)) { + ADD_66H_P(X86Reg::isXmm(o0) | X86Reg::isXmm(o1)); + + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + ADD_66H_P(X86Reg::isXmm(o0)); + + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + + // The following instruction uses the secondary opcode. + opCode = commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + + if (isign3 == ENC_OPS2(Reg, Imm)) { + ADD_66H_P(X86Reg::isXmm(o0)); + + imVal = static_cast(o1).getInt64(); + imLen = 1; + + rbReg = o0.getId(); + goto EmitX86R; + } + break; + + case X86Inst::kEncodingExtRmi: + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + case X86Inst::kEncodingExtRmi_P: + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + ADD_66H_P(X86Reg::isXmm(o0) | X86Reg::isXmm(o1)); + + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + ADD_66H_P(X86Reg::isXmm(o0)); + + opReg = o0.getId(); + rmRel = &o1; + goto EmitX86M; + } + break; + + // ------------------------------------------------------------------------ + // [Extrq / Insertq (SSE4A)] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingExtExtrq: + opReg = o0.getId(); + rbReg = o1.getId(); + + if (isign3 == ENC_OPS2(Reg, Reg)) + goto EmitX86R; + + // The following instruction uses the secondary opcode. + opCode = commonData->getAltOpCode(); + + if (isign3 == ENC_OPS3(Reg, Imm, Imm)) { + imVal = (static_cast(o1).getUInt32() ) + + (static_cast(o2).getUInt32() << 8) ; + imLen = 2; + + rbReg = x86ExtractO(opCode); + goto EmitX86R; + } + break; + + case X86Inst::kEncodingExtInsertq: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + opReg = o0.getId(); + rbReg = o1.getId(); + + if (isign4 == ENC_OPS2(Reg, Reg)) + goto EmitX86R; + + // The following instruction uses the secondary opcode. + opCode = commonData->getAltOpCode(); + + if (isign4 == ENC_OPS4(Reg, Reg, Imm, Imm)) { + imVal = (static_cast(o2).getUInt32() ) + + (static_cast(o3).getUInt32() << 8) ; + imLen = 2; + goto EmitX86R; + } + break; + } + + // ------------------------------------------------------------------------ + // [3dNow] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingExt3dNow: + // Every 3dNow instruction starts with 0x0F0F and the actual opcode is + // stored as 8-bit immediate. + imVal = opCode & 0xFF; + imLen = 1; + + opCode = X86Inst::kOpCode_MM_0F | 0x0F; + opReg = o0.getId(); + + if (isign3 == ENC_OPS2(Reg, Reg)) { + rbReg = o1.getId(); + goto EmitX86R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + rmRel = &o1; + goto EmitX86M; + } + break; + + // ------------------------------------------------------------------------ + // [VEX/EVEX] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingVexOp: + goto EmitVexEvexOp; + + case X86Inst::kEncodingVexKmov: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + + // Form 'k, reg'. + if (X86Reg::isGp(o1)) { + opCode = commonData->getAltOpCode(); + goto EmitVexEvexR; + } + + // Form 'reg, k'. + if (X86Reg::isGp(o0)) { + opCode = commonData->getAltOpCode() + 1; + goto EmitVexEvexR; + } + + // Form 'k, k'. + if (!(options & X86Inst::kOptionModMR)) + goto EmitVexEvexR; + + opCode++; + Utils::swap(opReg, rbReg); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + + goto EmitVexEvexM; + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + + opCode++; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexM: + if (isign3 == ENC_OPS1(Mem)) { + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexM_VM: + if (isign3 == ENC_OPS1(Mem)) { + opCode |= x86OpCodeLByVMem(o0); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexMr_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexMr_VM: + if (isign3 == ENC_OPS2(Mem, Reg)) { + opCode |= std::max(x86OpCodeLByVMem(o0), x86OpCodeLBySize(o1.getSize())); + + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexMri_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexMri: + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Mem, Reg, Imm)) { + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRm_ZDI: + if (ASMJIT_UNLIKELY(!o2.isNone() && !x86IsImplicitMem(o2, X86Gp::kIdDi))) + goto InvalidInstruction; + + isign3 &= 0x3F; + goto CaseVexRm; + + case X86Inst::kEncodingVexRm_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRm: +CaseVexRm: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRm_VM: + if (isign3 == ENC_OPS2(Reg, Mem)) { + opCode |= std::max(x86OpCodeLByVMem(o1), x86OpCodeLBySize(o0.getSize())); + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRmi_Wx: + ADD_REX_W(X86Reg::isGpq(o0) | X86Reg::isGpq(o1)); + goto CaseVexRmi; + + case X86Inst::kEncodingVexRmi_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRmi: +CaseVexRmi: + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvm: +CaseVexRvm: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { +CaseVexRvm_R: + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvm_ZDX_Wx: + if (ASMJIT_UNLIKELY(!o3.isNone() && !X86Reg::isGp(o3, X86Gp::kIdDx))) + goto InvalidInstruction; + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvm_Wx: + ADD_REX_W(X86Reg::isGpq(o0) | X86Reg::isGpq(o1)); + goto CaseVexRvm; + + case X86Inst::kEncodingVexRvm_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + goto CaseVexRvm; + + case X86Inst::kEncodingVexRvmr_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvmr: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + imVal = o3.getId() << 4; + imLen = 1; + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Mem, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + break; + } + + case X86Inst::kEncodingVexRvmi_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvmi: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + imVal = static_cast(o3).getInt64(); + imLen = 1; + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Imm)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Mem, Imm)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + break; + } + + case X86Inst::kEncodingVexRmv_Wx: + ADD_REX_W(X86Reg::isGpq(o0) | X86Reg::isGpq(o2)); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRmv: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRmvRm_VM: + if (isign3 == ENC_OPS2(Reg, Mem)) { + opCode = commonData->getAltOpCode(); + opCode |= std::max(x86OpCodeLByVMem(o1), x86OpCodeLBySize(o0.getSize())); + + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRmv_VM: + if (isign3 == ENC_OPS3(Reg, Mem, Reg)) { + opCode |= std::max(x86OpCodeLByVMem(o1), x86OpCodeLBySize(o0.getSize() | o2.getSize())); + + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + + case X86Inst::kEncodingVexRmvi: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + imVal = static_cast(o3).getInt64(); + imLen = 1; + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Imm)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Mem, Reg, Imm)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + } + + case X86Inst::kEncodingVexMovdMovq: + if (isign3 == ENC_OPS2(Reg, Reg)) { + if (X86Reg::isGp(o0)) { + opCode = commonData->getAltOpCode(); + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitVexEvexR; + } + + if (X86Reg::isGp(o1)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + } + + // If this is a 'W' version (movq) then allow also vmovq 'xmm|xmm' form. + if (opCode & X86Inst::kOpCode_EW) + goto CaseVexRmMr; + else + goto CaseVexRmMr_AfterRegReg; + + case X86Inst::kEncodingVexRmMr_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRmMr: +CaseVexRmMr: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + +CaseVexRmMr_AfterRegReg: + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + + // The following instruction uses the secondary opcode. + opCode &= X86Inst::kOpCode_LL_Mask; + opCode |= commonData->getAltOpCode(); + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmRmv: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rbReg = o1.getId(); + + if (!(options & X86Inst::kOptionModMR)) + goto EmitVexEvexR; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + + ADD_VEX_W(true); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + + ADD_VEX_W(true); + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmRmi_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvmRmi: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + + // The following instructions use the secondary opcode. + opCode &= X86Inst::kOpCode_LL_Mask; + opCode |= commonData->getAltOpCode(); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmRmvRmi: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rbReg = o1.getId(); + + if (!(options & X86Inst::kOptionModMR)) + goto EmitVexEvexR; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + + ADD_VEX_W(true); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o2.getId()); + rmRel = &o1; + + goto EmitVexEvexM; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + + ADD_VEX_W(true); + goto EmitVexEvexM; + } + + // The following instructions use the secondary opcode. + opCode = commonData->getAltOpCode(); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = o0.getId(); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmMr: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + + // The following instructions use the secondary opcode. + opCode = commonData->getAltOpCode(); + + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = o1.getId(); + rbReg = o0.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmMvr_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvmMvr: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + + // The following instruction uses the secondary opcode. + opCode &= X86Inst::kOpCode_LL_Mask; + opCode |= commonData->getAltOpCode(); + + if (isign3 == ENC_OPS3(Mem, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o2.getId(), o1.getId()); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvmVmi_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvmVmi: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Reg, Mem)) { + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + goto EmitVexEvexM; + } + + // The following instruction uses the secondary opcode. + opCode &= X86Inst::kOpCode_LL_Mask; + opCode |= commonData->getAltOpCode(); + opReg = x86ExtractO(opCode); + + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexVm_Wx: + ADD_REX_W(X86Reg::isGpq(o0) | X86Reg::isGpq(o1)); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexVm: + if (isign3 == ENC_OPS2(Reg, Reg)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexEvexVmi_Lx: + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) + opCode |= X86Inst::kOpCode_MM_ForceEvex; + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexVmi_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexVmi: + imVal = static_cast(o2).getInt64(); + imLen = 1; + + if (isign3 == ENC_OPS3(Reg, Reg, Imm)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rbReg = o1.getId(); + goto EmitVexEvexR; + } + + if (isign3 == ENC_OPS3(Reg, Mem, Imm)) { + opReg = x86PackRegAndVvvvv(opReg, o0.getId()); + rmRel = &o1; + goto EmitVexEvexM; + } + break; + + case X86Inst::kEncodingVexRvrmRvmr_Lx: + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingVexRvrmRvmr: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Reg)) { + imVal = o3.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Mem)) { + imVal = o2.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o3; + + ADD_VEX_W(true); + goto EmitVexEvexM; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Mem, Reg)) { + imVal = o3.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + + goto EmitVexEvexM; + } + break; + } + + case X86Inst::kEncodingVexRvrmiRvmri_Lx: { + if (!(options & CodeEmitter::kOptionOp4) || !_op4.isImm()) + goto InvalidInstruction; + + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize() | o2.getSize() | o3.getSize()); + + imVal = static_cast(_op4).getUInt8() & 0x0F; + imLen = 1; + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Reg)) { + imVal |= o3.getId() << 4; + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Mem)) { + imVal |= o2.getId() << 4; + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o3; + + ADD_VEX_W(true); + goto EmitVexEvexM; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Mem, Reg)) { + imVal |= o3.getId() << 4; + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + + goto EmitVexEvexM; + } + break; + } + + case X86Inst::kEncodingVexMovssMovsd: + if (isign3 == ENC_OPS3(Reg, Reg, Reg)) { + goto CaseVexRvm_R; + } + + if (isign3 == ENC_OPS2(Reg, Mem)) { + opReg = o0.getId(); + rmRel = &o1; + goto EmitVexEvexM; + } + + if (isign3 == ENC_OPS2(Mem, Reg)) { + opCode = commonData->getAltOpCode(); + opReg = o1.getId(); + rmRel = &o0; + goto EmitVexEvexM; + } + break; + + // ------------------------------------------------------------------------ + // [FMA4] + // ------------------------------------------------------------------------ + + case X86Inst::kEncodingFma4_Lx: + // It's fine to just check the first operand, second is just for sanity. + opCode |= x86OpCodeLBySize(o0.getSize() | o1.getSize()); + ASMJIT_FALLTHROUGH; + + case X86Inst::kEncodingFma4: { + const uint32_t isign4 = isign3 + (o3.getOp() << 9); + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Reg)) { + imVal = o3.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rbReg = o2.getId(); + + goto EmitVexEvexR; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Reg, Mem)) { + imVal = o2.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o3; + + ADD_VEX_W(true); + goto EmitVexEvexM; + } + + if (isign4 == ENC_OPS4(Reg, Reg, Mem, Reg)) { + imVal = o3.getId() << 4; + imLen = 1; + + opReg = x86PackRegAndVvvvv(o0.getId(), o1.getId()); + rmRel = &o2; + + goto EmitVexEvexM; + } + break; + } + } + goto InvalidInstruction; + + // -------------------------------------------------------------------------- + // [Emit - X86] + // -------------------------------------------------------------------------- + +EmitX86Op: + // Emit mandatory instruction prefix. + EMIT_PP(opCode); + + // Emit REX prefix (64-bit only). + { + uint32_t rex = x86ExtractREX(opCode, options); + if (rex) { + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + EMIT_BYTE(rex | kX86ByteRex); + } + } + + // Emit instruction opcodes. + EMIT_MM_OP(opCode); + + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + +EmitX86OpReg: + // Emit mandatory instruction prefix. + EMIT_PP(opCode); + + // Emit REX prefix (64-bit only). + { + uint32_t rex = x86ExtractREX(opCode, options) | + (opReg >> 3); // Rex.B (0x01). + if (rex) { + EMIT_BYTE(rex | kX86ByteRex); + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + opReg &= 0x7; + } + } + + // Emit instruction opcodes. + opCode += opReg; + EMIT_MM_OP(opCode); + + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + +EmitX86OpImplicitMem: + // NOTE: Don't change the emit order here, it's compatible with KeyStone/LLVM. + rmInfo = x86MemInfo[rmRel->as().getBaseIndexType()]; + if (ASMJIT_UNLIKELY(rmRel->as().hasOffset() || (rmInfo & kX86MemInfo_Index))) + goto InvalidInstruction; + + // Emit mandatory instruction prefix. + EMIT_PP(opCode); + + // Emit REX prefix (64-bit only). + { + uint32_t rex = x86ExtractREX(opCode, options); + if (rex) { + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + EMIT_BYTE(rex | kX86ByteRex); + } + } + + // Segment-override prefix. + if (rmRel->as().hasSegment()) + EMIT_BYTE(x86SegmentPrefix[rmRel->as().getSegmentId()]); + + // Address-override prefix. + if (rmInfo & _getAddressOverrideMask()) + EMIT_BYTE(0x67); + + // Emit instruction opcodes. + EMIT_MM_OP(opCode); + + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + +EmitX86R: + // Mandatory instruction prefix. + EMIT_PP(opCode); + + // Rex prefix (64-bit only). + { + uint32_t rex = x86ExtractREX(opCode, options) | + ((opReg & 0x08) >> 1) | // REX.R (0x04). + ((rbReg ) >> 3) ; // REX.B (0x01). + if (rex) { + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + EMIT_BYTE(rex | kX86ByteRex); + opReg &= 0x07; + rbReg &= 0x07; + } + } + + // Instruction opcodes. + EMIT_MM_OP(opCode); + // ModR. + EMIT_BYTE(x86EncodeMod(3, opReg, rbReg)); + + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + +EmitX86M: + ASMJIT_ASSERT(rmRel != nullptr); + ASMJIT_ASSERT(rmRel->getOp() == Operand::kOpMem); + rmInfo = x86MemInfo[rmRel->as().getBaseIndexType()]; + + // GP instructions have never compressed displacement specified. + ASMJIT_ASSERT((opCode & X86Inst::kOpCode_CDSHL_Mask) == 0); + + // Segment-override prefix. + if (rmRel->as().hasSegment()) + EMIT_BYTE(x86SegmentPrefix[rmRel->as().getSegmentId()]); + + // Address-override prefix. + if (rmInfo & _getAddressOverrideMask()) + EMIT_BYTE(0x67); + + // Mandatory instruction prefix. + EMIT_PP(opCode); + + rbReg = rmRel->as().getBaseId(); + rxReg = rmRel->as().getIndexId(); + + // REX prefix (64-bit only). + { + uint32_t rex; + + rex = (rbReg >> 3) & 0x01; // REX.B (0x01). + rex |= (rxReg >> 2) & 0x02; // REX.X (0x02). + rex |= (opReg >> 1) & 0x04; // REX.R (0x04). + + rex &= rmInfo; + rex |= x86ExtractREX(opCode, options); + + if (rex) { + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + EMIT_BYTE(rex | kX86ByteRex); + opReg &= 0x07; + } + } + + // Instruction opcodes. + EMIT_MM_OP(opCode); + // ... Fall through ... + + // -------------------------------------------------------------------------- + // [Emit - MOD/SIB] + // -------------------------------------------------------------------------- + +EmitModSib: + if (!(rmInfo & (kX86MemInfo_Index | kX86MemInfo_67H_X86))) { + // ==========|> [BASE + DISP8|DISP32]. + if (rmInfo & kX86MemInfo_BaseGp) { + rbReg &= 0x7; + relOffset = rmRel->as().getOffsetLo32(); + + uint32_t mod = x86EncodeMod(0, opReg, rbReg); + if (rbReg == X86Gp::kIdSp) { + // [XSP|R12]. + if (relOffset == 0) { + EMIT_BYTE(mod); + EMIT_BYTE(x86EncodeSib(0, 4, 4)); + } + // [XSP|R12 + DISP8|DISP32]. + else { + uint32_t cdShift = (opCode & X86Inst::kOpCode_CDSHL_Mask) >> X86Inst::kOpCode_CDSHL_Shift; + int32_t cdOffset = relOffset >> cdShift; + + if (Utils::isInt8(cdOffset) && relOffset == (cdOffset << cdShift)) { + EMIT_BYTE(mod + 0x40); // <- MOD(1, opReg, rbReg). + EMIT_BYTE(x86EncodeSib(0, 4, 4)); + EMIT_BYTE(cdOffset & 0xFF); + } + else { + EMIT_BYTE(mod + 0x80); // <- MOD(2, opReg, rbReg). + EMIT_BYTE(x86EncodeSib(0, 4, 4)); + EMIT_32(relOffset); + } + } + } + else if (rbReg != X86Gp::kIdBp && relOffset == 0) { + // [BASE]. + EMIT_BYTE(mod); + } + else { + // [BASE + DISP8|DISP32]. + uint32_t cdShift = (opCode & X86Inst::kOpCode_CDSHL_Mask) >> X86Inst::kOpCode_CDSHL_Shift; + int32_t cdOffset = relOffset >> cdShift; + + if (Utils::isInt8(cdOffset) && relOffset == (cdOffset << cdShift)) { + EMIT_BYTE(mod + 0x40); + EMIT_BYTE(cdOffset & 0xFF); + } + else { + EMIT_BYTE(mod + 0x80); + EMIT_32(relOffset); + } + } + } + // ==========|> [ABSOLUTE | DISP32]. + else if (!(rmInfo & (kX86MemInfo_BaseLabel | kX86MemInfo_BaseRip))) { + if (is32Bit()) { + relOffset = rmRel->as().getOffsetLo32(); + EMIT_BYTE(x86EncodeMod(0, opReg, 5)); + EMIT_32(relOffset); + } + else { + uint64_t baseAddress = getCodeInfo().getBaseAddress(); + relOffset = rmRel->as().getOffsetLo32(); + + // Prefer absolute addressing mode if FS|GS segment override is present. + bool absoluteValid = rmRel->as().getOffsetHi32() == (relOffset >> 31); + bool preferAbsolute = (rmRel->as().getSegmentId() >= X86Seg::kIdFs) || rmRel->as().isAbs(); + + // If we know the base address and the memory operand points to an + // absolute address it's possible to calculate REL32 that can be + // be used as [RIP+REL32] in 64-bit mode. + if (baseAddress != Globals::kNoBaseAddress && !preferAbsolute) { + const uint32_t kModRel32Size = 5; + uint64_t rip64 = baseAddress + + static_cast((uintptr_t)(cursor - _bufferData)) + imLen + kModRel32Size; + + uint64_t rel64 = static_cast(rmRel->as().getOffset()) - rip64; + if (Utils::isInt32(static_cast(rel64))) { + EMIT_BYTE(x86EncodeMod(0, opReg, 5)); + EMIT_32(static_cast(rel64 & 0xFFFFFFFFU)); + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + } + } + + if (ASMJIT_UNLIKELY(!absoluteValid)) + goto InvalidAddress64Bit; + + EMIT_BYTE(x86EncodeMod(0, opReg, 4)); + EMIT_BYTE(x86EncodeSib(0, 4, 5)); + EMIT_32(relOffset); + } + } + // ==========|> [LABEL|RIP + DISP32] + else { + EMIT_BYTE(x86EncodeMod(0, opReg, 5)); + + if (is32Bit()) { +EmitModSib_LabelRip_X86: + if (ASMJIT_UNLIKELY(_code->_relocations.willGrow(&_code->_baseHeap) != kErrorOk)) + goto NoHeapMemory; + + relOffset = rmRel->as().getOffsetLo32(); + if (rmInfo & kX86MemInfo_BaseLabel) { + // [LABEL->ABS]. + label = _code->getLabelEntry(rmRel->as().getBaseId()); + if (!label) goto InvalidLabel; + + err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, 4); + if (ASMJIT_UNLIKELY(err)) goto Failed; + + re->_sourceOffset = static_cast((uintptr_t)(cursor - _bufferData)); + re->_data = static_cast(relOffset); + + if (label->isBound()) { + // Bound label. + re->_data += static_cast(label->getOffset()); + EMIT_32(0); + } + else { + // Non-bound label. + relOffset = -4 - imLen; + relSize = 4; + goto EmitRel; + } + } + else { + // [RIP->ABS]. + err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, 4); + if (ASMJIT_UNLIKELY(err)) goto Failed; + + re->_sourceOffset = static_cast((uintptr_t)(cursor - _bufferData)); + re->_data = re->_sourceOffset + + static_cast(static_cast(relOffset)); + EMIT_32(0); + } + } + else { + relOffset = rmRel->as().getOffsetLo32(); + if (rmInfo & kX86MemInfo_BaseLabel) { + // [RIP]. + label = _code->getLabelEntry(rmRel->as().getBaseId()); + if (!label) goto InvalidLabel; + + relOffset -= (4 + imLen); + if (label->isBound()) { + // Bound label. + relOffset += label->getOffset() - static_cast((intptr_t)(cursor - _bufferData)); + EMIT_32(static_cast(relOffset)); + } + else { + // Non-bound label. + relSize = 4; + goto EmitRel; + } + } + else { + // [RIP]. + EMIT_32(static_cast(relOffset)); + } + } + } + } + else if (!(rmInfo & kX86MemInfo_67H_X86)) { + // ESP|RSP can't be used as INDEX in pure SIB mode, however, VSIB mode + // allows XMM4|YMM4|ZMM4 (that's why the check is before the label). + if (ASMJIT_UNLIKELY(rxReg == X86Gp::kIdSp)) goto InvalidAddressIndex; + +EmitModVSib: + rxReg &= 0x7; + + // ==========|> [BASE + INDEX + DISP8|DISP16|DISP32]. + if (rmInfo & kX86MemInfo_BaseGp) { + rbReg &= 0x7; + relOffset = rmRel->as().getOffsetLo32(); + + uint32_t mod = x86EncodeMod(0, opReg, 4); + uint32_t sib = x86EncodeSib(rmRel->as().getShift(), rxReg, rbReg); + + if (relOffset == 0 && rbReg != X86Gp::kIdBp) { + // [BASE + INDEX << SHIFT]. + EMIT_BYTE(mod); + EMIT_BYTE(sib); + } + else { + uint32_t cdShift = (opCode & X86Inst::kOpCode_CDSHL_Mask) >> X86Inst::kOpCode_CDSHL_Shift; + int32_t cdOffset = relOffset >> cdShift; + + if (Utils::isInt8(cdOffset) && relOffset == (cdOffset << cdShift)) { + // [BASE + INDEX << SHIFT + DISP8]. + EMIT_BYTE(mod + 0x40); // <- MOD(1, opReg, 4). + EMIT_BYTE(sib); + EMIT_BYTE(cdOffset); + } + else { + // [BASE + INDEX << SHIFT + DISP16|DISP32]. + EMIT_BYTE(mod + 0x80); // <- MOD(2, opReg, 4). + EMIT_BYTE(sib); + EMIT_32(relOffset); + } + } + } + // ==========|> [INDEX + DISP16|DISP32]. + else if (!(rmInfo & (kX86MemInfo_BaseLabel | kX86MemInfo_BaseRip))) { + // [INDEX << SHIFT + DISP32]. + EMIT_BYTE(x86EncodeMod(0, opReg, 4)); + EMIT_BYTE(x86EncodeSib(rmRel->as().getShift(), rxReg, 5)); + + relOffset = rmRel->as().getOffsetLo32(); + EMIT_32(relOffset); + } + // ==========|> [LABEL|RIP + INDEX + DISP32]. + else { + if (is32Bit()) { + EMIT_BYTE(x86EncodeMod(0, opReg, 4)); + EMIT_BYTE(x86EncodeSib(rmRel->as().getShift(), rxReg, 5)); + goto EmitModSib_LabelRip_X86; + } + else { + goto InvalidAddress; + } + } + } + else { + // 16-bit address mode (32-bit mode with 67 override prefix). + relOffset = (static_cast(rmRel->as().getOffsetLo32()) << 16) >> 16; + + // NOTE: 16-bit addresses don't use SIB byte and their encoding differs. We + // use a table-based approach to calculate the proper MOD byte as it's easier. + // Also, not all BASE [+ INDEX] combinations are supported in 16-bit mode, so + // this may fail. + const uint32_t kBaseGpIdx = (kX86MemInfo_BaseGp | kX86MemInfo_Index); + + if (rmInfo & kBaseGpIdx) { + // ==========|> [BASE + INDEX + DISP16]. + uint32_t mod; + + rbReg &= 0x7; + rxReg &= 0x7; + + if ((rmInfo & kBaseGpIdx) == kBaseGpIdx) { + uint32_t shf = rmRel->as().getShift(); + if (ASMJIT_UNLIKELY(shf != 0)) + goto InvalidAddress; + mod = x86Mod16BaseIndexTable[(rbReg << 3) + rxReg]; + } + else { + if (rmInfo & kX86MemInfo_Index) + rbReg = rxReg; + mod = x86Mod16BaseTable[rbReg]; + } + + if (ASMJIT_UNLIKELY(mod == 0xFF)) + goto InvalidAddress; + + mod += opReg << 3; + if (relOffset == 0 && mod != 0x06) { + EMIT_BYTE(mod); + } + else if (Utils::isInt8(relOffset)) { + EMIT_BYTE(mod + 0x40); + EMIT_BYTE(relOffset); + } + else { + EMIT_BYTE(mod + 0x80); + EMIT_16(relOffset); + } + } + else { + // Not supported in 16-bit addresses. + if (rmInfo & (kX86MemInfo_BaseRip | kX86MemInfo_BaseLabel)) + goto InvalidAddress; + + // ==========|> [DISP16]. + EMIT_BYTE(opReg | 0x06); + EMIT_16(relOffset); + } + } + + if (imLen != 0) + goto EmitImm; + else + goto EmitDone; + + // -------------------------------------------------------------------------- + // [Emit - FPU] + // -------------------------------------------------------------------------- + +EmitFpuOp: + // Mandatory instruction prefix. + EMIT_PP(opCode); + + // FPU instructions consist of two opcodes. + EMIT_BYTE(opCode >> X86Inst::kOpCode_FPU_2B_Shift); + EMIT_BYTE(opCode); + goto EmitDone; + + // -------------------------------------------------------------------------- + // [Emit - VEX / EVEX] + // -------------------------------------------------------------------------- + +EmitVexEvexOp: + { + // These don't use immediate. + ASMJIT_ASSERT(imLen == 0); + + // Only 'vzeroall' and 'vzeroupper' instructions use this encoding, they + // don't define 'W' to be '1' so we can just check the 'mmmmm' field. Both + // functions can encode by using VEV2 prefix so VEV3 is basically only used + // when forced from outside. + ASMJIT_ASSERT((opCode & X86Inst::kOpCode_W) == 0); + + uint32_t x = ((opCode & X86Inst::kOpCode_MM_Mask ) >> (X86Inst::kOpCode_MM_Shift )) | + ((opCode & X86Inst::kOpCode_LL_Mask ) >> (X86Inst::kOpCode_LL_Shift - 10)) | + ((opCode & X86Inst::kOpCode_PP_VEXMask) >> (X86Inst::kOpCode_PP_Shift - 8)) | + ((options & X86Inst::kOptionVex3 ) >> (X86Inst::kOpCode_MM_Shift )) ; + if (x & 0x04U) { + x = (x & (0x4 ^ 0xFFFF)) << 8; // [00000000|00000Lpp|0000m0mm|00000000]. + x ^= (kX86ByteVex3) | // [........|00000Lpp|0000m0mm|__VEX3__]. + (0x07U << 13) | // [........|00000Lpp|1110m0mm|__VEX3__]. + (0x0FU << 19) | // [........|01111Lpp|1110m0mm|__VEX3__]. + (opCode << 24) ; // [_OPCODE_|01111Lpp|1110m0mm|__VEX3__]. + + EMIT_32(x); + goto EmitDone; + } + else { + x = ((x >> 8) ^ x) ^ 0xF9; + EMIT_BYTE(kX86ByteVex2); + EMIT_BYTE(x); + EMIT_BYTE(opCode); + goto EmitDone; + } + } + +EmitVexEvexR: + { + // VEX instructions use only 0-1 BYTE immediate. + ASMJIT_ASSERT(imLen <= 1); + + // Construct `x` - a complete EVEX|VEX prefix. + uint32_t x = ((opReg << 4) & 0xF980U) | // [........|........|Vvvvv..R|R.......]. + ((rbReg << 2) & 0x0060U) | // [........|........|........|.BB.....]. + (x86ExtractLLMM(opCode, options)); // [........|.LL.....|Vvvvv..R|RBBmmmmm]. + opReg &= 0x7; + + // Handle AVX512 options by a single branch. + const uint32_t kAvx512Options = X86Inst::kOptionOpExtra | + X86Inst::kOptionKZ | + X86Inst::kOption1ToX | + X86Inst::kOptionSAE | + X86Inst::kOptionER ; + if (options & kAvx512Options) { + // Memory broadcast without a memory operand is invalid. + if (ASMJIT_UNLIKELY(options & X86Inst::kOption1ToX)) + goto InvalidBroadcast; + + // TODO: {sae} and {er} + + // NOTE: We consider a valid construct internally even when {kz} was + // specified without specifying the register. In that case it would be + // `k0` and basically everything should be zeroed. It's valid EVEX. + if (options & X86Inst::kOptionOpExtra) + x |= _opExtra.getId() << 16; + + x |= options & X86Inst::kOptionKZ; // [........|zLL..aaa|Vvvvv..R|RBBmmmmm]. + } + + // Check if EVEX is required by checking bits in `x` : [........|xx...xxx|x......x|.x.x....]. + if (x & 0x00C78150U) { + uint32_t y = ((x << 4) & 0x00080000U) | // [........|....V...|........|........]. + ((x >> 4) & 0x00000010U) ; // [........|....V...|........|...R....]. + x = (x & 0x00FF78E3U) | y; // [........|zLL.Vaaa|0vvvv000|RBBR00mm]. + x = (x << 8) | // [zLL.Vaaa|0vvvv000|RBBR00mm|00000000]. + ((opCode >> kSHR_W_PP) & 0x00830000U) | // [zLL.Vaaa|Wvvvv0pp|RBBR00mm|00000000]. + ((opCode >> kSHR_W_EW) & 0x00800000U) ; // [zLL.Vaaa|Wvvvv0pp|RBBR00mm|00000000] (added EVEX.W). + // _ ____ ____ + x ^= 0x087CF000U | kX86ByteEvex; // [zLL.Vaaa|Wvvvv1pp|RBBR00mm|01100010]. + + EMIT_32(x); + EMIT_BYTE(opCode); + + rbReg &= 0x7; + EMIT_BYTE(x86EncodeMod(3, opReg, rbReg)); + + if (imLen == 0) goto EmitDone; + EMIT_BYTE(imVal & 0xFF); + goto EmitDone; + } + + // Not EVEX, prepare `x` for VEX2 or VEX3: x = [........|00L00000|0vvvv000|R0B0mmmm]. + x |= ((opCode >> (kSHR_W_PP + 8)) & 0x8300U) | // [00000000|00L00000|Wvvvv0pp|R0B0mmmm]. + ((x >> 11 ) & 0x0400U) ; // [00000000|00L00000|WvvvvLpp|R0B0mmmm]. + + // Check if VEX3 is required / forced: [........|........|x.......|..x..x..]. + if (x & 0x0008024U) { + uint32_t xorMsk = x86VEXPrefix[x & 0xF] | (opCode << 24); + + // Clear 'FORCE-VEX3' bit and all high bits. + x = (x & (0x4 ^ 0xFFFF)) << 8; // [00000000|WvvvvLpp|R0B0m0mm|00000000]. + // ____ _ _ + x ^= xorMsk; // [_OPCODE_|WvvvvLpp|R1Bmmmmm|VEX3|XOP]. + EMIT_32(x); + + rbReg &= 0x7; + EMIT_BYTE(x86EncodeMod(3, opReg, rbReg)); + + if (imLen == 0) goto EmitDone; + EMIT_BYTE(imVal & 0xFF); + goto EmitDone; + } + else { + // 'mmmmm' must be '00001'. + ASMJIT_ASSERT((x & 0x1F) == 0x01); + + x = ((x >> 8) ^ x) ^ 0xF9; + EMIT_BYTE(kX86ByteVex2); + EMIT_BYTE(x); + EMIT_BYTE(opCode); + + rbReg &= 0x7; + EMIT_BYTE(x86EncodeMod(3, opReg, rbReg)); + + if (imLen == 0) goto EmitDone; + EMIT_BYTE(imVal & 0xFF); + goto EmitDone; + } + } + +EmitVexEvexM: + ASMJIT_ASSERT(rmRel != nullptr); + ASMJIT_ASSERT(rmRel->getOp() == Operand::kOpMem); + rmInfo = x86MemInfo[rmRel->as().getBaseIndexType()]; + + // Segment-override prefix. + if (rmRel->as().hasSegment()) + EMIT_BYTE(x86SegmentPrefix[rmRel->as().getSegmentId()]); + + // Address-override prefix. + if (rmInfo & _getAddressOverrideMask()) + EMIT_BYTE(0x67); + + rbReg = rmRel->as().hasBaseReg() ? rmRel->as().getBaseId() : uint32_t(0); + rxReg = rmRel->as().hasIndexReg() ? rmRel->as().getIndexId() : uint32_t(0); + + { + // VEX instructions use only 0-1 BYTE immediate. + ASMJIT_ASSERT(imLen <= 1); + + // Construct `x` - a complete EVEX|VEX prefix. + uint32_t x = ((opReg << 4 ) & 0x0000F980U) | // [........|........|Vvvvv..R|R.......]. + ((rxReg << 3 ) & 0x00000040U) | // [........|........|........|.X......]. + ((rxReg << 15) & 0x00080000U) | // [........|....X...|........|........]. + ((rbReg << 2 ) & 0x00000020U) | // [........|........|........|..B.....]. + (x86ExtractLLMM(opCode, options)); // [........|.LL.X...|Vvvvv..R|RXBmmmmm]. + opReg &= 0x07U; + + // Handle AVX512 options by a single branch. + const uint32_t kAvx512Options = X86Inst::kOptionOpExtra | + X86Inst::kOption1ToX | + X86Inst::kOptionKZ | + X86Inst::kOptionSAE | + X86Inst::kOptionER ; + if (options & kAvx512Options) { + // {er} and {sae} are both invalid if memory operand is used. + if (ASMJIT_UNLIKELY(options & (X86Inst::kOptionSAE | X86Inst::kOptionER))) + goto InvalidEROrSAE; + + // NOTE: We consider a valid construct internally even when {kz} was + // specified without specifying the register. In that case it would be + // `k0` and basically everything would be zeroed. It's a valid EVEX. + if (options & X86Inst::kOptionOpExtra) + x |= _opExtra.getId() << 16; + + x |= options & (X86Inst::kOption1ToX | // [........|.LLbXaaa|Vvvvv..R|RXBmmmmm]. + X86Inst::kOptionKZ ); // [........|zLLbXaaa|Vvvvv..R|RXBmmmmm]. + } + + // Check if EVEX is required by checking bits in `x` : [........|xx.xxxxx|x......x|...x....]. + if (x & 0x00DF8110U) { + uint32_t y = ((x << 4) & 0x00080000U) | // [........|....V...|........|........]. + ((x >> 4) & 0x00000010U) ; // [........|....V...|........|...R....]. + x = (x & 0xFFFF78E3U) | y; // [........|zLLbVaaa|0vvvv000|RXBR00mm]. + x = (x << 8) | // [zLLbVaaa|0vvvv000|RBBR00mm|00000000]. + ((opCode >> kSHR_W_PP) & 0x00830000U) | // [zLLbVaaa|Wvvvv0pp|RBBR00mm|00000000]. + ((opCode >> kSHR_W_EW) & 0x00800000U) ; // [zLLbVaaa|Wvvvv0pp|RBBR00mm|00000000] (added EVEX.W). + // _ ____ ____ + x ^= 0x087CF000U | kX86ByteEvex; // [zLLbVaaa|Wvvvv1pp|RBBR00mm|01100010]. + + EMIT_32(x); + EMIT_BYTE(opCode); + + if (opCode & 0x10000000U) { + // Broadcast, change the compressed displacement scale to either x4 (SHL 2) or x8 (SHL 3) + // depending on instruction's W. If 'W' is 1 'SHL' must be 3, otherwise it must be 2. + opCode &=~static_cast(X86Inst::kOpCode_CDSHL_Mask); + opCode |= ((x & 0x00800000U) ? 3 : 2) << X86Inst::kOpCode_CDSHL_Shift; + } + else { + // Add the compressed displacement 'SHF' to the opcode based on 'TTWLL'. + uint32_t TTWLL = ((opCode >> (X86Inst::kOpCode_CDTT_Shift - 3)) & 0x18) + + ((opCode >> (X86Inst::kOpCode_W_Shift - 2)) & 0x04) + + ((x >> 29) & 0x3); + opCode += x86CDisp8SHL[TTWLL]; + } + } + else { + // Not EVEX, prepare `x` for VEX2 or VEX3: x = [........|00L00000|0vvvv000|RXB0mmmm]. + x |= ((opCode >> (kSHR_W_PP + 8)) & 0x8300U) | // [00000000|00L00000|Wvvvv0pp|RXB0mmmm]. + ((x >> 11 ) & 0x0400U) ; // [00000000|00L00000|WvvvvLpp|RXB0mmmm]. + + // Clear a possible CDisp specified by EVEX. + opCode &= ~X86Inst::kOpCode_CDSHL_Mask; + + // Check if VEX3 is required / forced: [........|........|x.......|.xx..x..]. + if (x & 0x0008064U) { + uint32_t xorMsk = x86VEXPrefix[x & 0xF] | (opCode << 24); + + // Clear 'FORCE-VEX3' bit and all high bits. + x = (x & (0x4 ^ 0xFFFF)) << 8; // [00000000|WvvvvLpp|RXB0m0mm|00000000]. + // ____ ___ + x ^= xorMsk; // [_OPCODE_|WvvvvLpp|RXBmmmmm|VEX3_XOP]. + EMIT_32(x); + } + else { + // 'mmmmm' must be '00001'. + ASMJIT_ASSERT((x & 0x1F) == 0x01); + + x = ((x >> 8) ^ x) ^ 0xF9; + EMIT_BYTE(kX86ByteVex2); + EMIT_BYTE(x); + EMIT_BYTE(opCode); + } + } + } + + // MOD|SIB address. + if (!commonData->hasFlag(X86Inst::kInstFlagVM)) + goto EmitModSib; + + // MOD|VSIB address without INDEX is invalid. + if (rmInfo & kX86MemInfo_Index) + goto EmitModVSib; + goto InvalidInstruction; + + // -------------------------------------------------------------------------- + // [Emit - Jmp/Jcc/Call] + // -------------------------------------------------------------------------- + + // TODO: Should be adjusted after there support for multiple sections feature is added. +EmitJmpCall: + { + // Emit REX prefix if asked for (64-bit only). + uint32_t rex = x86ExtractREX(opCode, options); + if (rex) { + if (options & X86Inst::_kOptionInvalidRex) + goto InvalidRexPrefix; + EMIT_BYTE(rex | kX86ByteRex); + } + + uint64_t ip = static_cast((intptr_t)(cursor - _bufferData)); + uint32_t rel32 = 0; + uint32_t opCode8 = commonData->getAltOpCode(); + + uint32_t inst8Size = 1 + 1; // OPCODE + REL8 . + uint32_t inst32Size = 1 + 4; // [PREFIX] OPCODE + REL32. + + // Jcc instructions with 32-bit displacement use 0x0F prefix, + // other instructions don't. No other prefixes are used by X86. + ASMJIT_ASSERT((opCode8 & X86Inst::kOpCode_MM_Mask) == 0); + ASMJIT_ASSERT((opCode & X86Inst::kOpCode_MM_Mask) == 0 || + (opCode & X86Inst::kOpCode_MM_Mask) == X86Inst::kOpCode_MM_0F); + + inst32Size += static_cast( + (opCode & X86Inst::kOpCode_MM_Mask) == X86Inst::kOpCode_MM_0F); + + if (rmRel->isLabel()) { + label = _code->getLabelEntry(rmRel->as