mirror of
https://github.com/holub/mame
synced 2025-06-04 03:46:29 +03:00
asmjist: sync with upstream (nw)
This commit is contained in:
parent
7b6dbd7a14
commit
19214dd8e6
110
3rdparty/asmjit/.travis.yml
vendored
110
3rdparty/asmjit/.travis.yml
vendored
@ -12,6 +12,13 @@ dist: bionic
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- name: "Source Code Check"
|
||||
env: BUILD_MATRIX="SOURCE_CODE_CHECK=1"
|
||||
os: linux
|
||||
language: node_js
|
||||
node_js:
|
||||
- node
|
||||
|
||||
- name: "Linux Clang Default [64-bit] [DBG]"
|
||||
env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=clang-9 && CXX=clang++-9"
|
||||
os: linux
|
||||
@ -57,6 +64,33 @@ matrix:
|
||||
- sourceline: "ppa:ubuntu-toolchain-r/test"
|
||||
packages: [clang++-9]
|
||||
|
||||
- name: "Linux Clang Default [64-bit] [REL] [NoDeprecated]"
|
||||
env: BUILD_MATRIX="BUILD_TYPE=Release && CC=clang-9 && CXX=clang++-9" EXTRA_OPTIONS="-DASMJIT_NO_DEPRECATED=1"
|
||||
os: linux
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: "ppa:ubuntu-toolchain-r/test"
|
||||
packages: [clang++-9]
|
||||
|
||||
- name: "Linux Clang Default [64-bit] [REL] [NoIntrinsics]"
|
||||
env: BUILD_MATRIX="BUILD_TYPE=Release && CC=clang-9 && CXX=clang++-9" EXTRA_OPTIONS="-DASMJIT_NO_INTRINSICS=1"
|
||||
os: linux
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: "ppa:ubuntu-toolchain-r/test"
|
||||
packages: [clang++-9]
|
||||
|
||||
- name: "Linux Clang Default [64-bit] [REL] [NoLogging]"
|
||||
env: BUILD_MATRIX="BUILD_TYPE=Release && CC=clang-9 && CXX=clang++-9" EXTRA_OPTIONS="-DASMJIT_NO_LOGGING=1"
|
||||
os: linux
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: "ppa:ubuntu-toolchain-r/test"
|
||||
packages: [clang++-9]
|
||||
|
||||
- name: "Linux Clang Default [64-bit] [REL] [NoBuilder]"
|
||||
env: BUILD_MATRIX="BUILD_TYPE=Release && CC=clang-9 && CXX=clang++-9" EXTRA_OPTIONS="-DASMJIT_NO_BUILDER=1"
|
||||
os: linux
|
||||
@ -75,15 +109,6 @@ matrix:
|
||||
- sourceline: "ppa:ubuntu-toolchain-r/test"
|
||||
packages: [clang++-9]
|
||||
|
||||
- name: "Linux Clang Default [64-bit] [REL] [NoLogging]"
|
||||
env: BUILD_MATRIX="BUILD_TYPE=Release && CC=clang-9 && CXX=clang++-9" EXTRA_OPTIONS="-DASMJIT_NO_LOGGING=1"
|
||||
os: linux
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: "ppa:ubuntu-toolchain-r/test"
|
||||
packages: [clang++-9]
|
||||
|
||||
- name: "Linux GCC 4.8 [32-bit] [DBG]"
|
||||
env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-4.8 && CXX=g++-4.8" CXXFLAGS=-m32 LDFLAGS=-m32
|
||||
os: linux
|
||||
@ -268,36 +293,51 @@ before_install:
|
||||
- eval "$BUILD_MATRIX"
|
||||
|
||||
before_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- |
|
||||
if [[ "$BUILD_TOOLCHAIN" =~ ^Visual\ Studio ]]; then
|
||||
cmake .. -G"${BUILD_TOOLCHAIN}" -DASMJIT_TEST=1 ${EXTRA_OPTIONS}
|
||||
else
|
||||
cmake .. -G"${BUILD_TOOLCHAIN}" -DASMJIT_TEST=1 ${EXTRA_OPTIONS} -DCMAKE_PREFIX_PATH="${MINGW_PATH}" -DCMAKE_BUILD_TYPE="${BUILD_TYPE}"
|
||||
if [ -z $SOURCE_CODE_CHECK ]; then
|
||||
mkdir build
|
||||
cd build
|
||||
if [[ "$BUILD_TOOLCHAIN" =~ ^Visual\ Studio ]]; then
|
||||
cmake .. -G"${BUILD_TOOLCHAIN}" -DASMJIT_TEST=1 ${EXTRA_OPTIONS}
|
||||
else
|
||||
cmake .. -G"${BUILD_TOOLCHAIN}" -DASMJIT_TEST=1 ${EXTRA_OPTIONS} -DCMAKE_PREFIX_PATH="${MINGW_PATH}" -DCMAKE_BUILD_TYPE="${BUILD_TYPE}"
|
||||
fi
|
||||
cd ..
|
||||
fi
|
||||
- cd ..
|
||||
|
||||
script:
|
||||
- cd build
|
||||
- |
|
||||
if [[ "$BUILD_TOOLCHAIN" =~ ^Visual\ Studio ]]; then
|
||||
cmake --build . --config ${BUILD_TYPE} -- -nologo -v:minimal
|
||||
cd ${BUILD_TYPE}
|
||||
if [ -z $SOURCE_CODE_CHECK ]; then
|
||||
cd build
|
||||
if [[ "$BUILD_TOOLCHAIN" =~ ^Visual\ Studio ]]; then
|
||||
cmake --build . --config ${BUILD_TYPE} -- -nologo -v:minimal || exit 1
|
||||
cd ${BUILD_TYPE}
|
||||
else
|
||||
cmake --build . || exit 1
|
||||
fi
|
||||
echo ""
|
||||
echo "=== Starting Tests ==="
|
||||
echo ""
|
||||
if [ "$USE_VALGRIND" = "1" ]; then
|
||||
RUN_CMD="valgrind --leak-check=full --show-reachable=yes --track-origins=yes"
|
||||
fi
|
||||
eval "$RUN_CMD ./asmjit_test_unit --quick" || exit 1
|
||||
echo ""
|
||||
eval "$RUN_CMD ./asmjit_test_opcode > /dev/null" || exit 1
|
||||
echo ""
|
||||
eval "$RUN_CMD ./asmjit_test_x86_asm" || exit 1
|
||||
echo ""
|
||||
eval "$RUN_CMD ./asmjit_test_x86_sections" || exit 1
|
||||
if [ -f ./asmjit_test_x86_instinfo ]; then
|
||||
echo ""
|
||||
eval "$RUN_CMD ./asmjit_test_x86_instinfo" || exit 1
|
||||
fi
|
||||
if [ -f ./asmjit_test_x86_cc ]; then
|
||||
echo ""
|
||||
eval "$RUN_CMD ./asmjit_test_x86_cc" || exit 1
|
||||
fi
|
||||
else
|
||||
cmake --build .
|
||||
fi
|
||||
|
||||
- |
|
||||
if [ "$USE_VALGRIND" = "1" ]; then
|
||||
RUN_CMD="valgrind --leak-check=full --show-reachable=yes --track-origins=yes"
|
||||
fi
|
||||
|
||||
- eval "$RUN_CMD ./asmjit_test_unit --quick"
|
||||
- eval "$RUN_CMD ./asmjit_test_opcode > /dev/null"
|
||||
- eval "$RUN_CMD ./asmjit_test_x86_asm"
|
||||
- eval "$RUN_CMD ./asmjit_test_x86_sections"
|
||||
- |
|
||||
if [ -f ./asmjit_test_x86_cc ]; then
|
||||
eval "$RUN_CMD ./asmjit_test_x86_cc"
|
||||
cd tools
|
||||
./enumgen.sh --verify || exit 1
|
||||
cd ..
|
||||
fi
|
||||
|
60
3rdparty/asmjit/CMakeLists.txt
vendored
60
3rdparty/asmjit/CMakeLists.txt
vendored
@ -1,7 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
|
||||
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(SET CMP0063 NEW) # Honor visibility properties.
|
||||
|
||||
if(POLICY CMP0063)
|
||||
cmake_policy(SET CMP0063 NEW) # Honor visibility properties.
|
||||
endif()
|
||||
|
||||
if(POLICY CMP0092)
|
||||
cmake_policy(SET CMP0092 NEW) # Don't add -W3 warning level by default.
|
||||
endif()
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
@ -62,9 +69,9 @@ set(ASMJIT_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE PATH "Location of 'asmji
|
||||
set(ASMJIT_TEST ${ASMJIT_TEST} CACHE BOOL "Build 'asmjit' test applications")
|
||||
set(ASMJIT_EMBED ${ASMJIT_EMBED} CACHE BOOL "Embed 'asmjit' library (no targets)")
|
||||
set(ASMJIT_STATIC ${ASMJIT_STATIC} CACHE BOOL "Build 'asmjit' library as static")
|
||||
set(ASMJIT_SANITIZE ${ASMJIT_SANITIZE} CACHE BOOL "Build with C/C++ sanitizers enabled")
|
||||
set(ASMJIT_SANITIZE ${ASMJIT_SANITIZE} CACHE STRING "Build with sanitizers: 'address', 'undefined', etc...")
|
||||
set(ASMJIT_BUILD_X86 ${ASMJIT_BUILD_X86} CACHE BOOL "Build X86 backends (X86 and X86_64)")
|
||||
set(ASMJIT_BUILD_ARM ${ASMJIT_BUILD_ARM} CACHE BOOL "Build ARM backends")
|
||||
set(ASMJIT_BUILD_ARM ${ASMJIT_BUILD_ARM} CACHE BOOL "Build ARM backends (ARM/Trumb and AArch64")
|
||||
|
||||
# =============================================================================
|
||||
# [AsmJit - Project]
|
||||
@ -167,14 +174,13 @@ set(ASMJIT_PRIVATE_CFLAGS_REL "") # Private compiler flags used b
|
||||
set(ASMJIT_SANITIZE_CFLAGS "") # Compiler flags required by currently enabled sanitizers.
|
||||
set(ASMJIT_SANITIZE_LFLAGS "") # Linker flags required by currently enabled sanitizers.
|
||||
|
||||
# TODO: Backward compatibility.
|
||||
# We will have to keep this most likely forever as some users may still be using it.
|
||||
set(ASMJIT_INCLUDE_DIR "${ASMJIT_INCLUDE_DIRS}")
|
||||
|
||||
if (NOT ASMJIT_NO_CUSTOM_FLAGS)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
|
||||
list(APPEND ASMJIT_PRIVATE_CFLAGS
|
||||
-MP # [+] Multi-Process Compilation.
|
||||
-GR- # [-] Runtime type information.
|
||||
-GF # [+] Eliminate duplicate strings.
|
||||
-Zc:inline # [+] Remove unreferenced COMDAT.
|
||||
-Zc:strictStrings # [+] Strict const qualification of string literals.
|
||||
@ -189,7 +195,7 @@ if (NOT ASMJIT_NO_CUSTOM_FLAGS)
|
||||
-O2 # [+] Favor speed over size.
|
||||
-Oi) # [+] Generate intrinsic functions.
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(GNU|Clang|AppleClang)$")
|
||||
list(APPEND ASMJIT_PRIVATE_CFLAGS -Wall -Wextra)
|
||||
list(APPEND ASMJIT_PRIVATE_CFLAGS -Wall -Wextra -Wconversion)
|
||||
list(APPEND ASMJIT_PRIVATE_CFLAGS -fno-math-errno)
|
||||
list(APPEND ASMJIT_PRIVATE_CFLAGS_REL -O2)
|
||||
|
||||
@ -197,8 +203,10 @@ if (NOT ASMJIT_NO_CUSTOM_FLAGS)
|
||||
-fno-threadsafe-statics
|
||||
-fno-semantic-interposition)
|
||||
|
||||
# The following flags can save few bytes in the resulting binary.
|
||||
asmjit_detect_cflags(ASMJIT_PRIVATE_CFLAGS_REL
|
||||
-fmerge-all-constants)
|
||||
-fmerge-all-constants # Merge all constants even if it violates ISO C++.
|
||||
-fno-enforce-eh-specs) # Don't enforce termination if noexcept function throws.
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -248,6 +256,7 @@ foreach(build_option ASMJIT_STATIC
|
||||
ASMJIT_BUILD_X86
|
||||
#ASMJIT_BUILD_ARM
|
||||
ASMJIT_BUILD_A64
|
||||
ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_NO_JIT
|
||||
ASMJIT_NO_LOGGING
|
||||
ASMJIT_NO_BUILDER
|
||||
@ -289,6 +298,7 @@ set(ASMJIT_SRC_LIST
|
||||
asmjit/core/builder.h
|
||||
asmjit/core/callconv.cpp
|
||||
asmjit/core/callconv.h
|
||||
asmjit/core/codebuffer.h
|
||||
asmjit/core/codebufferwriter_p.h
|
||||
asmjit/core/codeholder.cpp
|
||||
asmjit/core/codeholder.h
|
||||
@ -301,7 +311,15 @@ set(ASMJIT_SRC_LIST
|
||||
asmjit/core/datatypes.h
|
||||
asmjit/core/emitter.cpp
|
||||
asmjit/core/emitter.h
|
||||
asmjit/core/emitterutils.cpp
|
||||
asmjit/core/emitterutils_p.h
|
||||
asmjit/core/environment.cpp
|
||||
asmjit/core/environment.h
|
||||
asmjit/core/errorhandler.cpp
|
||||
asmjit/core/errorhandler.h
|
||||
asmjit/core/features.h
|
||||
asmjit/core/formatter.cpp
|
||||
asmjit/core/formatter.h
|
||||
asmjit/core/func.cpp
|
||||
asmjit/core/func.h
|
||||
asmjit/core/globals.cpp
|
||||
@ -312,8 +330,8 @@ set(ASMJIT_SRC_LIST
|
||||
asmjit/core/jitallocator.h
|
||||
asmjit/core/jitruntime.cpp
|
||||
asmjit/core/jitruntime.h
|
||||
asmjit/core/logging.cpp
|
||||
asmjit/core/logging.h
|
||||
asmjit/core/logger.cpp
|
||||
asmjit/core/logger.h
|
||||
asmjit/core/misc_p.h
|
||||
asmjit/core/operand.cpp
|
||||
asmjit/core/operand.h
|
||||
@ -353,6 +371,8 @@ set(ASMJIT_SRC_LIST
|
||||
asmjit/core/zonevector.h
|
||||
|
||||
asmjit/x86.h
|
||||
asmjit/x86/x86archdata.cpp
|
||||
asmjit/x86/x86archdata_p.h
|
||||
asmjit/x86/x86assembler.cpp
|
||||
asmjit/x86/x86assembler.h
|
||||
asmjit/x86/x86builder.cpp
|
||||
@ -364,6 +384,8 @@ set(ASMJIT_SRC_LIST
|
||||
asmjit/x86/x86emitter.h
|
||||
asmjit/x86/x86features.cpp
|
||||
asmjit/x86/x86features.h
|
||||
asmjit/x86/x86formatter.cpp
|
||||
asmjit/x86/x86formatter_p.h
|
||||
asmjit/x86/x86globals.h
|
||||
asmjit/x86/x86internal.cpp
|
||||
asmjit/x86/x86internal_p.h
|
||||
@ -372,8 +394,6 @@ set(ASMJIT_SRC_LIST
|
||||
asmjit/x86/x86instdb_p.h
|
||||
asmjit/x86/x86instapi.cpp
|
||||
asmjit/x86/x86instapi_p.h
|
||||
asmjit/x86/x86logging.cpp
|
||||
asmjit/x86/x86logging_p.h
|
||||
asmjit/x86/x86operand.cpp
|
||||
asmjit/x86/x86operand.h
|
||||
asmjit/x86/x86rapass.cpp
|
||||
@ -473,11 +493,27 @@ if (NOT ASMJIT_EMBED)
|
||||
CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL})
|
||||
endforeach()
|
||||
|
||||
if (NOT ASMJIT_NO_INTROSPECTION)
|
||||
asmjit_add_target(asmjit_test_x86_instinfo TEST
|
||||
SOURCES test/asmjit_test_x86_instinfo.cpp
|
||||
LIBRARIES AsmJit::AsmJit
|
||||
CFLAGS ${ASMJIT_PRIVATE_CFLAGS}
|
||||
CFLAGS_DBG ${ASMJIT_PRIVATE_CFLAGS_DBG}
|
||||
CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL})
|
||||
endif()
|
||||
|
||||
if (NOT (ASMJIT_NO_BUILDER OR ASMJIT_NO_COMPILER))
|
||||
# Vectorcall tests and XMM tests require at least SSE2 (required in 32-bit mode).
|
||||
set(sse2_flags "")
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
|
||||
asmjit_detect_cflags(sse2_flags "-arch:SSE2")
|
||||
else()
|
||||
asmjit_detect_cflags(sse2_flags "-msse2")
|
||||
endif()
|
||||
asmjit_add_target(asmjit_test_x86_cc TEST
|
||||
SOURCES test/asmjit_test_x86_cc.cpp
|
||||
LIBRARIES AsmJit::AsmJit
|
||||
CFLAGS ${ASMJIT_PRIVATE_CFLAGS}
|
||||
CFLAGS ${ASMJIT_PRIVATE_CFLAGS} ${sse2_flags}
|
||||
CFLAGS_DBG ${ASMJIT_PRIVATE_CFLAGS_DBG}
|
||||
CFLAGS_REL ${ASMJIT_PRIVATE_CFLAGS_REL})
|
||||
endif()
|
||||
|
1940
3rdparty/asmjit/README.md
vendored
1940
3rdparty/asmjit/README.md
vendored
File diff suppressed because it is too large
Load Diff
146
3rdparty/asmjit/src/asmjit.natvis
vendored
146
3rdparty/asmjit/src/asmjit.natvis
vendored
@ -35,25 +35,25 @@
|
||||
</Type>
|
||||
|
||||
<Type Name="asmjit::Operand_">
|
||||
<Intrinsic Name="opType" Expression="(unsigned int)(_signature & 0x7)"/>
|
||||
<Intrinsic Name="opSize" Expression="(_signature >> 24) & 0xFF"/>
|
||||
<Intrinsic Name="opType" Expression="(unsigned int)(_signature & 0x7)" />
|
||||
<Intrinsic Name="opSize" Expression="(_signature >> 24) & 0xFF" />
|
||||
|
||||
<Intrinsic Name="regType" Expression="(_signature >> 3) & 0x1F"/>
|
||||
<Intrinsic Name="regGroup" Expression="(_signature >> 8) & 0xF"/>
|
||||
<Intrinsic Name="regType" Expression="(_signature >> 3) & 0x1F" />
|
||||
<Intrinsic Name="regGroup" Expression="(_signature >> 8) & 0xF" />
|
||||
|
||||
<Intrinsic Name="memBaseType" Expression="(_signature >> 3) & 0x1F"/>
|
||||
<Intrinsic Name="memIndexType" Expression="(_signature >> 8) & 0x1F"/>
|
||||
<Intrinsic Name="memAddrType" Expression="(_signature >> 13) & 0x3"/>
|
||||
<Intrinsic Name="memRegHome" Expression="(_signature >> 15) & 0x1"/>
|
||||
<Intrinsic Name="memBaseType" Expression="(_signature >> 3) & 0x1F" />
|
||||
<Intrinsic Name="memIndexType" Expression="(_signature >> 8) & 0x1F" />
|
||||
<Intrinsic Name="memAddrType" Expression="(_signature >> 13) & 0x3" />
|
||||
<Intrinsic Name="memRegHome" Expression="(_signature >> 15) & 0x1" />
|
||||
|
||||
<Intrinsic Name="memBaseId" Expression="_baseId"/>
|
||||
<Intrinsic Name="memIndexId" Expression="_data[0]"/>
|
||||
<Intrinsic Name="memBaseId" Expression="_baseId" />
|
||||
<Intrinsic Name="memIndexId" Expression="_data[0]" />
|
||||
|
||||
<Intrinsic Name="memOffset32b" Expression="(__int64)int(_data[1])"/>
|
||||
<Intrinsic Name="memOffset64b" Expression="(__int64) ((unsigned __int64)_baseId << 32) | ((unsigned __int64)_data[1])"/>
|
||||
<Intrinsic Name="memOffset" Expression="memBaseType() != 0 ? memOffset32b() : memOffset64b()"/>
|
||||
<Intrinsic Name="memOffset32b" Expression="(__int64)int(_data[1])" />
|
||||
<Intrinsic Name="memOffset64b" Expression="(__int64) ((unsigned __int64)_baseId << 32) | ((unsigned __int64)_data[1])" />
|
||||
<Intrinsic Name="memOffset" Expression="memBaseType() != 0 ? memOffset32b() : memOffset64b()" />
|
||||
|
||||
<Intrinsic Name="immValue" Expression="((__int64)_data[1] << 32) | (__int64)_data[0]"/>
|
||||
<Intrinsic Name="immValue" Expression="((__int64)_data[1] << 32) | (__int64)_data[0]" />
|
||||
|
||||
<DisplayString Condition="opType() == 0">[None]</DisplayString>
|
||||
<DisplayString Condition="opType() == 1">[Reg] {{ id={_baseId, d} group={regGroup(), d} type={regType(), d} size={opSize(), d} }}</DisplayString>
|
||||
@ -80,4 +80,122 @@
|
||||
<Item Name="_data[ImmLo]" Condition="opType() == 3">_data[1]</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="asmjit::FuncValue">
|
||||
<Intrinsic Name="isReg" Expression="(_data & asmjit::FuncValue::kFlagIsReg) != 0" />
|
||||
<Intrinsic Name="isStack" Expression="(_data & asmjit::FuncValue::kFlagIsStack) != 0" />
|
||||
<Intrinsic Name="isIndirect" Expression="(_data & asmjit::FuncValue::kFlagIsIndirect) != 0" />
|
||||
<Intrinsic Name="isDone" Expression="(_data & asmjit::FuncValue::kFlagIsDone) != 0" />
|
||||
|
||||
<Intrinsic Name="typeId" Expression="((_data & asmjit::FuncValue::kTypeIdMask) >> asmjit::FuncValue::kTypeIdShift)" />
|
||||
<Intrinsic Name="regId" Expression="((_data & asmjit::FuncValue::kRegIdMask) >> asmjit::FuncValue::kRegIdShift)" />
|
||||
<Intrinsic Name="regType" Expression="((_data & asmjit::FuncValue::kRegTypeMask) >> asmjit::FuncValue::kRegTypeShift)" />
|
||||
<Intrinsic Name="stackOffset" Expression="((_data & asmjit::FuncValue::kStackOffsetMask) >> asmjit::FuncValue::kStackOffsetShift)" />
|
||||
|
||||
<DisplayString Condition="isReg()">[RegValue {{ regType={regType()} indirect={isIndirect()} done={isDone()} }}]</DisplayString>
|
||||
<DisplayString Condition="isStack()">[StackValue {{ indirect={isIndirect()} done={isDone()} }}]</DisplayString>
|
||||
<DisplayString Condition="!isReg() && !isStack()">[Unknown]</DisplayString>
|
||||
|
||||
<Expand HideRawView="true">
|
||||
<Item Name="data">_data</Item>
|
||||
<Item Name="typeId">(asmjit::Type::Id)(typeId())</Item>
|
||||
<Item Name="regType" Condition="isReg()">(asmjit::BaseReg::RegType)regType()</Item>
|
||||
<Item Name="regId" Condition="isReg()">regId()</Item>
|
||||
<Item Name="stackOffset" Condition="isStack()">stackOffset()</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="asmjit::BaseNode">
|
||||
<Intrinsic Name="nodeType" Expression="_any._nodeType" />
|
||||
|
||||
<Intrinsic Name="isInst" Expression="nodeType() == asmjit::BaseNode::kNodeInst"></Intrinsic>
|
||||
<Intrinsic Name="isSection" Expression="nodeType() == asmjit::BaseNode::kNodeSection"></Intrinsic>
|
||||
<Intrinsic Name="isLabel" Expression="nodeType() == asmjit::BaseNode::kNodeLabel"></Intrinsic>
|
||||
<Intrinsic Name="isAlign" Expression="nodeType() == asmjit::BaseNode::kNodeAlign"></Intrinsic>
|
||||
<Intrinsic Name="isEmbedData" Expression="nodeType() == asmjit::BaseNode::kNodeEmbedData"></Intrinsic>
|
||||
<Intrinsic Name="isEmbedLabel" Expression="nodeType() == asmjit::BaseNode::kNodeEmbedLabel"></Intrinsic>
|
||||
<Intrinsic Name="isEmbedLabelDelta" Expression="nodeType() == asmjit::BaseNode::kNodeEmbedLabelDelta"></Intrinsic>
|
||||
<Intrinsic Name="isConstPool" Expression="nodeType() == asmjit::BaseNode::kNodeConstPool"></Intrinsic>
|
||||
<Intrinsic Name="isComment" Expression="nodeType() == asmjit::BaseNode::kNodeComment"></Intrinsic>
|
||||
<Intrinsic Name="isSentinel" Expression="nodeType() == asmjit::BaseNode::kNodeSentinel"></Intrinsic>
|
||||
<Intrinsic Name="isJump" Expression="nodeType() == asmjit::BaseNode::kNodeJump"></Intrinsic>
|
||||
<Intrinsic Name="isFunc" Expression="nodeType() == asmjit::BaseNode::kNodeFunc"></Intrinsic>
|
||||
<Intrinsic Name="isFuncRet" Expression="nodeType() == asmjit::BaseNode::kNodeFuncRet"></Intrinsic>
|
||||
<Intrinsic Name="isInvoke" Expression="nodeType() == asmjit::BaseNode::kNodeInvoke"></Intrinsic>
|
||||
|
||||
<Intrinsic Name="actsAsInst" Expression="isInst() || isJump() || isFunc() || isFuncRet() || isInvoke()" />
|
||||
<Intrinsic Name="actsAsLabel" Expression="isLabel() || isFunc()" />
|
||||
|
||||
<DisplayString Condition="isInst()">[InstNode]</DisplayString>
|
||||
<DisplayString Condition="isSentinel()">[SectionNode]</DisplayString>
|
||||
<DisplayString Condition="isLabel()">[LabelNode]</DisplayString>
|
||||
<DisplayString Condition="isAlign()">[AlignNode]</DisplayString>
|
||||
<DisplayString Condition="isEmbedData()">[EmbedDataNode]</DisplayString>
|
||||
<DisplayString Condition="isEmbedLabel()">[EmbedLabelNode]</DisplayString>
|
||||
<DisplayString Condition="isEmbedLabelDelta()">[EmbedLabelDeltaNode]</DisplayString>
|
||||
<DisplayString Condition="isConstPool()">[ConstPoolNode]</DisplayString>
|
||||
<DisplayString Condition="isComment()">[CommentNode]</DisplayString>
|
||||
<DisplayString Condition="isSentinel()">[SentinelNode]</DisplayString>
|
||||
<DisplayString Condition="isJump()">[JumpNode]</DisplayString>
|
||||
<DisplayString Condition="isFunc()">[FuncNode]</DisplayString>
|
||||
<DisplayString Condition="isFuncRet()">[FuncRetNode]</DisplayString>
|
||||
<DisplayString Condition="isInvoke()">[InvokeNode]</DisplayString>
|
||||
<DisplayString Condition="nodeType() == 0 || nodeType() > 18">[UnknownNode {nodeType(), d}]</DisplayString>
|
||||
|
||||
<Expand HideRawView="true">
|
||||
<Item Name="prev">_prev</Item>
|
||||
<Item Name="next">_next</Item>
|
||||
|
||||
<Item Name="nodeType">(asmjit::BaseNode::NodeType)_any._nodeType</Item>
|
||||
<Item Name="nodeFlags">(asmjit::BaseNode::Flags)_any._nodeFlags</Item>
|
||||
|
||||
<Item Name="position">_position</Item>
|
||||
<Item Name="userData.u64">_userDataU64</Item>
|
||||
<Item Name="userData.ptr">_userDataPtr</Item>
|
||||
<Item Name="passData">_passData</Item>
|
||||
<Item Name="inlineComment">_inlineComment, s8</Item>
|
||||
|
||||
<Item Name="baseInst" Condition="actsAsInst()">((asmjit::InstNode*)this)->_baseInst</Item>
|
||||
<Item Name="opCount" Condition="actsAsInst()">_inst._opCount</Item>
|
||||
<Item Name="opCapacity" Condition="actsAsInst()">_inst._opCapacity</Item>
|
||||
<Item Name="opArray" Condition="actsAsInst()">((asmjit::InstNode*)this)->_opArray, [_inst._opCount]</Item>
|
||||
|
||||
<Item Name="sectionId" Condition="isSection()">((asmjit::SectionNode*)this)->_id</Item>
|
||||
<Item Name="nextSection" Condition="isSection()">((asmjit::SectionNode*)this)->_nextSection</Item>
|
||||
|
||||
<Item Name="labelId" Condition="isLabel()">((asmjit::LabelNode*)this)->_id</Item>
|
||||
|
||||
<Item Name="alignMode" Condition="isAlign()">((asmjit::AlignNode*)this)->_alignMode</Item>
|
||||
<Item Name="alignment" Condition="isAlign()">((asmjit::AlignNode*)this)->_alignment</Item>
|
||||
|
||||
<Item Name="typeId" Condition="isEmbedData()">_embed._typeId, d</Item>
|
||||
<Item Name="typeSize" Condition="isEmbedData()">_embed._typeSize, d</Item>
|
||||
<Item Name="itemCount" Condition="isEmbedData()">((asmjit::EmbedDataNode*)this)->_itemCount</Item>
|
||||
<Item Name="repeatCount" Condition="isEmbedData()">((asmjit::EmbedDataNode*)this)->_repeatCount</Item>
|
||||
<Item Name="inlineData" Condition="isEmbedData()">((asmjit::EmbedDataNode*)this)->_inlineData</Item>
|
||||
<Item Name="externalData" Condition="isEmbedData()">((asmjit::EmbedDataNode*)this)->_externalData</Item>
|
||||
|
||||
<Item Name="labelId" Condition="isEmbedLabel()">((asmjit::EmbedLabelNode*)this)->_id</Item>
|
||||
|
||||
<Item Name="labelId" Condition="isEmbedLabelDelta()">((asmjit::EmbedLabelDeltaNode*)this)->_id</Item>
|
||||
<Item Name="baseId" Condition="isEmbedLabelDelta()">((asmjit::EmbedLabelDeltaNode*)this)->_baseId</Item>
|
||||
<Item Name="dataSize" Condition="isEmbedLabelDelta()">((asmjit::EmbedLabelDeltaNode*)this)->_dataSize</Item>
|
||||
|
||||
<Item Name="constPool" Condition="isConstPool()">((asmjit::ConstPoolNode*)this)->_constPool</Item>
|
||||
|
||||
<Item Name="sentinel.sentinelType" Condition="isSentinel()">(asmjit::SentinelNode::SentinelType)_sentinel._sentinelType</Item>
|
||||
|
||||
<Item Name="annotation" Condition="isJump()">((asmjit::JumpNode*)this)->_annotation</Item>
|
||||
|
||||
<Item Name="funcDetail" Condition="isFunc()">((asmjit::FuncNode*)this)->_funcDetail</Item>
|
||||
<Item Name="frame" Condition="isFunc()">((asmjit::FuncNode*)this)->_frame</Item>
|
||||
<Item Name="exitNode" Condition="isFunc()">((asmjit::FuncNode*)this)->_exitNode</Item>
|
||||
<Item Name="end" Condition="isFunc()">((asmjit::FuncNode*)this)->_end</Item>
|
||||
<Item Name="args" Condition="isFunc()">((asmjit::FuncNode*)this)->_args, [((asmjit::FuncNode*)this)->_funcDetail._argCount]</Item>
|
||||
|
||||
<Item Name="funcDetail" Condition="isInvoke()">((asmjit::InvokeNode*)this)->_funcDetail</Item>
|
||||
<Item Name="rets" Condition="isInvoke()">((asmjit::InvokeNode*)this)->_rets, [((asmjit::InvokeNode*)this)->_funcDetail._retCount]</Item>
|
||||
<Item Name="args" Condition="isInvoke()">((asmjit::InvokeNode*)this)->_args, [((asmjit::InvokeNode*)this)->_funcDetail._argCount]</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
|
21
3rdparty/asmjit/src/asmjit/asmjit.h
vendored
21
3rdparty/asmjit/src/asmjit/asmjit.h
vendored
@ -24,27 +24,6 @@
|
||||
#ifndef ASMJIT_ASMJIT_H_INCLUDED
|
||||
#define ASMJIT_ASMJIT_H_INCLUDED
|
||||
|
||||
//! \mainpage API Reference
|
||||
//!
|
||||
//! AsmJit C++ API reference documentation generated by Doxygen.
|
||||
//!
|
||||
//! Introduction provided by the project page at https://github.com/asmjit/asmjit.
|
||||
//!
|
||||
//! \section main_groups Groups
|
||||
//!
|
||||
//! The documentation is split into the following groups:
|
||||
//!
|
||||
//! $$DOCS_GROUP_OVERVIEW$$
|
||||
//!
|
||||
//! \section main_other Other Pages
|
||||
//!
|
||||
//! - <a href="annotated.html">Class List</a> - List of classes sorted alphabetically
|
||||
//! - <a href="namespaceasmjit.html">AsmJit Namespace</a> - List of symbols provided by `asmjit` namespace
|
||||
|
||||
//! \namespace asmjit
|
||||
//!
|
||||
//! Root namespace used by AsmJit.
|
||||
|
||||
#include "./core.h"
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
|
2009
3rdparty/asmjit/src/asmjit/core.h
vendored
2009
3rdparty/asmjit/src/asmjit/core.h
vendored
File diff suppressed because it is too large
Load Diff
@ -53,7 +53,7 @@
|
||||
|
||||
#include "./api-config.h"
|
||||
|
||||
#if !defined(ASMJIT_BUILD_DEBUG) && ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4, 4, 0)
|
||||
#if !defined(ASMJIT_BUILD_DEBUG) && defined(__GNUC__) && !defined(__clang__)
|
||||
#define ASMJIT_FAVOR_SIZE __attribute__((__optimize__("Os")))
|
||||
#define ASMJIT_FAVOR_SPEED __attribute__((__optimize__("O3")))
|
||||
#elif ASMJIT_CXX_HAS_ATTRIBUTE(__minsize__, 0)
|
||||
|
313
3rdparty/asmjit/src/asmjit/core/api-config.h
vendored
313
3rdparty/asmjit/src/asmjit/core/api-config.h
vendored
@ -28,78 +28,90 @@
|
||||
// [asmjit::Version]
|
||||
// ============================================================================
|
||||
|
||||
#define ASMJIT_LIBRARY_VERSION 0x010200 /* 1.2.0 */
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
//! AsmJit library version in `(Major << 16) | (Minor << 8) | (Patch)` format.
|
||||
#define ASMJIT_LIBRARY_VERSION 0x010400 /* 1.4.0 */
|
||||
|
||||
//! \}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Options]
|
||||
// [asmjit::Build - Documentation]
|
||||
// ============================================================================
|
||||
|
||||
// AsmJit Static Builds and Embedding
|
||||
// ----------------------------------
|
||||
//
|
||||
// These definitions can be used to enable static library build. Embed is used
|
||||
// when AsmJit's source code is embedded directly in another project, implies
|
||||
// static build as well.
|
||||
//
|
||||
// #define ASMJIT_EMBED // Asmjit is embedded (implies ASMJIT_BUILD_STATIC).
|
||||
#define ASMJIT_STATIC // Enable static-library build.
|
||||
// NOTE: Doxygen cannot document macros that are not defined, that's why we have
|
||||
// to define them and then undefine them, so it won't use the macros with its
|
||||
// own preprocessor.
|
||||
#ifdef _DOXYGEN
|
||||
namespace asmjit {
|
||||
|
||||
// AsmJit Build Mode
|
||||
// -----------------
|
||||
//
|
||||
// These definitions control the build mode and tracing support. The build mode
|
||||
// should be auto-detected at compile time, but it's possible to override it in
|
||||
// case that the auto-detection fails.
|
||||
//
|
||||
// Tracing is a feature that is never compiled by default and it's only used to
|
||||
// debug AsmJit itself.
|
||||
//
|
||||
// #define ASMJIT_BUILD_DEBUG // Always use debug-mode (ASMJIT_ASSERT enabled).
|
||||
// #define ASMJIT_BUILD_RELEASE // Always use release-mode (ASMJIT_ASSERT disabled).
|
||||
//! \addtogroup asmjit_build
|
||||
//! \{
|
||||
|
||||
// AsmJit Build Backends
|
||||
// ---------------------
|
||||
//
|
||||
// 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 // Enable X86 targets (X86 and X86_64).
|
||||
// #define ASMJIT_BUILD_ARM // Enable ARM targets (ARM and AArch64).
|
||||
// #define ASMJIT_BUILD_HOST // Enable targets based on target arch (default).
|
||||
//! Asmjit is embedded, implies \ref ASMJIT_STATIC.
|
||||
#define ASMJIT_EMBED
|
||||
|
||||
// AsmJit Build Options
|
||||
// --------------------
|
||||
//
|
||||
// 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 BaseCompiler).
|
||||
//
|
||||
// AsmJit features are enabled by default.
|
||||
// #define ASMJIT_NO_BUILDER // Disable Builder (completely).
|
||||
// #define ASMJIT_NO_COMPILER // Disable Compiler (completely).
|
||||
// #define ASMJIT_NO_JIT // Disable JIT memory manager and JitRuntime.
|
||||
// #define ASMJIT_NO_LOGGING // Disable logging and formatting (completely).
|
||||
// #define ASMJIT_NO_TEXT // Disable everything that contains text
|
||||
// // representation (instructions, errors, ...).
|
||||
// #define ASMJIT_NO_VALIDATION // Disable validation API and options.
|
||||
// #define ASMJIT_NO_INTROSPECTION // Disable API related to instruction database.
|
||||
// // (validation, cpu features, rw-info, etc).
|
||||
//! Enables static-library build.
|
||||
#define ASMJIT_STATIC
|
||||
|
||||
// ASMJIT_NO_BUILDER implies ASMJIT_NO_COMPILER.
|
||||
#if defined(ASMJIT_NO_BUILDER) && !defined(ASMJIT_NO_COMPILER)
|
||||
#define ASMJIT_NO_COMPILER
|
||||
#endif
|
||||
//! Defined when AsmJit's build configuration is 'Debug'.
|
||||
//!
|
||||
//! \note Can be defined explicitly to bypass autodetection.
|
||||
#define ASMJIT_BUILD_DEBUG
|
||||
|
||||
// Prevent compile-time errors caused by misconfiguration.
|
||||
#if defined(ASMJIT_NO_TEXT) && !defined(ASMJIT_NO_LOGGING)
|
||||
#pragma "ASMJIT_NO_TEXT can only be defined when ASMJIT_NO_LOGGING is defined."
|
||||
#undef ASMJIT_NO_TEXT
|
||||
#endif
|
||||
//! Defined when AsmJit's build configuration is 'Release'.
|
||||
//!
|
||||
//! \note Can be defined explicitly to bypass autodetection.
|
||||
#define ASMJIT_BUILD_RELEASE
|
||||
|
||||
#if defined(ASMJIT_NO_INTROSPECTION) && !defined(ASMJIT_NO_COMPILER)
|
||||
#pragma message("ASMJIT_NO_INTROSPECTION can only be defined when ASMJIT_NO_COMPILER is defined")
|
||||
#undef ASMJIT_NO_INTROSPECTION
|
||||
#endif
|
||||
//! Defined to build X86/X64 backend.
|
||||
#define ASMJIT_BUILD_X86
|
||||
|
||||
//! Defined to build ARM/AArch64 backend.
|
||||
#define ASMJIT_BUILD_ARM
|
||||
|
||||
//! Defined to build host backend autodetected at compile-time.
|
||||
#define ASMJIT_BUILD_HOST
|
||||
|
||||
//! Disables deprecated API at compile time.
|
||||
#define ASMJIT_NO_DEPRECATED
|
||||
|
||||
//! Disables \ref asmjit_builder functionality completely.
|
||||
#define ASMJIT_NO_BUILDER
|
||||
|
||||
//! Disables \ref asmjit_compiler functionality completely.
|
||||
#define ASMJIT_NO_COMPILER
|
||||
|
||||
//! Disables JIT memory management and \ref JitRuntime.
|
||||
#define ASMJIT_NO_JIT
|
||||
|
||||
//! Disables \ref Logger and \ref Formatter.
|
||||
#define ASMJIT_NO_LOGGING
|
||||
|
||||
//! Disables everything that contains text.
|
||||
#define ASMJIT_NO_TEXT
|
||||
|
||||
//! Disables instruction validation API.
|
||||
#define ASMJIT_NO_VALIDATION
|
||||
|
||||
//! Disables instruction introspection API.
|
||||
#define ASMJIT_NO_INTROSPECTION
|
||||
|
||||
// Avoid doxygen preprocessor using feature-selection definitions.
|
||||
#undef ASMJIT_NO_DEPRECATED
|
||||
#undef ASMJIT_NO_BUILDER
|
||||
#undef ASMJIT_NO_COMPILER
|
||||
#undef ASMJIT_NO_JIT
|
||||
#undef ASMJIT_NO_LOGGING
|
||||
#undef ASMJIT_NO_TEXT
|
||||
#undef ASMJIT_NO_VALIDATION
|
||||
#undef ASMJIT_NO_INTROSPECTION
|
||||
|
||||
//! \}
|
||||
|
||||
} // {asmjit}
|
||||
#endif // _DOXYGEN
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Dependencies]
|
||||
@ -122,23 +134,45 @@
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Options]
|
||||
// ============================================================================
|
||||
|
||||
// ASMJIT_NO_BUILDER implies ASMJIT_NO_COMPILER.
|
||||
#if defined(ASMJIT_NO_BUILDER) && !defined(ASMJIT_NO_COMPILER)
|
||||
#define ASMJIT_NO_COMPILER
|
||||
#endif
|
||||
|
||||
// Prevent compile-time errors caused by misconfiguration.
|
||||
#if defined(ASMJIT_NO_TEXT) && !defined(ASMJIT_NO_LOGGING)
|
||||
#pragma "ASMJIT_NO_TEXT can only be defined when ASMJIT_NO_LOGGING is defined."
|
||||
#undef ASMJIT_NO_TEXT
|
||||
#endif
|
||||
|
||||
#if defined(ASMJIT_NO_INTROSPECTION) && !defined(ASMJIT_NO_COMPILER)
|
||||
#pragma message("ASMJIT_NO_INTROSPECTION can only be defined when ASMJIT_NO_COMPILER is defined")
|
||||
#undef ASMJIT_NO_INTROSPECTION
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Build - Globals - Deprecated]
|
||||
// ============================================================================
|
||||
|
||||
// DEPRECATED: Will be removed in the future.
|
||||
#if defined(ASMJIT_BUILD_EMBED) || defined(ASMJIT_BUILD_STATIC)
|
||||
#if defined(ASMJIT_BUILD_EMBED)
|
||||
#pragma message("'ASMJIT_BUILD_EMBED' is deprecated, use 'ASMJIT_STATIC'")
|
||||
#endif
|
||||
#if defined(ASMJIT_BUILD_STATIC)
|
||||
#pragma message("'ASMJIT_BUILD_STATIC' is deprecated, use 'ASMJIT_STATIC'")
|
||||
#endif
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
#if defined(ASMJIT_BUILD_EMBED) || defined(ASMJIT_BUILD_STATIC)
|
||||
#if defined(ASMJIT_BUILD_EMBED)
|
||||
#pragma message("'ASMJIT_BUILD_EMBED' is deprecated, use 'ASMJIT_STATIC'")
|
||||
#endif
|
||||
#if defined(ASMJIT_BUILD_STATIC)
|
||||
#pragma message("'ASMJIT_BUILD_STATIC' is deprecated, use 'ASMJIT_STATIC'")
|
||||
#endif
|
||||
|
||||
#if !defined(ASMJIT_STATIC)
|
||||
#define ASMJIT_STATIC
|
||||
#if !defined(ASMJIT_STATIC)
|
||||
#define ASMJIT_STATIC
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Build - Globals - Build Mode]
|
||||
@ -181,7 +215,7 @@
|
||||
#define ASMJIT_ARCH_MIPS 0
|
||||
#endif
|
||||
|
||||
#define ASMJIT_ARCH_BITS (ASMJIT_ARCH_X86 | ASMJIT_ARCH_ARM | ASMJIT_ARCH_MIPS)
|
||||
#define ASMJIT_ARCH_BITS (ASMJIT_ARCH_X86 | ASMJIT_ARCH_ARM | ASMJIT_ARCH_MIPS)
|
||||
#if ASMJIT_ARCH_BITS == 0
|
||||
#undef ASMJIT_ARCH_BITS
|
||||
#if defined (__LP64__) || defined(_LP64)
|
||||
@ -221,11 +255,8 @@
|
||||
// [asmjit::Build - Globals - C++ Compiler and Features Detection]
|
||||
// ============================================================================
|
||||
|
||||
#define ASMJIT_CXX_CLANG 0
|
||||
#define ASMJIT_CXX_GNU 0
|
||||
#define ASMJIT_CXX_INTEL 0
|
||||
#define ASMJIT_CXX_MSC 0
|
||||
#define ASMJIT_CXX_MAKE_VER(MAJOR, MINOR, PATCH) ((MAJOR) * 10000000 + (MINOR) * 100000 + (PATCH))
|
||||
#define ASMJIT_CXX_MAKE_VER(MAJOR, MINOR) ((MAJOR) * 1000 + (MINOR))
|
||||
|
||||
// Intel Compiler [pretends to be GNU or MSC, so it must be checked first]:
|
||||
// - https://software.intel.com/en-us/articles/c0x-features-supported-by-intel-c-compiler
|
||||
@ -233,9 +264,6 @@
|
||||
// - https://software.intel.com/en-us/articles/c17-features-supported-by-intel-c-compiler
|
||||
#if defined(__INTEL_COMPILER)
|
||||
|
||||
#undef ASMJIT_CXX_INTEL
|
||||
#define ASMJIT_CXX_INTEL ASMJIT_CXX_MAKE_VER(__INTEL_COMPILER / 100, (__INTEL_COMPILER / 10) % 10, __INTEL_COMPILER % 10)
|
||||
|
||||
// MSC Compiler:
|
||||
// - https://msdn.microsoft.com/en-us/library/hh567368.aspx
|
||||
//
|
||||
@ -247,72 +275,26 @@
|
||||
// - 19.10.0 == VS2017
|
||||
#elif defined(_MSC_VER) && defined(_MSC_FULL_VER)
|
||||
|
||||
#undef ASMJIT_CXX_MSC
|
||||
#if _MSC_VER == _MSC_FULL_VER / 10000
|
||||
#define ASMJIT_CXX_MSC ASMJIT_CXX_MAKE_VER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000)
|
||||
#else
|
||||
#define ASMJIT_CXX_MSC ASMJIT_CXX_MAKE_VER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000)
|
||||
#endif
|
||||
|
||||
// Clang Compiler [Pretends to be GNU, so it must be checked before]:
|
||||
// - https://clang.llvm.org/cxx_status.html
|
||||
#elif defined(__clang_major__) && defined(__clang_minor__) && defined(__clang_patchlevel__)
|
||||
|
||||
#undef ASMJIT_CXX_CLANG
|
||||
#define ASMJIT_CXX_CLANG ASMJIT_CXX_MAKE_VER(__clang_major__, __clang_minor__, __clang_patchlevel__)
|
||||
|
||||
// GNU Compiler:
|
||||
// - https://gcc.gnu.org/projects/cxx-status.html
|
||||
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
|
||||
|
||||
#undef ASMJIT_CXX_GNU
|
||||
#define ASMJIT_CXX_GNU ASMJIT_CXX_MAKE_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
|
||||
#define ASMJIT_CXX_GNU ASMJIT_CXX_MAKE_VER(__GNUC__, __GNUC_MINOR__)
|
||||
|
||||
#endif
|
||||
|
||||
// Compiler features detection macros.
|
||||
#if ASMJIT_CXX_CLANG && defined(__has_builtin)
|
||||
#define ASMJIT_CXX_HAS_BUILTIN(NAME, CHECK) (__has_builtin(NAME))
|
||||
#else
|
||||
#define ASMJIT_CXX_HAS_BUILTIN(NAME, CHECK) (!(!(CHECK)))
|
||||
#endif
|
||||
|
||||
#if ASMJIT_CXX_CLANG && defined(__has_extension)
|
||||
#define ASMJIT_CXX_HAS_FEATURE(NAME, CHECK) (__has_extension(NAME))
|
||||
#elif ASMJIT_CXX_CLANG && defined(__has_feature)
|
||||
#define ASMJIT_CXX_HAS_FEATURE(NAME, CHECK) (__has_feature(NAME))
|
||||
#else
|
||||
#define ASMJIT_CXX_HAS_FEATURE(NAME, CHECK) (!(!(CHECK)))
|
||||
#endif
|
||||
|
||||
#if ASMJIT_CXX_CLANG && defined(__has_attribute)
|
||||
#if defined(__clang__) && defined(__has_attribute)
|
||||
#define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (__has_attribute(NAME))
|
||||
#else
|
||||
#define ASMJIT_CXX_HAS_ATTRIBUTE(NAME, CHECK) (!(!(CHECK)))
|
||||
#endif
|
||||
|
||||
#if ASMJIT_CXX_CLANG && defined(__has_cpp_attribute)
|
||||
#define ASMJIT_CXX_HAS_CPP_ATTRIBUTE(NAME, CHECK) (__has_cpp_attribute(NAME))
|
||||
#else
|
||||
#define ASMJIT_CXX_HAS_CPP_ATTRIBUTE(NAME, CHECK) (!(!(CHECK)))
|
||||
#endif
|
||||
|
||||
// Compiler features by vendor.
|
||||
#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED)
|
||||
#define ASMJIT_CXX_HAS_NATIVE_WCHAR_T 0
|
||||
#else
|
||||
#define ASMJIT_CXX_HAS_NATIVE_WCHAR_T 1
|
||||
#endif
|
||||
|
||||
#if ASMJIT_CXX_HAS_FEATURE(cxx_unicode_literals, ( \
|
||||
(ASMJIT_CXX_INTEL >= ASMJIT_CXX_MAKE_VER(14, 0, 0)) || \
|
||||
(ASMJIT_CXX_MSC >= ASMJIT_CXX_MAKE_VER(19, 0, 0)) || \
|
||||
(ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4 , 5, 0) && __cplusplus >= 201103L) ))
|
||||
#define ASMJIT_CXX_HAS_UNICODE_LITERALS 1
|
||||
#else
|
||||
#define ASMJIT_CXX_HAS_UNICODE_LITERALS 0
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Build - Globals - API Decorators & Language Extensions]
|
||||
// ============================================================================
|
||||
@ -394,6 +376,15 @@
|
||||
#define ASMJIT_REGPARM(N)
|
||||
#endif
|
||||
|
||||
#if ASMJIT_ARCH_X86 && defined(_WIN32) && defined(_MSC_VER)
|
||||
#define ASMJIT_VECTORCALL __vectorcall
|
||||
#elif ASMJIT_ARCH_X86 && defined(_WIN32)
|
||||
#define ASMJIT_VECTORCALL __attribute__((__vectorcall__))
|
||||
#else
|
||||
#define ASMJIT_VECTORCALL
|
||||
#endif
|
||||
|
||||
|
||||
// Type alignment (not allowed by C++11 'alignas' keyword).
|
||||
#if defined(__GNUC__)
|
||||
#define ASMJIT_ALIGN_TYPE(TYPE, N) __attribute__((__aligned__(N))) TYPE
|
||||
@ -403,13 +394,22 @@
|
||||
#define ASMJIT_ALIGN_TYPE(TYPE, N) TYPE
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_MAY_ALIAS
|
||||
//!
|
||||
//! Expands to `__attribute__((__may_alias__))` if supported.
|
||||
#if defined(__GNUC__)
|
||||
#define ASMJIT_MAY_ALIAS __attribute__((__may_alias__))
|
||||
#else
|
||||
#define ASMJIT_MAY_ALIAS
|
||||
#endif
|
||||
|
||||
// Annotations.
|
||||
//! \def ASMJIT_LIKELY(...)
|
||||
//!
|
||||
//! Condition is likely to be taken (mostly error handling and edge cases).
|
||||
|
||||
//! \def ASMJIT_UNLIKELY(...)
|
||||
//!
|
||||
//! Condition is unlikely to be taken (mostly error handling and edge cases).
|
||||
#if defined(__GNUC__)
|
||||
#define ASMJIT_LIKELY(...) __builtin_expect(!!(__VA_ARGS__), 1)
|
||||
#define ASMJIT_UNLIKELY(...) __builtin_expect(!!(__VA_ARGS__), 0)
|
||||
@ -418,29 +418,42 @@
|
||||
#define ASMJIT_UNLIKELY(...) (__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_FALLTHROUGH
|
||||
//!
|
||||
//! Portable [[fallthrough]] attribute.
|
||||
#if defined(__clang__) && __cplusplus >= 201103L
|
||||
#define ASMJIT_FALLTHROUGH [[clang::fallthrough]]
|
||||
#elif ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(7, 0, 0)
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 7
|
||||
#define ASMJIT_FALLTHROUGH __attribute__((__fallthrough__))
|
||||
#else
|
||||
#define ASMJIT_FALLTHROUGH ((void)0) /* fallthrough */
|
||||
#endif
|
||||
|
||||
//! \def ASMJIT_DEPRECATED
|
||||
//!
|
||||
//! Marks function, class, struct, enum, or anything else as deprecated.
|
||||
#if defined(__GNUC__)
|
||||
#define ASMJIT_DEPRECATED(MESSAGE) __attribute__((__deprecated__(MESSAGE)))
|
||||
#if defined(__clang__)
|
||||
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE) __attribute__((__deprecated__(MESSAGE)))
|
||||
#else
|
||||
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE) /* not usable if a deprecated function uses it */
|
||||
#endif
|
||||
#elif defined(_MSC_VER)
|
||||
#define ASMJIT_DEPRECATED(MESSAGE) __declspec(deprecated(MESSAGE))
|
||||
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE) /* not usable if a deprecated function uses it */
|
||||
#else
|
||||
#define ASMJIT_DEPRECATED(MESSAGE)
|
||||
#define ASMJIT_DEPRECATED_STRUCT(MESSAGE)
|
||||
#endif
|
||||
|
||||
// Utilities.
|
||||
#define ASMJIT_OFFSET_OF(STRUCT, MEMBER) ((int)(intptr_t)((const char*)&((const STRUCT*)0x100)->MEMBER) - 0x100)
|
||||
#define ASMJIT_ARRAY_SIZE(X) uint32_t(sizeof(X) / sizeof(X[0]))
|
||||
|
||||
#if ASMJIT_CXX_HAS_ATTRIBUTE(attribute_deprecated_with_message, ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4, 5, 0))
|
||||
#define ASMJIT_DEPRECATED(DECL, MESSAGE) DECL __attribute__((__deprecated__(MESSAGE)))
|
||||
#elif ASMJIT_CXX_MSC
|
||||
#define ASMJIT_DEPRECATED(DECL, MESSAGE) __declspec(deprecated(MESSAGE)) DECL
|
||||
#else
|
||||
#define ASMJIT_DEPRECATED(DECL, MESSAGE) DECL
|
||||
#endif
|
||||
|
||||
#if ASMJIT_CXX_HAS_ATTRIBUTE(no_sanitize, 0)
|
||||
#define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF __attribute__((__no_sanitize__("undefined")))
|
||||
#elif ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4, 9, 0)
|
||||
#elif ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4, 9)
|
||||
#define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF __attribute__((__no_sanitize_undefined__))
|
||||
#else
|
||||
#define ASMJIT_ATTRIBUTE_NO_SANITIZE_UNDEF
|
||||
@ -459,8 +472,7 @@
|
||||
#define ASMJIT_END_NAMESPACE \
|
||||
_Pragma("clang diagnostic pop") \
|
||||
}
|
||||
#elif ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(4, 0, 0) && \
|
||||
ASMJIT_CXX_GNU < ASMJIT_CXX_MAKE_VER(5, 0, 0)
|
||||
#elif defined(__GNUC__) && __GNUC__ == 4
|
||||
#define ASMJIT_BEGIN_NAMESPACE \
|
||||
namespace asmjit { \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
@ -468,7 +480,7 @@
|
||||
#define ASMJIT_END_NAMESPACE \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
}
|
||||
#elif ASMJIT_CXX_GNU >= ASMJIT_CXX_MAKE_VER(8, 0, 0)
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 8
|
||||
#define ASMJIT_BEGIN_NAMESPACE \
|
||||
namespace asmjit { \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
@ -480,8 +492,8 @@
|
||||
#define ASMJIT_BEGIN_NAMESPACE \
|
||||
namespace asmjit { \
|
||||
__pragma(warning(push)) \
|
||||
__pragma(warning(disable: 4127)) /* conditional expression is constant*/\
|
||||
__pragma(warning(disable: 4201)) /* nameless struct/union */
|
||||
__pragma(warning(disable: 4127)) /* conditional expression is const */ \
|
||||
__pragma(warning(disable: 4201)) /* nameless struct/union */
|
||||
#define ASMJIT_END_NAMESPACE \
|
||||
__pragma(warning(pop)) \
|
||||
}
|
||||
@ -521,13 +533,8 @@
|
||||
// [asmjit::Build - Globals - Cleanup]
|
||||
// ============================================================================
|
||||
|
||||
// Try to cleanup things not used in other public headers.
|
||||
#ifndef ASMJIT_EXPORTS
|
||||
#undef ASMJIT_CXX_CLANG
|
||||
#undef ASMJIT_CXX_GNU
|
||||
#undef ASMJIT_CXX_INTEL
|
||||
#undef ASMJIT_CXX_MSC
|
||||
#undef ASMJIT_CXX_MAKE_VER
|
||||
#endif
|
||||
// Cleanup definitions that are only used within this header file.
|
||||
#undef ASMJIT_CXX_GNU
|
||||
#undef ASMJIT_CXX_MAKE_VER
|
||||
|
||||
#endif // ASMJIT_CORE_API_CONFIG_H_INCLUDED
|
||||
|
141
3rdparty/asmjit/src/asmjit/core/arch.cpp
vendored
141
3rdparty/asmjit/src/asmjit/core/arch.cpp
vendored
@ -23,151 +23,34 @@
|
||||
|
||||
#include "../core/api-build_p.h"
|
||||
#include "../core/arch.h"
|
||||
#include "../core/support.h"
|
||||
#include "../core/type.h"
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
#include "../x86/x86operand.h"
|
||||
#include "../x86/x86archdata_p.h"
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
#include "../arm/armoperand.h"
|
||||
#include "../arm/armarchdata_p.h"
|
||||
#endif
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ArchInfo]
|
||||
// ============================================================================
|
||||
|
||||
// NOTE: Keep `const constexpr` otherwise MSC would not compile this code correctly.
|
||||
static const constexpr uint32_t archInfoTable[] = {
|
||||
// <--------------------+---------------------+-------------------+-------+
|
||||
// | Type | SubType | GPInfo|
|
||||
// <--------------------+---------------------+-------------------+-------+
|
||||
Support::bytepack32_4x8(ArchInfo::kIdNone , ArchInfo::kSubIdNone, 0, 0),
|
||||
Support::bytepack32_4x8(ArchInfo::kIdX86 , ArchInfo::kSubIdNone, 4, 8),
|
||||
Support::bytepack32_4x8(ArchInfo::kIdX64 , ArchInfo::kSubIdNone, 8, 16),
|
||||
Support::bytepack32_4x8(ArchInfo::kIdA32 , ArchInfo::kSubIdNone, 4, 16),
|
||||
Support::bytepack32_4x8(ArchInfo::kIdA64 , ArchInfo::kSubIdNone, 8, 32)
|
||||
};
|
||||
|
||||
ASMJIT_FAVOR_SIZE void ArchInfo::init(uint32_t id, uint32_t subId) noexcept {
|
||||
uint32_t index = id < ASMJIT_ARRAY_SIZE(archInfoTable) ? id : uint32_t(0);
|
||||
|
||||
// Make sure the `archInfoTable` array is correctly indexed.
|
||||
_signature = archInfoTable[index];
|
||||
ASMJIT_ASSERT(_id == index);
|
||||
|
||||
// Even if the architecture is not known we setup its id and sub-id,
|
||||
// however, such architecture is not really useful.
|
||||
_id = uint8_t(id);
|
||||
_subId = uint8_t(subId);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ArchUtils]
|
||||
// ============================================================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegInfo(uint32_t archId, uint32_t& typeIdInOut, RegInfo& regInfo) noexcept {
|
||||
uint32_t typeId = typeIdInOut;
|
||||
ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfoOut) noexcept {
|
||||
// Zero the output in case the input is invalid.
|
||||
*typeIdOut = 0;
|
||||
regInfoOut->reset();
|
||||
|
||||
// Zero the signature so it's clear in case that typeId is not invalid.
|
||||
regInfo._signature = 0;
|
||||
|
||||
// TODO: Move to X86 backend.
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (ArchInfo::isX86Family(archId)) {
|
||||
// Passed RegType instead of TypeId?
|
||||
if (typeId <= BaseReg::kTypeMax)
|
||||
typeId = x86::opData.archRegs.regTypeToTypeId[typeId];
|
||||
if (Environment::isFamilyX86(arch))
|
||||
return x86::ArchInternal::typeIdToRegInfo(arch, typeId, typeIdOut, regInfoOut);
|
||||
#endif
|
||||
|
||||
if (ASMJIT_UNLIKELY(!Type::isValid(typeId)))
|
||||
return DebugUtils::errored(kErrorInvalidTypeId);
|
||||
|
||||
// First normalize architecture dependent types.
|
||||
if (Type::isAbstract(typeId)) {
|
||||
if (typeId == Type::kIdIntPtr)
|
||||
typeId = (archId == ArchInfo::kIdX86) ? Type::kIdI32 : Type::kIdI64;
|
||||
else
|
||||
typeId = (archId == ArchInfo::kIdX86) ? Type::kIdU32 : Type::kIdU64;
|
||||
}
|
||||
|
||||
// Type size helps to construct all groupss of registers. If the size is zero
|
||||
// then the TypeId is invalid.
|
||||
uint32_t size = Type::sizeOf(typeId);
|
||||
if (ASMJIT_UNLIKELY(!size))
|
||||
return DebugUtils::errored(kErrorInvalidTypeId);
|
||||
|
||||
if (ASMJIT_UNLIKELY(typeId == Type::kIdF80))
|
||||
return DebugUtils::errored(kErrorInvalidUseOfF80);
|
||||
|
||||
uint32_t regType = 0;
|
||||
|
||||
switch (typeId) {
|
||||
case Type::kIdI8:
|
||||
case Type::kIdU8:
|
||||
regType = x86::Reg::kTypeGpbLo;
|
||||
break;
|
||||
|
||||
case Type::kIdI16:
|
||||
case Type::kIdU16:
|
||||
regType = x86::Reg::kTypeGpw;
|
||||
break;
|
||||
|
||||
case Type::kIdI32:
|
||||
case Type::kIdU32:
|
||||
regType = x86::Reg::kTypeGpd;
|
||||
break;
|
||||
|
||||
case Type::kIdI64:
|
||||
case Type::kIdU64:
|
||||
if (archId == ArchInfo::kIdX86)
|
||||
return DebugUtils::errored(kErrorInvalidUseOfGpq);
|
||||
|
||||
regType = x86::Reg::kTypeGpq;
|
||||
break;
|
||||
|
||||
// F32 and F64 are always promoted to use vector registers.
|
||||
case Type::kIdF32:
|
||||
typeId = Type::kIdF32x1;
|
||||
regType = x86::Reg::kTypeXmm;
|
||||
break;
|
||||
|
||||
case Type::kIdF64:
|
||||
typeId = Type::kIdF64x1;
|
||||
regType = x86::Reg::kTypeXmm;
|
||||
break;
|
||||
|
||||
// Mask registers {k}.
|
||||
case Type::kIdMask8:
|
||||
case Type::kIdMask16:
|
||||
case Type::kIdMask32:
|
||||
case Type::kIdMask64:
|
||||
regType = x86::Reg::kTypeKReg;
|
||||
break;
|
||||
|
||||
// MMX registers.
|
||||
case Type::kIdMmx32:
|
||||
case Type::kIdMmx64:
|
||||
regType = x86::Reg::kTypeMm;
|
||||
break;
|
||||
|
||||
// XMM|YMM|ZMM registers.
|
||||
default:
|
||||
if (size <= 16)
|
||||
regType = x86::Reg::kTypeXmm;
|
||||
else if (size == 32)
|
||||
regType = x86::Reg::kTypeYmm;
|
||||
else
|
||||
regType = x86::Reg::kTypeZmm;
|
||||
break;
|
||||
}
|
||||
|
||||
typeIdInOut = typeId;
|
||||
regInfo._signature = x86::opData.archRegs.regInfo[regType].signature();
|
||||
return kErrorOk;
|
||||
}
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (Environment::isFamilyARM(arch))
|
||||
return arm::ArchInternal::typeIdToRegInfo(arch, typeId, typeIdOut, regInfoOut);
|
||||
#endif
|
||||
|
||||
return DebugUtils::errored(kErrorInvalidArch);
|
||||
|
156
3rdparty/asmjit/src/asmjit/core/arch.h
vendored
156
3rdparty/asmjit/src/asmjit/core/arch.h
vendored
@ -24,7 +24,7 @@
|
||||
#ifndef ASMJIT_CORE_ARCH_H_INCLUDED
|
||||
#define ASMJIT_CORE_ARCH_H_INCLUDED
|
||||
|
||||
#include "../core/globals.h"
|
||||
#include "../core/environment.h"
|
||||
#include "../core/operand.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
@ -32,154 +32,11 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ArchInfo]
|
||||
// ============================================================================
|
||||
|
||||
class ArchInfo {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
//! Architecture id.
|
||||
uint8_t _id;
|
||||
//! Architecture sub-id.
|
||||
uint8_t _subId;
|
||||
//! Default size of a general purpose register.
|
||||
uint8_t _gpSize;
|
||||
//! Count of all general purpose registers.
|
||||
uint8_t _gpCount;
|
||||
};
|
||||
//! Architecture signature (32-bit int).
|
||||
uint32_t _signature;
|
||||
};
|
||||
|
||||
//! Architecture id.
|
||||
enum Id : uint32_t {
|
||||
kIdNone = 0, //!< No/Unknown architecture.
|
||||
|
||||
// X86 architectures.
|
||||
kIdX86 = 1, //!< X86 architecture (32-bit).
|
||||
kIdX64 = 2, //!< X64 architecture (64-bit) (AMD64).
|
||||
|
||||
// ARM architectures.
|
||||
kIdA32 = 3, //!< ARM 32-bit architecture (AArch32/ARM/THUMB).
|
||||
kIdA64 = 4, //!< ARM 64-bit architecture (AArch64).
|
||||
|
||||
//! Architecture detected at compile-time (architecture of the host).
|
||||
kIdHost = ASMJIT_ARCH_X86 == 32 ? kIdX86 :
|
||||
ASMJIT_ARCH_X86 == 64 ? kIdX64 :
|
||||
ASMJIT_ARCH_ARM == 32 ? kIdA32 :
|
||||
ASMJIT_ARCH_ARM == 64 ? kIdA64 : kIdNone
|
||||
};
|
||||
|
||||
//! Architecture sub-type or execution mode.
|
||||
enum SubType : uint32_t {
|
||||
kSubIdNone = 0, //!< Default mode (or no specific mode).
|
||||
|
||||
// X86 sub-types.
|
||||
kSubIdX86_AVX = 1, //!< Code generation uses AVX by default (VEC instructions).
|
||||
kSubIdX86_AVX2 = 2, //!< Code generation uses AVX2 by default (VEC instructions).
|
||||
kSubIdX86_AVX512 = 3, //!< Code generation uses AVX-512F by default (+32 vector regs).
|
||||
kSubIdX86_AVX512VL = 4, //!< Code generation uses AVX-512F-VL by default (+VL extensions).
|
||||
|
||||
// ARM sub-types.
|
||||
kSubIdA32_Thumb = 8, //!< THUMB|THUMBv2 sub-type (only ARM in 32-bit mode).
|
||||
|
||||
#if (ASMJIT_ARCH_X86) && defined(__AVX512VL__)
|
||||
kSubIdHost = kSubIdX86_AVX512VL
|
||||
#elif (ASMJIT_ARCH_X86) && defined(__AVX512F__)
|
||||
kSubIdHost = kSubIdX86_AVX512
|
||||
#elif (ASMJIT_ARCH_X86) && defined(__AVX2__)
|
||||
kSubIdHost = kSubIdX86_AVX2
|
||||
#elif (ASMJIT_ARCH_X86) && defined(__AVX__)
|
||||
kSubIdHost = kSubIdX86_AVX
|
||||
#elif (ASMJIT_ARCH_ARM == 32) && (defined(_M_ARMT) || defined(__thumb__) || defined(__thumb2__))
|
||||
kSubIdHost = kSubIdA32_Thumb
|
||||
#else
|
||||
kSubIdHost = 0
|
||||
#endif
|
||||
};
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
inline ArchInfo() noexcept : _signature(0) {}
|
||||
inline ArchInfo(const ArchInfo& other) noexcept : _signature(other._signature) {}
|
||||
inline explicit ArchInfo(uint32_t type, uint32_t subType = kSubIdNone) noexcept { init(type, subType); }
|
||||
inline explicit ArchInfo(Globals::NoInit_) noexcept {}
|
||||
|
||||
inline static ArchInfo host() noexcept { return ArchInfo(kIdHost, kSubIdHost); }
|
||||
|
||||
inline bool isInitialized() const noexcept { return _id != kIdNone; }
|
||||
|
||||
ASMJIT_API void init(uint32_t type, uint32_t subType = kSubIdNone) noexcept;
|
||||
inline void reset() noexcept { _signature = 0; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Overloaded Operators
|
||||
//! \{
|
||||
|
||||
inline ArchInfo& operator=(const ArchInfo& other) noexcept = default;
|
||||
|
||||
inline bool operator==(const ArchInfo& other) const noexcept { return _signature == other._signature; }
|
||||
inline bool operator!=(const ArchInfo& other) const noexcept { return _signature != other._signature; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the architecture id, see `Id`.
|
||||
inline uint32_t archId() const noexcept { return _id; }
|
||||
|
||||
//! Returns the architecture sub-id, see `SubType`.
|
||||
//!
|
||||
//! X86 & X64
|
||||
//! ---------
|
||||
//!
|
||||
//! Architecture subtype describe the highest instruction-set level that can
|
||||
//! be used.
|
||||
//!
|
||||
//! A32 & A64
|
||||
//! ---------
|
||||
//!
|
||||
//! Architecture mode means the instruction encoding to be used when generating
|
||||
//! machine code, thus mode can be used to force generation of THUMB and THUMBv2
|
||||
//! encoding or regular ARM encoding.
|
||||
inline uint32_t archSubId() const noexcept { return _subId; }
|
||||
|
||||
//! Tests whether this architecture is 32-bit.
|
||||
inline bool is32Bit() const noexcept { return _gpSize == 4; }
|
||||
//! Tests whether this architecture is 64-bit.
|
||||
inline bool is64Bit() const noexcept { return _gpSize == 8; }
|
||||
|
||||
//! Tests whether this architecture is X86, X64.
|
||||
inline bool isX86Family() const noexcept { return isX86Family(_id); }
|
||||
//! Tests whether this architecture is ARM32 or ARM64.
|
||||
inline bool isArmFamily() const noexcept { return isArmFamily(_id); }
|
||||
|
||||
//! Returns the native size of a general-purpose register.
|
||||
inline uint32_t gpSize() const noexcept { return _gpSize; }
|
||||
//! Returns number of general-purpose registers.
|
||||
inline uint32_t gpCount() const noexcept { return _gpCount; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Static Functions
|
||||
//! \{
|
||||
|
||||
static inline bool isX86Family(uint32_t archId) noexcept { return archId >= kIdX86 && archId <= kIdX64; }
|
||||
static inline bool isArmFamily(uint32_t archId) noexcept { return archId >= kIdA32 && archId <= kIdA64; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ArchRegs]
|
||||
// ============================================================================
|
||||
|
||||
//! Information about all architecture registers.
|
||||
//! Information about registers of a CPU architecture.
|
||||
struct ArchRegs {
|
||||
//! Register information and signatures indexed by `BaseReg::RegType`.
|
||||
RegInfo regInfo[BaseReg::kTypeMax + 1];
|
||||
@ -193,9 +50,12 @@ struct ArchRegs {
|
||||
// [asmjit::ArchUtils]
|
||||
// ============================================================================
|
||||
|
||||
struct ArchUtils {
|
||||
ASMJIT_API static Error typeIdToRegInfo(uint32_t archId, uint32_t& typeIdInOut, RegInfo& regInfo) noexcept;
|
||||
};
|
||||
//! Architecture utilities.
|
||||
namespace ArchUtils {
|
||||
|
||||
ASMJIT_API Error typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfo) noexcept;
|
||||
|
||||
} // {ArchUtils}
|
||||
|
||||
//! \}
|
||||
|
||||
|
326
3rdparty/asmjit/src/asmjit/core/assembler.cpp
vendored
326
3rdparty/asmjit/src/asmjit/core/assembler.cpp
vendored
@ -25,7 +25,9 @@
|
||||
#include "../core/assembler.h"
|
||||
#include "../core/codebufferwriter_p.h"
|
||||
#include "../core/constpool.h"
|
||||
#include "../core/logging.h"
|
||||
#include "../core/emitterutils_p.h"
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
@ -39,9 +41,7 @@ BaseAssembler::BaseAssembler() noexcept
|
||||
_section(nullptr),
|
||||
_bufferData(nullptr),
|
||||
_bufferEnd(nullptr),
|
||||
_bufferPtr(nullptr),
|
||||
_op4(),
|
||||
_op5() {}
|
||||
_bufferPtr(nullptr) {}
|
||||
BaseAssembler::~BaseAssembler() noexcept {}
|
||||
|
||||
// ============================================================================
|
||||
@ -50,7 +50,7 @@ BaseAssembler::~BaseAssembler() noexcept {}
|
||||
|
||||
Error BaseAssembler::setOffset(size_t offset) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
size_t size = Support::max<size_t>(_section->bufferSize(), this->offset());
|
||||
if (ASMJIT_UNLIKELY(offset > size))
|
||||
@ -60,25 +60,6 @@ Error BaseAssembler::setOffset(size_t offset) {
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseAssembler - Logging]
|
||||
// ============================================================================
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
static void BaseAssembler_logLabel(BaseAssembler* self, const Label& label) noexcept {
|
||||
Logger* logger = self->_code->_logger;
|
||||
|
||||
StringTmp<512> sb;
|
||||
size_t binSize = logger->hasFlag(FormatOptions::kFlagMachineCode) ? size_t(0) : std::numeric_limits<size_t>::max();
|
||||
|
||||
sb.appendChars(' ', logger->indentation(FormatOptions::kIndentationLabel));
|
||||
Logging::formatLabel(sb, logger->flags(), self, label.id());
|
||||
sb.appendChar(':');
|
||||
Logging::formatLine(sb, nullptr, binSize, 0, 0, self->_inlineComment);
|
||||
logger->log(sb.data(), sb.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseAssembler - Section Management]
|
||||
// ============================================================================
|
||||
@ -100,8 +81,8 @@ Error BaseAssembler::section(Section* section) {
|
||||
return reportError(DebugUtils::errored(kErrorInvalidSection));
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (hasEmitterOption(kOptionLoggingEnabled))
|
||||
_code->_logger->logf(".section %s {#%u}\n", section->name(), section->id());
|
||||
if (_logger)
|
||||
_logger->logf(".section %s {#%u}\n", section->name(), section->id());
|
||||
#endif
|
||||
|
||||
BaseAssembler_initSection(this, section);
|
||||
@ -119,7 +100,8 @@ Label BaseAssembler::newLabel() {
|
||||
Error err = _code->newLabelEntry(&le);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
reportError(err);
|
||||
labelId = le->id();
|
||||
else
|
||||
labelId = le->id();
|
||||
}
|
||||
return Label(labelId);
|
||||
}
|
||||
@ -131,20 +113,21 @@ Label BaseAssembler::newNamedLabel(const char* name, size_t nameSize, uint32_t t
|
||||
Error err = _code->newNamedLabelEntry(&le, name, nameSize, type, parentId);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
reportError(err);
|
||||
labelId = le->id();
|
||||
else
|
||||
labelId = le->id();
|
||||
}
|
||||
return Label(labelId);
|
||||
}
|
||||
|
||||
Error BaseAssembler::bind(const Label& label) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
Error err = _code->bindLabel(label, _section->id(), offset());
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (hasEmitterOption(kOptionLoggingEnabled))
|
||||
BaseAssembler_logLabel(this, label);
|
||||
if (_logger)
|
||||
EmitterUtils::logLabelBound(this, label);
|
||||
#endif
|
||||
|
||||
resetInlineComment();
|
||||
@ -154,125 +137,6 @@ Error BaseAssembler::bind(const Label& label) {
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseAssembler - Emit (Low-Level)]
|
||||
// ============================================================================
|
||||
|
||||
Error BaseAssembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) {
|
||||
_op4 = o4;
|
||||
_op5 = o5;
|
||||
_instOptions |= BaseInst::kOptionOp4Op5Used;
|
||||
return _emit(instId, o0, o1, o2, o3);
|
||||
}
|
||||
|
||||
Error BaseAssembler::_emitOpArray(uint32_t instId, const Operand_* operands, size_t count) {
|
||||
const Operand_* o0 = &operands[0];
|
||||
const Operand_* o1 = &operands[1];
|
||||
const Operand_* o2 = &operands[2];
|
||||
const Operand_* o3 = &operands[3];
|
||||
|
||||
switch (count) {
|
||||
case 0: o0 = &Globals::none; ASMJIT_FALLTHROUGH;
|
||||
case 1: o1 = &Globals::none; ASMJIT_FALLTHROUGH;
|
||||
case 2: o2 = &Globals::none; ASMJIT_FALLTHROUGH;
|
||||
case 3: o3 = &Globals::none; ASMJIT_FALLTHROUGH;
|
||||
case 4:
|
||||
return _emit(instId, *o0, *o1, *o2, *o3);
|
||||
|
||||
case 5:
|
||||
_op4 = operands[4];
|
||||
_op5.reset();
|
||||
_instOptions |= BaseInst::kOptionOp4Op5Used;
|
||||
return _emit(instId, *o0, *o1, *o2, *o3);
|
||||
|
||||
case 6:
|
||||
_op4 = operands[4];
|
||||
_op5 = operands[5];
|
||||
_instOptions |= BaseInst::kOptionOp4Op5Used;
|
||||
return _emit(instId, *o0, *o1, *o2, *o3);
|
||||
|
||||
default:
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
void BaseAssembler::_emitLog(
|
||||
uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3,
|
||||
uint32_t relSize, uint32_t immSize, uint8_t* afterCursor) {
|
||||
|
||||
Logger* logger = _code->logger();
|
||||
ASMJIT_ASSERT(logger != nullptr);
|
||||
ASMJIT_ASSERT(options & BaseEmitter::kOptionLoggingEnabled);
|
||||
|
||||
StringTmp<256> sb;
|
||||
uint32_t flags = logger->flags();
|
||||
|
||||
uint8_t* beforeCursor = _bufferPtr;
|
||||
intptr_t emittedSize = (intptr_t)(afterCursor - beforeCursor);
|
||||
|
||||
Operand_ operands[Globals::kMaxOpCount];
|
||||
operands[0].copyFrom(o0);
|
||||
operands[1].copyFrom(o1);
|
||||
operands[2].copyFrom(o2);
|
||||
operands[3].copyFrom(o3);
|
||||
|
||||
if (options & BaseInst::kOptionOp4Op5Used) {
|
||||
operands[4].copyFrom(_op4);
|
||||
operands[5].copyFrom(_op5);
|
||||
}
|
||||
else {
|
||||
operands[4].reset();
|
||||
operands[5].reset();
|
||||
}
|
||||
|
||||
sb.appendChars(' ', logger->indentation(FormatOptions::kIndentationCode));
|
||||
Logging::formatInstruction(sb, flags, this, archId(), BaseInst(instId, options, _extraReg), operands, Globals::kMaxOpCount);
|
||||
|
||||
if ((flags & FormatOptions::kFlagMachineCode) != 0)
|
||||
Logging::formatLine(sb, _bufferPtr, size_t(emittedSize), relSize, immSize, inlineComment());
|
||||
else
|
||||
Logging::formatLine(sb, nullptr, std::numeric_limits<size_t>::max(), 0, 0, inlineComment());
|
||||
logger->log(sb);
|
||||
}
|
||||
|
||||
Error BaseAssembler::_emitFailed(
|
||||
Error err,
|
||||
uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) {
|
||||
|
||||
StringTmp<256> sb;
|
||||
sb.appendString(DebugUtils::errorAsString(err));
|
||||
sb.appendString(": ");
|
||||
|
||||
Operand_ operands[Globals::kMaxOpCount];
|
||||
operands[0].copyFrom(o0);
|
||||
operands[1].copyFrom(o1);
|
||||
operands[2].copyFrom(o2);
|
||||
operands[3].copyFrom(o3);
|
||||
|
||||
if (options & BaseInst::kOptionOp4Op5Used) {
|
||||
operands[4].copyFrom(_op4);
|
||||
operands[5].copyFrom(_op5);
|
||||
}
|
||||
else {
|
||||
operands[4].reset();
|
||||
operands[5].reset();
|
||||
}
|
||||
|
||||
Logging::formatInstruction(sb, 0, this, archId(), BaseInst(instId, options, _extraReg), operands, Globals::kMaxOpCount);
|
||||
|
||||
if (inlineComment()) {
|
||||
sb.appendString(" ; ");
|
||||
sb.appendString(inlineComment());
|
||||
}
|
||||
|
||||
resetInstOptions();
|
||||
resetExtraReg();
|
||||
resetInlineComment();
|
||||
return reportError(err, sb.data());
|
||||
}
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseAssembler - Embed]
|
||||
// ============================================================================
|
||||
@ -290,12 +154,12 @@ static const DataSizeByPower dataSizeByPowerTable[] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
Error BaseAssembler::embed(const void* data, uint32_t dataSize) {
|
||||
Error BaseAssembler::embed(const void* data, size_t dataSize) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
if (dataSize == 0)
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
return kErrorOk;
|
||||
|
||||
CodeBufferWriter writer(this);
|
||||
ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize));
|
||||
@ -303,17 +167,83 @@ Error BaseAssembler::embed(const void* data, uint32_t dataSize) {
|
||||
writer.emitData(data, dataSize);
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled)))
|
||||
_code->_logger->logBinary(data, dataSize);
|
||||
if (_logger)
|
||||
_logger->logBinary(data, dataSize);
|
||||
#endif
|
||||
|
||||
writer.done(this);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseAssembler::embedDataArray(uint32_t typeId, const void* data, size_t itemCcount, size_t repeatCount) {
|
||||
uint32_t deabstractDelta = Type::deabstractDeltaOfSize(registerSize());
|
||||
uint32_t finalTypeId = Type::deabstract(typeId, deabstractDelta);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!Type::isValid(finalTypeId)))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
if (itemCcount == 0 || repeatCount == 0)
|
||||
return kErrorOk;
|
||||
|
||||
uint32_t typeSize = Type::sizeOf(finalTypeId);
|
||||
Support::FastUInt8 of = 0;
|
||||
|
||||
size_t dataSize = Support::mulOverflow(itemCcount, size_t(typeSize), &of);
|
||||
size_t totalSize = Support::mulOverflow(dataSize, repeatCount, &of);
|
||||
|
||||
if (ASMJIT_UNLIKELY(of))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
CodeBufferWriter writer(this);
|
||||
ASMJIT_PROPAGATE(writer.ensureSpace(this, totalSize));
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
const uint8_t* start = writer.cursor();
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < repeatCount; i++) {
|
||||
writer.emitData(data, dataSize);
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (_logger)
|
||||
_logger->logBinary(start, totalSize);
|
||||
#endif
|
||||
|
||||
writer.done(this);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseAssembler::embedConstPool(const Label& label, const ConstPool& pool) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
if (ASMJIT_UNLIKELY(!isLabelValid(label)))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidLabel));
|
||||
|
||||
ASMJIT_PROPAGATE(align(kAlignData, uint32_t(pool.alignment())));
|
||||
ASMJIT_PROPAGATE(bind(label));
|
||||
|
||||
size_t size = pool.size();
|
||||
CodeBufferWriter writer(this);
|
||||
ASMJIT_PROPAGATE(writer.ensureSpace(this, size));
|
||||
|
||||
pool.fill(writer.cursor());
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (_logger)
|
||||
_logger->logBinary(writer.cursor(), size);
|
||||
#endif
|
||||
|
||||
writer.advance(size);
|
||||
writer.done(this);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseAssembler::embedLabel(const Label& label) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
ASMJIT_ASSERT(_code != nullptr);
|
||||
RelocEntry* re;
|
||||
@ -322,30 +252,22 @@ Error BaseAssembler::embedLabel(const Label& label) {
|
||||
if (ASMJIT_UNLIKELY(!le))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidLabel));
|
||||
|
||||
uint32_t dataSize = gpSize();
|
||||
uint32_t dataSize = registerSize();
|
||||
ASMJIT_ASSERT(dataSize <= 8);
|
||||
|
||||
CodeBufferWriter writer(this);
|
||||
ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize));
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled))) {
|
||||
if (_logger) {
|
||||
StringTmp<256> sb;
|
||||
sb.appendFormat(".%s ", dataSizeByPowerTable[Support::ctz(dataSize)].str);
|
||||
Logging::formatLabel(sb, 0, this, label.id());
|
||||
sb.appendChar('\n');
|
||||
_code->_logger->log(sb);
|
||||
sb.appendFormat("%s ", dataSizeByPowerTable[Support::ctz(dataSize)].str);
|
||||
Formatter::formatLabel(sb, 0, this, label.id());
|
||||
sb.append('\n');
|
||||
_logger->log(sb);
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: Does it make sense to calculate the address here if everything is known?
|
||||
/*
|
||||
if (_code->hasBaseAddress() && currentSection() == _code->textSection() && le->isBound()) {
|
||||
uint64_t addr = _code->baseAddress() + _code->textSection()->offset() + le->offset();
|
||||
writer.emitValueLE(addr, dataSize);
|
||||
}
|
||||
*/
|
||||
|
||||
Error err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, dataSize);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
@ -371,9 +293,9 @@ Error BaseAssembler::embedLabel(const Label& label) {
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, uint32_t dataSize) {
|
||||
Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, size_t dataSize) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
LabelEntry* labelEntry = _code->labelEntry(label);
|
||||
LabelEntry* baseEntry = _code->labelEntry(base);
|
||||
@ -382,7 +304,7 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, uint
|
||||
return reportError(DebugUtils::errored(kErrorInvalidLabel));
|
||||
|
||||
if (dataSize == 0)
|
||||
dataSize = gpSize();
|
||||
dataSize = registerSize();
|
||||
|
||||
if (ASMJIT_UNLIKELY(!Support::isPowerOf2(dataSize) || dataSize > 8))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidOperandSize));
|
||||
@ -391,14 +313,14 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, uint
|
||||
ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize));
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled))) {
|
||||
if (_logger) {
|
||||
StringTmp<256> sb;
|
||||
sb.appendFormat(".%s (", dataSizeByPowerTable[Support::ctz(dataSize)].str);
|
||||
Logging::formatLabel(sb, 0, this, label.id());
|
||||
sb.appendString(" - ");
|
||||
Logging::formatLabel(sb, 0, this, base.id());
|
||||
sb.appendString(")\n");
|
||||
_code->_logger->log(sb);
|
||||
Formatter::formatLabel(sb, 0, this, label.id());
|
||||
sb.append(" - ");
|
||||
Formatter::formatLabel(sb, 0, this, base.id());
|
||||
sb.append(")\n");
|
||||
_logger->log(sb);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -409,7 +331,7 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, uint
|
||||
}
|
||||
else {
|
||||
RelocEntry* re;
|
||||
Error err = _code->newRelocEntry(&re, RelocEntry::kTypeExpression, dataSize);
|
||||
Error err = _code->newRelocEntry(&re, RelocEntry::kTypeExpression, uint32_t(dataSize));
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
@ -433,46 +355,18 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, uint
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseAssembler::embedConstPool(const Label& label, const ConstPool& pool) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!isLabelValid(label)))
|
||||
return DebugUtils::errored(kErrorInvalidLabel);
|
||||
|
||||
ASMJIT_PROPAGATE(align(kAlignData, uint32_t(pool.alignment())));
|
||||
ASMJIT_PROPAGATE(bind(label));
|
||||
|
||||
size_t size = pool.size();
|
||||
CodeBufferWriter writer(this);
|
||||
ASMJIT_PROPAGATE(writer.ensureSpace(this, size));
|
||||
|
||||
pool.fill(writer.cursor());
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (ASMJIT_UNLIKELY(hasEmitterOption(kOptionLoggingEnabled)))
|
||||
_code->_logger->logBinary(writer.cursor(), size);
|
||||
#endif
|
||||
|
||||
writer.advance(size);
|
||||
writer.done(this);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseAssembler - Comment]
|
||||
// ============================================================================
|
||||
|
||||
Error BaseAssembler::comment(const char* data, size_t size) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (hasEmitterOption(kOptionLoggingEnabled)) {
|
||||
Logger* logger = _code->logger();
|
||||
logger->log(data, size);
|
||||
logger->log("\n", 1);
|
||||
if (_logger) {
|
||||
_logger->log(data, size);
|
||||
_logger->log("\n", 1);
|
||||
return kErrorOk;
|
||||
}
|
||||
#else
|
||||
@ -492,10 +386,6 @@ Error BaseAssembler::onAttach(CodeHolder* code) noexcept {
|
||||
// Attach to the end of the .text section.
|
||||
BaseAssembler_initSection(this, code->_sections[0]);
|
||||
|
||||
// And reset everything that is used temporarily.
|
||||
_op4.reset();
|
||||
_op5.reset();
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
@ -504,10 +394,6 @@ Error BaseAssembler::onDetach(CodeHolder* code) noexcept {
|
||||
_bufferData = nullptr;
|
||||
_bufferEnd = nullptr;
|
||||
_bufferPtr = nullptr;
|
||||
|
||||
_op4.reset();
|
||||
_op5.reset();
|
||||
|
||||
return Base::onDetach(code);
|
||||
}
|
||||
|
||||
|
64
3rdparty/asmjit/src/asmjit/core/assembler.h
vendored
64
3rdparty/asmjit/src/asmjit/core/assembler.h
vendored
@ -31,14 +31,23 @@
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \addtogroup asmjit_assembler
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseAssembler]
|
||||
// ============================================================================
|
||||
|
||||
//! Base encoder (assembler).
|
||||
//! Base assembler.
|
||||
//!
|
||||
//! This is a base class that provides interface used by architecture specific
|
||||
//! assembler implementations. Assembler doesn't hold any data, instead it's
|
||||
//! attached to \ref CodeHolder, which provides all the data that Assembler
|
||||
//! needs and which can be altered by it.
|
||||
//!
|
||||
//! Check out architecture specific assemblers for more details and examples:
|
||||
//!
|
||||
//! - \ref x86::Assembler - X86/X64 assembler implementation.
|
||||
class ASMJIT_VIRTAPI BaseAssembler : public BaseEmitter {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(BaseAssembler)
|
||||
@ -52,10 +61,6 @@ public:
|
||||
uint8_t* _bufferEnd;
|
||||
//! Pointer in the CodeBuffer of the current section.
|
||||
uint8_t* _bufferPtr;
|
||||
//! 5th operand data, used only temporarily.
|
||||
Operand_ _op4;
|
||||
//! 6th operand data, used only temporarily.
|
||||
Operand_ _op5;
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
@ -77,10 +82,11 @@ public:
|
||||
|
||||
//! Returns the current position in the CodeBuffer.
|
||||
inline size_t offset() const noexcept { return (size_t)(_bufferPtr - _bufferData); }
|
||||
|
||||
//! Sets the current position in the CodeBuffer to `offset`.
|
||||
//!
|
||||
//! \note The `offset` cannot be outside of the buffer size (even if it's
|
||||
//! within buffer's capacity).
|
||||
//! \note The `offset` cannot be greater than buffer size even if it's
|
||||
//! within the buffer's capacity.
|
||||
ASMJIT_API Error setOffset(size_t offset);
|
||||
|
||||
//! Returns the start of the CodeBuffer in the current section.
|
||||
@ -95,6 +101,7 @@ public:
|
||||
//! \name Section Management
|
||||
//! \{
|
||||
|
||||
//! Returns the current section.
|
||||
inline Section* currentSection() const noexcept { return _section; }
|
||||
|
||||
ASMJIT_API Error section(Section* section) override;
|
||||
@ -110,47 +117,16 @@ public:
|
||||
|
||||
//! \}
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \name Emit
|
||||
//! \{
|
||||
|
||||
using BaseEmitter::_emit;
|
||||
|
||||
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) override;
|
||||
ASMJIT_API Error _emitOpArray(uint32_t instId, const Operand_* operands, size_t count) override;
|
||||
|
||||
protected:
|
||||
#ifndef ASMJIT_NO_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 immSize, uint8_t* afterCursor);
|
||||
|
||||
Error _emitFailed(
|
||||
Error err,
|
||||
uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3);
|
||||
#else
|
||||
inline Error _emitFailed(
|
||||
uint32_t err,
|
||||
uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) {
|
||||
|
||||
DebugUtils::unused(instId, options, o0, o1, o2, o3);
|
||||
resetInstOptions();
|
||||
resetInlineComment();
|
||||
return reportError(err);
|
||||
}
|
||||
#endif
|
||||
public:
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
//! \name Embed
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error embed(const void* data, uint32_t dataSize) override;
|
||||
ASMJIT_API Error embedLabel(const Label& label) override;
|
||||
ASMJIT_API Error embedLabelDelta(const Label& label, const Label& base, uint32_t dataSize) override;
|
||||
ASMJIT_API Error embed(const void* data, size_t dataSize) override;
|
||||
ASMJIT_API Error embedDataArray(uint32_t typeId, const void* data, size_t itemCcount, size_t repeatCount = 1) override;
|
||||
ASMJIT_API Error embedConstPool(const Label& label, const ConstPool& pool) override;
|
||||
|
||||
ASMJIT_API Error embedLabel(const Label& label) override;
|
||||
ASMJIT_API Error embedLabelDelta(const Label& label, const Label& base, size_t dataSize) override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Comment
|
||||
|
531
3rdparty/asmjit/src/asmjit/core/builder.cpp
vendored
531
3rdparty/asmjit/src/asmjit/core/builder.cpp
vendored
@ -25,7 +25,10 @@
|
||||
#ifndef ASMJIT_NO_BUILDER
|
||||
|
||||
#include "../core/builder.h"
|
||||
#include "../core/logging.h"
|
||||
#include "../core/emitterutils_p.h"
|
||||
#include "../core/errorhandler.h"
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
@ -41,12 +44,22 @@ class PostponedErrorHandler : public ErrorHandler {
|
||||
public:
|
||||
void handleError(Error err, const char* message, BaseEmitter* origin) override {
|
||||
DebugUtils::unused(err, origin);
|
||||
_message.assignString(message);
|
||||
_message.assign(message);
|
||||
}
|
||||
|
||||
StringTmp<128> _message;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseBuilder - Utilities]
|
||||
// ============================================================================
|
||||
|
||||
static void BaseBuilder_deletePasses(BaseBuilder* self) noexcept {
|
||||
for (Pass* pass : self->_passes)
|
||||
pass->~Pass();
|
||||
self->_passes.reset();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseBuilder - Construction / Destruction]
|
||||
// ============================================================================
|
||||
@ -63,132 +76,101 @@ BaseBuilder::BaseBuilder() noexcept
|
||||
_firstNode(nullptr),
|
||||
_lastNode(nullptr),
|
||||
_nodeFlags(0) {}
|
||||
BaseBuilder::~BaseBuilder() noexcept {}
|
||||
|
||||
BaseBuilder::~BaseBuilder() noexcept {
|
||||
BaseBuilder_deletePasses(this);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseBuilder - Node Management]
|
||||
// ============================================================================
|
||||
|
||||
LabelNode* BaseBuilder::newLabelNode() noexcept {
|
||||
LabelNode* node = newNodeT<LabelNode>();
|
||||
if (!node || registerLabelNode(node) != kErrorOk)
|
||||
return nullptr;
|
||||
return node;
|
||||
Error BaseBuilder::_newInstNode(InstNode** out, uint32_t instId, uint32_t instOptions, uint32_t opCount) {
|
||||
uint32_t opCapacity = InstNode::capacityOfOpCount(opCount);
|
||||
ASMJIT_ASSERT(opCapacity >= InstNode::kBaseOpCapacity);
|
||||
|
||||
InstNode* node = _allocator.allocT<InstNode>(InstNode::nodeSizeOfOpCapacity(opCapacity));
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
*out = new(node) InstNode(this, instId, instOptions, opCount, opCapacity);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
AlignNode* BaseBuilder::newAlignNode(uint32_t alignMode, uint32_t alignment) noexcept {
|
||||
return newNodeT<AlignNode>(alignMode, alignment);
|
||||
|
||||
Error BaseBuilder::_newLabelNode(LabelNode** out) {
|
||||
*out = nullptr;
|
||||
|
||||
ASMJIT_PROPAGATE(_newNodeT<LabelNode>(out));
|
||||
return registerLabelNode(*out);
|
||||
}
|
||||
|
||||
EmbedDataNode* BaseBuilder::newEmbedDataNode(const void* data, uint32_t size) noexcept {
|
||||
if (size > EmbedDataNode::kInlineBufferSize) {
|
||||
void* cloned = _dataZone.alloc(size);
|
||||
if (ASMJIT_UNLIKELY(!cloned))
|
||||
return nullptr;
|
||||
Error BaseBuilder::_newAlignNode(AlignNode** out, uint32_t alignMode, uint32_t alignment) {
|
||||
*out = nullptr;
|
||||
return _newNodeT<AlignNode>(out, alignMode, alignment);
|
||||
}
|
||||
|
||||
if (data)
|
||||
memcpy(cloned, data, size);
|
||||
data = cloned;
|
||||
Error BaseBuilder::_newEmbedDataNode(EmbedDataNode** out, uint32_t typeId, const void* data, size_t itemCount, size_t repeatCount) {
|
||||
*out = nullptr;
|
||||
|
||||
uint32_t deabstractDelta = Type::deabstractDeltaOfSize(registerSize());
|
||||
uint32_t finalTypeId = Type::deabstract(typeId, deabstractDelta);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!Type::isValid(finalTypeId)))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
uint32_t typeSize = Type::sizeOf(finalTypeId);
|
||||
Support::FastUInt8 of = 0;
|
||||
|
||||
size_t dataSize = Support::mulOverflow(itemCount, size_t(typeSize), &of);
|
||||
if (ASMJIT_UNLIKELY(of))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
EmbedDataNode* node;
|
||||
ASMJIT_PROPAGATE(_newNodeT<EmbedDataNode>(&node));
|
||||
|
||||
node->_embed._typeId = uint8_t(typeId);
|
||||
node->_embed._typeSize = uint8_t(typeSize);
|
||||
node->_itemCount = itemCount;
|
||||
node->_repeatCount = repeatCount;
|
||||
|
||||
uint8_t* dstData = node->_inlineData;
|
||||
if (dataSize > EmbedDataNode::kInlineBufferSize) {
|
||||
dstData = static_cast<uint8_t*>(_dataZone.alloc(dataSize, 8));
|
||||
if (ASMJIT_UNLIKELY(!dstData))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
node->_externalData = dstData;
|
||||
}
|
||||
|
||||
return newNodeT<EmbedDataNode>(const_cast<void*>(data), size);
|
||||
if (data)
|
||||
memcpy(dstData, data, dataSize);
|
||||
|
||||
*out = node;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ConstPoolNode* BaseBuilder::newConstPoolNode() noexcept {
|
||||
ConstPoolNode* node = newNodeT<ConstPoolNode>();
|
||||
if (!node || registerLabelNode(node) != kErrorOk)
|
||||
return nullptr;
|
||||
return node;
|
||||
Error BaseBuilder::_newConstPoolNode(ConstPoolNode** out) {
|
||||
*out = nullptr;
|
||||
|
||||
ASMJIT_PROPAGATE(_newNodeT<ConstPoolNode>(out));
|
||||
return registerLabelNode(*out);
|
||||
}
|
||||
|
||||
CommentNode* BaseBuilder::newCommentNode(const char* data, size_t size) noexcept {
|
||||
Error BaseBuilder::_newCommentNode(CommentNode** out, const char* data, size_t size) {
|
||||
*out = nullptr;
|
||||
|
||||
if (data) {
|
||||
if (size == SIZE_MAX)
|
||||
size = strlen(data);
|
||||
|
||||
if (size > 0) {
|
||||
data = static_cast<char*>(_dataZone.dup(data, size, true));
|
||||
if (!data) return nullptr;
|
||||
if (ASMJIT_UNLIKELY(!data))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
}
|
||||
}
|
||||
|
||||
return newNodeT<CommentNode>(data);
|
||||
}
|
||||
|
||||
InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0) noexcept {
|
||||
uint32_t opCount = 1;
|
||||
uint32_t opCapacity = InstNode::capacityOfOpCount(opCount);
|
||||
ASMJIT_ASSERT(opCapacity >= 4);
|
||||
|
||||
InstNode* node = _allocator.allocT<InstNode>(InstNode::nodeSizeOfOpCapacity(opCapacity));
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return nullptr;
|
||||
|
||||
node = new(node) InstNode(this, instId, instOptions, opCount, opCapacity);
|
||||
node->setOp(0, o0);
|
||||
node->resetOps(opCount, opCapacity);
|
||||
return node;
|
||||
}
|
||||
|
||||
InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1) noexcept {
|
||||
uint32_t opCount = 2;
|
||||
uint32_t opCapacity = InstNode::capacityOfOpCount(opCount);
|
||||
ASMJIT_ASSERT(opCapacity >= 4);
|
||||
|
||||
InstNode* node = _allocator.allocT<InstNode>(InstNode::nodeSizeOfOpCapacity(opCapacity));
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return nullptr;
|
||||
|
||||
node = new(node) InstNode(this, instId, instOptions, opCount, opCapacity);
|
||||
node->setOp(0, o0);
|
||||
node->setOp(1, o1);
|
||||
node->resetOps(opCount, opCapacity);
|
||||
return node;
|
||||
}
|
||||
|
||||
InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1, const Operand_& o2) noexcept {
|
||||
uint32_t opCount = 3;
|
||||
uint32_t opCapacity = InstNode::capacityOfOpCount(opCount);
|
||||
ASMJIT_ASSERT(opCapacity >= 4);
|
||||
|
||||
InstNode* node = _allocator.allocT<InstNode>(InstNode::nodeSizeOfOpCapacity(opCapacity));
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return nullptr;
|
||||
|
||||
node = new(node) InstNode(this, instId, instOptions, opCount, opCapacity);
|
||||
node->setOp(0, o0);
|
||||
node->setOp(1, o1);
|
||||
node->setOp(2, o2);
|
||||
node->resetOps(opCount, opCapacity);
|
||||
return node;
|
||||
}
|
||||
|
||||
InstNode* BaseBuilder::newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) noexcept {
|
||||
uint32_t opCount = 4;
|
||||
uint32_t opCapacity = InstNode::capacityOfOpCount(opCount);
|
||||
ASMJIT_ASSERT(opCapacity >= 4);
|
||||
|
||||
InstNode* node = _allocator.allocT<InstNode>(InstNode::nodeSizeOfOpCapacity(opCapacity));
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return nullptr;
|
||||
|
||||
node = new(node) InstNode(this, instId, instOptions, opCount, opCapacity);
|
||||
node->setOp(0, o0);
|
||||
node->setOp(1, o1);
|
||||
node->setOp(2, o2);
|
||||
node->setOp(3, o3);
|
||||
node->resetOps(opCount, opCapacity);
|
||||
return node;
|
||||
}
|
||||
|
||||
InstNode* BaseBuilder::newInstNodeRaw(uint32_t instId, uint32_t instOptions, uint32_t opCount) noexcept {
|
||||
uint32_t opCapacity = InstNode::capacityOfOpCount(opCount);
|
||||
ASMJIT_ASSERT(opCapacity >= 4);
|
||||
|
||||
InstNode* node = _allocator.allocT<InstNode>(InstNode::nodeSizeOfOpCapacity(opCapacity));
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return nullptr;
|
||||
return new(node) InstNode(this, instId, instOptions, opCount, opCapacity);
|
||||
return _newNodeT<CommentNode>(out, data);
|
||||
}
|
||||
|
||||
BaseNode* BaseBuilder::addNode(BaseNode* node) noexcept {
|
||||
@ -368,34 +350,42 @@ BaseNode* BaseBuilder::setCursor(BaseNode* node) noexcept {
|
||||
// [asmjit::BaseBuilder - Section]
|
||||
// ============================================================================
|
||||
|
||||
Error BaseBuilder::sectionNodeOf(SectionNode** pOut, uint32_t sectionId) noexcept {
|
||||
Error BaseBuilder::sectionNodeOf(SectionNode** out, uint32_t sectionId) {
|
||||
*out = nullptr;
|
||||
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!_code->isSectionValid(sectionId)))
|
||||
return DebugUtils::errored(kErrorInvalidSection);
|
||||
return reportError(DebugUtils::errored(kErrorInvalidSection));
|
||||
|
||||
if (sectionId >= _sectionNodes.size())
|
||||
ASMJIT_PROPAGATE(_sectionNodes.resize(&_allocator, sectionId + 1));
|
||||
if (sectionId >= _sectionNodes.size()) {
|
||||
Error err = _sectionNodes.reserve(&_allocator, sectionId + 1);
|
||||
if (ASMJIT_UNLIKELY(err != kErrorOk))
|
||||
return reportError(err);
|
||||
}
|
||||
|
||||
SectionNode* node = nullptr;
|
||||
if (sectionId < _sectionNodes.size())
|
||||
node = _sectionNodes[sectionId];
|
||||
|
||||
SectionNode* node = _sectionNodes[sectionId];
|
||||
if (!node) {
|
||||
node = newNodeT<SectionNode>(sectionId);
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
ASMJIT_PROPAGATE(_newNodeT<SectionNode>(&node, sectionId));
|
||||
|
||||
// We have already reserved enough space, this cannot fail now.
|
||||
if (sectionId >= _sectionNodes.size())
|
||||
_sectionNodes.resize(&_allocator, sectionId + 1);
|
||||
|
||||
_sectionNodes[sectionId] = node;
|
||||
}
|
||||
|
||||
*pOut = node;
|
||||
*out = node;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::section(Section* section) {
|
||||
SectionNode* node;
|
||||
Error err = sectionNodeOf(&node, section->id());
|
||||
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
ASMJIT_PROPAGATE(sectionNodeOf(&node, section->id()));
|
||||
|
||||
if (!node->isActive()) {
|
||||
// Insert the section at the end if it was not part of the code.
|
||||
@ -444,7 +434,9 @@ void BaseBuilder::updateSectionLinks() noexcept {
|
||||
// [asmjit::BaseBuilder - Labels]
|
||||
// ============================================================================
|
||||
|
||||
Error BaseBuilder::labelNodeOf(LabelNode** pOut, uint32_t labelId) noexcept {
|
||||
Error BaseBuilder::labelNodeOf(LabelNode** out, uint32_t labelId) {
|
||||
*out = nullptr;
|
||||
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
@ -457,22 +449,18 @@ Error BaseBuilder::labelNodeOf(LabelNode** pOut, uint32_t labelId) noexcept {
|
||||
|
||||
LabelNode* node = _labelNodes[index];
|
||||
if (!node) {
|
||||
node = newNodeT<LabelNode>(labelId);
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
ASMJIT_PROPAGATE(_newNodeT<LabelNode>(&node, labelId));
|
||||
_labelNodes[index] = node;
|
||||
}
|
||||
|
||||
*pOut = node;
|
||||
*out = node;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::registerLabelNode(LabelNode* node) noexcept {
|
||||
Error BaseBuilder::registerLabelNode(LabelNode* node) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
// Don't call `reportError()` from here, we are noexcept and we are called
|
||||
// by `newLabelNode()` and `newFuncNode()`, which are noexcept as well.
|
||||
LabelEntry* le;
|
||||
ASMJIT_PROPAGATE(_code->newLabelEntry(&le));
|
||||
uint32_t labelId = le->id();
|
||||
@ -482,68 +470,58 @@ Error BaseBuilder::registerLabelNode(LabelNode* node) noexcept {
|
||||
ASMJIT_PROPAGATE(_labelNodes.resize(&_allocator, labelId + 1));
|
||||
|
||||
_labelNodes[labelId] = node;
|
||||
node->_id = labelId;
|
||||
node->_labelId = labelId;
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
static Error BaseBuilder_newLabelInternal(BaseBuilder* self, uint32_t labelId) noexcept {
|
||||
static Error BaseBuilder_newLabelInternal(BaseBuilder* self, uint32_t labelId) {
|
||||
ASMJIT_ASSERT(self->_labelNodes.size() < labelId + 1);
|
||||
LabelNode* node = self->newNodeT<LabelNode>(labelId);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
uint32_t growBy = labelId - self->_labelNodes.size();
|
||||
Error err = self->_labelNodes.willGrow(&self->_allocator, growBy);
|
||||
|
||||
ASMJIT_PROPAGATE(self->_labelNodes.resize(&self->_allocator, labelId + 1));
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return self->reportError(err);
|
||||
|
||||
LabelNode* node;
|
||||
ASMJIT_PROPAGATE(self->_newNodeT<LabelNode>(&node, labelId));
|
||||
|
||||
self->_labelNodes.resize(&self->_allocator, labelId + 1);
|
||||
self->_labelNodes[labelId] = node;
|
||||
node->_id = labelId;
|
||||
node->_labelId = labelId;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Label BaseBuilder::newLabel() {
|
||||
uint32_t labelId = Globals::kInvalidId;
|
||||
if (_code) {
|
||||
LabelEntry* le;
|
||||
Error err = _code->newLabelEntry(&le);
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
reportError(err);
|
||||
}
|
||||
else {
|
||||
err = BaseBuilder_newLabelInternal(this, le->id());
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
reportError(err);
|
||||
else
|
||||
labelId = le->id();
|
||||
}
|
||||
LabelEntry* le;
|
||||
|
||||
if (_code &&
|
||||
_code->newLabelEntry(&le) == kErrorOk &&
|
||||
BaseBuilder_newLabelInternal(this, le->id()) == kErrorOk) {
|
||||
labelId = le->id();
|
||||
}
|
||||
|
||||
return Label(labelId);
|
||||
}
|
||||
|
||||
Label BaseBuilder::newNamedLabel(const char* name, size_t nameSize, uint32_t type, uint32_t parentId) {
|
||||
uint32_t labelId = Globals::kInvalidId;
|
||||
if (_code) {
|
||||
LabelEntry* le;
|
||||
Error err = _code->newNamedLabelEntry(&le, name, nameSize, type, parentId);
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
reportError(err);
|
||||
}
|
||||
else {
|
||||
err = BaseBuilder_newLabelInternal(this, le->id());
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
reportError(err);
|
||||
else
|
||||
labelId = le->id();
|
||||
}
|
||||
LabelEntry* le;
|
||||
|
||||
if (_code &&
|
||||
_code->newNamedLabelEntry(&le, name, nameSize, type, parentId) == kErrorOk &&
|
||||
BaseBuilder_newLabelInternal(this, le->id()) == kErrorOk) {
|
||||
labelId = le->id();
|
||||
}
|
||||
|
||||
return Label(labelId);
|
||||
}
|
||||
|
||||
Error BaseBuilder::bind(const Label& label) {
|
||||
LabelNode* node;
|
||||
Error err = labelNodeOf(&node, label);
|
||||
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
ASMJIT_PROPAGATE(labelNodeOf(&node, label));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
@ -610,7 +588,6 @@ Error BaseBuilder::runPasses() {
|
||||
if (_passes.empty())
|
||||
return kErrorOk;
|
||||
|
||||
Logger* logger = code()->logger();
|
||||
ErrorHandler* prev = errorHandler();
|
||||
PostponedErrorHandler postponed;
|
||||
|
||||
@ -619,8 +596,9 @@ Error BaseBuilder::runPasses() {
|
||||
|
||||
for (Pass* pass : _passes) {
|
||||
_passZone.reset();
|
||||
err = pass->run(&_passZone, logger);
|
||||
if (err) break;
|
||||
err = pass->run(&_passZone, _logger);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
_passZone.reset();
|
||||
setErrorHandler(prev);
|
||||
@ -635,104 +613,21 @@ Error BaseBuilder::runPasses() {
|
||||
// [asmjit::BaseBuilder - Emit]
|
||||
// ============================================================================
|
||||
|
||||
Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) {
|
||||
uint32_t opCount = 4;
|
||||
Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt) {
|
||||
uint32_t opCount = EmitterUtils::opCountFromEmitArgs(o0, o1, o2, opExt);
|
||||
uint32_t options = instOptions() | forcedInstOptions();
|
||||
|
||||
if (o3.isNone()) {
|
||||
opCount = 3;
|
||||
if (o2.isNone()) {
|
||||
opCount = 2;
|
||||
if (o1.isNone()) {
|
||||
opCount = 1;
|
||||
if (o0.isNone())
|
||||
opCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t options = instOptions() | globalInstOptions();
|
||||
if (options & BaseInst::kOptionReserved) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
#ifndef ASMJIT_NO_VALIDATION
|
||||
// Strict validation.
|
||||
if (hasEmitterOption(kOptionStrictValidation)) {
|
||||
Operand_ opArray[4];
|
||||
opArray[0].copyFrom(o0);
|
||||
opArray[1].copyFrom(o1);
|
||||
opArray[2].copyFrom(o2);
|
||||
opArray[3].copyFrom(o3);
|
||||
|
||||
Error err = InstAPI::validate(archId(), BaseInst(instId, options, _extraReg), opArray, opCount);
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
resetInstOptions();
|
||||
resetExtraReg();
|
||||
resetInlineComment();
|
||||
return reportError(err);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Clear options that should never be part of `InstNode`.
|
||||
options &= ~BaseInst::kOptionReserved;
|
||||
}
|
||||
|
||||
uint32_t opCapacity = InstNode::capacityOfOpCount(opCount);
|
||||
ASMJIT_ASSERT(opCapacity >= 4);
|
||||
|
||||
InstNode* node = _allocator.allocT<InstNode>(InstNode::nodeSizeOfOpCapacity(opCapacity));
|
||||
const char* comment = inlineComment();
|
||||
|
||||
resetInstOptions();
|
||||
resetInlineComment();
|
||||
|
||||
if (ASMJIT_UNLIKELY(!node)) {
|
||||
resetExtraReg();
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
}
|
||||
|
||||
node = new(node) InstNode(this, instId, options, opCount, opCapacity);
|
||||
node->setExtraReg(extraReg());
|
||||
node->setOp(0, o0);
|
||||
node->setOp(1, o1);
|
||||
node->setOp(2, o2);
|
||||
node->setOp(3, o3);
|
||||
node->resetOps(4, opCapacity);
|
||||
|
||||
if (comment)
|
||||
node->setInlineComment(static_cast<char*>(_dataZone.dup(comment, strlen(comment), true)));
|
||||
|
||||
addNode(node);
|
||||
resetExtraReg();
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) {
|
||||
uint32_t opCount = Globals::kMaxOpCount;
|
||||
if (o5.isNone()) {
|
||||
opCount = 5;
|
||||
if (o4.isNone())
|
||||
return _emit(instId, o0, o1, o2, o3);
|
||||
}
|
||||
|
||||
uint32_t options = instOptions() | globalInstOptions();
|
||||
if (ASMJIT_UNLIKELY(options & BaseInst::kOptionReserved)) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
#ifndef ASMJIT_NO_VALIDATION
|
||||
// Strict validation.
|
||||
if (hasEmitterOption(kOptionStrictValidation)) {
|
||||
if (hasValidationOption(kValidationOptionIntermediate)) {
|
||||
Operand_ opArray[Globals::kMaxOpCount];
|
||||
opArray[0].copyFrom(o0);
|
||||
opArray[1].copyFrom(o1);
|
||||
opArray[2].copyFrom(o2);
|
||||
opArray[3].copyFrom(o3);
|
||||
opArray[4].copyFrom(o4);
|
||||
opArray[5].copyFrom(o5);
|
||||
EmitterUtils::opArrayFromEmitArgs(opArray, o0, o1, o2, opExt);
|
||||
|
||||
Error err = InstAPI::validate(archId(), BaseInst(instId, options, _extraReg), opArray, opCount);
|
||||
Error err = InstAPI::validate(arch(), BaseInst(instId, options, _extraReg), opArray, opCount);
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
resetInstOptions();
|
||||
resetExtraReg();
|
||||
@ -747,7 +642,7 @@ Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1
|
||||
}
|
||||
|
||||
uint32_t opCapacity = InstNode::capacityOfOpCount(opCount);
|
||||
ASMJIT_ASSERT(opCapacity >= opCount);
|
||||
ASMJIT_ASSERT(opCapacity >= InstNode::kBaseOpCapacity);
|
||||
|
||||
InstNode* node = _allocator.allocT<InstNode>(InstNode::nodeSizeOfOpCapacity(opCapacity));
|
||||
const char* comment = inlineComment();
|
||||
@ -765,11 +660,9 @@ Error BaseBuilder::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1
|
||||
node->setOp(0, o0);
|
||||
node->setOp(1, o1);
|
||||
node->setOp(2, o2);
|
||||
node->setOp(3, o3);
|
||||
node->setOp(4, o4);
|
||||
|
||||
if (opCapacity > 5)
|
||||
node->setOp(5, o5);
|
||||
for (uint32_t i = 3; i < opCount; i++)
|
||||
node->setOp(i, opExt[i - 3]);
|
||||
node->resetOpRange(opCount, opCapacity);
|
||||
|
||||
if (comment)
|
||||
node->setInlineComment(static_cast<char*>(_dataZone.dup(comment, strlen(comment), true)));
|
||||
@ -787,9 +680,8 @@ Error BaseBuilder::align(uint32_t alignMode, uint32_t alignment) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
AlignNode* node = newAlignNode(alignMode, alignment);
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
AlignNode* node;
|
||||
ASMJIT_PROPAGATE(_newAlignNode(&node, alignMode, alignment));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
@ -799,37 +691,23 @@ Error BaseBuilder::align(uint32_t alignMode, uint32_t alignment) {
|
||||
// [asmjit::BaseBuilder - Embed]
|
||||
// ============================================================================
|
||||
|
||||
Error BaseBuilder::embed(const void* data, uint32_t dataSize) {
|
||||
Error BaseBuilder::embed(const void* data, size_t dataSize) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
EmbedDataNode* node = newEmbedDataNode(data, dataSize);
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
EmbedDataNode* node;
|
||||
ASMJIT_PROPAGATE(_newEmbedDataNode(&node, Type::kIdU8, data, dataSize));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::embedLabel(const Label& label) {
|
||||
Error BaseBuilder::embedDataArray(uint32_t typeId, const void* data, size_t itemCount, size_t itemRepeat) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
EmbedLabelNode* node = newNodeT<EmbedLabelNode>(label.id());
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::embedLabelDelta(const Label& label, const Label& base, uint32_t dataSize) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
EmbedLabelDeltaNode* node = newNodeT<EmbedLabelDeltaNode>(label.id(), base.id(), dataSize);
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
EmbedDataNode* node;
|
||||
ASMJIT_PROPAGATE(_newEmbedDataNode(&node, typeId, data, itemCount, itemRepeat));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
@ -845,15 +723,43 @@ Error BaseBuilder::embedConstPool(const Label& label, const ConstPool& pool) {
|
||||
ASMJIT_PROPAGATE(align(kAlignData, uint32_t(pool.alignment())));
|
||||
ASMJIT_PROPAGATE(bind(label));
|
||||
|
||||
EmbedDataNode* node = newEmbedDataNode(nullptr, uint32_t(pool.size()));
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
EmbedDataNode* node;
|
||||
ASMJIT_PROPAGATE(_newEmbedDataNode(&node, Type::kIdU8, nullptr, pool.size()));
|
||||
|
||||
pool.fill(node->data());
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::embedLabel(const Label& label) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
|
||||
EmbedLabelNode* node;
|
||||
ASMJIT_PROPAGATE(_newNodeT<EmbedLabelNode>(&node, label.id()));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseBuilder::embedLabelDelta(const Label& label, const Label& base, size_t dataSize) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
// If dataSize is zero it means that the size is the same as target register
|
||||
// width, however, if it's provided we really want to validate whether it's
|
||||
// within the possible range.
|
||||
if (dataSize && (!Support::isPowerOf2(dataSize) || dataSize > 8))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
EmbedLabelDeltaNode* node;
|
||||
ASMJIT_PROPAGATE(_newNodeT<EmbedLabelDeltaNode>(&node, label.id(), base.id(), uint32_t(dataSize)));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseBuilder - Comment]
|
||||
// ============================================================================
|
||||
@ -862,9 +768,8 @@ Error BaseBuilder::comment(const char* data, size_t size) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
CommentNode* node = newCommentNode(data, size);
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
CommentNode* node;
|
||||
ASMJIT_PROPAGATE(_newCommentNode(&node, data, size));
|
||||
|
||||
addNode(node);
|
||||
return kErrorOk;
|
||||
@ -878,12 +783,38 @@ Error BaseBuilder::serialize(BaseEmitter* dst) {
|
||||
Error err = kErrorOk;
|
||||
BaseNode* node_ = _firstNode;
|
||||
|
||||
Operand_ opArray[Globals::kMaxOpCount];
|
||||
|
||||
do {
|
||||
dst->setInlineComment(node_->inlineComment());
|
||||
|
||||
if (node_->isInst()) {
|
||||
InstNode* node = node_->as<InstNode>();
|
||||
err = dst->emitInst(node->baseInst(), node->operands(), node->opCount());
|
||||
|
||||
// NOTE: Inlined to remove one additional call per instruction.
|
||||
dst->setInstOptions(node->instOptions());
|
||||
dst->setExtraReg(node->extraReg());
|
||||
|
||||
const Operand_* op = node->operands();
|
||||
const Operand_* opExt = EmitterUtils::noExt;
|
||||
|
||||
uint32_t opCount = node->opCount();
|
||||
if (opCount > 3) {
|
||||
uint32_t i = 4;
|
||||
opArray[3] = op[3];
|
||||
|
||||
while (i < opCount) {
|
||||
opArray[i].copyFrom(op[i]);
|
||||
i++;
|
||||
}
|
||||
while (i < Globals::kMaxOpCount) {
|
||||
opArray[i].reset();
|
||||
i++;
|
||||
}
|
||||
opExt = opArray + 3;
|
||||
}
|
||||
|
||||
err = dst->_emit(node->id(), op[0], op[1], op[2], opExt);
|
||||
}
|
||||
else if (node_->isLabel()) {
|
||||
if (node_->isConstPool()) {
|
||||
@ -901,7 +832,7 @@ Error BaseBuilder::serialize(BaseEmitter* dst) {
|
||||
}
|
||||
else if (node_->isEmbedData()) {
|
||||
EmbedDataNode* node = node_->as<EmbedDataNode>();
|
||||
err = dst->embed(node->data(), node->size());
|
||||
err = dst->embedDataArray(node->typeId(), node->data(), node->itemCount(), node->repeatCount());
|
||||
}
|
||||
else if (node_->isEmbedLabel()) {
|
||||
EmbedLabelNode* node = node_->as<EmbedLabelNode>();
|
||||
@ -927,23 +858,6 @@ Error BaseBuilder::serialize(BaseEmitter* dst) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseBuilder - Logging]
|
||||
// ============================================================================
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
Error BaseBuilder::dump(String& sb, uint32_t flags) const noexcept {
|
||||
BaseNode* node = _firstNode;
|
||||
while (node) {
|
||||
ASMJIT_PROPAGATE(Logging::formatNode(sb, flags, this, node));
|
||||
sb.appendChar('\n');
|
||||
node = node->next();
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseBuilder - Events]
|
||||
// ============================================================================
|
||||
@ -962,7 +876,6 @@ Error BaseBuilder::onAttach(CodeHolder* code) noexcept {
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
_cursor = initialSection;
|
||||
_firstNode = initialSection;
|
||||
_lastNode = initialSection;
|
||||
@ -972,7 +885,7 @@ Error BaseBuilder::onAttach(CodeHolder* code) noexcept {
|
||||
}
|
||||
|
||||
Error BaseBuilder::onDetach(CodeHolder* code) noexcept {
|
||||
_passes.reset();
|
||||
BaseBuilder_deletePasses(this);
|
||||
_sectionNodes.reset();
|
||||
_labelNodes.reset();
|
||||
|
||||
|
430
3rdparty/asmjit/src/asmjit/core/builder.h
vendored
430
3rdparty/asmjit/src/asmjit/core/builder.h
vendored
@ -30,10 +30,12 @@
|
||||
#include "../core/assembler.h"
|
||||
#include "../core/codeholder.h"
|
||||
#include "../core/constpool.h"
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/inst.h"
|
||||
#include "../core/operand.h"
|
||||
#include "../core/string.h"
|
||||
#include "../core/support.h"
|
||||
#include "../core/type.h"
|
||||
#include "../core/zone.h"
|
||||
#include "../core/zonevector.h"
|
||||
|
||||
@ -68,6 +70,18 @@ class JumpAnnotation;
|
||||
// [asmjit::BaseBuilder]
|
||||
// ============================================================================
|
||||
|
||||
//! Builder interface.
|
||||
//!
|
||||
//! `BaseBuilder` interface was designed to be used as a \ref BaseAssembler
|
||||
//! replacement in case pre-processing or post-processing of the generated code
|
||||
//! is required. The code can be modified during or after code generation. Pre
|
||||
//! or post processing can be done manually or through a \ref Pass object. \ref
|
||||
//! BaseBuilder stores the emitted code as a double-linked list of nodes, which
|
||||
//! allows O(1) insertion and removal during processing.
|
||||
//!
|
||||
//! Check out architecture specific builders for more details and examples:
|
||||
//!
|
||||
//! - \ref x86::Builder - X86/X64 builder implementation.
|
||||
class ASMJIT_VIRTAPI BaseBuilder : public BaseEmitter {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(BaseBuilder)
|
||||
@ -127,33 +141,26 @@ public:
|
||||
//! \remarks The pointer returned (if non-null) is owned by the Builder or
|
||||
//! Compiler. When the Builder/Compiler is destroyed it destroys all nodes
|
||||
//! it created so no manual memory management is required.
|
||||
template<typename T>
|
||||
inline T* newNodeT() noexcept {
|
||||
return _allocator.newT<T>(this);
|
||||
}
|
||||
|
||||
//! \overload
|
||||
template<typename T, typename... Args>
|
||||
inline T* newNodeT(Args&&... args) noexcept {
|
||||
return _allocator.newT<T>(this, std::forward<Args>(args)...);
|
||||
inline Error _newNodeT(T** out, Args&&... args) {
|
||||
*out = _allocator.newT<T>(this, std::forward<Args>(args)...);
|
||||
if (ASMJIT_UNLIKELY(!*out))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
//! Creates a new `LabelNode`.
|
||||
ASMJIT_API LabelNode* newLabelNode() noexcept;
|
||||
//! Creates a new `AlignNode`.
|
||||
ASMJIT_API AlignNode* newAlignNode(uint32_t alignMode, uint32_t alignment) noexcept;
|
||||
//! Creates a new `EmbedDataNode`.
|
||||
ASMJIT_API EmbedDataNode* newEmbedDataNode(const void* data, uint32_t size) noexcept;
|
||||
//! Creates a new `ConstPoolNode`.
|
||||
ASMJIT_API ConstPoolNode* newConstPoolNode() noexcept;
|
||||
//! Creates a new `CommentNode`.
|
||||
ASMJIT_API CommentNode* newCommentNode(const char* data, size_t size) noexcept;
|
||||
|
||||
ASMJIT_API InstNode* newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0) noexcept;
|
||||
ASMJIT_API InstNode* newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1) noexcept;
|
||||
ASMJIT_API InstNode* newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1, const Operand_& o2) noexcept;
|
||||
ASMJIT_API InstNode* newInstNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) noexcept;
|
||||
ASMJIT_API InstNode* newInstNodeRaw(uint32_t instId, uint32_t instOptions, uint32_t opCount) noexcept;
|
||||
//! Creates a new \ref InstNode.
|
||||
ASMJIT_API Error _newInstNode(InstNode** out, uint32_t instId, uint32_t instOptions, uint32_t opCount);
|
||||
//! Creates a new \ref LabelNode.
|
||||
ASMJIT_API Error _newLabelNode(LabelNode** out);
|
||||
//! Creates a new \ref AlignNode.
|
||||
ASMJIT_API Error _newAlignNode(AlignNode** out, uint32_t alignMode, uint32_t alignment);
|
||||
//! Creates a new \ref EmbedDataNode.
|
||||
ASMJIT_API Error _newEmbedDataNode(EmbedDataNode** out, uint32_t typeId, const void* data, size_t itemCount, size_t repeatCount = 1);
|
||||
//! Creates a new \ref ConstPoolNode.
|
||||
ASMJIT_API Error _newConstPoolNode(ConstPoolNode** out);
|
||||
//! Creates a new \ref CommentNode.
|
||||
ASMJIT_API Error _newCommentNode(CommentNode** out, const char* data, size_t size);
|
||||
|
||||
//! Adds `node` after the current and sets the current node to the given `node`.
|
||||
ASMJIT_API BaseNode* addNode(BaseNode* node) noexcept;
|
||||
@ -173,7 +180,9 @@ public:
|
||||
//! added they are always added after the cursor and the cursor is changed
|
||||
//! to be that newly added node. Use `setCursor()` to change where new nodes
|
||||
//! are inserted.
|
||||
inline BaseNode* cursor() const noexcept { return _cursor; }
|
||||
inline BaseNode* cursor() const noexcept {
|
||||
return _cursor;
|
||||
}
|
||||
|
||||
//! Sets the current node to `node` and return the previous one.
|
||||
ASMJIT_API BaseNode* setCursor(BaseNode* node) noexcept;
|
||||
@ -182,7 +191,9 @@ public:
|
||||
//!
|
||||
//! Only use this function if you are concerned about performance and want
|
||||
//! this inlined (for example if you set the cursor in a loop, etc...).
|
||||
inline void _setCursor(BaseNode* node) noexcept { _cursor = node; }
|
||||
inline void _setCursor(BaseNode* node) noexcept {
|
||||
_cursor = node;
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
@ -193,7 +204,9 @@ public:
|
||||
//!
|
||||
//! \note If a section of some id is not associated with the Builder/Compiler
|
||||
//! it would be null, so always check for nulls if you iterate over the vector.
|
||||
inline const ZoneVector<SectionNode*>& sectionNodes() const noexcept { return _sectionNodes; }
|
||||
inline const ZoneVector<SectionNode*>& sectionNodes() const noexcept {
|
||||
return _sectionNodes;
|
||||
}
|
||||
|
||||
//! Tests whether the `SectionNode` of the given `sectionId` was registered.
|
||||
inline bool hasRegisteredSectionNode(uint32_t sectionId) const noexcept {
|
||||
@ -205,7 +218,7 @@ public:
|
||||
//! \remarks This function will either get the existing `SectionNode` or create
|
||||
//! it in case it wasn't created before. You can check whether a section has a
|
||||
//! registered `SectionNode` by using `BaseBuilder::hasRegisteredSectionNode()`.
|
||||
ASMJIT_API Error sectionNodeOf(SectionNode** pOut, uint32_t sectionId) noexcept;
|
||||
ASMJIT_API Error sectionNodeOf(SectionNode** out, uint32_t sectionId);
|
||||
|
||||
ASMJIT_API Error section(Section* section) override;
|
||||
|
||||
@ -221,7 +234,7 @@ public:
|
||||
//! \name Label Management
|
||||
//! \{
|
||||
|
||||
//! Returns a vector of LabelNode nodes.
|
||||
//! Returns a vector of \ref LabelNode nodes.
|
||||
//!
|
||||
//! \note If a label of some id is not associated with the Builder/Compiler
|
||||
//! it would be null, so always check for nulls if you iterate over the vector.
|
||||
@ -237,24 +250,24 @@ public:
|
||||
return hasRegisteredLabelNode(label.id());
|
||||
}
|
||||
|
||||
//! Gets or creates a `LabelNode` that matches the given `labelId`.
|
||||
//! Gets or creates a \ref LabelNode that matches the given `labelId`.
|
||||
//!
|
||||
//! \remarks This function will either get the existing `LabelNode` or create
|
||||
//! it in case it wasn't created before. You can check whether a label has a
|
||||
//! registered `LabelNode` by using `BaseBuilder::hasRegisteredLabelNode()`.
|
||||
ASMJIT_API Error labelNodeOf(LabelNode** pOut, uint32_t labelId) noexcept;
|
||||
//! registered `LabelNode` by calling \ref BaseBuilder::hasRegisteredLabelNode().
|
||||
ASMJIT_API Error labelNodeOf(LabelNode** out, uint32_t labelId);
|
||||
|
||||
//! \overload
|
||||
inline Error labelNodeOf(LabelNode** pOut, const Label& label) noexcept {
|
||||
return labelNodeOf(pOut, label.id());
|
||||
inline Error labelNodeOf(LabelNode** out, const Label& label) {
|
||||
return labelNodeOf(out, label.id());
|
||||
}
|
||||
|
||||
//! Registers this label node [Internal].
|
||||
//! Registers this \ref LabelNode (internal).
|
||||
//!
|
||||
//! This function is used internally to register a newly created `LabelNode`
|
||||
//! with this instance of Builder/Compiler. Use `labelNodeOf()` functions to
|
||||
//! get back `LabelNode` from a label or its identifier.
|
||||
ASMJIT_API Error registerLabelNode(LabelNode* node) noexcept;
|
||||
//! with this instance of Builder/Compiler. Use \ref labelNodeOf() functions
|
||||
//! to get back \ref LabelNode from a label or its identifier.
|
||||
ASMJIT_API Error registerLabelNode(LabelNode* node);
|
||||
|
||||
ASMJIT_API Label newLabel() override;
|
||||
ASMJIT_API Label newNamedLabel(const char* name, size_t nameSize = SIZE_MAX, uint32_t type = Label::kTypeGlobal, uint32_t parentId = Globals::kInvalidId) override;
|
||||
@ -284,12 +297,14 @@ public:
|
||||
inline T* newPassT(Args&&... args) noexcept { return _codeZone.newT<T>(std::forward<Args>(args)...); }
|
||||
|
||||
template<typename T>
|
||||
inline Error addPassT() noexcept { return addPass(newPassT<T>()); }
|
||||
inline Error addPassT() { return addPass(newPassT<T>()); }
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline Error addPassT(Args&&... args) noexcept { return addPass(newPassT<T, Args...>(std::forward<Args>(args)...)); }
|
||||
inline Error addPassT(Args&&... args) { return addPass(newPassT<T, Args...>(std::forward<Args>(args)...)); }
|
||||
|
||||
//! Returns `Pass` by name.
|
||||
//!
|
||||
//! If the pass having the given `name` doesn't exist `nullptr` is returned.
|
||||
ASMJIT_API Pass* passByName(const char* name) const noexcept;
|
||||
//! Adds `pass` to the list of passes.
|
||||
ASMJIT_API Error addPass(Pass* pass) noexcept;
|
||||
@ -304,8 +319,7 @@ public:
|
||||
//! \name Emit
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) override;
|
||||
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) override;
|
||||
ASMJIT_API Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt) override;
|
||||
|
||||
//! \}
|
||||
|
||||
@ -319,11 +333,13 @@ public:
|
||||
//! \name Embed
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error embed(const void* data, uint32_t dataSize) override;
|
||||
ASMJIT_API Error embedLabel(const Label& label) override;
|
||||
ASMJIT_API Error embedLabelDelta(const Label& label, const Label& base, uint32_t dataSize) override;
|
||||
ASMJIT_API Error embed(const void* data, size_t dataSize) override;
|
||||
ASMJIT_API Error embedDataArray(uint32_t typeId, const void* data, size_t count, size_t repeat = 1) override;
|
||||
ASMJIT_API Error embedConstPool(const Label& label, const ConstPool& pool) override;
|
||||
|
||||
ASMJIT_API Error embedLabel(const Label& label) override;
|
||||
ASMJIT_API Error embedLabelDelta(const Label& label, const Label& base, size_t dataSize) override;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Comment
|
||||
@ -345,15 +361,6 @@ public:
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Logging
|
||||
//! \{
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
ASMJIT_API Error dump(String& sb, uint32_t flags = 0) const noexcept;
|
||||
#endif
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Events
|
||||
//! \{
|
||||
|
||||
@ -361,6 +368,15 @@ public:
|
||||
ASMJIT_API Error onDetach(CodeHolder* code) noexcept override;
|
||||
|
||||
//! \}
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
ASMJIT_DEPRECATED("Use Formatter::formatNodeList(sb, formatFlags, builder)")
|
||||
inline Error dump(String& sb, uint32_t formatFlags = 0) const noexcept {
|
||||
return Formatter::formatNodeList(sb, formatFlags, this);
|
||||
}
|
||||
#endif // !ASMJIT_NO_LOGGING
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@ -369,11 +385,11 @@ public:
|
||||
|
||||
//! Base node.
|
||||
//!
|
||||
//! Every node represents a building-block used by `BaseBuilder`. It can be
|
||||
//! instruction, data, label, comment, directive, or any other high-level
|
||||
//! Every node represents a building-block used by \ref BaseBuilder. 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 `BaseBuilder` can define its own nodes that it
|
||||
//! can lower to basic nodes.
|
||||
//! Every class that inherits \ref BaseBuilder can define its own high-level
|
||||
//! nodes that can be later lowered to basic nodes like instructions.
|
||||
class BaseNode {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(BaseNode)
|
||||
@ -385,7 +401,7 @@ public:
|
||||
//! Next node.
|
||||
BaseNode* _next;
|
||||
};
|
||||
//! Links (previous and next nodes).
|
||||
//! Links (an alternative view to previous and next nodes).
|
||||
BaseNode* _links[2];
|
||||
};
|
||||
|
||||
@ -401,6 +417,7 @@ public:
|
||||
uint8_t _reserved1;
|
||||
};
|
||||
|
||||
//! Data used by \ref InstNode.
|
||||
struct InstData {
|
||||
//! Node type, see \ref NodeType.
|
||||
uint8_t _nodeType;
|
||||
@ -412,6 +429,19 @@ public:
|
||||
uint8_t _opCapacity;
|
||||
};
|
||||
|
||||
//! Data used by \ref EmbedDataNode.
|
||||
struct EmbedData {
|
||||
//! Node type, see \ref NodeType.
|
||||
uint8_t _nodeType;
|
||||
//! Node flags, see \ref Flags.
|
||||
uint8_t _nodeFlags;
|
||||
//! Type id, see \ref Type::Id.
|
||||
uint8_t _typeId;
|
||||
//! Size of `_typeId`.
|
||||
uint8_t _typeSize;
|
||||
};
|
||||
|
||||
//! Data used by \ref SentinelNode.
|
||||
struct SentinelData {
|
||||
//! Node type, see \ref NodeType.
|
||||
uint8_t _nodeType;
|
||||
@ -423,9 +453,15 @@ public:
|
||||
uint8_t _reserved1;
|
||||
};
|
||||
|
||||
//! Data that can have different meaning dependning on \ref NodeType.
|
||||
union {
|
||||
//! Data useful by any node type.
|
||||
AnyData _any;
|
||||
//! Data specific to \ref InstNode.
|
||||
InstData _inst;
|
||||
//! Data specific to \ref EmbedDataNode.
|
||||
EmbedData _embed;
|
||||
//! Data specific to \ref SentinelNode.
|
||||
SentinelData _sentinel;
|
||||
};
|
||||
|
||||
@ -434,7 +470,9 @@ public:
|
||||
|
||||
//! Value reserved for AsmJit users never touched by AsmJit itself.
|
||||
union {
|
||||
//! User data as 64-bit integer.
|
||||
uint64_t _userDataU64;
|
||||
//! User data as pointer.
|
||||
void* _userDataPtr;
|
||||
};
|
||||
|
||||
@ -451,54 +489,66 @@ public:
|
||||
|
||||
// [BaseBuilder]
|
||||
|
||||
//! Node is `InstNode` or `InstExNode`.
|
||||
//! Node is \ref InstNode or \ref InstExNode.
|
||||
kNodeInst = 1,
|
||||
//! Node is `SectionNode`.
|
||||
//! Node is \ref SectionNode.
|
||||
kNodeSection = 2,
|
||||
//! Node is `LabelNode`.
|
||||
//! Node is \ref LabelNode.
|
||||
kNodeLabel = 3,
|
||||
//! Node is `AlignNode`.
|
||||
//! Node is \ref AlignNode.
|
||||
kNodeAlign = 4,
|
||||
//! Node is `EmbedDataNode`.
|
||||
//! Node is \ref EmbedDataNode.
|
||||
kNodeEmbedData = 5,
|
||||
//! Node is `EmbedLabelNode`.
|
||||
//! Node is \ref EmbedLabelNode.
|
||||
kNodeEmbedLabel = 6,
|
||||
//! Node is `EmbedLabelDeltaNode`.
|
||||
//! Node is \ref EmbedLabelDeltaNode.
|
||||
kNodeEmbedLabelDelta = 7,
|
||||
//! Node is `ConstPoolNode`.
|
||||
//! Node is \ref ConstPoolNode.
|
||||
kNodeConstPool = 8,
|
||||
//! Node is `CommentNode`.
|
||||
//! Node is \ref CommentNode.
|
||||
kNodeComment = 9,
|
||||
//! Node is `SentinelNode`.
|
||||
//! Node is \ref SentinelNode.
|
||||
kNodeSentinel = 10,
|
||||
|
||||
// [BaseCompiler]
|
||||
|
||||
//! Node is `JumpNode` (acts as InstNode).
|
||||
//! Node is \ref JumpNode (acts as InstNode).
|
||||
kNodeJump = 15,
|
||||
//! Node is `FuncNode` (acts as LabelNode).
|
||||
//! Node is \ref FuncNode (acts as LabelNode).
|
||||
kNodeFunc = 16,
|
||||
//! Node is `FuncRetNode` (acts as InstNode).
|
||||
//! Node is \ref FuncRetNode (acts as InstNode).
|
||||
kNodeFuncRet = 17,
|
||||
//! Node is `FuncCallNode` (acts as InstNode).
|
||||
kNodeFuncCall = 18,
|
||||
//! Node is \ref InvokeNode (acts as InstNode).
|
||||
kNodeInvoke = 18,
|
||||
|
||||
// [UserDefined]
|
||||
|
||||
//! First id of a user-defined node.
|
||||
kNodeUser = 32
|
||||
kNodeUser = 32,
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
kNodeFuncCall = kNodeInvoke
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
};
|
||||
|
||||
//! Node flags, specify what the node is and/or does.
|
||||
enum Flags : uint32_t {
|
||||
kFlagIsCode = 0x01u, //!< Node is code that can be executed (instruction, label, align, etc...).
|
||||
kFlagIsData = 0x02u, //!< Node is data that cannot be executed (data, const-pool, etc...).
|
||||
kFlagIsInformative = 0x04u, //!< Node is informative, can be removed and ignored.
|
||||
kFlagIsRemovable = 0x08u, //!< Node can be safely removed if unreachable.
|
||||
kFlagHasNoEffect = 0x10u, //!< Node does nothing when executed (label, align, explicit nop).
|
||||
kFlagActsAsInst = 0x20u, //!< Node is an instruction or acts as it.
|
||||
kFlagActsAsLabel = 0x40u, //!< Node is a label or acts as it.
|
||||
kFlagIsActive = 0x80u //!< Node is active (part of the code).
|
||||
//! Node is code that can be executed (instruction, label, align, etc...).
|
||||
kFlagIsCode = 0x01u,
|
||||
//! Node is data that cannot be executed (data, const-pool, etc...).
|
||||
kFlagIsData = 0x02u,
|
||||
//! Node is informative, can be removed and ignored.
|
||||
kFlagIsInformative = 0x04u,
|
||||
//! Node can be safely removed if unreachable.
|
||||
kFlagIsRemovable = 0x08u,
|
||||
//! Node does nothing when executed (label, align, explicit nop).
|
||||
kFlagHasNoEffect = 0x10u,
|
||||
//! Node is an instruction or acts as it.
|
||||
kFlagActsAsInst = 0x20u,
|
||||
//! Node is a label or acts as it.
|
||||
kFlagActsAsLabel = 0x40u,
|
||||
//! Node is active (part of the code).
|
||||
kFlagIsActive = 0x80u
|
||||
};
|
||||
|
||||
//! \name Construction & Destruction
|
||||
@ -572,8 +622,13 @@ public:
|
||||
inline bool isFunc() const noexcept { return type() == kNodeFunc; }
|
||||
//! Tests whether this node is `FuncRetNode`.
|
||||
inline bool isFuncRet() const noexcept { return type() == kNodeFuncRet; }
|
||||
//! Tests whether this node is `FuncCallNode`.
|
||||
inline bool isFuncCall() const noexcept { return type() == kNodeFuncCall; }
|
||||
//! Tests whether this node is `InvokeNode`.
|
||||
inline bool isInvoke() const noexcept { return type() == kNodeInvoke; }
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("Use isInvoke")
|
||||
inline bool isFuncCall() const noexcept { return isInvoke(); }
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
|
||||
//! Returns the node flags, see \ref Flags.
|
||||
inline uint32_t flags() const noexcept { return _any._nodeFlags; }
|
||||
@ -697,12 +752,13 @@ public:
|
||||
_inst._opCount = uint8_t(opCount);
|
||||
}
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! Reset all built-in operands, including `extraReg`.
|
||||
inline void _resetOps() noexcept {
|
||||
_baseInst.resetExtraReg();
|
||||
for (uint32_t i = 0, count = opCapacity(); i < count; i++)
|
||||
_opArray[i].reset();
|
||||
resetOpRange(0, opCapacity());
|
||||
}
|
||||
//! \endcond
|
||||
|
||||
//! \}
|
||||
|
||||
@ -739,12 +795,12 @@ public:
|
||||
//! Resets extra register operand.
|
||||
inline void resetExtraReg() noexcept { _baseInst.resetExtraReg(); }
|
||||
|
||||
//! Returns operands count.
|
||||
//! Returns operand count.
|
||||
inline uint32_t opCount() const noexcept { return _inst._opCount; }
|
||||
//! Returns operands capacity.
|
||||
//! Returns operand capacity.
|
||||
inline uint32_t opCapacity() const noexcept { return _inst._opCapacity; }
|
||||
|
||||
//! Sets operands count.
|
||||
//! Sets operand count.
|
||||
inline void setOpCount(uint32_t opCount) noexcept { _inst._opCount = uint8_t(opCount); }
|
||||
|
||||
//! Returns operands array.
|
||||
@ -752,27 +808,32 @@ public:
|
||||
//! Returns operands array (const).
|
||||
inline const Operand* operands() const noexcept { return (const Operand*)_opArray; }
|
||||
|
||||
inline Operand& opType(uint32_t index) noexcept {
|
||||
//! Returns operand at the given `index`.
|
||||
inline Operand& op(uint32_t index) noexcept {
|
||||
ASMJIT_ASSERT(index < opCapacity());
|
||||
return _opArray[index].as<Operand>();
|
||||
}
|
||||
|
||||
inline const Operand& opType(uint32_t index) const noexcept {
|
||||
//! Returns operand at the given `index` (const).
|
||||
inline const Operand& op(uint32_t index) const noexcept {
|
||||
ASMJIT_ASSERT(index < opCapacity());
|
||||
return _opArray[index].as<Operand>();
|
||||
}
|
||||
|
||||
//! Sets operand at the given `index` to `op`.
|
||||
inline void setOp(uint32_t index, const Operand_& op) noexcept {
|
||||
ASMJIT_ASSERT(index < opCapacity());
|
||||
_opArray[index].copyFrom(op);
|
||||
}
|
||||
|
||||
//! Resets operand at the given `index` to none.
|
||||
inline void resetOp(uint32_t index) noexcept {
|
||||
ASMJIT_ASSERT(index < opCapacity());
|
||||
_opArray[index].reset();
|
||||
}
|
||||
|
||||
inline void resetOps(uint32_t start, uint32_t end) noexcept {
|
||||
//! Resets operands at `[start, end)` range.
|
||||
inline void resetOpRange(uint32_t start, uint32_t end) noexcept {
|
||||
for (uint32_t i = start; i < end; i++)
|
||||
_opArray[i].reset();
|
||||
}
|
||||
@ -816,6 +877,7 @@ public:
|
||||
//! \name Rewriting
|
||||
//! \{
|
||||
|
||||
//! \cond INTERNAL
|
||||
inline uint32_t* _getRewriteArray() noexcept { return &_baseInst._extraReg._id; }
|
||||
inline const uint32_t* _getRewriteArray() const noexcept { return &_baseInst._extraReg._id; }
|
||||
|
||||
@ -833,12 +895,14 @@ public:
|
||||
uint32_t* array = _getRewriteArray();
|
||||
array[index] = id;
|
||||
}
|
||||
//! \endcond
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Static Functions
|
||||
//! \{
|
||||
|
||||
//! \cond INTERNAL
|
||||
static inline uint32_t capacityOfOpCount(uint32_t opCount) noexcept {
|
||||
return opCount <= kBaseOpCapacity ? kBaseOpCapacity : Globals::kMaxOpCount;
|
||||
}
|
||||
@ -847,6 +911,7 @@ public:
|
||||
size_t base = sizeof(InstNode) - kBaseOpCapacity * sizeof(Operand);
|
||||
return base + opCapacity * sizeof(Operand);
|
||||
}
|
||||
//! \endcond
|
||||
|
||||
//! \}
|
||||
};
|
||||
@ -855,7 +920,7 @@ public:
|
||||
// [asmjit::InstExNode]
|
||||
// ============================================================================
|
||||
|
||||
//! Instruction node with maximum number of operands..
|
||||
//! Instruction node with maximum number of operands.
|
||||
//!
|
||||
//! This node is created automatically by Builder/Compiler in case that the
|
||||
//! required number of operands exceeds the default capacity of `InstNode`.
|
||||
@ -925,27 +990,33 @@ class LabelNode : public BaseNode {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(LabelNode)
|
||||
|
||||
uint32_t _id;
|
||||
//! Label identifier.
|
||||
uint32_t _labelId;
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a new `LabelNode` instance.
|
||||
inline LabelNode(BaseBuilder* cb, uint32_t id = 0) noexcept
|
||||
inline LabelNode(BaseBuilder* cb, uint32_t labelId = 0) noexcept
|
||||
: BaseNode(cb, kNodeLabel, kFlagHasNoEffect | kFlagActsAsLabel),
|
||||
_id(id) {}
|
||||
_labelId(labelId) {}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns \ref Label representation of the \ref LabelNode.
|
||||
inline Label label() const noexcept { return Label(_labelId); }
|
||||
//! Returns the id of the label.
|
||||
inline uint32_t id() const noexcept { return _id; }
|
||||
//! Returns the label as `Label` operand.
|
||||
inline Label label() const noexcept { return Label(_id); }
|
||||
inline uint32_t labelId() const noexcept { return _labelId; }
|
||||
|
||||
//! \}
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("Use labelId() instead")
|
||||
inline uint32_t id() const noexcept { return labelId(); }
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@ -1005,37 +1076,28 @@ public:
|
||||
ASMJIT_NONCOPYABLE(EmbedDataNode)
|
||||
|
||||
enum : uint32_t {
|
||||
kInlineBufferSize = uint32_t(64 - sizeof(BaseNode) - 4)
|
||||
kInlineBufferSize = 128 - (sizeof(BaseNode) + sizeof(size_t) * 2)
|
||||
};
|
||||
|
||||
size_t _itemCount;
|
||||
size_t _repeatCount;
|
||||
|
||||
union {
|
||||
struct {
|
||||
//! Embedded data buffer.
|
||||
uint8_t _buf[kInlineBufferSize];
|
||||
//! Size of the data.
|
||||
uint32_t _size;
|
||||
};
|
||||
struct {
|
||||
//! Pointer to external data.
|
||||
uint8_t* _externalPtr;
|
||||
};
|
||||
uint8_t* _externalData;
|
||||
uint8_t _inlineData[kInlineBufferSize];
|
||||
};
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a new `EmbedDataNode` instance.
|
||||
inline EmbedDataNode(BaseBuilder* cb, void* data, uint32_t size) noexcept
|
||||
: BaseNode(cb, kNodeEmbedData, kFlagIsData) {
|
||||
|
||||
if (size <= kInlineBufferSize) {
|
||||
if (data)
|
||||
memcpy(_buf, data, size);
|
||||
}
|
||||
else {
|
||||
_externalPtr = static_cast<uint8_t*>(data);
|
||||
}
|
||||
_size = size;
|
||||
inline EmbedDataNode(BaseBuilder* cb) noexcept
|
||||
: BaseNode(cb, kNodeEmbedData, kFlagIsData),
|
||||
_itemCount(0),
|
||||
_repeatCount(0) {
|
||||
_embed._typeId = uint8_t(Type::kIdU8),
|
||||
_embed._typeSize = uint8_t(1);
|
||||
memset(_inlineData, 0, kInlineBufferSize);
|
||||
}
|
||||
|
||||
//! \}
|
||||
@ -1043,10 +1105,32 @@ public:
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns pointer to the data.
|
||||
inline uint8_t* data() const noexcept { return _size <= kInlineBufferSize ? const_cast<uint8_t*>(_buf) : _externalPtr; }
|
||||
//! Returns size of the data.
|
||||
inline uint32_t size() const noexcept { return _size; }
|
||||
//! Returns \ref Type::Id of the data.
|
||||
inline uint32_t typeId() const noexcept { return _embed._typeId; }
|
||||
//! Returns the size of a single data element.
|
||||
inline uint32_t typeSize() const noexcept { return _embed._typeSize; }
|
||||
|
||||
//! Returns a pointer to the data casted to `uint8_t`.
|
||||
inline uint8_t* data() const noexcept {
|
||||
return dataSize() <= kInlineBufferSize ? const_cast<uint8_t*>(_inlineData) : _externalData;
|
||||
}
|
||||
|
||||
//! Returns a pointer to the data casted to `T`.
|
||||
template<typename T>
|
||||
inline T* dataAs() const noexcept { return reinterpret_cast<T*>(data()); }
|
||||
|
||||
//! Returns the number of (typed) items in the array.
|
||||
inline size_t itemCount() const noexcept { return _itemCount; }
|
||||
|
||||
//! Returns how many times the data is repeated (default 1).
|
||||
//!
|
||||
//! Repeated data is useful when defining constants for SIMD, for example.
|
||||
inline size_t repeatCount() const noexcept { return _repeatCount; }
|
||||
|
||||
//! Returns the size of the data, not considering the number of times it repeats.
|
||||
//!
|
||||
//! \note The returned value is the same as `typeSize() * itemCount()`.
|
||||
inline size_t dataSize() const noexcept { return typeSize() * _itemCount; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
@ -1060,32 +1144,37 @@ class EmbedLabelNode : public BaseNode {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(EmbedLabelNode)
|
||||
|
||||
uint32_t _id;
|
||||
uint32_t _labelId;
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a new `EmbedLabelNode` instance.
|
||||
inline EmbedLabelNode(BaseBuilder* cb, uint32_t id = 0) noexcept
|
||||
inline EmbedLabelNode(BaseBuilder* cb, uint32_t labelId = 0) noexcept
|
||||
: BaseNode(cb, kNodeEmbedLabel, kFlagIsData),
|
||||
_id(id) {}
|
||||
_labelId(labelId) {}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the label to embed as \ref Label operand.
|
||||
inline Label label() const noexcept { return Label(_labelId); }
|
||||
//! Returns the id of the label.
|
||||
inline uint32_t id() const noexcept { return _id; }
|
||||
//! Sets the label id (use with caution, improper use can break a lot of things).
|
||||
inline void setId(uint32_t id) noexcept { _id = id; }
|
||||
inline uint32_t labelId() const noexcept { return _labelId; }
|
||||
|
||||
//! Returns the label as `Label` operand.
|
||||
inline Label label() const noexcept { return Label(_id); }
|
||||
//! Sets the label id from `label` operand.
|
||||
inline void setLabel(const Label& label) noexcept { setId(label.id()); }
|
||||
inline void setLabel(const Label& label) noexcept { setLabelId(label.id()); }
|
||||
//! Sets the label id (use with caution, improper use can break a lot of things).
|
||||
inline void setLabelId(uint32_t labelId) noexcept { _labelId = labelId; }
|
||||
|
||||
//! \}
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("Use labelId() instead")
|
||||
inline uint32_t id() const noexcept { return labelId(); }
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@ -1097,18 +1186,18 @@ class EmbedLabelDeltaNode : public BaseNode {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(EmbedLabelDeltaNode)
|
||||
|
||||
uint32_t _id;
|
||||
uint32_t _baseId;
|
||||
uint32_t _labelId;
|
||||
uint32_t _baseLabelId;
|
||||
uint32_t _dataSize;
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a new `EmbedLabelDeltaNode` instance.
|
||||
inline EmbedLabelDeltaNode(BaseBuilder* cb, uint32_t id = 0, uint32_t baseId = 0, uint32_t dataSize = 0) noexcept
|
||||
inline EmbedLabelDeltaNode(BaseBuilder* cb, uint32_t labelId = 0, uint32_t baseLabelId = 0, uint32_t dataSize = 0) noexcept
|
||||
: BaseNode(cb, kNodeEmbedLabelDelta, kFlagIsData),
|
||||
_id(id),
|
||||
_baseId(baseId),
|
||||
_labelId(labelId),
|
||||
_baseLabelId(baseLabelId),
|
||||
_dataSize(dataSize) {}
|
||||
|
||||
//! \}
|
||||
@ -1116,28 +1205,46 @@ public:
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the id of the label.
|
||||
inline uint32_t id() const noexcept { return _id; }
|
||||
//! Sets the label id.
|
||||
inline void setId(uint32_t id) noexcept { _id = id; }
|
||||
//! Returns the label as `Label` operand.
|
||||
inline Label label() const noexcept { return Label(_id); }
|
||||
inline Label label() const noexcept { return Label(_labelId); }
|
||||
//! Returns the id of the label.
|
||||
inline uint32_t labelId() const noexcept { return _labelId; }
|
||||
|
||||
//! Sets the label id from `label` operand.
|
||||
inline void setLabel(const Label& label) noexcept { setId(label.id()); }
|
||||
inline void setLabel(const Label& label) noexcept { setLabelId(label.id()); }
|
||||
//! Sets the label id.
|
||||
inline void setLabelId(uint32_t labelId) noexcept { _labelId = labelId; }
|
||||
|
||||
//! Returns the id of the base label.
|
||||
inline uint32_t baseId() const noexcept { return _baseId; }
|
||||
//! Sets the base label id.
|
||||
inline void setBaseId(uint32_t baseId) noexcept { _baseId = baseId; }
|
||||
//! Returns the base label as `Label` operand.
|
||||
inline Label baseLabel() const noexcept { return Label(_baseId); }
|
||||
//! Sets the base label id from `label` operand.
|
||||
inline void setBaseLabel(const Label& baseLabel) noexcept { setBaseId(baseLabel.id()); }
|
||||
inline Label baseLabel() const noexcept { return Label(_baseLabelId); }
|
||||
//! Returns the id of the base label.
|
||||
inline uint32_t baseLabelId() const noexcept { return _baseLabelId; }
|
||||
|
||||
//! Sets the base label id from `label` operand.
|
||||
inline void setBaseLabel(const Label& baseLabel) noexcept { setBaseLabelId(baseLabel.id()); }
|
||||
//! Sets the base label id.
|
||||
inline void setBaseLabelId(uint32_t baseLabelId) noexcept { _baseLabelId = baseLabelId; }
|
||||
|
||||
//! Returns the size of the embedded label address.
|
||||
inline uint32_t dataSize() const noexcept { return _dataSize; }
|
||||
//! Sets the size of the embedded label address.
|
||||
inline void setDataSize(uint32_t dataSize) noexcept { _dataSize = dataSize; }
|
||||
|
||||
//! \}
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("Use labelId() instead")
|
||||
inline uint32_t id() const noexcept { return labelId(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use setLabelId() instead")
|
||||
inline void setId(uint32_t id) noexcept { setLabelId(id); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use baseLabelId() instead")
|
||||
inline uint32_t baseId() const noexcept { return baseLabelId(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use setBaseLabelId() instead")
|
||||
inline void setBaseId(uint32_t id) noexcept { setBaseLabelId(id); }
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@ -1229,7 +1336,9 @@ public:
|
||||
|
||||
//! Type of the sentinel (purery informative purpose).
|
||||
enum SentinelType : uint32_t {
|
||||
//! Type of the sentinel is not known.
|
||||
kSentinelUnknown = 0u,
|
||||
//! This is a sentinel used at the end of \ref FuncNode.
|
||||
kSentinelFuncEnd = 1u
|
||||
};
|
||||
|
||||
@ -1248,8 +1357,15 @@ public:
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
inline uint32_t sentinelType() const noexcept { return _sentinel._sentinelType; }
|
||||
inline void setSentinelType(uint32_t type) noexcept { _sentinel._sentinelType = uint8_t(type); }
|
||||
//! Returns the type of the sentinel.
|
||||
inline uint32_t sentinelType() const noexcept {
|
||||
return _sentinel._sentinelType;
|
||||
}
|
||||
|
||||
//! Sets the type of the sentinel.
|
||||
inline void setSentinelType(uint32_t type) noexcept {
|
||||
_sentinel._sentinelType = uint8_t(type);
|
||||
}
|
||||
|
||||
//! \}
|
||||
};
|
||||
@ -1280,7 +1396,9 @@ public:
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns \ref BaseBuilder associated with the pass.
|
||||
inline const BaseBuilder* cb() const noexcept { return _cb; }
|
||||
//! Returns the name of the pass.
|
||||
inline const char* name() const noexcept { return _name; }
|
||||
|
||||
//! \}
|
||||
@ -1292,7 +1410,7 @@ public:
|
||||
//!
|
||||
//! This is the only function that is called by the `BaseBuilder` to process
|
||||
//! the code. It passes `zone`, which will be reset after the `run()` finishes.
|
||||
virtual Error run(Zone* zone, Logger* logger) noexcept = 0;
|
||||
virtual Error run(Zone* zone, Logger* logger) = 0;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
10
3rdparty/asmjit/src/asmjit/core/callconv.cpp
vendored
10
3rdparty/asmjit/src/asmjit/core/callconv.cpp
vendored
@ -40,17 +40,17 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::CallConv - Init / Reset]
|
||||
// ============================================================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId) noexcept {
|
||||
ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId, const Environment& environment) noexcept {
|
||||
reset();
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (CallConv::isX86Family(ccId))
|
||||
return x86::CallConvInternal::init(*this, ccId);
|
||||
if (environment.isFamilyX86())
|
||||
return x86::CallConvInternal::init(*this, ccId, environment);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (CallConv::isArmFamily(ccId))
|
||||
return arm::CallConvInternal::init(*this, ccId);
|
||||
if (environment.isFamilyARM())
|
||||
return arm::CallConvInternal::init(*this, ccIdv, environment);
|
||||
#endif
|
||||
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
|
291
3rdparty/asmjit/src/asmjit/core/callconv.h
vendored
291
3rdparty/asmjit/src/asmjit/core/callconv.h
vendored
@ -30,7 +30,7 @@
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_func
|
||||
//! \addtogroup asmjit_function
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
@ -46,8 +46,8 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
struct CallConv {
|
||||
//! Calling convention id, see `Id`.
|
||||
uint8_t _id;
|
||||
//! Architecture id (see `ArchInfo::Id`).
|
||||
uint8_t _archId;
|
||||
//! Architecture identifier, see \ref Environment::Arch.
|
||||
uint8_t _arch;
|
||||
//! Register assignment strategy.
|
||||
uint8_t _strategy;
|
||||
//! Flags.
|
||||
@ -78,179 +78,117 @@ struct CallConv {
|
||||
uint32_t packed[(kMaxRegArgsPerGroup + 3) / 4];
|
||||
};
|
||||
|
||||
//! Passed registers' order, per group.
|
||||
//! Passed registers' order, per register group.
|
||||
RegOrder _passedOrder[BaseReg::kGroupVirt];
|
||||
|
||||
//! Calling convention id.
|
||||
//!
|
||||
//! Calling conventions can be divided into the following groups:
|
||||
//!
|
||||
//! - Universal - calling conventions are applicable to any target. They
|
||||
//! will be converted to a target dependent calling convention at runtime
|
||||
//! by \ref init(). The purpose of these conventions is to make using
|
||||
//! functions less target dependent and closer to how they are declared
|
||||
//! in C and C++.
|
||||
//!
|
||||
//! - Target specific - calling conventions that are used by a particular
|
||||
//! architecture and ABI. For example Windows 64-bit calling convention
|
||||
//! and AMD64 SystemV calling convention.
|
||||
enum Id : uint32_t {
|
||||
//! None or invalid (can't be used).
|
||||
kIdNone = 0,
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// [Universal]
|
||||
// [Universal Calling Conventions]
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// TODO: To make this possible we need to know target ARCH and ABI.
|
||||
|
||||
/*
|
||||
|
||||
// Universal calling conventions are applicable to any target and are
|
||||
// converted to target dependent conventions at runtime. The purpose of
|
||||
// these conventions is to make using functions less target dependent.
|
||||
|
||||
//! Standard function call or explicit `__cdecl` where it can be specified.
|
||||
//!
|
||||
//! This is a universal convention, which is used to initialize specific
|
||||
//! calling connventions based on architecture, platform, and its ABI.
|
||||
kIdCDecl = 1,
|
||||
|
||||
//! `__stdcall` on targets that support this calling convention.
|
||||
//!
|
||||
//! \note This calling convention is only supported on 32-bit X86. If used
|
||||
//! on environment that doesn't support this calling convention \ref kIdCDecl
|
||||
//! will be used instead.
|
||||
kIdStdCall = 2,
|
||||
|
||||
//! `__fastcall` on targets that support this calling convention.
|
||||
//!
|
||||
//! \note This calling convention is only supported on 32-bit X86. If used
|
||||
//! on environment that doesn't support this calling convention \ref kIdCDecl
|
||||
//! will be used instead.
|
||||
kIdFastCall = 3,
|
||||
|
||||
//! `__vectorcall` on targets that support this calling convention.
|
||||
//!
|
||||
//! \note This calling convention is only supported on 32-bit and 64-bit
|
||||
//! X86 architecture on Windows platform. If used on environment that doesn't
|
||||
//! support this calling convention \ref kIdCDecl will be used instead.
|
||||
kIdVectorCall = 4,
|
||||
|
||||
//! `__thiscall` on targets that support this calling convention.
|
||||
//!
|
||||
//! \note This calling convention is only supported on 32-bit X86 Windows
|
||||
//! platform. If used on environment that doesn't support this calling
|
||||
//! convention \ref kIdCDecl will be used instead.
|
||||
kIdThisCall = 5,
|
||||
|
||||
//! `__attribute__((regparm(1)))` convention (GCC and Clang).
|
||||
kIdRegParm1 = 6,
|
||||
//! `__attribute__((regparm(2)))` convention (GCC and Clang).
|
||||
kIdRegParm2 = 7,
|
||||
//! `__attribute__((regparm(3)))` convention (GCC and Clang).
|
||||
kIdRegParm3 = 8,
|
||||
|
||||
//! Soft-float calling convention (ARM).
|
||||
//!
|
||||
//! Floating point arguments are passed via general purpose registers.
|
||||
kIdSoftFloat = 9,
|
||||
|
||||
//! Hard-float calling convention (ARM).
|
||||
//!
|
||||
//! Floating point arguments are passed via SIMD registers.
|
||||
kIdHardFloat = 10,
|
||||
|
||||
//! AsmJit specific calling convention designed for calling functions
|
||||
//! inside a multimedia code that don't use many registers internally,
|
||||
//! but are long enough to be called and not inlined. These functions are
|
||||
//! usually used to calculate trigonometric functions, logarithms, etc...
|
||||
kIdLightCall2 = 10,
|
||||
kIdLightCall3 = 11,
|
||||
kIdLightCall4 = 12,
|
||||
*/
|
||||
kIdLightCall2 = 16,
|
||||
kIdLightCall3 = 17,
|
||||
kIdLightCall4 = 18,
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// [X86]
|
||||
// [ABI-Specific Calling Conventions]
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
//! X86 `__cdecl` calling convention (used by C runtime and libraries).
|
||||
kIdX86CDecl = 16,
|
||||
//! X86 `__stdcall` calling convention (used mostly by WinAPI).
|
||||
kIdX86StdCall = 17,
|
||||
//! X86 `__thiscall` calling convention (MSVC/Intel).
|
||||
kIdX86MsThisCall = 18,
|
||||
//! X86 `__fastcall` convention (MSVC/Intel).
|
||||
kIdX86MsFastCall = 19,
|
||||
//! X86 `__fastcall` convention (GCC and Clang).
|
||||
kIdX86GccFastCall = 20,
|
||||
//! X86 `regparm(1)` convention (GCC and Clang).
|
||||
kIdX86GccRegParm1 = 21,
|
||||
//! X86 `regparm(2)` convention (GCC and Clang).
|
||||
kIdX86GccRegParm2 = 22,
|
||||
//! X86 `regparm(3)` convention (GCC and Clang).
|
||||
kIdX86GccRegParm3 = 23,
|
||||
|
||||
kIdX86LightCall2 = 29,
|
||||
kIdX86LightCall3 = 30,
|
||||
kIdX86LightCall4 = 31,
|
||||
|
||||
//! X64 calling convention - WIN64-ABI.
|
||||
kIdX86Win64 = 32,
|
||||
//! X64 calling convention - SystemV / AMD64-ABI.
|
||||
kIdX86SysV64 = 33,
|
||||
|
||||
kIdX64LightCall2 = 45,
|
||||
kIdX64LightCall3 = 46,
|
||||
kIdX64LightCall4 = 47,
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// [ARM]
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
//! Legacy calling convention, floating point arguments are passed via GP registers.
|
||||
kIdArm32SoftFP = 48,
|
||||
//! Modern calling convention, uses VFP registers to pass floating point arguments.
|
||||
kIdArm32HardFP = 49,
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// [Internal]
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
//! \cond INTERNAL
|
||||
|
||||
_kIdX86Start = 16,
|
||||
_kIdX86End = 31,
|
||||
|
||||
_kIdX64Start = 32,
|
||||
_kIdX64End = 47,
|
||||
|
||||
_kIdArmStart = 48,
|
||||
_kIdArmEnd = 49,
|
||||
|
||||
//! \endcond
|
||||
kIdX64SystemV = 32,
|
||||
kIdX64Windows = 33,
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// [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 == 32
|
||||
|
||||
kIdHost = kIdX86CDecl,
|
||||
kIdHostCDecl = kIdX86CDecl,
|
||||
kIdHostStdCall = kIdX86StdCall,
|
||||
|
||||
# if defined(_MSC_VER)
|
||||
kIdHostFastCall = kIdX86MsFastCall,
|
||||
# elif defined(__GNUC__)
|
||||
kIdHostFastCall = kIdX86GccFastCall,
|
||||
# else
|
||||
kIdHostFastCall = kIdHost,
|
||||
# endif
|
||||
|
||||
kIdHostLightCall2 = kIdX86LightCall2,
|
||||
kIdHostLightCall3 = kIdX86LightCall3,
|
||||
kIdHostLightCall4 = kIdX86LightCall4
|
||||
|
||||
#elif ASMJIT_ARCH_X86 == 64
|
||||
|
||||
# if defined(_WIN32)
|
||||
kIdHost = kIdX86Win64,
|
||||
# else
|
||||
kIdHost = kIdX86SysV64,
|
||||
# endif
|
||||
|
||||
kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host.
|
||||
kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host.
|
||||
kIdHostFastCall = kIdHost, // Doesn't exist, redirected to host.
|
||||
|
||||
kIdHostLightCall2 = kIdX64LightCall2,
|
||||
kIdHostLightCall3 = kIdX64LightCall3,
|
||||
kIdHostLightCall4 = kIdX64LightCall4
|
||||
|
||||
#elif ASMJIT_ARCH_ARM == 32
|
||||
|
||||
# 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.
|
||||
|
||||
kIdHost =
|
||||
#if ASMJIT_ARCH_ARM == 32 && defined(__SOFTFP__)
|
||||
kIdSoftFloat
|
||||
#elif ASMJIT_ARCH_ARM == 32 && !defined(__SOFTFP__)
|
||||
kIdHardFloat
|
||||
#else
|
||||
|
||||
kIdHost = kIdNone,
|
||||
kIdHostCDecl = kIdHost,
|
||||
kIdHostStdCall = kIdHost,
|
||||
kIdHostFastCall = kIdHost
|
||||
|
||||
kIdCDecl
|
||||
#endif
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATE
|
||||
, kIdHostCDecl = kIdCDecl
|
||||
, kIdHostStdCall = kIdStdCall
|
||||
, kIdHostFastCall = kIdFastCall
|
||||
, kIdHostLightCall2 = kIdLightCall2
|
||||
, kIdHostLightCall3 = kIdLightCall3
|
||||
, kIdHostLightCall4 = kIdLightCall4
|
||||
#endif // !ASMJIT_NO_DEPRECATE
|
||||
};
|
||||
|
||||
//! Strategy used to assign registers to function arguments.
|
||||
@ -262,23 +200,50 @@ struct CallConv {
|
||||
//! as defined by WIN64 calling convention - it applies to 64-bit calling
|
||||
//! conventions only.
|
||||
enum Strategy : uint32_t {
|
||||
kStrategyDefault = 0, //!< Default register assignment strategy.
|
||||
kStrategyWin64 = 1 //!< WIN64 specific register assignment strategy.
|
||||
//! Default register assignment strategy.
|
||||
kStrategyDefault = 0,
|
||||
//! Windows 64-bit ABI register assignment strategy.
|
||||
kStrategyX64Windows = 1,
|
||||
//! Windows 64-bit __vectorcall register assignment strategy.
|
||||
kStrategyX64VectorCall = 2,
|
||||
|
||||
//! Number of assignment strategies.
|
||||
kStrategyCount = 3
|
||||
};
|
||||
|
||||
//! Calling convention flags.
|
||||
enum Flags : uint32_t {
|
||||
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).
|
||||
//! Callee is responsible for cleaning up the stack.
|
||||
kFlagCalleePopsStack = 0x01u,
|
||||
//! Pass vector arguments indirectly (as a pointer).
|
||||
kFlagIndirectVecArgs = 0x02u,
|
||||
//! Pass F32 and F64 arguments by VEC128 register.
|
||||
kFlagPassFloatsByVec = 0x04u,
|
||||
//! Pass MMX and vector arguments by stack if the function has variable arguments.
|
||||
kFlagPassVecByStackIfVA = 0x08u,
|
||||
//! MMX registers are passed and returned via GP registers.
|
||||
kFlagPassMmxByGp = 0x10u,
|
||||
//! MMX registers are passed and returned via XMM registers.
|
||||
kFlagPassMmxByXmm = 0x20u,
|
||||
//! Calling convention can be used with variable arguments.
|
||||
kFlagVarArgCompatible = 0x80u
|
||||
};
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error init(uint32_t ccId) noexcept;
|
||||
//! Initializes this calling convention to the given `ccId` based on the
|
||||
//! `environment`.
|
||||
//!
|
||||
//! See \ref Id and \ref Environment for more details.
|
||||
ASMJIT_API Error init(uint32_t ccId, const Environment& environment) noexcept;
|
||||
|
||||
//! Resets this CallConv struct into a defined state.
|
||||
//!
|
||||
//! It's recommended to reset the \ref CallConv struct in case you would
|
||||
//! like create a custom calling convention as it prevents from using an
|
||||
//! uninitialized data (CallConv doesn't have a constructor that would
|
||||
//! initialize it, it's just a struct).
|
||||
inline void reset() noexcept {
|
||||
memset(this, 0, sizeof(*this));
|
||||
memset(_passedOrder, 0xFF, sizeof(_passedOrder));
|
||||
@ -295,9 +260,9 @@ struct CallConv {
|
||||
inline void setId(uint32_t id) noexcept { _id = uint8_t(id); }
|
||||
|
||||
//! Returns the calling function architecture id.
|
||||
inline uint32_t archId() const noexcept { return _archId; }
|
||||
inline uint32_t arch() const noexcept { return _arch; }
|
||||
//! Sets the calling function architecture id.
|
||||
inline void setArchType(uint32_t archId) noexcept { _archId = uint8_t(archId); }
|
||||
inline void setArch(uint32_t arch) noexcept { _arch = uint8_t(arch); }
|
||||
|
||||
//! Returns the strategy used to assign registers to arguments, see `Strategy`.
|
||||
inline uint32_t strategy() const noexcept { return _strategy; }
|
||||
@ -337,11 +302,13 @@ struct CallConv {
|
||||
//! implement custom calling conventions that guarantee higher stack alignment.
|
||||
inline void setNaturalStackAlignment(uint32_t value) noexcept { _naturalStackAlignment = uint8_t(value); }
|
||||
|
||||
//! Returns the order of passed registers of the given `group`, see \ref BaseReg::RegGroup.
|
||||
inline const uint8_t* passedOrder(uint32_t group) const noexcept {
|
||||
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
|
||||
return _passedOrder[group].id;
|
||||
}
|
||||
|
||||
//! Returns the mask of passed registers of the given `group`, see \ref BaseReg::RegGroup.
|
||||
inline uint32_t passedRegs(uint32_t group) const noexcept {
|
||||
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
|
||||
return _passedRegs[group];
|
||||
@ -356,6 +323,7 @@ struct CallConv {
|
||||
_passedOrder[group].packed[3] = p3;
|
||||
}
|
||||
|
||||
//! Resets the order and mask of passed registers.
|
||||
inline void setPassedToNone(uint32_t group) noexcept {
|
||||
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
|
||||
|
||||
@ -363,6 +331,7 @@ struct CallConv {
|
||||
_passedRegs[group] = 0u;
|
||||
}
|
||||
|
||||
//! Sets the order and mask of passed registers.
|
||||
inline void setPassedOrder(uint32_t group, 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(group < BaseReg::kGroupVirt);
|
||||
|
||||
@ -383,25 +352,19 @@ struct CallConv {
|
||||
(a7 != 0xFF ? 1u << a7 : 0u) ;
|
||||
}
|
||||
|
||||
//! Returns preserved register mask of the given `group`, see \ref BaseReg::RegGroup.
|
||||
inline uint32_t preservedRegs(uint32_t group) const noexcept {
|
||||
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
|
||||
return _preservedRegs[group];
|
||||
}
|
||||
|
||||
//! Sets preserved register mask of the given `group`, see \ref BaseReg::RegGroup.
|
||||
inline void setPreservedRegs(uint32_t group, uint32_t regs) noexcept {
|
||||
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
|
||||
_preservedRegs[group] = regs;
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Static Functions
|
||||
//! \{
|
||||
|
||||
static inline bool isX86Family(uint32_t ccId) noexcept { return ccId >= _kIdX86Start && ccId <= _kIdX64End; }
|
||||
static inline bool isArmFamily(uint32_t ccId) noexcept { return ccId >= _kIdArmStart && ccId <= _kIdArmEnd; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
126
3rdparty/asmjit/src/asmjit/core/codebuffer.h
vendored
Normal file
126
3rdparty/asmjit/src/asmjit/core/codebuffer.h
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#ifndef ASMJIT_CORE_CODEBUFFER_H_INCLUDED
|
||||
#define ASMJIT_CORE_CODEBUFFER_H_INCLUDED
|
||||
|
||||
#include "../core/globals.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::CodeBuffer]
|
||||
// ============================================================================
|
||||
|
||||
//! Code or data buffer.
|
||||
struct CodeBuffer {
|
||||
//! The content of the buffer (data).
|
||||
uint8_t* _data;
|
||||
//! Number of bytes of `data` used.
|
||||
size_t _size;
|
||||
//! Buffer capacity (in bytes).
|
||||
size_t _capacity;
|
||||
//! Buffer flags.
|
||||
uint32_t _flags;
|
||||
|
||||
//! Code buffer flags.
|
||||
enum Flags : uint32_t {
|
||||
//! Buffer is external (not allocated by asmjit).
|
||||
kFlagIsExternal = 0x00000001u,
|
||||
//! Buffer is fixed (cannot be reallocated).
|
||||
kFlagIsFixed = 0x00000002u
|
||||
};
|
||||
|
||||
//! \name Overloaded Operators
|
||||
//! \{
|
||||
|
||||
//! Returns a referebce to the byte at the given `index`.
|
||||
inline uint8_t& operator[](size_t index) noexcept {
|
||||
ASMJIT_ASSERT(index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
//! \overload
|
||||
inline const uint8_t& operator[](size_t index) const noexcept {
|
||||
ASMJIT_ASSERT(index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns code buffer flags, see \ref Flags.
|
||||
inline uint32_t flags() const noexcept { return _flags; }
|
||||
//! Tests whether the code buffer has the given `flag` set.
|
||||
inline bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
|
||||
|
||||
//! Tests whether this code buffer has a fixed size.
|
||||
//!
|
||||
//! Fixed size means that the code buffer is fixed and cannot grow.
|
||||
inline bool isFixed() const noexcept { return hasFlag(kFlagIsFixed); }
|
||||
|
||||
//! Tests whether the data in this code buffer is external.
|
||||
//!
|
||||
//! External data can only be provided by users, it's never used by AsmJit.
|
||||
inline bool isExternal() const noexcept { return hasFlag(kFlagIsExternal); }
|
||||
|
||||
//! Tests whether the data in this code buffer is allocated (non-null).
|
||||
inline bool isAllocated() const noexcept { return _data != nullptr; }
|
||||
|
||||
//! Tests whether the code buffer is empty.
|
||||
inline bool empty() const noexcept { return !_size; }
|
||||
|
||||
//! Returns the size of the data.
|
||||
inline size_t size() const noexcept { return _size; }
|
||||
//! Returns the capacity of the data.
|
||||
inline size_t capacity() const noexcept { return _capacity; }
|
||||
|
||||
//! Returns the pointer to the data the buffer references.
|
||||
inline uint8_t* data() noexcept { return _data; }
|
||||
//! \overload
|
||||
inline const uint8_t* data() const noexcept { return _data; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Iterators
|
||||
//! \{
|
||||
|
||||
inline uint8_t* begin() noexcept { return _data; }
|
||||
inline const uint8_t* begin() const noexcept { return _data; }
|
||||
|
||||
inline uint8_t* end() noexcept { return _data + _size; }
|
||||
inline const uint8_t* end() const noexcept { return _data + _size; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_CODEBUFFER_H_INCLUDED
|
||||
|
@ -25,12 +25,13 @@
|
||||
#define ASMJIT_CORE_CODEBUFFERWRITER_P_H_INCLUDED
|
||||
|
||||
#include "../core/assembler.h"
|
||||
#include "../core/codebuffer.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_core
|
||||
//! \addtogroup asmjit_assembler
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
|
127
3rdparty/asmjit/src/asmjit/core/codeholder.cpp
vendored
127
3rdparty/asmjit/src/asmjit/core/codeholder.cpp
vendored
@ -23,7 +23,7 @@
|
||||
|
||||
#include "../core/api-build_p.h"
|
||||
#include "../core/assembler.h"
|
||||
#include "../core/logging.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
@ -77,13 +77,6 @@ public:
|
||||
LabelLink* _link;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ErrorHandler]
|
||||
// ============================================================================
|
||||
|
||||
ErrorHandler::ErrorHandler() noexcept {}
|
||||
ErrorHandler::~ErrorHandler() noexcept {}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::CodeHolder - Utilities]
|
||||
// ============================================================================
|
||||
@ -97,8 +90,8 @@ static void CodeHolder_resetInternal(CodeHolder* self, uint32_t resetPolicy) noe
|
||||
self->detach(emitters[--i]);
|
||||
|
||||
// Reset everything into its construction state.
|
||||
self->_codeInfo.reset();
|
||||
self->_emitterOptions = 0;
|
||||
self->_environment.reset();
|
||||
self->_baseAddress = Globals::kNoBaseAddress;
|
||||
self->_logger = nullptr;
|
||||
self->_errorHandler = nullptr;
|
||||
|
||||
@ -129,20 +122,10 @@ static void CodeHolder_resetInternal(CodeHolder* self, uint32_t resetPolicy) noe
|
||||
self->_zone.reset(resetPolicy);
|
||||
}
|
||||
|
||||
static void CodeHolder_modifyEmitterOptions(CodeHolder* self, uint32_t clear, uint32_t add) noexcept {
|
||||
uint32_t oldOpt = self->_emitterOptions;
|
||||
uint32_t newOpt = (oldOpt & ~clear) | add;
|
||||
|
||||
if (oldOpt == newOpt)
|
||||
return;
|
||||
|
||||
// Modify emitter options of `CodeHolder` itself.
|
||||
self->_emitterOptions = newOpt;
|
||||
|
||||
// Modify emitter options of all attached emitters.
|
||||
static void CodeHolder_onSettingsUpdated(CodeHolder* self) noexcept {
|
||||
// Notify all attached emitters about a settings update.
|
||||
for (BaseEmitter* emitter : self->emitters()) {
|
||||
emitter->_emitterOptions = (emitter->_emitterOptions & ~clear) | add;
|
||||
emitter->onUpdateGlobalInstOptions();
|
||||
emitter->onSettingsUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,8 +134,8 @@ static void CodeHolder_modifyEmitterOptions(CodeHolder* self, uint32_t clear, ui
|
||||
// ============================================================================
|
||||
|
||||
CodeHolder::CodeHolder() noexcept
|
||||
: _codeInfo(),
|
||||
_emitterOptions(0),
|
||||
: _environment(),
|
||||
_baseAddress(Globals::kNoBaseAddress),
|
||||
_logger(nullptr),
|
||||
_errorHandler(nullptr),
|
||||
_zone(16384 - Zone::kBlockOverhead),
|
||||
@ -177,7 +160,7 @@ inline void CodeHolder_setSectionDefaultName(
|
||||
section->_name.u32[1] = Support::bytepack32_4x8(uint8_t(c4), uint8_t(c5), uint8_t(c6), uint8_t(c7));
|
||||
}
|
||||
|
||||
Error CodeHolder::init(const CodeInfo& info) noexcept {
|
||||
Error CodeHolder::init(const Environment& environment, uint64_t baseAddress) noexcept {
|
||||
// Cannot reinitialize if it's locked or there is one or more emitter attached.
|
||||
if (isInitialized())
|
||||
return DebugUtils::errored(kErrorAlreadyInitialized);
|
||||
@ -185,7 +168,7 @@ Error CodeHolder::init(const CodeInfo& info) noexcept {
|
||||
// If we are just initializing there should be no emitters attached.
|
||||
ASMJIT_ASSERT(_emitters.empty());
|
||||
|
||||
// Create the default section and insert it to the `_sections` array.
|
||||
// Create a default section and insert it to the `_sections` array.
|
||||
Error err = _sections.willGrow(&_allocator);
|
||||
if (err == kErrorOk) {
|
||||
Section* section = _allocator.allocZeroedT<Section>();
|
||||
@ -204,7 +187,8 @@ Error CodeHolder::init(const CodeInfo& info) noexcept {
|
||||
return err;
|
||||
}
|
||||
else {
|
||||
_codeInfo = info;
|
||||
_environment = environment;
|
||||
_baseAddress = baseAddress;
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
@ -271,33 +255,27 @@ Error CodeHolder::detach(BaseEmitter* emitter) noexcept {
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::CodeHolder - Emitter Options]
|
||||
// ============================================================================
|
||||
|
||||
static constexpr uint32_t kEmitterOptionsFilter = ~uint32_t(BaseEmitter::kOptionLoggingEnabled);
|
||||
|
||||
void CodeHolder::addEmitterOptions(uint32_t options) noexcept {
|
||||
CodeHolder_modifyEmitterOptions(this, 0, options & kEmitterOptionsFilter);
|
||||
}
|
||||
|
||||
void CodeHolder::clearEmitterOptions(uint32_t options) noexcept {
|
||||
CodeHolder_modifyEmitterOptions(this, options & kEmitterOptionsFilter, 0);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::CodeHolder - Logging & Error Handling]
|
||||
// [asmjit::CodeHolder - Logging]
|
||||
// ============================================================================
|
||||
|
||||
void CodeHolder::setLogger(Logger* logger) noexcept {
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
_logger = logger;
|
||||
uint32_t option = !logger ? uint32_t(0) : uint32_t(BaseEmitter::kOptionLoggingEnabled);
|
||||
CodeHolder_modifyEmitterOptions(this, BaseEmitter::kOptionLoggingEnabled, option);
|
||||
CodeHolder_onSettingsUpdated(this);
|
||||
#else
|
||||
DebugUtils::unused(logger);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::CodeHolder - Error Handling]
|
||||
// ============================================================================
|
||||
|
||||
void CodeHolder::setErrorHandler(ErrorHandler* errorHandler) noexcept {
|
||||
_errorHandler = errorHandler;
|
||||
CodeHolder_onSettingsUpdated(this);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::CodeHolder - Code Buffer]
|
||||
// ============================================================================
|
||||
@ -440,7 +418,7 @@ Section* CodeHolder::ensureAddressTableSection() noexcept {
|
||||
if (_addressTableSection)
|
||||
return _addressTableSection;
|
||||
|
||||
newSection(&_addressTableSection, CodeHolder_addrTabName, sizeof(CodeHolder_addrTabName) - 1, 0, _codeInfo.gpSize());
|
||||
newSection(&_addressTableSection, CodeHolder_addrTabName, sizeof(CodeHolder_addrTabName) - 1, 0, _environment.registerSize());
|
||||
return _addressTableSection;
|
||||
}
|
||||
|
||||
@ -458,7 +436,7 @@ Error CodeHolder::addAddressToAddressTable(uint64_t address) noexcept {
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
|
||||
_addressTableEntries.insert(entry);
|
||||
section->_virtualSize += _codeInfo.gpSize();
|
||||
section->_virtualSize += _environment.registerSize();
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
@ -470,20 +448,24 @@ Error CodeHolder::addAddressToAddressTable(uint64_t address) noexcept {
|
||||
//! Only used to lookup a label from `_namedLabels`.
|
||||
class LabelByName {
|
||||
public:
|
||||
inline LabelByName(const char* key, size_t keySize, uint32_t hashCode) noexcept
|
||||
inline LabelByName(const char* key, size_t keySize, uint32_t hashCode, uint32_t parentId) noexcept
|
||||
: _key(key),
|
||||
_keySize(uint32_t(keySize)),
|
||||
_hashCode(hashCode) {}
|
||||
_hashCode(hashCode),
|
||||
_parentId(parentId) {}
|
||||
|
||||
inline uint32_t hashCode() const noexcept { return _hashCode; }
|
||||
|
||||
inline bool matches(const LabelEntry* entry) const noexcept {
|
||||
return entry->nameSize() == _keySize && ::memcmp(entry->name(), _key, _keySize) == 0;
|
||||
return entry->nameSize() == _keySize &&
|
||||
entry->parentId() == _parentId &&
|
||||
::memcmp(entry->name(), _key, _keySize) == 0;
|
||||
}
|
||||
|
||||
const char* _key;
|
||||
uint32_t _keySize;
|
||||
uint32_t _hashCode;
|
||||
uint32_t _parentId;
|
||||
};
|
||||
|
||||
// Returns a hash of `name` and fixes `nameSize` if it's `SIZE_MAX`.
|
||||
@ -539,7 +521,7 @@ LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t o
|
||||
}
|
||||
|
||||
Error CodeHolder::newLabelEntry(LabelEntry** entryOut) noexcept {
|
||||
*entryOut = 0;
|
||||
*entryOut = nullptr;
|
||||
|
||||
uint32_t labelId = _labelEntries.size();
|
||||
if (ASMJIT_UNLIKELY(labelId == Globals::kInvalidId))
|
||||
@ -561,7 +543,7 @@ Error CodeHolder::newLabelEntry(LabelEntry** entryOut) noexcept {
|
||||
}
|
||||
|
||||
Error CodeHolder::newNamedLabelEntry(LabelEntry** entryOut, const char* name, size_t nameSize, uint32_t type, uint32_t parentId) noexcept {
|
||||
*entryOut = 0;
|
||||
*entryOut = nullptr;
|
||||
uint32_t hashCode = CodeHolder_hashNameAndGetSize(name, nameSize);
|
||||
|
||||
if (ASMJIT_UNLIKELY(nameSize == 0))
|
||||
@ -580,7 +562,7 @@ Error CodeHolder::newNamedLabelEntry(LabelEntry** entryOut, const char* name, si
|
||||
|
||||
case Label::kTypeGlobal:
|
||||
if (ASMJIT_UNLIKELY(parentId != Globals::kInvalidId))
|
||||
return DebugUtils::errored(kErrorNonLocalLabelCantHaveParent);
|
||||
return DebugUtils::errored(kErrorNonLocalLabelCannotHaveParent);
|
||||
|
||||
break;
|
||||
|
||||
@ -591,7 +573,7 @@ Error CodeHolder::newNamedLabelEntry(LabelEntry** entryOut, const char* name, si
|
||||
// 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, nameSize, hashCode));
|
||||
LabelEntry* le = _namedLabels.get(LabelByName(name, nameSize, hashCode, parentId));
|
||||
if (ASMJIT_UNLIKELY(le))
|
||||
return DebugUtils::errored(kErrorLabelAlreadyDefined);
|
||||
|
||||
@ -610,7 +592,7 @@ Error CodeHolder::newNamedLabelEntry(LabelEntry** entryOut, const char* name, si
|
||||
le->_hashCode = hashCode;
|
||||
le->_setId(labelId);
|
||||
le->_type = uint8_t(type);
|
||||
le->_parentId = Globals::kInvalidId;
|
||||
le->_parentId = parentId;
|
||||
le->_offset = 0;
|
||||
ASMJIT_PROPAGATE(le->_name.setData(&_zone, name, nameSize));
|
||||
|
||||
@ -622,13 +604,14 @@ Error CodeHolder::newNamedLabelEntry(LabelEntry** entryOut, const char* name, si
|
||||
}
|
||||
|
||||
uint32_t CodeHolder::labelIdByName(const char* name, size_t nameSize, uint32_t parentId) noexcept {
|
||||
// TODO: Finalize - parent id is not used here?
|
||||
DebugUtils::unused(parentId);
|
||||
|
||||
uint32_t hashCode = CodeHolder_hashNameAndGetSize(name, nameSize);
|
||||
if (ASMJIT_UNLIKELY(!nameSize)) return 0;
|
||||
if (ASMJIT_UNLIKELY(!nameSize))
|
||||
return 0;
|
||||
|
||||
LabelEntry* le = _namedLabels.get(LabelByName(name, nameSize, hashCode));
|
||||
if (parentId != Globals::kInvalidId)
|
||||
hashCode ^= parentId;
|
||||
|
||||
LabelEntry* le = _namedLabels.get(LabelByName(name, nameSize, hashCode, parentId));
|
||||
return le ? le->id() : uint32_t(Globals::kInvalidId);
|
||||
}
|
||||
|
||||
@ -906,7 +889,6 @@ size_t CodeHolder::codeSize() const noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Not nice, maybe changing `codeSize()` to return `uint64_t` instead?
|
||||
if ((sizeof(uint64_t) > sizeof(size_t) && offset > SIZE_MAX) || of)
|
||||
return SIZE_MAX;
|
||||
|
||||
@ -918,8 +900,8 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept {
|
||||
if (ASMJIT_UNLIKELY(baseAddress == Globals::kNoBaseAddress))
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
|
||||
_codeInfo.setBaseAddress(baseAddress);
|
||||
uint32_t gpSize = _codeInfo.gpSize();
|
||||
_baseAddress = baseAddress;
|
||||
uint32_t addressSize = _environment.registerSize();
|
||||
|
||||
Section* addressTableSection = _addressTableSection;
|
||||
uint32_t addressTableEntryCount = 0;
|
||||
@ -980,7 +962,7 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept {
|
||||
|
||||
case RelocEntry::kTypeAbsToRel: {
|
||||
value -= baseAddress + sectionOffset + sourceOffset + regionSize;
|
||||
if (gpSize > 4 && !Support::isInt32(int64_t(value)))
|
||||
if (addressSize > 4 && !Support::isInt32(int64_t(value)))
|
||||
return DebugUtils::errored(kErrorRelocOffsetOutOfRange);
|
||||
break;
|
||||
}
|
||||
@ -1003,7 +985,7 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept {
|
||||
if (!atEntry->hasAssignedSlot())
|
||||
atEntry->_slot = addressTableEntryCount++;
|
||||
|
||||
size_t atEntryIndex = size_t(atEntry->slot()) * gpSize;
|
||||
size_t atEntryIndex = size_t(atEntry->slot()) * addressSize;
|
||||
uint64_t addrSrc = sectionOffset + sourceOffset + regionSize;
|
||||
uint64_t addrDst = addressTableSection->offset() + uint64_t(atEntryIndex);
|
||||
|
||||
@ -1064,7 +1046,7 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept {
|
||||
|
||||
// Fixup the virtual size of the address table if it's the last section.
|
||||
if (_sections.last() == addressTableSection) {
|
||||
size_t addressTableSize = addressTableEntryCount * gpSize;
|
||||
size_t addressTableSize = addressTableEntryCount * addressSize;
|
||||
addressTableSection->_buffer._size = addressTableSize;
|
||||
addressTableSection->_virtualSize = addressTableSize;
|
||||
}
|
||||
@ -1072,7 +1054,7 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept {
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error CodeHolder::copySectionData(void* dst, size_t dstSize, uint32_t sectionId, uint32_t options) noexcept {
|
||||
Error CodeHolder::copySectionData(void* dst, size_t dstSize, uint32_t sectionId, uint32_t copyOptions) noexcept {
|
||||
if (ASMJIT_UNLIKELY(!isSectionValid(sectionId)))
|
||||
return DebugUtils::errored(kErrorInvalidSection);
|
||||
|
||||
@ -1084,7 +1066,7 @@ Error CodeHolder::copySectionData(void* dst, size_t dstSize, uint32_t sectionId,
|
||||
|
||||
memcpy(dst, section->data(), bufferSize);
|
||||
|
||||
if (bufferSize < dstSize && (options & kCopyWithPadding)) {
|
||||
if (bufferSize < dstSize && (copyOptions & kCopyPadSectionBuffer)) {
|
||||
size_t paddingSize = dstSize - bufferSize;
|
||||
memset(static_cast<uint8_t*>(dst) + bufferSize, 0, paddingSize);
|
||||
}
|
||||
@ -1092,7 +1074,7 @@ Error CodeHolder::copySectionData(void* dst, size_t dstSize, uint32_t sectionId,
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error CodeHolder::copyFlattenedData(void* dst, size_t dstSize, uint32_t options) noexcept {
|
||||
Error CodeHolder::copyFlattenedData(void* dst, size_t dstSize, uint32_t copyOptions) noexcept {
|
||||
size_t end = 0;
|
||||
for (Section* section : _sections) {
|
||||
if (section->offset() > dstSize)
|
||||
@ -1108,7 +1090,7 @@ Error CodeHolder::copyFlattenedData(void* dst, size_t dstSize, uint32_t options)
|
||||
size_t paddingSize = 0;
|
||||
memcpy(dstTarget, section->data(), bufferSize);
|
||||
|
||||
if ((options & kCopyWithPadding) && bufferSize < section->virtualSize()) {
|
||||
if ((copyOptions & kCopyPadSectionBuffer) && bufferSize < section->virtualSize()) {
|
||||
paddingSize = Support::min<size_t>(dstSize - offset, size_t(section->virtualSize())) - bufferSize;
|
||||
memset(dstTarget + bufferSize, 0, paddingSize);
|
||||
}
|
||||
@ -1116,8 +1098,9 @@ Error CodeHolder::copyFlattenedData(void* dst, size_t dstSize, uint32_t options)
|
||||
end = Support::max(end, offset + bufferSize + paddingSize);
|
||||
}
|
||||
|
||||
// TODO: `end` is not used atm, we need an option to also pad anything beyond
|
||||
// the code in case that the destination was much larger (for example page-size).
|
||||
if (end < dstSize && (copyOptions & kCopyPadTargetBuffer)) {
|
||||
memset(static_cast<uint8_t*>(dst) + end, 0, dstSize - end);
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
385
3rdparty/asmjit/src/asmjit/core/codeholder.h
vendored
385
3rdparty/asmjit/src/asmjit/core/codeholder.h
vendored
@ -25,7 +25,9 @@
|
||||
#define ASMJIT_CORE_CODEHOLDER_H_INCLUDED
|
||||
|
||||
#include "../core/arch.h"
|
||||
#include "../core/codebuffer.h"
|
||||
#include "../core/datatypes.h"
|
||||
#include "../core/errorhandler.h"
|
||||
#include "../core/operand.h"
|
||||
#include "../core/string.h"
|
||||
#include "../core/support.h"
|
||||
@ -56,126 +58,14 @@ class Logger;
|
||||
|
||||
//! Align mode.
|
||||
enum AlignMode : uint32_t {
|
||||
kAlignCode = 0, //!< Align executable code.
|
||||
kAlignData = 1, //!< Align non-executable code.
|
||||
kAlignZero = 2, //!< Align by a sequence of zeros.
|
||||
kAlignCount = 3 //!< 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 `BaseEmitter`.
|
||||
//!
|
||||
//! Override `ErrorHandler::handleError()` to implement your own error handler.
|
||||
class ASMJIT_VIRTAPI ErrorHandler {
|
||||
public:
|
||||
ASMJIT_BASE_CLASS(ErrorHandler)
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Construction / Destruction]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Creates a new `ErrorHandler` instance.
|
||||
ASMJIT_API ErrorHandler() noexcept;
|
||||
//! Destroys the `ErrorHandler` instance.
|
||||
ASMJIT_API virtual ~ErrorHandler() noexcept;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Handle Error]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Error handler (must be reimplemented).
|
||||
//!
|
||||
//! 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. User-based error handling without throwing exception or using C's
|
||||
//! `longjmp()`. This is for users that don't use exceptions and want
|
||||
//! customized error handling.
|
||||
//!
|
||||
//! 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.
|
||||
//!
|
||||
//! 3. Using plain old C's `setjmp()` and `longjmp()`. Asmjit always puts
|
||||
//! `BaseEmitter` to a consistent state before calling `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, however,
|
||||
//! never jump outside of `CodeHolder` and `BaseEmitter` scope as you
|
||||
//! would leak memory.
|
||||
virtual void handleError(Error err, const char* message, BaseEmitter* origin) = 0;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::CodeBuffer]
|
||||
// ============================================================================
|
||||
|
||||
//! Code or data buffer.
|
||||
struct CodeBuffer {
|
||||
//! The content of the buffer (data).
|
||||
uint8_t* _data;
|
||||
//! Number of bytes of `data` used.
|
||||
size_t _size;
|
||||
//! Buffer capacity (in bytes).
|
||||
size_t _capacity;
|
||||
//! Buffer flags.
|
||||
uint32_t _flags;
|
||||
|
||||
enum Flags : uint32_t {
|
||||
//! Buffer is external (not allocated by asmjit).
|
||||
kFlagIsExternal = 0x00000001u,
|
||||
//! Buffer is fixed (cannot be reallocated).
|
||||
kFlagIsFixed = 0x00000002u
|
||||
};
|
||||
|
||||
//! \name Overloaded Operators
|
||||
//! \{
|
||||
|
||||
inline uint8_t& operator[](size_t index) noexcept {
|
||||
ASMJIT_ASSERT(index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
inline const uint8_t& operator[](size_t index) const noexcept {
|
||||
ASMJIT_ASSERT(index < _size);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
inline uint32_t flags() const noexcept { return _flags; }
|
||||
inline bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
|
||||
|
||||
inline bool isAllocated() const noexcept { return _data != nullptr; }
|
||||
inline bool isFixed() const noexcept { return hasFlag(kFlagIsFixed); }
|
||||
inline bool isExternal() const noexcept { return hasFlag(kFlagIsExternal); }
|
||||
|
||||
inline uint8_t* data() noexcept { return _data; }
|
||||
inline const uint8_t* data() const noexcept { return _data; }
|
||||
|
||||
inline bool empty() const noexcept { return !_size; }
|
||||
inline size_t size() const noexcept { return _size; }
|
||||
inline size_t capacity() const noexcept { return _capacity; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Iterators
|
||||
//! \{
|
||||
|
||||
inline uint8_t* begin() noexcept { return _data; }
|
||||
inline const uint8_t* begin() const noexcept { return _data; }
|
||||
|
||||
inline uint8_t* end() noexcept { return _data + _size; }
|
||||
inline const uint8_t* end() const noexcept { return _data + _size; }
|
||||
|
||||
//! \}
|
||||
//! Align executable code.
|
||||
kAlignCode = 0,
|
||||
//! Align non-executable code.
|
||||
kAlignData = 1,
|
||||
//! Align by a sequence of zeros.
|
||||
kAlignZero = 2,
|
||||
//! Count of alignment modes.
|
||||
kAlignCount = 3
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@ -204,31 +94,48 @@ public:
|
||||
|
||||
//! Section flags.
|
||||
enum Flags : uint32_t {
|
||||
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 and can be deleted by `Target`.
|
||||
//! Executable (.text sections).
|
||||
kFlagExec = 0x00000001u,
|
||||
//! Read-only (.text and .data sections).
|
||||
kFlagConst = 0x00000002u,
|
||||
//! Zero initialized by the loader (BSS).
|
||||
kFlagZero = 0x00000004u,
|
||||
//! Info / comment flag.
|
||||
kFlagInfo = 0x00000008u,
|
||||
//! Section created implicitly and can be deleted by \ref Target.
|
||||
kFlagImplicit = 0x80000000u
|
||||
};
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the section id.
|
||||
inline uint32_t id() const noexcept { return _id; }
|
||||
//! Returns the section name, as a null terminated string.
|
||||
inline const char* name() const noexcept { return _name.str; }
|
||||
|
||||
//! Returns the section data.
|
||||
inline uint8_t* data() noexcept { return _buffer.data(); }
|
||||
//! \overload
|
||||
inline const uint8_t* data() const noexcept { return _buffer.data(); }
|
||||
|
||||
//! Returns the section flags, see \ref Flags.
|
||||
inline uint32_t flags() const noexcept { return _flags; }
|
||||
//! Tests whether the section has the given `flag`.
|
||||
inline bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
|
||||
//! Adds `flags` to the section flags.
|
||||
inline void addFlags(uint32_t flags) noexcept { _flags |= flags; }
|
||||
//! Removes `flags` from the section flags.
|
||||
inline void clearFlags(uint32_t flags) noexcept { _flags &= ~flags; }
|
||||
|
||||
//! Returns the minimum section alignment
|
||||
inline uint32_t alignment() const noexcept { return _alignment; }
|
||||
//! Sets the minimum section alignment
|
||||
inline void setAlignment(uint32_t alignment) noexcept { _alignment = alignment; }
|
||||
|
||||
//! Returns the section offset, relative to base.
|
||||
inline uint64_t offset() const noexcept { return _offset; }
|
||||
//! Set the section offset.
|
||||
inline void setOffset(uint64_t offset) noexcept { _offset = offset; }
|
||||
|
||||
//! Returns the virtual size of the section.
|
||||
@ -277,46 +184,73 @@ struct LabelLink {
|
||||
// [asmjit::Expression]
|
||||
// ============================================================================
|
||||
|
||||
//! Expression node that can reference constants, labels, and another expressions.
|
||||
struct Expression {
|
||||
//! Operation type.
|
||||
enum OpType : uint8_t {
|
||||
//! Addition.
|
||||
kOpAdd = 0,
|
||||
//! Subtraction.
|
||||
kOpSub = 1,
|
||||
//! Multiplication
|
||||
kOpMul = 2,
|
||||
//! Logical left shift.
|
||||
kOpSll = 3,
|
||||
//! Logical right shift.
|
||||
kOpSrl = 4,
|
||||
//! Arithmetic right shift.
|
||||
kOpSra = 5
|
||||
};
|
||||
|
||||
//! Type of \ref Value.
|
||||
enum ValueType : uint8_t {
|
||||
//! No value or invalid.
|
||||
kValueNone = 0,
|
||||
//! Value is 64-bit unsigned integer (constant).
|
||||
kValueConstant = 1,
|
||||
//! Value is \ref LabelEntry, which references a \ref Label.
|
||||
kValueLabel = 2,
|
||||
//! Value is \ref Expression
|
||||
kValueExpression = 3
|
||||
};
|
||||
|
||||
//! Expression value.
|
||||
union Value {
|
||||
//! Constant.
|
||||
uint64_t constant;
|
||||
//! Pointer to another expression.
|
||||
Expression* expression;
|
||||
//! Poitner to \ref LabelEntry.
|
||||
LabelEntry* label;
|
||||
};
|
||||
|
||||
//! Operation type.
|
||||
uint8_t opType;
|
||||
//! Value types of \ref value.
|
||||
uint8_t valueType[2];
|
||||
//! Reserved for future use, should be initialized to zero.
|
||||
uint8_t reserved[5];
|
||||
//! Expression left and right values.
|
||||
Value value[2];
|
||||
|
||||
//! Resets the whole expression.
|
||||
//!
|
||||
//! Changes both values to \ref kValueNone.
|
||||
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
|
||||
|
||||
//! Sets the value type at `index` to \ref kValueConstant and its content to `constant`.
|
||||
inline void setValueAsConstant(size_t index, uint64_t constant) noexcept {
|
||||
valueType[index] = kValueConstant;
|
||||
value[index].constant = constant;
|
||||
}
|
||||
|
||||
inline void setValueAsLabel(size_t index, LabelEntry* label) noexcept {
|
||||
//! Sets the value type at `index` to \ref kValueLabel and its content to `labelEntry`.
|
||||
inline void setValueAsLabel(size_t index, LabelEntry* labelEntry) noexcept {
|
||||
valueType[index] = kValueLabel;
|
||||
value[index].label = label;
|
||||
value[index].label = labelEntry;
|
||||
}
|
||||
|
||||
//! Sets the value type at `index` to \ref kValueExpression and its content to `expression`.
|
||||
inline void setValueAsExpression(size_t index, Expression* expression) noexcept {
|
||||
valueType[index] = kValueLabel;
|
||||
value[index].expression = expression;
|
||||
@ -510,11 +444,14 @@ struct RelocEntry {
|
||||
// [asmjit::AddressTableEntry]
|
||||
// ============================================================================
|
||||
|
||||
//! Entry in an address table.
|
||||
class AddressTableEntry : public ZoneTreeNodeT<AddressTableEntry> {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(AddressTableEntry)
|
||||
|
||||
//! Address.
|
||||
uint64_t _address;
|
||||
//! Slot.
|
||||
uint32_t _slot;
|
||||
|
||||
//! \name Construction & Destruction
|
||||
@ -547,22 +484,24 @@ public:
|
||||
// [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 `BaseAssembler` and/or `BaseBuilder`.
|
||||
//! Contains basic information about the target architecture and its options.
|
||||
//!
|
||||
//! \note `CodeHolder` has ability to attach an `ErrorHandler`, however, the
|
||||
//! error handler is not triggered by `CodeHolder` itself, it's only used by
|
||||
//! emitters attached to `CodeHolder`.
|
||||
//! In addition, it holds assembled code & data (including sections, labels, and
|
||||
//! relocation information). `CodeHolder` can store both binary and intermediate
|
||||
//! representation of assembly, which can be generated by \ref BaseAssembler,
|
||||
//! \ref BaseBuilder, and \ref BaseCompiler
|
||||
//!
|
||||
//! \note `CodeHolder` has an ability to attach an \ref ErrorHandler, however,
|
||||
//! the error handler is not triggered by `CodeHolder` itself, it's instead
|
||||
//! propagated to all emitters that attach to it.
|
||||
class CodeHolder {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(CodeHolder)
|
||||
|
||||
//! Basic information about the code (architecture and other info).
|
||||
CodeInfo _codeInfo;
|
||||
//! Emitter options, propagated to all emitters when changed.
|
||||
uint32_t _emitterOptions;
|
||||
//! Environment information.
|
||||
Environment _environment;
|
||||
//! Base address or \ref Globals::kNoBaseAddress.
|
||||
uint64_t _baseAddress;
|
||||
|
||||
//! Attached `Logger`, used by all consumers.
|
||||
Logger* _logger;
|
||||
@ -574,7 +513,7 @@ public:
|
||||
//! Zone allocator, used to manage internal containers.
|
||||
ZoneAllocator _allocator;
|
||||
|
||||
//! Attached code emitters.
|
||||
//! Attached emitters.
|
||||
ZoneVector<BaseEmitter*> _emitters;
|
||||
//! Section entries.
|
||||
ZoneVector<Section*> _sections;
|
||||
@ -592,6 +531,25 @@ public:
|
||||
//! Address table entries.
|
||||
ZoneTree<AddressTableEntry> _addressTableEntries;
|
||||
|
||||
//! Options that can be used with \ref copySectionData() and \ref copyFlattenedData().
|
||||
enum CopyOptions : uint32_t {
|
||||
//! If virtual size of a section is greater than the size of its \ref CodeBuffer
|
||||
//! then all bytes between the buffer size and virtual size will be zeroed.
|
||||
//! If this option is not set then those bytes would be left as is, which
|
||||
//! means that if the user didn't initialize them they would have a previous
|
||||
//! content, which may be unwanted.
|
||||
kCopyPadSectionBuffer = 0x00000001u,
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
kCopyWithPadding = kCopyPadSectionBuffer,
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
|
||||
//! Zeroes the target buffer if the flattened data is less than the destination
|
||||
//! size. This option works only with \ref copyFlattenedData() as it processes
|
||||
//! multiple sections. It is ignored by \ref copySectionData().
|
||||
kCopyPadTargetBuffer = 0x00000002u
|
||||
};
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
@ -600,10 +558,13 @@ public:
|
||||
//! Destroys the CodeHolder.
|
||||
ASMJIT_API ~CodeHolder() noexcept;
|
||||
|
||||
inline bool isInitialized() const noexcept { return _codeInfo.isInitialized(); }
|
||||
//! Tests whether the `CodeHolder` has been initialized.
|
||||
//!
|
||||
//! Emitters can be only attached to initialized `CodeHolder` instances.
|
||||
inline bool isInitialized() const noexcept { return _environment.isInitialized(); }
|
||||
|
||||
//! Initializes CodeHolder to hold code described by `codeInfo`.
|
||||
ASMJIT_API Error init(const CodeInfo& info) noexcept;
|
||||
//! Initializes CodeHolder to hold code described by code `info`.
|
||||
ASMJIT_API Error init(const Environment& environment, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept;
|
||||
//! Detaches all code-generators attached and resets the `CodeHolder`.
|
||||
ASMJIT_API void reset(uint32_t resetPolicy = Globals::kResetSoft) noexcept;
|
||||
|
||||
@ -622,65 +583,62 @@ public:
|
||||
//! \name Allocators
|
||||
//! \{
|
||||
|
||||
//! Returns the allocator that the `CodeHolder` uses.
|
||||
//!
|
||||
//! \note This should be only used for AsmJit's purposes. Code holder uses
|
||||
//! arena allocator to allocate everything, so anything allocated through
|
||||
//! this allocator will be invalidated by \ref CodeHolder::reset() or by
|
||||
//! CodeHolder's destructor.
|
||||
inline ZoneAllocator* allocator() const noexcept { return const_cast<ZoneAllocator*>(&_allocator); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Code Emitter
|
||||
//! \{
|
||||
|
||||
inline const ZoneVector<BaseEmitter*>& emitters() const noexcept { return _emitters; }
|
||||
|
||||
//! Returns global emitter options, internally propagated to all attached emitters.
|
||||
inline uint32_t emitterOptions() const noexcept { return _emitterOptions; }
|
||||
|
||||
//! Enables the given global emitter `options` and propagates the resulting
|
||||
//! options to all attached emitters.
|
||||
ASMJIT_API void addEmitterOptions(uint32_t options) noexcept;
|
||||
|
||||
//! Disables the given global emitter `options` and propagates the resulting
|
||||
//! options to all attached emitters.
|
||||
ASMJIT_API void clearEmitterOptions(uint32_t options) noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Code & Architecture
|
||||
//! \{
|
||||
|
||||
//! Returns the target architecture information, see `ArchInfo`.
|
||||
inline const ArchInfo& archInfo() const noexcept { return _codeInfo.archInfo(); }
|
||||
//! Returns the target code information, see `CodeInfo`.
|
||||
inline const CodeInfo& codeInfo() const noexcept { return _codeInfo; }
|
||||
//! Returns the target environment information, see \ref Environment.
|
||||
inline const Environment& environment() const noexcept { return _environment; }
|
||||
|
||||
//! Returns the target architecture id.
|
||||
inline uint32_t archId() const noexcept { return archInfo().archId(); }
|
||||
//! Returns the target architecture sub-id.
|
||||
inline uint32_t archSubId() const noexcept { return archInfo().archSubId(); }
|
||||
//! Returns the target architecture.
|
||||
inline uint32_t arch() const noexcept { return environment().arch(); }
|
||||
//! Returns the target sub-architecture.
|
||||
inline uint32_t subArch() const noexcept { return environment().subArch(); }
|
||||
|
||||
//! Tests whether a static base-address is set.
|
||||
inline bool hasBaseAddress() const noexcept { return _codeInfo.hasBaseAddress(); }
|
||||
//! Returns a static base-address (uint64_t).
|
||||
inline uint64_t baseAddress() const noexcept { return _codeInfo.baseAddress(); }
|
||||
inline bool hasBaseAddress() const noexcept { return _baseAddress != Globals::kNoBaseAddress; }
|
||||
//! Returns a static base-address or \ref Globals::kNoBaseAddress, if not set.
|
||||
inline uint64_t baseAddress() const noexcept { return _baseAddress; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Logging & Error Handling
|
||||
//! \name Emitters
|
||||
//! \{
|
||||
|
||||
//! Returns the attached logger.
|
||||
//! Returns a vector of attached emitters.
|
||||
inline const ZoneVector<BaseEmitter*>& emitters() const noexcept { return _emitters; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Logging
|
||||
//! \{
|
||||
|
||||
//! Returns the attached logger, see \ref Logger.
|
||||
inline Logger* logger() const noexcept { return _logger; }
|
||||
//! Attaches a `logger` to CodeHolder and propagates it to all attached emitters.
|
||||
ASMJIT_API void setLogger(Logger* logger) noexcept;
|
||||
//! Resets the logger to none.
|
||||
inline void resetLogger() noexcept { setLogger(nullptr); }
|
||||
|
||||
//! Tests whether the global error handler is attached.
|
||||
//! \name Error Handling
|
||||
//! \{
|
||||
|
||||
//! Tests whether the CodeHolder has an attached error handler, see \ref ErrorHandler.
|
||||
inline bool hasErrorHandler() const noexcept { return _errorHandler != nullptr; }
|
||||
//! Returns the global error handler.
|
||||
//! Returns the attached error handler.
|
||||
inline ErrorHandler* errorHandler() const noexcept { return _errorHandler; }
|
||||
//! Sets the global error handler.
|
||||
inline void setErrorHandler(ErrorHandler* handler) noexcept { _errorHandler = handler; }
|
||||
//! Resets the global error handler to none.
|
||||
//! Attach an error handler to this `CodeHolder`.
|
||||
ASMJIT_API void setErrorHandler(ErrorHandler* errorHandler) noexcept;
|
||||
//! Resets the error handler to none.
|
||||
inline void resetErrorHandler() noexcept { setErrorHandler(nullptr); }
|
||||
|
||||
//! \}
|
||||
@ -688,7 +646,16 @@ public:
|
||||
//! \name Code Buffer
|
||||
//! \{
|
||||
|
||||
//! Makes sure that at least `n` bytes can be added to CodeHolder's buffer `cb`.
|
||||
//!
|
||||
//! \note The buffer `cb` must be managed by `CodeHolder` - otherwise the
|
||||
//! behavior of the function is undefined.
|
||||
ASMJIT_API Error growBuffer(CodeBuffer* cb, size_t n) noexcept;
|
||||
|
||||
//! Reserves the size of `cb` to at least `n` bytes.
|
||||
//!
|
||||
//! \note The buffer `cb` must be managed by `CodeHolder` - otherwise the
|
||||
//! behavior of the function is undefined.
|
||||
ASMJIT_API Error reserveBuffer(CodeBuffer* cb, size_t n) noexcept;
|
||||
|
||||
//! \}
|
||||
@ -719,7 +686,7 @@ public:
|
||||
|
||||
//! Returns '.text' section (section that commonly represents code).
|
||||
//!
|
||||
//! \note Text section is always the first section in `CodeHolder::sections()` array.
|
||||
//! \note Text section is always the first section in \ref CodeHolder::sections() array.
|
||||
inline Section* textSection() const noexcept { return _sections[0]; }
|
||||
|
||||
//! Tests whether '.addrtab' section exists.
|
||||
@ -729,6 +696,8 @@ public:
|
||||
//!
|
||||
//! This section is used exclusively by AsmJit to store absolute 64-bit
|
||||
//! addresses that cannot be encoded in instructions like 'jmp' or 'call'.
|
||||
//!
|
||||
//! \note This section is created on demand, the returned pointer can be null.
|
||||
inline Section* addressTableSection() const noexcept { return _addressTableSection; }
|
||||
|
||||
//! Ensures that '.addrtab' section exists (creates it if it doesn't) and
|
||||
@ -825,18 +794,38 @@ public:
|
||||
//! Returns `Error`, does not report error to `ErrorHandler`.
|
||||
ASMJIT_API Error newLabelEntry(LabelEntry** entryOut) noexcept;
|
||||
|
||||
//! Creates a new named label label-type `type`.
|
||||
//! Creates a new named \ref LabelEntry of the given label `type`.
|
||||
//!
|
||||
//! Returns `Error`, does not report a possible error to `ErrorHandler`.
|
||||
//! \param entryOut Where to store the created \ref LabelEntry.
|
||||
//! \param name The name of the label.
|
||||
//! \param nameSize The length of `name` argument, or `SIZE_MAX` if `name` is
|
||||
//! a null terminated string, which means that the `CodeHolder` will
|
||||
//! use `strlen()` to determine the length.
|
||||
//! \param type The type of the label to create, see \ref Label::LabelType.
|
||||
//! \param parentId Parent id of a local label, otherwise it must be
|
||||
//! \ref Globals::kInvalidId.
|
||||
//!
|
||||
//! \retval Always returns \ref Error, does not report a possible error to
|
||||
//! the attached \ref ErrorHandler.
|
||||
//!
|
||||
//! AsmJit has a support for local labels (\ref Label::kTypeLocal) which
|
||||
//! require a parent label id (parentId). The names of local labels can
|
||||
//! conflict with names of other local labels that have a different parent.
|
||||
ASMJIT_API Error newNamedLabelEntry(LabelEntry** entryOut, const char* name, size_t nameSize, uint32_t type, uint32_t parentId = Globals::kInvalidId) noexcept;
|
||||
|
||||
//! Returns a label id by name.
|
||||
ASMJIT_API uint32_t labelIdByName(const char* name, size_t nameSize = SIZE_MAX, uint32_t parentId = Globals::kInvalidId) noexcept;
|
||||
|
||||
//! Returns a label by name.
|
||||
//!
|
||||
//! If the named label doesn't a default constructed \ref Label is returned,
|
||||
//! which has its id set to \ref Globals::kInvalidId.
|
||||
inline Label labelByName(const char* name, size_t nameSize = SIZE_MAX, uint32_t parentId = Globals::kInvalidId) noexcept {
|
||||
return Label(labelIdByName(name, nameSize, parentId));
|
||||
}
|
||||
|
||||
//! Returns a label id by name.
|
||||
//!
|
||||
//! If the named label doesn't exist \ref Globals::kInvalidId is returned.
|
||||
ASMJIT_API uint32_t labelIdByName(const char* name, size_t nameSize = SIZE_MAX, uint32_t parentId = Globals::kInvalidId) noexcept;
|
||||
|
||||
//! Tests whether there are any unresolved label links.
|
||||
inline bool hasUnresolvedLinks() const noexcept { return _unresolvedLinkCount != 0; }
|
||||
//! Returns the number of label links, which are unresolved.
|
||||
@ -903,24 +892,34 @@ public:
|
||||
//! \note This should never be called more than once.
|
||||
ASMJIT_API Error relocateToBase(uint64_t baseAddress) noexcept;
|
||||
|
||||
//! Options that can be used with \ref copySectionData().
|
||||
enum CopyOptions : uint32_t {
|
||||
//! If virtual size of the section is larger than the size of its buffer
|
||||
//! then all bytes between buffer size and virtual size will be zeroed.
|
||||
kCopyWithPadding = 0x1
|
||||
};
|
||||
|
||||
//! Copies a single section into `dst`.
|
||||
ASMJIT_API Error copySectionData(void* dst, size_t dstSize, uint32_t sectionId, uint32_t options = 0) noexcept;
|
||||
ASMJIT_API Error copySectionData(void* dst, size_t dstSize, uint32_t sectionId, uint32_t copyOptions = 0) noexcept;
|
||||
|
||||
//! Copies all sections into `dst`.
|
||||
//!
|
||||
//! This should only be used if the data was flattened and there are no gaps
|
||||
//! between the sections. The `dstSize` is always checked and the copy will
|
||||
//! never write anything outside the provided buffer.
|
||||
ASMJIT_API Error copyFlattenedData(void* dst, size_t dstSize, uint32_t options = 0) noexcept;
|
||||
ASMJIT_API Error copyFlattenedData(void* dst, size_t dstSize, uint32_t copyOptions = 0) noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("Use 'CodeHolder::init(const Environment& environment, uint64_t baseAddress)' instead")
|
||||
inline Error init(const CodeInfo& codeInfo) noexcept { return init(codeInfo._environment, codeInfo._baseAddress); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use nevironment() instead")
|
||||
inline CodeInfo codeInfo() const noexcept { return CodeInfo(_environment, _baseAddress); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use BaseEmitter::encodingOptions() - this function always returns zero")
|
||||
inline uint32_t emitterOptions() const noexcept { return 0; }
|
||||
|
||||
ASMJIT_DEPRECATED("Use BaseEmitter::addEncodingOptions() - this function does nothing")
|
||||
inline void addEmitterOptions(uint32_t options) noexcept { DebugUtils::unused(options); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use BaseEmitter::clearEncodingOptions() - this function does nothing")
|
||||
inline void clearEmitterOptions(uint32_t options) noexcept { DebugUtils::unused(options); }
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
340
3rdparty/asmjit/src/asmjit/core/compiler.cpp
vendored
340
3rdparty/asmjit/src/asmjit/core/compiler.cpp
vendored
@ -27,7 +27,7 @@
|
||||
#include "../core/assembler.h"
|
||||
#include "../core/compiler.h"
|
||||
#include "../core/cpuinfo.h"
|
||||
#include "../core/logging.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/rapass_p.h"
|
||||
#include "../core/rastack_p.h"
|
||||
#include "../core/support.h"
|
||||
@ -40,12 +40,12 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// ============================================================================
|
||||
|
||||
class GlobalConstPoolPass : public Pass {
|
||||
ASMJIT_NONCOPYABLE(GlobalConstPoolPass)
|
||||
typedef Pass Base;
|
||||
ASMJIT_NONCOPYABLE(GlobalConstPoolPass)
|
||||
|
||||
GlobalConstPoolPass() noexcept : Pass("GlobalConstPoolPass") {}
|
||||
|
||||
Error run(Zone* zone, Logger* logger) noexcept override {
|
||||
Error run(Zone* zone, Logger* logger) override {
|
||||
DebugUtils::unused(zone, logger);
|
||||
|
||||
// Flush the global constant pool.
|
||||
@ -54,15 +54,16 @@ class GlobalConstPoolPass : public Pass {
|
||||
compiler->addAfter(compiler->_globalConstPool, compiler->lastNode());
|
||||
compiler->_globalConstPool = nullptr;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::FuncCallNode - Arg / Ret]
|
||||
// [asmjit::InvokeNode - Arg / Ret]
|
||||
// ============================================================================
|
||||
|
||||
bool FuncCallNode::_setArg(uint32_t i, const Operand_& op) noexcept {
|
||||
bool InvokeNode::_setArg(uint32_t i, const Operand_& op) noexcept {
|
||||
if ((i & ~kFuncArgHi) >= _funcDetail.argCount())
|
||||
return false;
|
||||
|
||||
@ -70,7 +71,7 @@ bool FuncCallNode::_setArg(uint32_t i, const Operand_& op) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FuncCallNode::_setRet(uint32_t i, const Operand_& op) noexcept {
|
||||
bool InvokeNode::_setRet(uint32_t i, const Operand_& op) noexcept {
|
||||
if (i >= 2)
|
||||
return false;
|
||||
|
||||
@ -90,72 +91,81 @@ BaseCompiler::BaseCompiler() noexcept
|
||||
_localConstPool(nullptr),
|
||||
_globalConstPool(nullptr) {
|
||||
|
||||
_type = kTypeCompiler;
|
||||
_emitterType = uint8_t(kTypeCompiler);
|
||||
_validationFlags = uint8_t(InstAPI::kValidationFlagVirtRegs);
|
||||
}
|
||||
BaseCompiler::~BaseCompiler() noexcept {}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseCompiler - Function API]
|
||||
// [asmjit::BaseCompiler - Function Management]
|
||||
// ============================================================================
|
||||
|
||||
FuncNode* BaseCompiler::newFunc(const FuncSignature& sign) noexcept {
|
||||
Error err;
|
||||
Error BaseCompiler::_newFuncNode(FuncNode** out, const FuncSignature& signature) {
|
||||
*out = nullptr;
|
||||
|
||||
FuncNode* func = newNodeT<FuncNode>();
|
||||
if (ASMJIT_UNLIKELY(!func)) {
|
||||
reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
return nullptr;
|
||||
}
|
||||
// Create FuncNode together with all the required surrounding nodes.
|
||||
FuncNode* funcNode;
|
||||
ASMJIT_PROPAGATE(_newNodeT<FuncNode>(&funcNode));
|
||||
ASMJIT_PROPAGATE(_newLabelNode(&funcNode->_exitNode));
|
||||
ASMJIT_PROPAGATE(_newNodeT<SentinelNode>(&funcNode->_end, SentinelNode::kSentinelFuncEnd));
|
||||
|
||||
err = registerLabelNode(func);
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
// TODO: Calls reportError, maybe rethink noexcept?
|
||||
reportError(err);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create helper nodes.
|
||||
func->_exitNode = newLabelNode();
|
||||
func->_end = newNodeT<SentinelNode>(SentinelNode::kSentinelFuncEnd);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!func->_exitNode || !func->_end)) {
|
||||
reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Initialize the function info.
|
||||
err = func->detail().init(sign);
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
reportError(err);
|
||||
return nullptr;
|
||||
}
|
||||
// Initialize the function's detail info.
|
||||
Error err = funcNode->detail().init(signature, environment());
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
// If the Target guarantees greater stack alignment than required by the
|
||||
// calling convention then override it as we can prevent having to perform
|
||||
// dynamic stack alignment
|
||||
if (func->_funcDetail._callConv.naturalStackAlignment() < _codeInfo.stackAlignment())
|
||||
func->_funcDetail._callConv.setNaturalStackAlignment(_codeInfo.stackAlignment());
|
||||
uint32_t environmentStackAlignment = _environment.stackAlignment();
|
||||
|
||||
if (funcNode->_funcDetail._callConv.naturalStackAlignment() < environmentStackAlignment)
|
||||
funcNode->_funcDetail._callConv.setNaturalStackAlignment(environmentStackAlignment);
|
||||
|
||||
// Initialize the function frame.
|
||||
err = func->_frame.init(func->_funcDetail);
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
reportError(err);
|
||||
return nullptr;
|
||||
}
|
||||
err = funcNode->_frame.init(funcNode->_funcDetail);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
// Allocate space for function arguments.
|
||||
func->_args = nullptr;
|
||||
if (func->argCount() != 0) {
|
||||
func->_args = _allocator.allocT<VirtReg*>(func->argCount() * sizeof(VirtReg*));
|
||||
if (ASMJIT_UNLIKELY(!func->_args)) {
|
||||
reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memset(func->_args, 0, func->argCount() * sizeof(VirtReg*));
|
||||
funcNode->_args = nullptr;
|
||||
if (funcNode->argCount() != 0) {
|
||||
funcNode->_args = _allocator.allocT<VirtReg*>(funcNode->argCount() * sizeof(VirtReg*));
|
||||
if (ASMJIT_UNLIKELY(!funcNode->_args))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
memset(funcNode->_args, 0, funcNode->argCount() * sizeof(VirtReg*));
|
||||
}
|
||||
|
||||
return func;
|
||||
ASMJIT_PROPAGATE(registerLabelNode(funcNode));
|
||||
|
||||
*out = funcNode;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::_addFuncNode(FuncNode** out, const FuncSignature& signature) {
|
||||
ASMJIT_PROPAGATE(_newFuncNode(out, signature));
|
||||
addFunc(*out);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newRetNode(FuncRetNode** out, const Operand_& o0, const Operand_& o1) {
|
||||
uint32_t opCount = !o1.isNone() ? 2u : !o0.isNone() ? 1u : 0u;
|
||||
FuncRetNode* node;
|
||||
|
||||
ASMJIT_PROPAGATE(_newNodeT<FuncRetNode>(&node));
|
||||
node->setOpCount(opCount);
|
||||
node->setOp(0, o0);
|
||||
node->setOp(1, o1);
|
||||
node->resetOpRange(2, node->opCapacity());
|
||||
|
||||
*out = node;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::_addRetNode(FuncRetNode** out, const Operand_& o0, const Operand_& o1) {
|
||||
ASMJIT_PROPAGATE(_newRetNode(out, o0, o1));
|
||||
addNode(*out);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
FuncNode* BaseCompiler::addFunc(FuncNode* func) {
|
||||
@ -165,25 +175,15 @@ FuncNode* BaseCompiler::addFunc(FuncNode* func) {
|
||||
addNode(func); // Function node.
|
||||
BaseNode* prev = cursor(); // {CURSOR}.
|
||||
addNode(func->exitNode()); // Function exit label.
|
||||
addNode(func->endNode()); // Function end marker.
|
||||
addNode(func->endNode()); // Function end sentinel.
|
||||
|
||||
_setCursor(prev);
|
||||
return func;
|
||||
}
|
||||
|
||||
FuncNode* BaseCompiler::addFunc(const FuncSignature& sign) {
|
||||
FuncNode* func = newFunc(sign);
|
||||
|
||||
if (!func) {
|
||||
reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return addFunc(func);
|
||||
}
|
||||
|
||||
Error BaseCompiler::endFunc() {
|
||||
FuncNode* func = _func;
|
||||
|
||||
if (ASMJIT_UNLIKELY(!func))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidState));
|
||||
|
||||
@ -199,6 +199,7 @@ Error BaseCompiler::endFunc() {
|
||||
|
||||
SentinelNode* end = func->endNode();
|
||||
setCursor(end);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
@ -217,71 +218,43 @@ Error BaseCompiler::setArg(uint32_t argIndex, const BaseReg& r) {
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
FuncRetNode* BaseCompiler::newRet(const Operand_& o0, const Operand_& o1) noexcept {
|
||||
FuncRetNode* node = newNodeT<FuncRetNode>();
|
||||
if (!node) {
|
||||
reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node->setOp(0, o0);
|
||||
node->setOp(1, o1);
|
||||
node->setOpCount(!o1.isNone() ? 2u : !o0.isNone() ? 1u : 0u);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
FuncRetNode* BaseCompiler::addRet(const Operand_& o0, const Operand_& o1) noexcept {
|
||||
FuncRetNode* node = newRet(o0, o1);
|
||||
if (!node) return nullptr;
|
||||
return addNode(node)->as<FuncRetNode>();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseCompiler - Call]
|
||||
// [asmjit::BaseCompiler - Function Invocation]
|
||||
// ============================================================================
|
||||
|
||||
FuncCallNode* BaseCompiler::newCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept {
|
||||
FuncCallNode* node = newNodeT<FuncCallNode>(instId, 0u);
|
||||
if (ASMJIT_UNLIKELY(!node)) {
|
||||
reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
return nullptr;
|
||||
}
|
||||
Error BaseCompiler::_newInvokeNode(InvokeNode** out, uint32_t instId, const Operand_& o0, const FuncSignature& signature) {
|
||||
InvokeNode* node;
|
||||
ASMJIT_PROPAGATE(_newNodeT<InvokeNode>(&node, instId, 0u));
|
||||
|
||||
node->setOpCount(1);
|
||||
node->setOp(0, o0);
|
||||
node->resetOp(1);
|
||||
node->resetOp(2);
|
||||
node->resetOp(3);
|
||||
node->resetOpRange(1, node->opCapacity());
|
||||
|
||||
Error err = node->detail().init(sign);
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
reportError(err);
|
||||
return nullptr;
|
||||
Error err = node->detail().init(signature, environment());
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
// Skip the allocation if there are no arguments.
|
||||
uint32_t argCount = signature.argCount();
|
||||
if (argCount) {
|
||||
node->_args = static_cast<Operand*>(_allocator.alloc(argCount * sizeof(Operand)));
|
||||
if (!node->_args)
|
||||
reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
memset(node->_args, 0, argCount * sizeof(Operand));
|
||||
}
|
||||
|
||||
// If there are no arguments skip the allocation.
|
||||
uint32_t nArgs = sign.argCount();
|
||||
if (!nArgs) return node;
|
||||
|
||||
node->_args = static_cast<Operand*>(_allocator.alloc(nArgs * sizeof(Operand)));
|
||||
if (!node->_args) {
|
||||
reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memset(node->_args, 0, nArgs * sizeof(Operand));
|
||||
return node;
|
||||
*out = node;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
FuncCallNode* BaseCompiler::addCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept {
|
||||
FuncCallNode* node = newCall(instId, o0, sign);
|
||||
if (!node) return nullptr;
|
||||
return addNode(node)->as<FuncCallNode>();
|
||||
Error BaseCompiler::_addInvokeNode(InvokeNode** out, uint32_t instId, const Operand_& o0, const FuncSignature& signature) {
|
||||
ASMJIT_PROPAGATE(_newInvokeNode(out, instId, o0, signature));
|
||||
addNode(*out);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseCompiler - Vars]
|
||||
// [asmjit::BaseCompiler - Virtual Registers]
|
||||
// ============================================================================
|
||||
|
||||
static void BaseCompiler_assignGenericName(BaseCompiler* self, VirtReg* vReg) {
|
||||
@ -294,16 +267,19 @@ static void BaseCompiler_assignGenericName(BaseCompiler* self, VirtReg* vReg) {
|
||||
vReg->_name.setData(&self->_dataZone, buf, unsigned(size));
|
||||
}
|
||||
|
||||
VirtReg* BaseCompiler::newVirtReg(uint32_t typeId, uint32_t signature, const char* name) noexcept {
|
||||
Error BaseCompiler::newVirtReg(VirtReg** out, uint32_t typeId, uint32_t signature, const char* name) {
|
||||
*out = nullptr;
|
||||
uint32_t index = _vRegArray.size();
|
||||
if (ASMJIT_UNLIKELY(index >= uint32_t(Operand::kVirtIdCount)))
|
||||
return nullptr;
|
||||
|
||||
if (_vRegArray.willGrow(&_allocator) != kErrorOk)
|
||||
return nullptr;
|
||||
if (ASMJIT_UNLIKELY(index >= uint32_t(Operand::kVirtIdCount)))
|
||||
return reportError(DebugUtils::errored(kErrorTooManyVirtRegs));
|
||||
|
||||
if (ASMJIT_UNLIKELY(_vRegArray.willGrow(&_allocator) != kErrorOk))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
VirtReg* vReg = _vRegZone.allocZeroedT<VirtReg>();
|
||||
if (ASMJIT_UNLIKELY(!vReg)) return nullptr;
|
||||
if (ASMJIT_UNLIKELY(!vReg))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
uint32_t size = Type::sizeOf(typeId);
|
||||
uint32_t alignment = Support::min<uint32_t>(size, 64);
|
||||
@ -320,26 +296,28 @@ VirtReg* BaseCompiler::newVirtReg(uint32_t typeId, uint32_t signature, const cha
|
||||
#endif
|
||||
|
||||
_vRegArray.appendUnsafe(vReg);
|
||||
return vReg;
|
||||
}
|
||||
*out = vReg;
|
||||
|
||||
Error BaseCompiler::_newReg(BaseReg& out, uint32_t typeId, const char* name) {
|
||||
RegInfo regInfo;
|
||||
|
||||
Error err = ArchUtils::typeIdToRegInfo(archId(), typeId, regInfo);
|
||||
if (ASMJIT_UNLIKELY(err)) return reportError(err);
|
||||
|
||||
VirtReg* vReg = newVirtReg(typeId, regInfo.signature(), name);
|
||||
if (ASMJIT_UNLIKELY(!vReg)) {
|
||||
out.reset();
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
}
|
||||
|
||||
out._initReg(regInfo.signature(), vReg->id());
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newRegFmt(BaseReg& out, uint32_t typeId, const char* fmt, ...) {
|
||||
Error BaseCompiler::_newReg(BaseReg* out, uint32_t typeId, const char* name) {
|
||||
out->reset();
|
||||
|
||||
RegInfo regInfo;
|
||||
Error err = ArchUtils::typeIdToRegInfo(arch(), typeId, &typeId, ®Info);
|
||||
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
VirtReg* vReg;
|
||||
ASMJIT_PROPAGATE(newVirtReg(&vReg, typeId, regInfo.signature(), name));
|
||||
|
||||
out->_initReg(regInfo.signature(), vReg->id());
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newRegFmt(BaseReg* out, uint32_t typeId, const char* fmt, ...) {
|
||||
va_list ap;
|
||||
StringTmp<256> sb;
|
||||
|
||||
@ -350,7 +328,9 @@ Error BaseCompiler::_newRegFmt(BaseReg& out, uint32_t typeId, const char* fmt, .
|
||||
return _newReg(out, typeId, sb.data());
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newReg(BaseReg& out, const BaseReg& ref, const char* name) {
|
||||
Error BaseCompiler::_newReg(BaseReg* out, const BaseReg& ref, const char* name) {
|
||||
out->reset();
|
||||
|
||||
RegInfo regInfo;
|
||||
uint32_t typeId;
|
||||
|
||||
@ -412,20 +392,18 @@ Error BaseCompiler::_newReg(BaseReg& out, const BaseReg& ref, const char* name)
|
||||
typeId = ref.type();
|
||||
}
|
||||
|
||||
Error err = ArchUtils::typeIdToRegInfo(archId(), typeId, regInfo);
|
||||
if (ASMJIT_UNLIKELY(err)) return reportError(err);
|
||||
Error err = ArchUtils::typeIdToRegInfo(arch(), typeId, &typeId, ®Info);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
VirtReg* vReg = newVirtReg(typeId, regInfo.signature(), name);
|
||||
if (ASMJIT_UNLIKELY(!vReg)) {
|
||||
out.reset();
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
}
|
||||
VirtReg* vReg;
|
||||
ASMJIT_PROPAGATE(newVirtReg(&vReg, typeId, regInfo.signature(), name));
|
||||
|
||||
out._initReg(regInfo.signature(), vReg->id());
|
||||
out->_initReg(regInfo.signature(), vReg->id());
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newRegFmt(BaseReg& out, const BaseReg& ref, const char* fmt, ...) {
|
||||
Error BaseCompiler::_newRegFmt(BaseReg* out, const BaseReg& ref, const char* fmt, ...) {
|
||||
va_list ap;
|
||||
StringTmp<256> sb;
|
||||
|
||||
@ -436,7 +414,9 @@ Error BaseCompiler::_newRegFmt(BaseReg& out, const BaseReg& ref, const char* fmt
|
||||
return _newReg(out, ref, sb.data());
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newStack(BaseMem& out, uint32_t size, uint32_t alignment, const char* name) {
|
||||
Error BaseCompiler::_newStack(BaseMem* out, uint32_t size, uint32_t alignment, const char* name) {
|
||||
out->reset();
|
||||
|
||||
if (size == 0)
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
@ -449,22 +429,19 @@ Error BaseCompiler::_newStack(BaseMem& out, uint32_t size, uint32_t alignment, c
|
||||
if (alignment > 64)
|
||||
alignment = 64;
|
||||
|
||||
VirtReg* vReg = newVirtReg(0, 0, name);
|
||||
if (ASMJIT_UNLIKELY(!vReg)) {
|
||||
out.reset();
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
}
|
||||
VirtReg* vReg;
|
||||
ASMJIT_PROPAGATE(newVirtReg(&vReg, 0, 0, name));
|
||||
|
||||
vReg->_virtSize = size;
|
||||
vReg->_isStack = true;
|
||||
vReg->_alignment = uint8_t(alignment);
|
||||
|
||||
// Set the memory operand to GPD/GPQ and its id to VirtReg.
|
||||
out = BaseMem(BaseMem::Decomposed { _gpRegInfo.type(), vReg->id(), BaseReg::kTypeNone, 0, 0, 0, BaseMem::kSignatureMemRegHomeFlag });
|
||||
*out = BaseMem(BaseMem::Decomposed { _gpRegInfo.type(), vReg->id(), BaseReg::kTypeNone, 0, 0, 0, BaseMem::kSignatureMemRegHomeFlag });
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::setStackSize(uint32_t virtId, uint32_t newSize, uint32_t newAlignment) noexcept {
|
||||
Error BaseCompiler::setStackSize(uint32_t virtId, uint32_t newSize, uint32_t newAlignment) {
|
||||
if (!isVirtIdValid(virtId))
|
||||
return DebugUtils::errored(kErrorInvalidVirtId);
|
||||
|
||||
@ -493,8 +470,10 @@ Error BaseCompiler::setStackSize(uint32_t virtId, uint32_t newSize, uint32_t new
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::_newConst(BaseMem& out, uint32_t scope, const void* data, size_t size) {
|
||||
Error BaseCompiler::_newConst(BaseMem* out, uint32_t scope, const void* data, size_t size) {
|
||||
out->reset();
|
||||
ConstPoolNode** pPool;
|
||||
|
||||
if (scope == ConstPool::kScopeLocal)
|
||||
pPool = &_localConstPool;
|
||||
else if (scope == ConstPool::kScopeGlobal)
|
||||
@ -502,29 +481,26 @@ Error BaseCompiler::_newConst(BaseMem& out, uint32_t scope, const void* data, si
|
||||
else
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
ConstPoolNode* pool = *pPool;
|
||||
if (!pool) {
|
||||
pool = newConstPoolNode();
|
||||
if (ASMJIT_UNLIKELY(!pool))
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
*pPool = pool;
|
||||
}
|
||||
if (!*pPool)
|
||||
ASMJIT_PROPAGATE(_newConstPoolNode(pPool));
|
||||
|
||||
ConstPoolNode* pool = *pPool;
|
||||
size_t off;
|
||||
Error err = pool->add(data, size, off);
|
||||
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return reportError(err);
|
||||
|
||||
out = BaseMem(BaseMem::Decomposed {
|
||||
*out = BaseMem(BaseMem::Decomposed {
|
||||
Label::kLabelTag, // Base type.
|
||||
pool->id(), // Base id.
|
||||
pool->labelId(), // Base id.
|
||||
0, // Index type.
|
||||
0, // Index id.
|
||||
int32_t(off), // Offset.
|
||||
uint32_t(size), // Size.
|
||||
0 // Flags.
|
||||
});
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
@ -553,38 +529,38 @@ void BaseCompiler::rename(const BaseReg& reg, const char* fmt, ...) {
|
||||
// [asmjit::BaseCompiler - Jump Annotations]
|
||||
// ============================================================================
|
||||
|
||||
JumpNode* BaseCompiler::newJumpNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, JumpAnnotation* annotation) noexcept {
|
||||
uint32_t opCount = 1;
|
||||
Error BaseCompiler::newJumpNode(JumpNode** out, uint32_t instId, uint32_t instOptions, const Operand_& o0, JumpAnnotation* annotation) {
|
||||
JumpNode* node = _allocator.allocT<JumpNode>();
|
||||
uint32_t opCount = 1;
|
||||
|
||||
if (ASMJIT_UNLIKELY(!node))
|
||||
return nullptr;
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
|
||||
node = new(node) JumpNode(this, instId, instOptions, opCount, annotation);
|
||||
node->setOp(0, o0);
|
||||
node->resetOps(opCount, JumpNode::kBaseOpCapacity);
|
||||
return node;
|
||||
node->resetOpRange(opCount, JumpNode::kBaseOpCapacity);
|
||||
|
||||
*out = node;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseCompiler::emitAnnotatedJump(uint32_t instId, const Operand_& o0, JumpAnnotation* annotation) {
|
||||
uint32_t options = instOptions() | globalInstOptions();
|
||||
uint32_t options = instOptions() | forcedInstOptions();
|
||||
RegOnly extra = extraReg();
|
||||
const char* comment = inlineComment();
|
||||
|
||||
JumpNode* node = newJumpNode(instId, options, o0, annotation);
|
||||
|
||||
resetInstOptions();
|
||||
resetInlineComment();
|
||||
resetExtraReg();
|
||||
|
||||
if (ASMJIT_UNLIKELY(!node)) {
|
||||
resetExtraReg();
|
||||
return reportError(DebugUtils::errored(kErrorOutOfMemory));
|
||||
}
|
||||
JumpNode* node;
|
||||
ASMJIT_PROPAGATE(newJumpNode(&node, instId, options, o0, annotation));
|
||||
|
||||
node->setExtraReg(extraReg());
|
||||
node->setExtraReg(extra);
|
||||
if (comment)
|
||||
node->setInlineComment(static_cast<char*>(_dataZone.dup(comment, strlen(comment), true)));
|
||||
|
||||
addNode(node);
|
||||
resetExtraReg();
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
@ -644,7 +620,7 @@ FuncPass::FuncPass(const char* name) noexcept
|
||||
// [asmjit::FuncPass - Run]
|
||||
// ============================================================================
|
||||
|
||||
Error FuncPass::run(Zone* zone, Logger* logger) noexcept {
|
||||
Error FuncPass::run(Zone* zone, Logger* logger) {
|
||||
BaseNode* node = cb()->firstNode();
|
||||
if (!node) return kErrorOk;
|
||||
|
||||
|
244
3rdparty/asmjit/src/asmjit/core/compiler.h
vendored
244
3rdparty/asmjit/src/asmjit/core/compiler.h
vendored
@ -51,7 +51,7 @@ class JumpAnnotation;
|
||||
class JumpNode;
|
||||
class FuncNode;
|
||||
class FuncRetNode;
|
||||
class FuncCallNode;
|
||||
class InvokeNode;
|
||||
|
||||
//! \addtogroup asmjit_compiler
|
||||
//! \{
|
||||
@ -60,7 +60,7 @@ class FuncCallNode;
|
||||
// [asmjit::VirtReg]
|
||||
// ============================================================================
|
||||
|
||||
//! Virtual register data (BaseCompiler).
|
||||
//! Virtual register data, managed by \ref BaseCompiler.
|
||||
class VirtReg {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(VirtReg)
|
||||
@ -192,14 +192,18 @@ public:
|
||||
//! primarily designed for merging multiple parts of code into a function
|
||||
//! without worrying about registers and function calling conventions.
|
||||
//!
|
||||
//! BaseCompiler can be used, with a minimum effort, to handle 32-bit and 64-bit
|
||||
//! code at the same time.
|
||||
//! BaseCompiler can be used, with a minimum effort, to handle 32-bit and
|
||||
//! 64-bit code generation within a single code base.
|
||||
//!
|
||||
//! BaseCompiler is based on BaseBuilder 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.
|
||||
//!
|
||||
//! Check out architecture specific compilers for more details and examples:
|
||||
//!
|
||||
//! - \ref x86::Compiler - X86/X64 compiler implementation.
|
||||
class ASMJIT_VIRTAPI BaseCompiler : public BaseBuilder {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(BaseCompiler)
|
||||
@ -229,38 +233,80 @@ public:
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Function API
|
||||
//! \name Function Management
|
||||
//! \{
|
||||
|
||||
//! Returns the current function.
|
||||
inline FuncNode* func() const noexcept { return _func; }
|
||||
|
||||
//! Creates a new `FuncNode`.
|
||||
ASMJIT_API FuncNode* newFunc(const FuncSignature& sign) noexcept;
|
||||
//! Adds a function `node` to the stream.
|
||||
//! Creates a new \ref FuncNode.
|
||||
ASMJIT_API Error _newFuncNode(FuncNode** out, const FuncSignature& signature);
|
||||
//! Creates a new \ref FuncNode adds it to the compiler.
|
||||
ASMJIT_API Error _addFuncNode(FuncNode** out, const FuncSignature& signature);
|
||||
|
||||
//! Creates a new \ref FuncRetNode.
|
||||
ASMJIT_API Error _newRetNode(FuncRetNode** out, const Operand_& o0, const Operand_& o1);
|
||||
//! Creates a new \ref FuncRetNode and adds it to the compiler.
|
||||
ASMJIT_API Error _addRetNode(FuncRetNode** out, const Operand_& o0, const Operand_& o1);
|
||||
|
||||
//! Creates a new \ref FuncNode with the given `signature` and returns it.
|
||||
inline FuncNode* newFunc(const FuncSignature& signature) {
|
||||
FuncNode* node;
|
||||
_newFuncNode(&node, signature);
|
||||
return node;
|
||||
}
|
||||
|
||||
//! Creates a new \ref FuncNode with the given `signature`, adds it to the
|
||||
//! compiler by using the \ref addFunc(FuncNode*) overload, and returns it.
|
||||
inline FuncNode* addFunc(const FuncSignature& signature) {
|
||||
FuncNode* node;
|
||||
_addFuncNode(&node, signature);
|
||||
return node;
|
||||
}
|
||||
|
||||
//! Adds a function `node` to the instruction stream.
|
||||
ASMJIT_API FuncNode* addFunc(FuncNode* func);
|
||||
//! Adds a new function.
|
||||
ASMJIT_API FuncNode* addFunc(const FuncSignature& sign);
|
||||
//! Emits a sentinel that marks the end of the current function.
|
||||
ASMJIT_API Error endFunc();
|
||||
|
||||
//! Sets a function argument at `argIndex` to `reg`.
|
||||
ASMJIT_API Error setArg(uint32_t argIndex, const BaseReg& reg);
|
||||
|
||||
//! Creates a new `FuncRetNode`.
|
||||
ASMJIT_API FuncRetNode* newRet(const Operand_& o0, const Operand_& o1) noexcept;
|
||||
//! Adds a new `FuncRetNode`.
|
||||
ASMJIT_API FuncRetNode* addRet(const Operand_& o0, const Operand_& o1) noexcept;
|
||||
inline FuncRetNode* newRet(const Operand_& o0, const Operand_& o1) {
|
||||
FuncRetNode* node;
|
||||
_newRetNode(&node, o0, o1);
|
||||
return node;
|
||||
}
|
||||
|
||||
inline FuncRetNode* addRet(const Operand_& o0, const Operand_& o1) {
|
||||
FuncRetNode* node;
|
||||
_addRetNode(&node, o0, o1);
|
||||
return node;
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Function Calls
|
||||
//! \name Function Invocation
|
||||
//! \{
|
||||
|
||||
//! Creates a new `FuncCallNode`.
|
||||
ASMJIT_API FuncCallNode* newCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept;
|
||||
//! Adds a new `FuncCallNode`.
|
||||
ASMJIT_API FuncCallNode* addCall(uint32_t instId, const Operand_& o0, const FuncSignature& sign) noexcept;
|
||||
//! Creates a new \ref InvokeNode.
|
||||
ASMJIT_API Error _newInvokeNode(InvokeNode** out, uint32_t instId, const Operand_& o0, const FuncSignature& signature);
|
||||
//! Creates a new \ref InvokeNode and adds it to Compiler.
|
||||
ASMJIT_API Error _addInvokeNode(InvokeNode** out, uint32_t instId, const Operand_& o0, const FuncSignature& signature);
|
||||
|
||||
//! Creates a new `InvokeNode`.
|
||||
inline InvokeNode* newCall(uint32_t instId, const Operand_& o0, const FuncSignature& signature) {
|
||||
InvokeNode* node;
|
||||
_newInvokeNode(&node, instId, o0, signature);
|
||||
return node;
|
||||
}
|
||||
|
||||
//! Adds a new `InvokeNode`.
|
||||
inline InvokeNode* addCall(uint32_t instId, const Operand_& o0, const FuncSignature& signature) {
|
||||
InvokeNode* node;
|
||||
_addInvokeNode(&node, instId, o0, signature);
|
||||
return node;
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
@ -268,13 +314,27 @@ public:
|
||||
//! \{
|
||||
|
||||
//! Creates a new virtual register representing the given `typeId` and `signature`.
|
||||
ASMJIT_API VirtReg* newVirtReg(uint32_t typeId, uint32_t signature, const char* name) noexcept;
|
||||
//!
|
||||
//! \note This function is public, but it's not generally recommended to be used
|
||||
//! by AsmJit users, use architecture-specific `newReg()` functionality instead
|
||||
//! or functions like \ref _newReg() and \ref _newRegFmt().
|
||||
ASMJIT_API Error newVirtReg(VirtReg** out, uint32_t typeId, uint32_t signature, const char* name);
|
||||
|
||||
ASMJIT_API Error _newReg(BaseReg& out, uint32_t typeId, const char* name = nullptr);
|
||||
ASMJIT_API Error _newRegFmt(BaseReg& out, uint32_t typeId, const char* fmt, ...);
|
||||
//! Creates a new virtual register of the given `typeId` and stores it to `out` operand.
|
||||
ASMJIT_API Error _newReg(BaseReg* out, uint32_t typeId, const char* name = nullptr);
|
||||
|
||||
ASMJIT_API Error _newReg(BaseReg& out, const BaseReg& ref, const char* name = nullptr);
|
||||
ASMJIT_API Error _newRegFmt(BaseReg& out, const BaseReg& ref, const char* fmt, ...);
|
||||
//! Creates a new virtual register of the given `typeId` and stores it to `out` operand.
|
||||
//!
|
||||
//! \note This version accepts a snprintf() format `fmt` followed by a variadic arguments.
|
||||
ASMJIT_API Error _newRegFmt(BaseReg* out, uint32_t typeId, const char* fmt, ...);
|
||||
|
||||
//! Creates a new virtual register compatible with the provided reference register `ref`.
|
||||
ASMJIT_API Error _newReg(BaseReg* out, const BaseReg& ref, const char* name = nullptr);
|
||||
|
||||
//! Creates a new virtual register compatible with the provided reference register `ref`.
|
||||
//!
|
||||
//! \note This version accepts a snprintf() format `fmt` followed by a variadic arguments.
|
||||
ASMJIT_API Error _newRegFmt(BaseReg* out, const BaseReg& ref, const char* fmt, ...);
|
||||
|
||||
//! Tests whether the given `id` is a valid virtual register id.
|
||||
inline bool isVirtIdValid(uint32_t id) const noexcept {
|
||||
@ -286,14 +346,20 @@ public:
|
||||
return isVirtIdValid(reg.id());
|
||||
}
|
||||
|
||||
//! Returns `VirtReg` associated with the given `id`.
|
||||
//! Returns \ref VirtReg associated with the given `id`.
|
||||
inline VirtReg* virtRegById(uint32_t id) const noexcept {
|
||||
ASMJIT_ASSERT(isVirtIdValid(id));
|
||||
return _vRegArray[Operand::virtIdToIndex(id)];
|
||||
}
|
||||
//! Returns `VirtReg` associated with the given `reg`.
|
||||
|
||||
//! Returns \ref VirtReg associated with the given `reg`.
|
||||
inline VirtReg* virtRegByReg(const BaseReg& reg) const noexcept { return virtRegById(reg.id()); }
|
||||
//! Returns `VirtReg` associated with the given `index`.
|
||||
|
||||
//! Returns \ref VirtReg associated with the given virtual register `index`.
|
||||
//!
|
||||
//! \note This is not the same as virtual register id. The conversion between
|
||||
//! id and its index is implemented by \ref Operand_::virtIdToIndex() and \ref
|
||||
//! Operand_::indexToVirtId() functions.
|
||||
inline VirtReg* virtRegByIndex(uint32_t index) const noexcept { return _vRegArray[index]; }
|
||||
|
||||
//! Returns an array of all virtual registers managed by the Compiler.
|
||||
@ -302,13 +368,16 @@ public:
|
||||
//! \name Stack
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error _newStack(BaseMem& out, uint32_t size, uint32_t alignment, const char* name = nullptr);
|
||||
//! Creates a new stack of the given `size` and `alignment` and stores it to `out`.
|
||||
//!
|
||||
//! \note `name` can be used to give the stack a name, for debugging purposes.
|
||||
ASMJIT_API Error _newStack(BaseMem* out, uint32_t size, uint32_t alignment, const char* name = nullptr);
|
||||
|
||||
//! Updates the stack size of a stack created by `_newStack()` by its `virtId`.
|
||||
ASMJIT_API Error setStackSize(uint32_t virtId, uint32_t newSize, uint32_t newAlignment = 0) noexcept;
|
||||
ASMJIT_API Error setStackSize(uint32_t virtId, uint32_t newSize, uint32_t newAlignment = 0);
|
||||
|
||||
//! Updates the stack size of a stack created by `_newStack()`.
|
||||
inline Error setStackSize(const BaseMem& mem, uint32_t newSize, uint32_t newAlignment = 0) noexcept {
|
||||
inline Error setStackSize(const BaseMem& mem, uint32_t newSize, uint32_t newAlignment = 0) {
|
||||
return setStackSize(mem.id(), newSize, newAlignment);
|
||||
}
|
||||
|
||||
@ -317,7 +386,11 @@ public:
|
||||
//! \name Constants
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error _newConst(BaseMem& out, uint32_t scope, const void* data, size_t size);
|
||||
//! Creates a new constant of the given `scope` (see \ref ConstPool::Scope).
|
||||
//!
|
||||
//! This function adds a constant of the given `size` to the built-in \ref
|
||||
//! ConstPool and stores the reference to that constant to the `out` operand.
|
||||
ASMJIT_API Error _newConst(BaseMem* out, uint32_t scope, const void* data, size_t size);
|
||||
|
||||
//! \}
|
||||
|
||||
@ -325,8 +398,6 @@ public:
|
||||
//! \{
|
||||
|
||||
//! Rename the given virtual register `reg` to a formatted string `fmt`.
|
||||
//!
|
||||
//! \note Only new name will appear in the logger.
|
||||
ASMJIT_API void rename(const BaseReg& reg, const char* fmt, ...);
|
||||
|
||||
//! \}
|
||||
@ -338,7 +409,7 @@ public:
|
||||
return _jumpAnnotations;
|
||||
}
|
||||
|
||||
ASMJIT_API JumpNode* newJumpNode(uint32_t instId, uint32_t instOptions, const Operand_& o0, JumpAnnotation* annotation) noexcept;
|
||||
ASMJIT_API Error newJumpNode(JumpNode** out, uint32_t instId, uint32_t instOptions, const Operand_& o0, JumpAnnotation* annotation);
|
||||
ASMJIT_API Error emitAnnotatedJump(uint32_t instId, const Operand_& o0, JumpAnnotation* annotation);
|
||||
|
||||
//! Returns a new `JumpAnnotation` instance, which can be used to aggregate
|
||||
@ -348,9 +419,12 @@ public:
|
||||
|
||||
//! \}
|
||||
|
||||
// TODO: These should be removed
|
||||
inline void alloc(BaseReg& reg) { DebugUtils::unused(reg); }
|
||||
inline void spill(BaseReg& reg) { DebugUtils::unused(reg); }
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("alloc() has no effect, it will be removed in the future")
|
||||
inline void alloc(BaseReg&) {}
|
||||
ASMJIT_DEPRECATED("spill() has no effect, it will be removed in the future")
|
||||
inline void spill(BaseReg&) {}
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
|
||||
//! \name Events
|
||||
//! \{
|
||||
@ -365,26 +439,44 @@ public:
|
||||
// [asmjit::JumpAnnotation]
|
||||
// ============================================================================
|
||||
|
||||
//! Jump annotation used to annotate jumps.
|
||||
//!
|
||||
//! \ref BaseCompiler allows to emit jumps where the target is either register
|
||||
//! or memory operand. Such jumps cannot be trivially inspected, so instead of
|
||||
//! doing heuristics AsmJit allows to annotate such jumps with possible targets.
|
||||
//! Register allocator then use the annotation to construct control-flow, which
|
||||
//! is then used by liveness analysis and other tools to prepare ground for
|
||||
//! register allocation.
|
||||
class JumpAnnotation {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(JumpAnnotation)
|
||||
|
||||
//! Compiler that owns this JumpAnnotation.
|
||||
BaseCompiler* _compiler;
|
||||
//! Annotation identifier.
|
||||
uint32_t _annotationId;
|
||||
//! Vector of label identifiers, see \ref labelIds().
|
||||
ZoneVector<uint32_t> _labelIds;
|
||||
|
||||
inline JumpAnnotation(BaseCompiler* compiler, uint32_t annotationId) noexcept
|
||||
: _compiler(compiler),
|
||||
_annotationId(annotationId) {}
|
||||
|
||||
//! Returns the compiler that owns this JumpAnnotation.
|
||||
inline BaseCompiler* compiler() const noexcept { return _compiler; }
|
||||
//! Returns the annotation id.
|
||||
inline uint32_t annotationId() const noexcept { return _annotationId; }
|
||||
//! Returns a vector of label identifiers that lists all targets of the jump.
|
||||
const ZoneVector<uint32_t>& labelIds() const noexcept { return _labelIds; }
|
||||
|
||||
//! Tests whether the given `label` is a target of this JumpAnnotation.
|
||||
inline bool hasLabel(const Label& label) const noexcept { return hasLabelId(label.id()); }
|
||||
//! Tests whether the given `labelId` is a target of this JumpAnnotation.
|
||||
inline bool hasLabelId(uint32_t labelId) const noexcept { return _labelIds.contains(labelId); }
|
||||
|
||||
//! Adds the `label` to the list of targets of this JumpAnnotation.
|
||||
inline Error addLabel(const Label& label) noexcept { return addLabelId(label.id()); }
|
||||
//! Adds the `labelId` to the list of targets of this JumpAnnotation.
|
||||
inline Error addLabelId(uint32_t labelId) noexcept { return _labelIds.append(&_compiler->_allocator, labelId); }
|
||||
};
|
||||
|
||||
@ -418,8 +510,11 @@ public:
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Tests whether this JumpNode has associated a \ref JumpAnnotation.
|
||||
inline bool hasAnnotation() const noexcept { return _annotation != nullptr; }
|
||||
//! Returns the \ref JumpAnnotation associated with this jump, or `nullptr`.
|
||||
inline JumpAnnotation* annotation() const noexcept { return _annotation; }
|
||||
//! Sets the \ref JumpAnnotation associated with this jump to `annotation`.
|
||||
inline void setAnnotation(JumpAnnotation* annotation) noexcept { _annotation = annotation; }
|
||||
|
||||
//! \}
|
||||
@ -429,7 +524,51 @@ public:
|
||||
// [asmjit::FuncNode]
|
||||
// ============================================================================
|
||||
|
||||
//! Function entry (BaseCompiler).
|
||||
//! Function node represents a function used by \ref BaseCompiler.
|
||||
//!
|
||||
//! A function is composed of the following:
|
||||
//!
|
||||
//! - Function entry, \ref FuncNode acts as a label, so the entry is implicit.
|
||||
//! To get the entry, simply use \ref FuncNode::label(), which is the same
|
||||
//! as \ref LabelNode::label().
|
||||
//!
|
||||
//! - Function exit, which is represented by \ref FuncNode::exitNode(). A
|
||||
//! helper function \ref FuncNode::exitLabel() exists and returns an exit
|
||||
//! label instead of node.
|
||||
//!
|
||||
//! - Function \ref FuncNode::endNode() sentinel. This node marks the end of
|
||||
//! a function - there should be no code that belongs to the function after
|
||||
//! this node, but the Compiler doesn't enforce that at the moment.
|
||||
//!
|
||||
//! - Function detail, see \ref FuncNode::detail().
|
||||
//!
|
||||
//! - Function frame, see \ref FuncNode::frame().
|
||||
//!
|
||||
//! - Function arguments mapped to virtual registers, see \ref FuncNode::args().
|
||||
//!
|
||||
//! In a node list, the function and its body looks like the following:
|
||||
//!
|
||||
//! \code{.unparsed}
|
||||
//! [...] - Anything before the function.
|
||||
//!
|
||||
//! [FuncNode] - Entry point of the function, acts as a label as well.
|
||||
//! <Prolog> - Prolog inserted by the register allocator.
|
||||
//! {...} - Function body - user code basically.
|
||||
//! [ExitLabel] - Exit label
|
||||
//! <Epilog> - Epilog inserted by the register allocator.
|
||||
//! <Return> - Return inserted by the register allocator.
|
||||
//! {...} - Can contain data or user code (error handling, special cases, ...).
|
||||
//! [FuncEnd] - End sentinel
|
||||
//!
|
||||
//! [...] - Anything after the function.
|
||||
//! \endcode
|
||||
//!
|
||||
//! When a function is added to the compiler by \ref BaseCompiler::addFunc() it
|
||||
//! actually inserts 3 nodes (FuncNode, ExitLabel, and FuncEnd) and sets the
|
||||
//! current cursor to be FuncNode. When \ref BaseCompiler::endFunc() is called
|
||||
//! the cursor is set to FuncEnd. This guarantees that user can use ExitLabel
|
||||
//! as a marker after additional code or data can be placed, and it's a common
|
||||
//! practice.
|
||||
class FuncNode : public LabelNode {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(FuncNode)
|
||||
@ -438,7 +577,7 @@ public:
|
||||
FuncDetail _funcDetail;
|
||||
//! Function frame.
|
||||
FuncFrame _frame;
|
||||
//! Function exit (label).
|
||||
//! Function exit label.
|
||||
LabelNode* _exitNode;
|
||||
//! Function end (sentinel).
|
||||
SentinelNode* _end;
|
||||
@ -510,7 +649,9 @@ public:
|
||||
_args[i] = nullptr;
|
||||
}
|
||||
|
||||
//! Returns function attributes.
|
||||
inline uint32_t attributes() const noexcept { return _frame.attributes(); }
|
||||
//! Adds `attrs` to the function attributes.
|
||||
inline void addAttributes(uint32_t attrs) noexcept { _frame.addAttributes(attrs); }
|
||||
|
||||
//! \}
|
||||
@ -520,7 +661,7 @@ public:
|
||||
// [asmjit::FuncRetNode]
|
||||
// ============================================================================
|
||||
|
||||
//! Function return (BaseCompiler).
|
||||
//! Function return, used by \ref BaseCompiler.
|
||||
class FuncRetNode : public InstNode {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(FuncRetNode)
|
||||
@ -537,13 +678,13 @@ public:
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::FuncCallNode]
|
||||
// [asmjit::InvokeNode]
|
||||
// ============================================================================
|
||||
|
||||
//! Function call (BaseCompiler).
|
||||
class FuncCallNode : public InstNode {
|
||||
//! Function invocation, used by \ref BaseCompiler.
|
||||
class InvokeNode : public InstNode {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(FuncCallNode)
|
||||
ASMJIT_NONCOPYABLE(InvokeNode)
|
||||
|
||||
//! Function detail.
|
||||
FuncDetail _funcDetail;
|
||||
@ -555,12 +696,12 @@ public:
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a new `FuncCallNode` instance.
|
||||
inline FuncCallNode(BaseBuilder* cb, uint32_t instId, uint32_t options) noexcept
|
||||
//! Creates a new `InvokeNode` instance.
|
||||
inline InvokeNode(BaseBuilder* cb, uint32_t instId, uint32_t options) noexcept
|
||||
: InstNode(cb, instId, options, kBaseOpCapacity),
|
||||
_funcDetail(),
|
||||
_args(nullptr) {
|
||||
setType(kNodeFuncCall);
|
||||
setType(kNodeInvoke);
|
||||
_resetOps();
|
||||
_rets[0].reset();
|
||||
_rets[1].reset();
|
||||
@ -573,8 +714,8 @@ public:
|
||||
//! \{
|
||||
|
||||
//! Sets the function signature.
|
||||
inline Error setSignature(const FuncSignature& sign) noexcept {
|
||||
return _funcDetail.init(sign);
|
||||
inline Error init(const FuncSignature& signature, const Environment& environment) noexcept {
|
||||
return _funcDetail.init(signature, environment);
|
||||
}
|
||||
|
||||
//! Returns the function detail.
|
||||
@ -634,6 +775,7 @@ public:
|
||||
// [asmjit::FuncPass]
|
||||
// ============================================================================
|
||||
|
||||
//! Function pass extends \ref Pass with \ref FuncPass::runOnFunction().
|
||||
class ASMJIT_VIRTAPI FuncPass : public Pass {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(FuncPass)
|
||||
@ -658,10 +800,10 @@ public:
|
||||
//! \{
|
||||
|
||||
//! Calls `runOnFunction()` on each `FuncNode` node found.
|
||||
ASMJIT_API Error run(Zone* zone, Logger* logger) noexcept override;
|
||||
ASMJIT_API Error run(Zone* zone, Logger* logger) override;
|
||||
|
||||
//! Called once per `FuncNode`.
|
||||
virtual Error runOnFunction(Zone* zone, Logger* logger, FuncNode* func) noexcept = 0;
|
||||
virtual Error runOnFunction(Zone* zone, Logger* logger, FuncNode* func) = 0;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
37
3rdparty/asmjit/src/asmjit/core/constpool.h
vendored
37
3rdparty/asmjit/src/asmjit/core/constpool.h
vendored
@ -30,7 +30,7 @@
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \addtogroup asmjit_utilities
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
@ -65,9 +65,12 @@ public:
|
||||
|
||||
//! Zone-allocated const-pool gap created by two differently aligned constants.
|
||||
struct Gap {
|
||||
Gap* _next; //!< Pointer to the next gap
|
||||
size_t _offset; //!< Offset of the gap.
|
||||
size_t _size; //!< Remaining bytes of the gap (basically a gap size).
|
||||
//! Pointer to the next gap
|
||||
Gap* _next;
|
||||
//! Offset of the gap.
|
||||
size_t _offset;
|
||||
//! Remaining bytes of the gap (basically a gap size).
|
||||
size_t _size;
|
||||
};
|
||||
|
||||
//! Zone-allocated const-pool node.
|
||||
@ -75,6 +78,11 @@ public:
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(Node)
|
||||
|
||||
//! If this constant is shared with another.
|
||||
uint32_t _shared : 1;
|
||||
//! Data offset from the beginning of the pool.
|
||||
uint32_t _offset;
|
||||
|
||||
inline Node(size_t offset, bool shared) noexcept
|
||||
: ZoneTreeNodeT<Node>(),
|
||||
_shared(shared),
|
||||
@ -83,14 +91,13 @@ public:
|
||||
inline void* data() const noexcept {
|
||||
return static_cast<void*>(const_cast<ConstPool::Node*>(this) + 1);
|
||||
}
|
||||
|
||||
uint32_t _shared : 1; //!< If this constant is shared with another.
|
||||
uint32_t _offset; //!< Data offset from the beginning of the pool.
|
||||
};
|
||||
|
||||
//! Data comparer used internally.
|
||||
class Compare {
|
||||
public:
|
||||
size_t _dataSize;
|
||||
|
||||
inline Compare(size_t dataSize) noexcept
|
||||
: _dataSize(dataSize) {}
|
||||
|
||||
@ -101,12 +108,17 @@ public:
|
||||
inline int operator()(const Node& a, const void* data) const noexcept {
|
||||
return ::memcmp(a.data(), data, _dataSize);
|
||||
}
|
||||
|
||||
size_t _dataSize;
|
||||
};
|
||||
|
||||
//! Zone-allocated const-pool tree.
|
||||
struct Tree {
|
||||
//! RB tree.
|
||||
ZoneTree<Node> _tree;
|
||||
//! Size of the tree (number of nodes).
|
||||
size_t _size;
|
||||
//! Size of the data.
|
||||
size_t _dataSize;
|
||||
|
||||
inline explicit Tree(size_t dataSize = 0) noexcept
|
||||
: _tree(),
|
||||
_size(0),
|
||||
@ -177,13 +189,6 @@ public:
|
||||
memcpy(node->data(), data, size);
|
||||
return node;
|
||||
}
|
||||
|
||||
//! RB tree.
|
||||
ZoneTree<Node> _tree;
|
||||
//! Size of the tree (number of nodes).
|
||||
size_t _size;
|
||||
//! Size of the data.
|
||||
size_t _dataSize;
|
||||
};
|
||||
|
||||
//! \endcond
|
||||
|
30
3rdparty/asmjit/src/asmjit/core/cpuinfo.h
vendored
30
3rdparty/asmjit/src/asmjit/core/cpuinfo.h
vendored
@ -31,7 +31,7 @@
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_support
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
@ -41,8 +41,12 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
//! CPU information.
|
||||
class CpuInfo {
|
||||
public:
|
||||
//! CPU architecture information.
|
||||
ArchInfo _archInfo;
|
||||
//! Architecture.
|
||||
uint8_t _arch;
|
||||
//! Sub-architecture.
|
||||
uint8_t _subArch;
|
||||
//! Reserved for future use.
|
||||
uint16_t _reserved;
|
||||
//! CPU family ID.
|
||||
uint32_t _familyId;
|
||||
//! CPU model ID.
|
||||
@ -74,15 +78,15 @@ public:
|
||||
inline CpuInfo(const CpuInfo& other) noexcept = default;
|
||||
|
||||
inline explicit CpuInfo(Globals::NoInit_) noexcept
|
||||
: _archInfo(Globals::NoInit),
|
||||
_features(Globals::NoInit) {};
|
||||
: _features(Globals::NoInit) {};
|
||||
|
||||
//! Returns the host CPU information.
|
||||
ASMJIT_API static const CpuInfo& host() noexcept;
|
||||
|
||||
//! Initializes CpuInfo to the given architecture, see `ArchInfo`.
|
||||
inline void initArch(uint32_t archId, uint32_t archMode = 0) noexcept {
|
||||
_archInfo.init(archId, archMode);
|
||||
//! Initializes CpuInfo to the given architecture, see \ref Environment.
|
||||
inline void initArch(uint32_t arch, uint32_t subArch = 0u) noexcept {
|
||||
_arch = uint8_t(arch);
|
||||
_subArch = uint8_t(subArch);
|
||||
}
|
||||
|
||||
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
|
||||
@ -99,12 +103,10 @@ public:
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the CPU architecture information.
|
||||
inline const ArchInfo& archInfo() const noexcept { return _archInfo; }
|
||||
//! Returns the CPU architecture id, see `ArchInfo::Id`.
|
||||
inline uint32_t archId() const noexcept { return _archInfo.archId(); }
|
||||
//! Returns the CPU architecture sub-id, see `ArchInfo::SubId`.
|
||||
inline uint32_t archSubId() const noexcept { return _archInfo.archSubId(); }
|
||||
//! Returns the CPU architecture id, see \ref Environment::Arch.
|
||||
inline uint32_t arch() const noexcept { return _arch; }
|
||||
//! Returns the CPU architecture sub-id, see \ref Environment::SubArch.
|
||||
inline uint32_t subArch() const noexcept { return _subArch; }
|
||||
|
||||
//! Returns the CPU family ID.
|
||||
inline uint32_t familyId() const noexcept { return _familyId; }
|
||||
|
14
3rdparty/asmjit/src/asmjit/core/datatypes.h
vendored
14
3rdparty/asmjit/src/asmjit/core/datatypes.h
vendored
@ -26,17 +26,16 @@
|
||||
|
||||
#include "../core/globals.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
|
||||
//! \addtogroup asmjit_support
|
||||
//! \{
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Data64]
|
||||
// ============================================================================
|
||||
|
||||
//! 64-bit data useful for creating SIMD constants.
|
||||
union Data64 {
|
||||
union ASMJIT_DEPRECATED_STRUCT("Data64 is deprecated and will be removed in the future") Data64 {
|
||||
//! Array of eight 8-bit signed integers.
|
||||
int8_t sb[8];
|
||||
//! Array of eight 8-bit unsigned integers.
|
||||
@ -303,7 +302,7 @@ union Data64 {
|
||||
// ============================================================================
|
||||
|
||||
//! 128-bit data useful for creating SIMD constants.
|
||||
union Data128 {
|
||||
union ASMJIT_DEPRECATED_STRUCT("Data128 is deprecated and will be removed in the future") Data128 {
|
||||
//! Array of sixteen 8-bit signed integers.
|
||||
int8_t sb[16];
|
||||
//! Array of sixteen 8-bit unsigned integers.
|
||||
@ -648,7 +647,7 @@ union Data128 {
|
||||
// ============================================================================
|
||||
|
||||
//! 256-bit data useful for creating SIMD constants.
|
||||
union Data256 {
|
||||
union ASMJIT_DEPRECATED_STRUCT("Data256 is deprecated and will be removed in the future") Data256 {
|
||||
//! Array of thirty two 8-bit signed integers.
|
||||
int8_t sb[32];
|
||||
//! Array of thirty two 8-bit unsigned integers.
|
||||
@ -1066,8 +1065,7 @@ union Data256 {
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
#endif // ASMJIT_CORE_DATATYPES_H_INCLUDED
|
||||
|
282
3rdparty/asmjit/src/asmjit/core/emitter.cpp
vendored
282
3rdparty/asmjit/src/asmjit/core/emitter.cpp
vendored
@ -22,7 +22,9 @@
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "../core/api-build_p.h"
|
||||
#include "../core/logging.h"
|
||||
#include "../core/emitterutils_p.h"
|
||||
#include "../core/errorhandler.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
@ -41,56 +43,30 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::BaseEmitter - Construction / Destruction]
|
||||
// ============================================================================
|
||||
|
||||
BaseEmitter::BaseEmitter(uint32_t type) noexcept
|
||||
: _type(uint8_t(type)),
|
||||
_reserved(0),
|
||||
_flags(0),
|
||||
_emitterOptions(0),
|
||||
_code(nullptr),
|
||||
_errorHandler(nullptr),
|
||||
_codeInfo(),
|
||||
_gpRegInfo(),
|
||||
BaseEmitter::BaseEmitter(uint32_t emitterType) noexcept
|
||||
: _emitterType(uint8_t(emitterType)),
|
||||
_emitterFlags(0),
|
||||
_validationFlags(0),
|
||||
_validationOptions(0),
|
||||
_encodingOptions(0),
|
||||
_forcedInstOptions(BaseInst::kOptionReserved),
|
||||
_privateData(0),
|
||||
_code(nullptr),
|
||||
_logger(nullptr),
|
||||
_errorHandler(nullptr),
|
||||
_environment(),
|
||||
_gpRegInfo(),
|
||||
_instOptions(0),
|
||||
_globalInstOptions(BaseInst::kOptionReserved),
|
||||
_extraReg(),
|
||||
_inlineComment(nullptr) {}
|
||||
|
||||
BaseEmitter::~BaseEmitter() noexcept {
|
||||
if (_code) {
|
||||
_addFlags(kFlagDestroyed);
|
||||
_addEmitterFlags(kFlagDestroyed);
|
||||
_code->detach(this);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseEmitter - Code-Generation]
|
||||
// ============================================================================
|
||||
|
||||
Error BaseEmitter::_emitOpArray(uint32_t instId, const Operand_* operands, size_t count) {
|
||||
const Operand_* op = operands;
|
||||
const Operand& none_ = Globals::none;
|
||||
|
||||
switch (count) {
|
||||
case 0: return _emit(instId, none_, none_, none_, none_);
|
||||
case 1: return _emit(instId, op[0], none_, none_, none_);
|
||||
case 2: return _emit(instId, op[0], op[1], none_, none_);
|
||||
case 3: return _emit(instId, op[0], op[1], op[2], none_);
|
||||
case 4: return _emit(instId, op[0], op[1], op[2], op[3]);
|
||||
case 5: return _emit(instId, op[0], op[1], op[2], op[3], op[4], none_);
|
||||
case 6: return _emit(instId, op[0], op[1], op[2], op[3], op[4], op[5]);
|
||||
default: return DebugUtils::errored(kErrorInvalidArgument);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseEmitter - Finalize]
|
||||
// ============================================================================
|
||||
|
||||
Label BaseEmitter::labelByName(const char* name, size_t nameSize, uint32_t parentId) noexcept {
|
||||
return Label(_code ? _code->labelIdByName(name, nameSize, parentId) : uint32_t(Globals::kInvalidId));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseEmitter - Finalize]
|
||||
// ============================================================================
|
||||
@ -100,34 +76,178 @@ Error BaseEmitter::finalize() {
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseEmitter - Internals]
|
||||
// ============================================================================
|
||||
|
||||
static constexpr uint32_t kEmitterPreservedFlags =
|
||||
BaseEmitter::kFlagOwnLogger |
|
||||
BaseEmitter::kFlagOwnErrorHandler ;
|
||||
|
||||
static ASMJIT_NOINLINE void BaseEmitter_updateForcedOptions(BaseEmitter* self) noexcept {
|
||||
bool hasLogger = self->_logger != nullptr;
|
||||
bool hasValidationOptions;
|
||||
|
||||
if (self->emitterType() == BaseEmitter::kTypeAssembler)
|
||||
hasValidationOptions = self->hasValidationOption(BaseEmitter::kValidationOptionAssembler);
|
||||
else
|
||||
hasValidationOptions = self->hasValidationOption(BaseEmitter::kValidationOptionIntermediate);
|
||||
|
||||
self->_forcedInstOptions &= ~BaseInst::kOptionReserved;
|
||||
if (hasLogger || hasValidationOptions)
|
||||
self->_forcedInstOptions |= BaseInst::kOptionReserved;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseEmitter - Validation Options]
|
||||
// ============================================================================
|
||||
|
||||
void BaseEmitter::addValidationOptions(uint32_t options) noexcept {
|
||||
_validationOptions = uint8_t(_validationOptions | options);
|
||||
BaseEmitter_updateForcedOptions(this);
|
||||
}
|
||||
|
||||
void BaseEmitter::clearValidationOptions(uint32_t options) noexcept {
|
||||
_validationOptions = uint8_t(_validationOptions | options);
|
||||
BaseEmitter_updateForcedOptions(this);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseEmitter - Logging]
|
||||
// ============================================================================
|
||||
|
||||
void BaseEmitter::setLogger(Logger* logger) noexcept {
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (logger) {
|
||||
_logger = logger;
|
||||
_addEmitterFlags(kFlagOwnLogger);
|
||||
}
|
||||
else {
|
||||
_logger = nullptr;
|
||||
_clearEmitterFlags(kFlagOwnLogger);
|
||||
if (_code)
|
||||
_logger = _code->logger();
|
||||
}
|
||||
BaseEmitter_updateForcedOptions(this);
|
||||
#else
|
||||
DebugUtils::unused(logger);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseEmitter - Error Handling]
|
||||
// ============================================================================
|
||||
|
||||
Error BaseEmitter::reportError(Error err, const char* message) {
|
||||
ErrorHandler* handler = errorHandler();
|
||||
if (!handler) {
|
||||
if (code())
|
||||
handler = code()->errorHandler();
|
||||
void BaseEmitter::setErrorHandler(ErrorHandler* errorHandler) noexcept {
|
||||
if (errorHandler) {
|
||||
_errorHandler = errorHandler;
|
||||
_addEmitterFlags(kFlagOwnErrorHandler);
|
||||
}
|
||||
else {
|
||||
_errorHandler = nullptr;
|
||||
_clearEmitterFlags(kFlagOwnErrorHandler);
|
||||
if (_code)
|
||||
_errorHandler = _code->errorHandler();
|
||||
}
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
Error BaseEmitter::reportError(Error err, const char* message) {
|
||||
ErrorHandler* eh = _errorHandler;
|
||||
if (eh) {
|
||||
if (!message)
|
||||
message = DebugUtils::errorAsString(err);
|
||||
handler->handleError(err, message, this);
|
||||
eh->handleError(err, message, this);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseEmitter - Label Management]
|
||||
// [asmjit::BaseEmitter - Labels]
|
||||
// ============================================================================
|
||||
|
||||
Label BaseEmitter::labelByName(const char* name, size_t nameSize, uint32_t parentId) noexcept {
|
||||
return Label(_code ? _code->labelIdByName(name, nameSize, parentId) : uint32_t(Globals::kInvalidId));
|
||||
}
|
||||
|
||||
bool BaseEmitter::isLabelValid(uint32_t labelId) const noexcept {
|
||||
return _code && labelId < _code->labelCount();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseEmitter - Emit (Low-Level)]
|
||||
// ============================================================================
|
||||
|
||||
using EmitterUtils::noExt;
|
||||
|
||||
Error BaseEmitter::_emitI(uint32_t instId) {
|
||||
return _emit(instId, noExt[0], noExt[1], noExt[2], noExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(uint32_t instId, const Operand_& o0) {
|
||||
return _emit(instId, o0, noExt[1], noExt[2], noExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(uint32_t instId, const Operand_& o0, const Operand_& o1) {
|
||||
return _emit(instId, o0, o1, noExt[2], noExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2) {
|
||||
return _emit(instId, o0, o1, o2, noExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) {
|
||||
Operand_ opExt[3] = { o3 };
|
||||
return _emit(instId, o0, o1, o2, opExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4) {
|
||||
Operand_ opExt[3] = { o3, o4 };
|
||||
return _emit(instId, o0, o1, o2, opExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitI(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) {
|
||||
Operand_ opExt[3] = { o3, o4, o5 };
|
||||
return _emit(instId, o0, o1, o2, opExt);
|
||||
}
|
||||
|
||||
Error BaseEmitter::_emitOpArray(uint32_t instId, const Operand_* operands, size_t opCount) {
|
||||
const Operand_* op = operands;
|
||||
|
||||
Operand_ opExt[3];
|
||||
|
||||
switch (opCount) {
|
||||
case 0:
|
||||
return _emit(instId, noExt[0], noExt[1], noExt[2], noExt);
|
||||
|
||||
case 1:
|
||||
return _emit(instId, op[0], noExt[1], noExt[2], noExt);
|
||||
|
||||
case 2:
|
||||
return _emit(instId, op[0], op[1], noExt[2], noExt);
|
||||
|
||||
case 3:
|
||||
return _emit(instId, op[0], op[1], op[2], noExt);
|
||||
|
||||
case 4:
|
||||
opExt[0] = op[3];
|
||||
opExt[1].reset();
|
||||
opExt[2].reset();
|
||||
return _emit(instId, op[0], op[1], op[2], opExt);
|
||||
|
||||
case 5:
|
||||
opExt[0] = op[3];
|
||||
opExt[1] = op[4];
|
||||
opExt[2].reset();
|
||||
return _emit(instId, op[0], op[1], op[2], opExt);
|
||||
|
||||
case 6:
|
||||
return _emit(instId, op[0], op[1], op[2], op + 3);
|
||||
|
||||
default:
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseEmitter - Emit (High-Level)]
|
||||
// ============================================================================
|
||||
@ -137,12 +257,12 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitProlog(const FuncFrame& frame) {
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (archInfo().isX86Family())
|
||||
if (environment().isFamilyX86())
|
||||
return x86::X86Internal::emitProlog(as<x86::Emitter>(), frame);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (archInfo().isArmFamily())
|
||||
if (environment().isFamilyARM())
|
||||
return arm::ArmInternal::emitProlog(as<arm::Emitter>(), frame);
|
||||
#endif
|
||||
|
||||
@ -154,12 +274,12 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitEpilog(const FuncFrame& frame) {
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (archInfo().isX86Family())
|
||||
if (environment().isFamilyX86())
|
||||
return x86::X86Internal::emitEpilog(as<x86::Emitter>(), frame);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (archInfo().isArmFamily())
|
||||
if (environment().isFamilyARM())
|
||||
return arm::ArmInternal::emitEpilog(as<arm::Emitter>(), frame);
|
||||
#endif
|
||||
|
||||
@ -171,12 +291,12 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitArgsAssignment(const FuncFrame& frame,
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (archInfo().isX86Family())
|
||||
if (environment().isFamilyX86())
|
||||
return x86::X86Internal::emitArgsAssignment(as<x86::Emitter>(), frame, args);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (archInfo().isArmFamily())
|
||||
if (environment().isFamilyARM())
|
||||
return arm::ArmInternal::emitArgsAssignment(as<arm::Emitter>(), frame, args);
|
||||
#endif
|
||||
|
||||
@ -192,17 +312,11 @@ Error BaseEmitter::commentf(const char* fmt, ...) {
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
StringTmp<1024> sb;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
Error err = sb.appendVFormat(fmt, ap);
|
||||
Error err = commentv(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return err;
|
||||
|
||||
return comment(sb.data(), sb.size());
|
||||
return err;
|
||||
#else
|
||||
DebugUtils::unused(fmt);
|
||||
return kErrorOk;
|
||||
@ -215,8 +329,8 @@ Error BaseEmitter::commentv(const char* fmt, va_list ap) {
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
StringTmp<1024> sb;
|
||||
|
||||
Error err = sb.appendVFormat(fmt, ap);
|
||||
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
return err;
|
||||
|
||||
@ -233,40 +347,46 @@ Error BaseEmitter::commentv(const char* fmt, va_list ap) {
|
||||
|
||||
Error BaseEmitter::onAttach(CodeHolder* code) noexcept {
|
||||
_code = code;
|
||||
_codeInfo = code->codeInfo();
|
||||
_emitterOptions = code->emitterOptions();
|
||||
_environment = code->environment();
|
||||
|
||||
onUpdateGlobalInstOptions();
|
||||
onSettingsUpdated();
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error BaseEmitter::onDetach(CodeHolder* code) noexcept {
|
||||
DebugUtils::unused(code);
|
||||
|
||||
_flags = 0;
|
||||
_emitterOptions = 0;
|
||||
_errorHandler = nullptr;
|
||||
|
||||
_codeInfo.reset();
|
||||
_gpRegInfo.reset();
|
||||
_clearEmitterFlags(~kEmitterPreservedFlags);
|
||||
_forcedInstOptions = BaseInst::kOptionReserved;
|
||||
_privateData = 0;
|
||||
|
||||
if (!hasOwnLogger())
|
||||
_logger = nullptr;
|
||||
|
||||
if (!hasOwnErrorHandler())
|
||||
_errorHandler = nullptr;
|
||||
|
||||
_environment.reset();
|
||||
_gpRegInfo.reset();
|
||||
|
||||
_instOptions = 0;
|
||||
_globalInstOptions = BaseInst::kOptionReserved;
|
||||
_extraReg.reset();
|
||||
_inlineComment = nullptr;
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
void BaseEmitter::onUpdateGlobalInstOptions() noexcept {
|
||||
constexpr uint32_t kCriticalEmitterOptions =
|
||||
kOptionLoggingEnabled |
|
||||
kOptionStrictValidation ;
|
||||
void BaseEmitter::onSettingsUpdated() noexcept {
|
||||
// Only called when attached to CodeHolder by CodeHolder.
|
||||
ASMJIT_ASSERT(_code != nullptr);
|
||||
|
||||
_globalInstOptions &= ~BaseInst::kOptionReserved;
|
||||
if ((_emitterOptions & kCriticalEmitterOptions) != 0)
|
||||
_globalInstOptions |= BaseInst::kOptionReserved;
|
||||
if (!hasOwnLogger())
|
||||
_logger = _code->logger();
|
||||
|
||||
if (!hasOwnErrorHandler())
|
||||
_errorHandler = _code->errorHandler();
|
||||
|
||||
BaseEmitter_updateForcedOptions(this);
|
||||
}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
560
3rdparty/asmjit/src/asmjit/core/emitter.h
vendored
560
3rdparty/asmjit/src/asmjit/core/emitter.h
vendored
@ -25,9 +25,10 @@
|
||||
#define ASMJIT_CORE_EMITTER_H_INCLUDED
|
||||
|
||||
#include "../core/arch.h"
|
||||
#include "../core/codeholder.h"
|
||||
#include "../core/inst.h"
|
||||
#include "../core/operand.h"
|
||||
#include "../core/codeholder.h"
|
||||
#include "../core/type.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
@ -52,31 +53,40 @@ class ASMJIT_VIRTAPI BaseEmitter {
|
||||
public:
|
||||
ASMJIT_BASE_CLASS(BaseEmitter)
|
||||
|
||||
//! See `EmitterType`.
|
||||
uint8_t _type;
|
||||
//! Reserved for future use.
|
||||
uint8_t _reserved;
|
||||
//! See \ref BaseEmitter::Flags.
|
||||
uint16_t _flags;
|
||||
//! Emitter options, always in sync with CodeHolder.
|
||||
uint32_t _emitterOptions;
|
||||
//! See \ref EmitterType.
|
||||
uint8_t _emitterType;
|
||||
//! See \ref BaseEmitter::EmitterFlags.
|
||||
uint8_t _emitterFlags;
|
||||
//! Validation flags in case validation is used, see \ref InstAPI::ValidationFlags.
|
||||
//!
|
||||
//! \note Validation flags are specific to the emitter and they are setup at
|
||||
//! construction time and then never changed.
|
||||
uint8_t _validationFlags;
|
||||
//! Validation options, see \ref ValidationOptions.
|
||||
uint8_t _validationOptions;
|
||||
|
||||
//! CodeHolder the BaseEmitter is attached to.
|
||||
CodeHolder* _code;
|
||||
//! Attached `ErrorHandler`.
|
||||
ErrorHandler* _errorHandler;
|
||||
//! Encoding options, see \ref EncodingOptions.
|
||||
uint32_t _encodingOptions;
|
||||
|
||||
//! Basic information about the code (matches CodeHolder::_codeInfo).
|
||||
CodeInfo _codeInfo;
|
||||
//! Native GP register signature and signature related information.
|
||||
RegInfo _gpRegInfo;
|
||||
//! Forced instruction options, combined with \ref _instOptions by \ref emit().
|
||||
uint32_t _forcedInstOptions;
|
||||
//! Internal private data used freely by any emitter.
|
||||
uint32_t _privateData;
|
||||
|
||||
//! CodeHolder the emitter is attached to.
|
||||
CodeHolder* _code;
|
||||
//! Attached \ref Logger.
|
||||
Logger* _logger;
|
||||
//! Attached \ref ErrorHandler.
|
||||
ErrorHandler* _errorHandler;
|
||||
|
||||
//! Describes the target environment, matches \ref CodeHolder::environment().
|
||||
Environment _environment;
|
||||
//! Native GP register signature and signature related information.
|
||||
RegInfo _gpRegInfo;
|
||||
|
||||
//! Next instruction options (affects the next instruction).
|
||||
uint32_t _instOptions;
|
||||
//! Global Instruction options (combined with `_instOptions` by `emit...()`).
|
||||
uint32_t _globalInstOptions;
|
||||
//! Extra register (op-mask {k} on AVX-512) (affects the next instruction).
|
||||
RegOnly _extraReg;
|
||||
//! Inline comment of the next instruction (affects the next instruction).
|
||||
@ -86,41 +96,34 @@ public:
|
||||
enum EmitterType : uint32_t {
|
||||
//! Unknown or uninitialized.
|
||||
kTypeNone = 0,
|
||||
//! Emitter inherits from `BaseAssembler`.
|
||||
//! Emitter inherits from \ref BaseAssembler.
|
||||
kTypeAssembler = 1,
|
||||
//! Emitter inherits from `BaseBuilder`.
|
||||
//! Emitter inherits from \ref BaseBuilder.
|
||||
kTypeBuilder = 2,
|
||||
//! Emitter inherits from `BaseCompiler`.
|
||||
//! Emitter inherits from \ref BaseCompiler.
|
||||
kTypeCompiler = 3,
|
||||
|
||||
//! Count of emitter types.
|
||||
kTypeCount = 4
|
||||
};
|
||||
|
||||
//! Emitter flags.
|
||||
enum Flags : uint32_t {
|
||||
enum EmitterFlags : uint32_t {
|
||||
//! The emitter has its own \ref Logger (not propagated from \ref CodeHolder).
|
||||
kFlagOwnLogger = 0x10u,
|
||||
//! The emitter has its own \ref ErrorHandler (not propagated from \ref CodeHolder).
|
||||
kFlagOwnErrorHandler = 0x20u,
|
||||
//! The emitter was finalized.
|
||||
kFlagFinalized = 0x4000u,
|
||||
kFlagFinalized = 0x40u,
|
||||
//! The emitter was destroyed.
|
||||
kFlagDestroyed = 0x8000u
|
||||
kFlagDestroyed = 0x80u
|
||||
};
|
||||
|
||||
//! Emitter options.
|
||||
enum Options : uint32_t {
|
||||
//! Logging is enabled, `BaseEmitter::logger()` must return a valid logger.
|
||||
//! This option is set automatically by the emitter if the logger is present.
|
||||
//! User code should never alter this value.
|
||||
//!
|
||||
//! Default `false`.
|
||||
kOptionLoggingEnabled = 0x00000001u,
|
||||
|
||||
//! Stricly validate each instruction before it's emitted.
|
||||
//!
|
||||
//! Default `false`.
|
||||
kOptionStrictValidation = 0x00000002u,
|
||||
|
||||
//! Encoding options.
|
||||
enum EncodingOptions : uint32_t {
|
||||
//! Emit instructions that are optimized for size, if possible.
|
||||
//!
|
||||
//! Default `false`.
|
||||
//! Default: false.
|
||||
//!
|
||||
//! X86 Specific
|
||||
//! ------------
|
||||
@ -130,11 +133,11 @@ public:
|
||||
//! by taking advantage of implicit zero extension. For example instruction
|
||||
//! like `mov r64, imm` and `and r64, imm` can be translated to `mov r32, imm`
|
||||
//! and `and r32, imm` when the immediate constant is lesser than `2^31`.
|
||||
kOptionOptimizedForSize = 0x00000004u,
|
||||
kEncodingOptionOptimizeForSize = 0x00000001u,
|
||||
|
||||
//! Emit optimized code-alignment sequences.
|
||||
//!
|
||||
//! Default `false`.
|
||||
//! Default: false.
|
||||
//!
|
||||
//! X86 Specific
|
||||
//! ------------
|
||||
@ -144,11 +147,11 @@ public:
|
||||
//! more optimized align sequences for 2-11 bytes that may execute faster
|
||||
//! on certain CPUs. If this feature is enabled AsmJit will generate
|
||||
//! specialized sequences for alignment between 2 to 11 bytes.
|
||||
kOptionOptimizedAlign = 0x00000008u,
|
||||
kEncodingOptionOptimizedAlign = 0x00000002u,
|
||||
|
||||
//! Emit jump-prediction hints.
|
||||
//!
|
||||
//! Default `false`.
|
||||
//! Default: false.
|
||||
//!
|
||||
//! X86 Specific
|
||||
//! ------------
|
||||
@ -163,14 +166,56 @@ public:
|
||||
//! 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 and ignore static hints. This
|
||||
//! means that this feature can be used for annotation purposes.
|
||||
kOptionPredictedJumps = 0x00000010u
|
||||
//! means that this feature can be only used for annotation purposes.
|
||||
kEncodingOptionPredictedJumps = 0x00000010u
|
||||
};
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
enum EmitterOptions : uint32_t {
|
||||
kOptionOptimizedForSize = kEncodingOptionOptimizeForSize,
|
||||
kOptionOptimizedAlign = kEncodingOptionOptimizedAlign,
|
||||
kOptionPredictedJumps = kEncodingOptionPredictedJumps
|
||||
};
|
||||
#endif
|
||||
|
||||
//! Validation options are used to tell emitters to perform strict validation
|
||||
//! of instructions passed to \ref emit().
|
||||
//!
|
||||
//! \ref BaseAssembler implementation perform by default only basic checks
|
||||
//! that are necessary to identify all variations of an instruction so the
|
||||
//! correct encoding can be selected. This is fine for production-ready code
|
||||
//! as the assembler doesn't have to perform checks that would slow it down.
|
||||
//! However, sometimes these checks are beneficial especially when the project
|
||||
//! that uses AsmJit is in a development phase, in which mistakes happen often.
|
||||
//! To make the experience of using AsmJit seamless it offers validation
|
||||
//! features that can be controlled by `ValidationOptions`.
|
||||
enum ValidationOptions : uint32_t {
|
||||
//! Perform strict validation in \ref BaseAssembler::emit() implementations.
|
||||
//!
|
||||
//! This flag ensures that each instruction is checked before it's encoded
|
||||
//! into a binary representation. This flag is only relevant for \ref
|
||||
//! BaseAssembler implementations, but can be set in any other emitter type,
|
||||
//! in that case if that emitter needs to create an assembler on its own,
|
||||
//! for the purpose of \ref finalize() it would propagate this flag to such
|
||||
//! assembler so all instructions passed to it are explicitly validated.
|
||||
//!
|
||||
//! Default: false.
|
||||
kValidationOptionAssembler = 0x00000001u,
|
||||
|
||||
//! Perform strict validation in \ref BaseBuilder::emit() and \ref
|
||||
//! BaseCompiler::emit() implementations.
|
||||
//!
|
||||
//! This flag ensures that each instruction is checked before an \ref
|
||||
//! InstNode representing the instruction is created by Builder or Compiler.
|
||||
//!
|
||||
//! Default: false.
|
||||
kValidationOptionIntermediate = 0x00000002u
|
||||
};
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
ASMJIT_API explicit BaseEmitter(uint32_t type) noexcept;
|
||||
ASMJIT_API explicit BaseEmitter(uint32_t emitterType) noexcept;
|
||||
ASMJIT_API virtual ~BaseEmitter() noexcept;
|
||||
|
||||
//! \}
|
||||
@ -190,28 +235,28 @@ public:
|
||||
//! \{
|
||||
|
||||
//! Returns the type of this emitter, see `EmitterType`.
|
||||
inline uint32_t emitterType() const noexcept { return _type; }
|
||||
inline uint32_t emitterType() const noexcept { return _emitterType; }
|
||||
//! Returns emitter flags , see `Flags`.
|
||||
inline uint32_t emitterFlags() const noexcept { return _flags; }
|
||||
inline uint32_t emitterFlags() const noexcept { return _emitterFlags; }
|
||||
|
||||
//! Tests whether the emitter inherits from `BaseAssembler`.
|
||||
inline bool isAssembler() const noexcept { return _type == kTypeAssembler; }
|
||||
inline bool isAssembler() const noexcept { return _emitterType == kTypeAssembler; }
|
||||
//! Tests whether the emitter inherits from `BaseBuilder`.
|
||||
//!
|
||||
//! \note Both Builder and Compiler emitters would return `true`.
|
||||
inline bool isBuilder() const noexcept { return _type >= kTypeBuilder; }
|
||||
inline bool isBuilder() const noexcept { return _emitterType >= kTypeBuilder; }
|
||||
//! Tests whether the emitter inherits from `BaseCompiler`.
|
||||
inline bool isCompiler() const noexcept { return _type == kTypeCompiler; }
|
||||
inline bool isCompiler() const noexcept { return _emitterType == kTypeCompiler; }
|
||||
|
||||
//! Tests whether the emitter has the given `flag` enabled.
|
||||
inline bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
|
||||
inline bool hasEmitterFlag(uint32_t flag) const noexcept { return (_emitterFlags & flag) != 0; }
|
||||
//! Tests whether the emitter is finalized.
|
||||
inline bool isFinalized() const noexcept { return hasFlag(kFlagFinalized); }
|
||||
inline bool isFinalized() const noexcept { return hasEmitterFlag(kFlagFinalized); }
|
||||
//! Tests whether the emitter is destroyed (only used during destruction).
|
||||
inline bool isDestroyed() const noexcept { return hasFlag(kFlagDestroyed); }
|
||||
inline bool isDestroyed() const noexcept { return hasEmitterFlag(kFlagDestroyed); }
|
||||
|
||||
inline void _addFlags(uint32_t flags) noexcept { _flags = uint16_t(_flags | flags); }
|
||||
inline void _clearFlags(uint32_t flags) noexcept { _flags = uint16_t(_flags & ~flags); }
|
||||
inline void _addEmitterFlags(uint32_t flags) noexcept { _emitterFlags = uint8_t(_emitterFlags | flags); }
|
||||
inline void _clearEmitterFlags(uint32_t flags) noexcept { _emitterFlags = uint8_t(_emitterFlags & ~flags); }
|
||||
|
||||
//! \}
|
||||
|
||||
@ -220,90 +265,187 @@ public:
|
||||
|
||||
//! Returns the CodeHolder this emitter is attached to.
|
||||
inline CodeHolder* code() const noexcept { return _code; }
|
||||
//! Returns an information about the code, see `CodeInfo`.
|
||||
inline const CodeInfo& codeInfo() const noexcept { return _codeInfo; }
|
||||
//! Returns an information about the architecture, see `ArchInfo`.
|
||||
inline const ArchInfo& archInfo() const noexcept { return _codeInfo.archInfo(); }
|
||||
|
||||
//! Returns the target environment, see \ref Environment.
|
||||
//!
|
||||
//! The returned \ref Environment reference matches \ref CodeHolder::environment().
|
||||
inline const Environment& environment() const noexcept { return _environment; }
|
||||
|
||||
//! Tests whether the target architecture is 32-bit.
|
||||
inline bool is32Bit() const noexcept { return archInfo().is32Bit(); }
|
||||
inline bool is32Bit() const noexcept { return environment().is32Bit(); }
|
||||
//! Tests whether the target architecture is 64-bit.
|
||||
inline bool is64Bit() const noexcept { return archInfo().is64Bit(); }
|
||||
inline bool is64Bit() const noexcept { return environment().is64Bit(); }
|
||||
|
||||
//! Returns the target architecture type.
|
||||
inline uint32_t archId() const noexcept { return archInfo().archId(); }
|
||||
inline uint32_t arch() const noexcept { return environment().arch(); }
|
||||
//! Returns the target architecture sub-type.
|
||||
inline uint32_t archSubId() const noexcept { return archInfo().archSubId(); }
|
||||
inline uint32_t subArch() const noexcept { return environment().subArch(); }
|
||||
|
||||
//! Returns the target architecture's GP register size (4 or 8 bytes).
|
||||
inline uint32_t gpSize() const noexcept { return archInfo().gpSize(); }
|
||||
//! Returns the number of target GP registers.
|
||||
inline uint32_t gpCount() const noexcept { return archInfo().gpCount(); }
|
||||
inline uint32_t registerSize() const noexcept { return environment().registerSize(); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Initialization & Finalization
|
||||
//! \{
|
||||
|
||||
//! Tests whether the BaseEmitter is initialized (i.e. attached to the `CodeHolder`).
|
||||
//! Tests whether the emitter is initialized (i.e. attached to \ref CodeHolder).
|
||||
inline bool isInitialized() const noexcept { return _code != nullptr; }
|
||||
|
||||
//! Finalizes this emitter.
|
||||
//!
|
||||
//! Materializes the content of the emitter by serializing it to the attached
|
||||
//! \ref CodeHolder through an architecture specific \ref BaseAssembler. This
|
||||
//! function won't do anything if the emitter inherits from \ref BaseAssembler
|
||||
//! as assemblers emit directly to a \ref CodeBuffer held by \ref CodeHolder.
|
||||
//! However, if this is an emitter that inherits from \ref BaseBuilder or \ref
|
||||
//! BaseCompiler then these emitters need the materialization phase as they
|
||||
//! store their content in a representation not visible to \ref CodeHolder.
|
||||
ASMJIT_API virtual Error finalize();
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Emitter Options
|
||||
//! \name Logging
|
||||
//! \{
|
||||
|
||||
//! Tests whether the `option` is present in emitter options.
|
||||
inline bool hasEmitterOption(uint32_t option) const noexcept { return (_emitterOptions & option) != 0; }
|
||||
//! Returns the emitter options.
|
||||
inline uint32_t emitterOptions() const noexcept { return _emitterOptions; }
|
||||
//! Tests whether the emitter has a logger.
|
||||
inline bool hasLogger() const noexcept { return _logger != nullptr; }
|
||||
|
||||
// TODO: Deprecate and remove, CodeHolder::addEmitterOptions() is the way.
|
||||
inline void addEmitterOptions(uint32_t options) noexcept {
|
||||
_emitterOptions |= options;
|
||||
onUpdateGlobalInstOptions();
|
||||
}
|
||||
|
||||
inline void clearEmitterOptions(uint32_t options) noexcept {
|
||||
_emitterOptions &= ~options;
|
||||
onUpdateGlobalInstOptions();
|
||||
}
|
||||
|
||||
//! Returns the global instruction options.
|
||||
//! Tests whether the emitter has its own logger.
|
||||
//!
|
||||
//! Default instruction options are merged with instruction options before the
|
||||
//! instruction is encoded. These options have some bits reserved that are used
|
||||
//! for error handling, 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.
|
||||
inline uint32_t globalInstOptions() const noexcept { return _globalInstOptions; }
|
||||
//! Own logger means that it overrides the possible logger that may be used
|
||||
//! by \ref CodeHolder this emitter is attached to.
|
||||
inline bool hasOwnLogger() const noexcept { return hasEmitterFlag(kFlagOwnLogger); }
|
||||
|
||||
//! Returns the logger this emitter uses.
|
||||
//!
|
||||
//! The returned logger is either the emitter's own logger or it's logger
|
||||
//! used by \ref CodeHolder this emitter is attached to.
|
||||
inline Logger* logger() const noexcept { return _logger; }
|
||||
|
||||
//! Sets or resets the logger of the emitter.
|
||||
//!
|
||||
//! If the `logger` argument is non-null then the logger will be considered
|
||||
//! emitter's own logger, see \ref hasOwnLogger() for more details. If the
|
||||
//! given `logger` is null then the emitter will automatically use logger
|
||||
//! that is attached to the \ref CodeHolder this emitter is attached to.
|
||||
ASMJIT_API void setLogger(Logger* logger) noexcept;
|
||||
|
||||
//! Resets the logger of this emitter.
|
||||
//!
|
||||
//! The emitter will bail to using a logger attached to \ref CodeHolder this
|
||||
//! emitter is attached to, or no logger at all if \ref CodeHolder doesn't
|
||||
//! have one.
|
||||
inline void resetLogger() noexcept { return setLogger(nullptr); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Error Handling
|
||||
//! \{
|
||||
|
||||
//! Tests whether the local error handler is attached.
|
||||
//! Tests whether the emitter has an error handler attached.
|
||||
inline bool hasErrorHandler() const noexcept { return _errorHandler != nullptr; }
|
||||
//! Returns the local error handler.
|
||||
|
||||
//! Tests whether the emitter has its own error handler.
|
||||
//!
|
||||
//! Own error handler means that it overrides the possible error handler that
|
||||
//! may be used by \ref CodeHolder this emitter is attached to.
|
||||
inline bool hasOwnErrorHandler() const noexcept { return hasEmitterFlag(kFlagOwnErrorHandler); }
|
||||
|
||||
//! Returns the error handler this emitter uses.
|
||||
//!
|
||||
//! The returned error handler is either the emitter's own error handler or
|
||||
//! it's error handler used by \ref CodeHolder this emitter is attached to.
|
||||
inline ErrorHandler* errorHandler() const noexcept { return _errorHandler; }
|
||||
//! Sets the local error handler.
|
||||
inline void setErrorHandler(ErrorHandler* handler) noexcept { _errorHandler = handler; }
|
||||
//! Resets the local error handler (does nothing if not attached).
|
||||
|
||||
//! Sets or resets the error handler of the emitter.
|
||||
ASMJIT_API void setErrorHandler(ErrorHandler* errorHandler) noexcept;
|
||||
|
||||
//! Resets the error handler.
|
||||
inline void resetErrorHandler() noexcept { setErrorHandler(nullptr); }
|
||||
|
||||
//! Handles the given error in the following way:
|
||||
//! 1. Gets either Emitter's (preferred) or CodeHolder's ErrorHandler.
|
||||
//! 2. If exists, calls `ErrorHandler::handleError(error, message, this)`.
|
||||
//! 3. Returns the given `err` if ErrorHandler haven't thrown.
|
||||
//! 1. If the emitter has \ref ErrorHandler attached, it calls its
|
||||
//! \ref ErrorHandler::handleError() member function first, and
|
||||
//! then returns the error. The `handleError()` function may throw.
|
||||
//! 2. if the emitter doesn't have \ref ErrorHandler, the error is
|
||||
//! simply returned.
|
||||
ASMJIT_API Error reportError(Error err, const char* message = nullptr);
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Encoding Options
|
||||
//! \{
|
||||
|
||||
//! Returns encoding options, see \ref EncodingOptions.
|
||||
inline uint32_t encodingOptions() const noexcept { return _encodingOptions; }
|
||||
//! Tests whether the encoding `option` is set.
|
||||
inline bool hasEncodingOption(uint32_t option) const noexcept { return (_encodingOptions & option) != 0; }
|
||||
|
||||
//! Enables the given encoding `options`, see \ref EncodingOptions.
|
||||
inline void addEncodingOptions(uint32_t options) noexcept { _encodingOptions |= options; }
|
||||
//! Disables the given encoding `options`, see \ref EncodingOptions.
|
||||
inline void clearEncodingOptions(uint32_t options) noexcept { _encodingOptions &= ~options; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Validation Options
|
||||
//! \{
|
||||
|
||||
//! Returns the emitter's validation options, see \ref ValidationOptions.
|
||||
inline uint32_t validationOptions() const noexcept {
|
||||
return _validationOptions;
|
||||
}
|
||||
|
||||
//! Tests whether the given `option` is present in validation options.
|
||||
inline bool hasValidationOption(uint32_t option) const noexcept {
|
||||
return (_validationOptions & option) != 0;
|
||||
}
|
||||
|
||||
//! Activates the given validation `options`, see \ref ValidationOptions.
|
||||
//!
|
||||
//! This function is used to activate explicit validation options that will
|
||||
//! be then used by all emitter implementations. There are in general two
|
||||
//! possibilities:
|
||||
//!
|
||||
//! - Architecture specific assembler is used. In this case a
|
||||
//! \ref kValidationOptionAssembler can be used to turn on explicit
|
||||
//! validation that will be used before an instruction is emitted.
|
||||
//! This means that internally an extra step will be performed to
|
||||
//! make sure that the instruction is correct. This is needed, because
|
||||
//! by default assemblers prefer speed over strictness.
|
||||
//!
|
||||
//! This option should be used in debug builds as it's pretty expensive.
|
||||
//!
|
||||
//! - Architecture specific builder or compiler is used. In this case
|
||||
//! the user can turn on \ref kValidationOptionIntermediate option
|
||||
//! that adds explicit validation step before the Builder or Compiler
|
||||
//! creates an \ref InstNode to represent an emitted instruction. Error
|
||||
//! will be returned if the instruction is ill-formed. In addition,
|
||||
//! also \ref kValidationOptionAssembler can be used, which would not be
|
||||
//! consumed by Builder / Compiler directly, but it would be propagated
|
||||
//! to an architecture specific \ref BaseAssembler implementation it
|
||||
//! creates during \ref BaseEmitter::finalize().
|
||||
ASMJIT_API void addValidationOptions(uint32_t options) noexcept;
|
||||
|
||||
//! Deactivates the given validation `options`.
|
||||
//!
|
||||
//! See \ref addValidationOptions() and \ref ValidationOptions for more details.
|
||||
ASMJIT_API void clearValidationOptions(uint32_t options) noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Instruction Options
|
||||
//! \{
|
||||
|
||||
//! Returns forced instruction options.
|
||||
//!
|
||||
//! Forced instruction options are merged with next instruction options before
|
||||
//! the instruction is encoded. These options have some bits reserved that are
|
||||
//! used by error handling, logging, and instruction validation purposes. Other
|
||||
//! options are globals that affect each instruction.
|
||||
inline uint32_t forcedInstOptions() const noexcept { return _forcedInstOptions; }
|
||||
|
||||
//! Returns options of the next instruction.
|
||||
inline uint32_t instOptions() const noexcept { return _instOptions; }
|
||||
//! Returns options of the next instruction.
|
||||
@ -377,100 +519,49 @@ public:
|
||||
|
||||
// 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 some 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 out the remaining operands.
|
||||
// `_emit()` requires some code to pass it, which means that if we default to
|
||||
// 5 arguments in `_emit()` and instId the C++ compiler would have to generate
|
||||
// a virtual function call having 5 parameters and additional `this` argument,
|
||||
// which is quite a lot. Since by default most instructions have 2 to 3 operands
|
||||
// it's better to introduce helpers that pass from 0 to 6 operands that help to
|
||||
// reduce the size of emit(...) function call.
|
||||
|
||||
#define OP const Operand_&
|
||||
#define NONE Globals::none
|
||||
//! Emits an instruction (internal).
|
||||
ASMJIT_API Error _emitI(uint32_t instId);
|
||||
//! \overload
|
||||
ASMJIT_API Error _emitI(uint32_t instId, const Operand_& o0);
|
||||
//! \overload
|
||||
ASMJIT_API Error _emitI(uint32_t instId, const Operand_& o0, const Operand_& o1);
|
||||
//! \overload
|
||||
ASMJIT_API Error _emitI(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2);
|
||||
//! \overload
|
||||
ASMJIT_API Error _emitI(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3);
|
||||
//! \overload
|
||||
ASMJIT_API Error _emitI(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4);
|
||||
//! \overload
|
||||
ASMJIT_API Error _emitI(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5);
|
||||
|
||||
//! Emits an instruction.
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId) { return _emit(instId, NONE, NONE, NONE, NONE); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0) { return _emit(instId, o0, NONE, NONE, NONE); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1) { return _emit(instId, o0, o1, NONE, NONE); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2) { return _emit(instId, o0, o1, o2, NONE); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3) { return _emit(instId, o0, o1, o2, o3); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4) { return _emit(instId, o0, o1, o2, o3, o4, NONE); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, OP o5) { return _emit(instId, o0, o1, o2, o3, o4, o5); }
|
||||
//! Emits an instruction `instId` with the given `operands`.
|
||||
template<typename... Args>
|
||||
ASMJIT_INLINE Error emit(uint32_t instId, Args&&... operands) {
|
||||
return _emitI(instId, Support::ForwardOp<Args>::forward(operands)...);
|
||||
}
|
||||
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, int o0) { return _emit(instId, Imm(o0), NONE, NONE, NONE); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, int o1) { return _emit(instId, o0, Imm(o1), NONE, NONE); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, int o2) { return _emit(instId, o0, o1, Imm(o2), NONE); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, int o3) { return _emit(instId, o0, o1, o2, Imm(o3)); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, int o4) { return _emit(instId, o0, o1, o2, o3, Imm(o4), NONE); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, int o5) { return _emit(instId, o0, o1, o2, o3, o4, Imm(o5)); }
|
||||
inline Error emitOpArray(uint32_t instId, const Operand_* operands, size_t opCount) {
|
||||
return _emitOpArray(instId, operands, opCount);
|
||||
}
|
||||
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, int64_t o0) { return _emit(instId, Imm(o0), NONE, NONE, NONE); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, int64_t o1) { return _emit(instId, o0, Imm(o1), NONE, NONE); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, int64_t o2) { return _emit(instId, o0, o1, Imm(o2), NONE); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, int64_t o3) { return _emit(instId, o0, o1, o2, Imm(o3)); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, int64_t o4) { return _emit(instId, o0, o1, o2, o3, Imm(o4), NONE); }
|
||||
//! \overload
|
||||
ASMJIT_NOINLINE Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, int64_t o5) { return _emit(instId, o0, o1, o2, o3, o4, Imm(o5)); }
|
||||
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, unsigned int o0) { return emit(instId, int64_t(o0)); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, unsigned int o1) { return emit(instId, o0, int64_t(o1)); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, OP o1, unsigned int o2) { return emit(instId, o0, o1, int64_t(o2)); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, unsigned int o3) { return emit(instId, o0, o1, o2, int64_t(o3)); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, unsigned int o4) { return emit(instId, o0, o1, o2, o3, int64_t(o4)); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, unsigned int o5) { return emit(instId, o0, o1, o2, o3, o4, int64_t(o5)); }
|
||||
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, uint64_t o0) { return emit(instId, int64_t(o0)); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, uint64_t o1) { return emit(instId, o0, int64_t(o1)); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, OP o1, uint64_t o2) { return emit(instId, o0, o1, int64_t(o2)); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, uint64_t o3) { return emit(instId, o0, o1, o2, int64_t(o3)); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, uint64_t o4) { return emit(instId, o0, o1, o2, o3, int64_t(o4)); }
|
||||
//! \overload
|
||||
inline Error emit(uint32_t instId, OP o0, OP o1, OP o2, OP o3, OP o4, uint64_t o5) { return emit(instId, o0, o1, o2, o3, o4, int64_t(o5)); }
|
||||
|
||||
#undef NONE
|
||||
#undef OP
|
||||
|
||||
inline Error emitOpArray(uint32_t instId, const Operand_* operands, size_t count) { return _emitOpArray(instId, operands, count); }
|
||||
|
||||
inline Error emitInst(const BaseInst& inst, const Operand_* operands, size_t count) {
|
||||
inline Error emitInst(const BaseInst& inst, const Operand_* operands, size_t opCount) {
|
||||
setInstOptions(inst.options());
|
||||
setExtraReg(inst.extraReg());
|
||||
return _emitOpArray(inst.id(), operands, count);
|
||||
return _emitOpArray(inst.id(), operands, opCount);
|
||||
}
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! Emits instruction having max 4 operands.
|
||||
virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) = 0;
|
||||
//! Emits instruction having max 6 operands.
|
||||
virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) = 0;
|
||||
//! Emits an instruction - all 6 operands must be defined.
|
||||
virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* oExt) = 0;
|
||||
//! Emits instruction having operands stored in array.
|
||||
virtual Error _emitOpArray(uint32_t instId, const Operand_* operands, size_t count);
|
||||
virtual Error _emitOpArray(uint32_t instId, const Operand_* operands, size_t opCount);
|
||||
//! \endcond
|
||||
|
||||
//! \}
|
||||
@ -478,19 +569,19 @@ public:
|
||||
//! \name Emit Utilities
|
||||
//! \{
|
||||
|
||||
ASMJIT_API Error emitProlog(const FuncFrame& layout);
|
||||
ASMJIT_API Error emitEpilog(const FuncFrame& layout);
|
||||
ASMJIT_API Error emitArgsAssignment(const FuncFrame& layout, const FuncArgsAssignment& args);
|
||||
ASMJIT_API Error emitProlog(const FuncFrame& frame);
|
||||
ASMJIT_API Error emitEpilog(const FuncFrame& frame);
|
||||
ASMJIT_API Error emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args);
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Align
|
||||
//! \{
|
||||
|
||||
//! Aligns the current CodeBuffer to the `alignment` specified.
|
||||
//! Aligns the current CodeBuffer position 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 `AlignMode`.
|
||||
//! and the current location depends on the align `mode`, see \ref AlignMode.
|
||||
virtual Error align(uint32_t alignMode, uint32_t alignment) = 0;
|
||||
|
||||
//! \}
|
||||
@ -498,8 +589,44 @@ public:
|
||||
//! \name Embed
|
||||
//! \{
|
||||
|
||||
//! Embeds raw data into the CodeBuffer.
|
||||
virtual Error embed(const void* data, uint32_t dataSize) = 0;
|
||||
//! Embeds raw data into the \ref CodeBuffer.
|
||||
virtual Error embed(const void* data, size_t dataSize) = 0;
|
||||
|
||||
//! Embeds a typed data array.
|
||||
//!
|
||||
//! This is the most flexible function for embedding data as it allows to:
|
||||
//! - Assign a `typeId` to the data, so the emitter knows the type of
|
||||
//! items stored in `data`. Binary data should use \ref Type::kIdU8.
|
||||
//! - Repeat the given data `repeatCount` times, so the data can be used
|
||||
//! as a fill pattern for example, or as a pattern used by SIMD instructions.
|
||||
virtual Error embedDataArray(uint32_t typeId, const void* data, size_t itemCount, size_t repeatCount = 1) = 0;
|
||||
|
||||
//! Embeds int8_t `value` repeated by `repeatCount`.
|
||||
inline Error embedInt8(int8_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdI8, &value, 1, repeatCount); }
|
||||
//! Embeds uint8_t `value` repeated by `repeatCount`.
|
||||
inline Error embedUInt8(uint8_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdU8, &value, 1, repeatCount); }
|
||||
//! Embeds int16_t `value` repeated by `repeatCount`.
|
||||
inline Error embedInt16(int16_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdI16, &value, 1, repeatCount); }
|
||||
//! Embeds uint16_t `value` repeated by `repeatCount`.
|
||||
inline Error embedUInt16(uint16_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdU16, &value, 1, repeatCount); }
|
||||
//! Embeds int32_t `value` repeated by `repeatCount`.
|
||||
inline Error embedInt32(int32_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdI32, &value, 1, repeatCount); }
|
||||
//! Embeds uint32_t `value` repeated by `repeatCount`.
|
||||
inline Error embedUInt32(uint32_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdU32, &value, 1, repeatCount); }
|
||||
//! Embeds int64_t `value` repeated by `repeatCount`.
|
||||
inline Error embedInt64(int64_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdI64, &value, 1, repeatCount); }
|
||||
//! Embeds uint64_t `value` repeated by `repeatCount`.
|
||||
inline Error embedUInt64(uint64_t value, size_t repeatCount = 1) { return embedDataArray(Type::kIdU64, &value, 1, repeatCount); }
|
||||
//! Embeds a floating point `value` repeated by `repeatCount`.
|
||||
inline Error embedFloat(float value, size_t repeatCount = 1) { return embedDataArray(Type::kIdF32, &value, 1, repeatCount); }
|
||||
//! Embeds a floating point `value` repeated by `repeatCount`.
|
||||
inline Error embedDouble(double value, size_t repeatCount = 1) { return embedDataArray(Type::IdOfT<double>::kTypeId, &value, 1, repeatCount); }
|
||||
|
||||
//! Embeds a constant pool at the current offset by performing the following:
|
||||
//! 1. Aligns by using kAlignData to the minimum `pool` alignment.
|
||||
//! 2. Binds the ConstPool label so it's bound to an aligned location.
|
||||
//! 3. Emits ConstPool content.
|
||||
virtual Error embedConstPool(const Label& label, const ConstPool& pool) = 0;
|
||||
|
||||
//! Embeds an absolute label address as data (4 or 8 bytes).
|
||||
virtual Error embedLabel(const Label& label) = 0;
|
||||
@ -507,13 +634,7 @@ public:
|
||||
//! Embeds a delta (distance) between the `label` and `base` calculating it
|
||||
//! as `label - base`. This function was designed to make it easier to embed
|
||||
//! lookup tables where each index is a relative distance of two labels.
|
||||
virtual Error embedLabelDelta(const Label& label, const Label& base, uint32_t dataSize) = 0;
|
||||
|
||||
//! Embeds a constant pool at the current offset by performing the following:
|
||||
//! 1. Aligns by using kAlignData to the minimum `pool` alignment.
|
||||
//! 2. Binds the ConstPool label so it's bound to an aligned location.
|
||||
//! 3. Emits ConstPool content.
|
||||
virtual Error embedConstPool(const Label& label, const ConstPool& pool) = 0;
|
||||
virtual Error embedLabelDelta(const Label& label, const Label& base, size_t dataSize) = 0;
|
||||
|
||||
//! \}
|
||||
|
||||
@ -538,13 +659,48 @@ public:
|
||||
//! Called after the emitter was detached from `CodeHolder`.
|
||||
virtual Error onDetach(CodeHolder* code) noexcept = 0;
|
||||
|
||||
//! Called to update `_globalInstOptions` based on `_emitterOptions`.
|
||||
//! Called when \ref CodeHolder has updated an important setting, which
|
||||
//! involves the following:
|
||||
//!
|
||||
//! This function should only touch one bit `BaseInst::kOptionReserved`, which
|
||||
//! is used to handle errors and special-cases in a way that minimizes branching.
|
||||
ASMJIT_API void onUpdateGlobalInstOptions() noexcept;
|
||||
//! - \ref Logger has been changed (\ref CodeHolder::setLogger() has been
|
||||
//! called).
|
||||
//! - \ref ErrorHandler has been changed (\ref CodeHolder::setErrorHandler()
|
||||
//! has been called).
|
||||
//!
|
||||
//! This function ensures that the settings are properly propagated from
|
||||
//! \ref CodeHolder to the emitter.
|
||||
//!
|
||||
//! \note This function is virtual and can be overridden, however, if you
|
||||
//! do so, always call \ref BaseEmitter::onSettingsUpdated() within your
|
||||
//! own implementation to ensure that the emitter is in a consisten state.
|
||||
ASMJIT_API virtual void onSettingsUpdated() noexcept;
|
||||
|
||||
//! \}
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("Use environment() instead")
|
||||
inline CodeInfo codeInfo() const noexcept {
|
||||
return CodeInfo(_environment, _code ? _code->baseAddress() : Globals::kNoBaseAddress);
|
||||
}
|
||||
|
||||
ASMJIT_DEPRECATED("Use arch() instead")
|
||||
inline uint32_t archId() const noexcept { return arch(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use registerSize() instead")
|
||||
inline uint32_t gpSize() const noexcept { return registerSize(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use encodingOptions() instead")
|
||||
inline uint32_t emitterOptions() const noexcept { return encodingOptions(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use addEncodingOptions() instead")
|
||||
inline void addEmitterOptions(uint32_t options) noexcept { addEncodingOptions(options); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use clearEncodingOptions() instead")
|
||||
inline void clearEmitterOptions(uint32_t options) noexcept { clearEncodingOptions(options); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use forcedInstOptions() instead")
|
||||
inline uint32_t globalInstOptions() const noexcept { return forcedInstOptions(); }
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
150
3rdparty/asmjit/src/asmjit/core/emitterutils.cpp
vendored
Normal file
150
3rdparty/asmjit/src/asmjit/core/emitterutils.cpp
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "../core/api-build_p.h"
|
||||
#include "../core/assembler.h"
|
||||
#include "../core/emitterutils_p.h"
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::EmitterUtils]
|
||||
// ============================================================================
|
||||
|
||||
namespace EmitterUtils {
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
|
||||
Error formatLine(String& sb, const uint8_t* binData, size_t binSize, size_t dispSize, size_t immSize, const char* comment) noexcept {
|
||||
size_t currentSize = sb.size();
|
||||
size_t commentSize = comment ? Support::strLen(comment, Globals::kMaxCommentSize) : 0;
|
||||
|
||||
ASMJIT_ASSERT(binSize >= dispSize);
|
||||
const size_t kNoBinSize = SIZE_MAX;
|
||||
|
||||
if ((binSize != 0 && binSize != kNoBinSize) || commentSize) {
|
||||
size_t align = kMaxInstLineSize;
|
||||
char sep = ';';
|
||||
|
||||
for (size_t i = (binSize == kNoBinSize); i < 2; i++) {
|
||||
size_t begin = sb.size();
|
||||
ASMJIT_PROPAGATE(sb.padEnd(align));
|
||||
|
||||
if (sep) {
|
||||
ASMJIT_PROPAGATE(sb.append(sep));
|
||||
ASMJIT_PROPAGATE(sb.append(' '));
|
||||
}
|
||||
|
||||
// Append binary data or comment.
|
||||
if (i == 0) {
|
||||
ASMJIT_PROPAGATE(sb.appendHex(binData, binSize - dispSize - immSize));
|
||||
ASMJIT_PROPAGATE(sb.appendChars('.', dispSize * 2));
|
||||
ASMJIT_PROPAGATE(sb.appendHex(binData + binSize - immSize, immSize));
|
||||
if (commentSize == 0) break;
|
||||
}
|
||||
else {
|
||||
ASMJIT_PROPAGATE(sb.append(comment, commentSize));
|
||||
}
|
||||
|
||||
currentSize += sb.size() - begin;
|
||||
align += kMaxBinarySize;
|
||||
sep = '|';
|
||||
}
|
||||
}
|
||||
|
||||
return sb.append('\n');
|
||||
}
|
||||
|
||||
void logLabelBound(BaseAssembler* self, const Label& label) noexcept {
|
||||
Logger* logger = self->logger();
|
||||
|
||||
StringTmp<512> sb;
|
||||
size_t binSize = logger->hasFlag(FormatOptions::kFlagMachineCode) ? size_t(0) : SIZE_MAX;
|
||||
|
||||
sb.appendChars(' ', logger->indentation(FormatOptions::kIndentationLabel));
|
||||
Formatter::formatLabel(sb, logger->flags(), self, label.id());
|
||||
sb.append(':');
|
||||
EmitterUtils::formatLine(sb, nullptr, binSize, 0, 0, self->_inlineComment);
|
||||
logger->log(sb.data(), sb.size());
|
||||
}
|
||||
|
||||
void logInstructionEmitted(
|
||||
BaseAssembler* self,
|
||||
uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt,
|
||||
uint32_t relSize, uint32_t immSize, uint8_t* afterCursor) {
|
||||
|
||||
Logger* logger = self->logger();
|
||||
ASMJIT_ASSERT(logger != nullptr);
|
||||
|
||||
StringTmp<256> sb;
|
||||
uint32_t flags = logger->flags();
|
||||
|
||||
uint8_t* beforeCursor = self->bufferPtr();
|
||||
intptr_t emittedSize = (intptr_t)(afterCursor - beforeCursor);
|
||||
|
||||
Operand_ opArray[Globals::kMaxOpCount];
|
||||
EmitterUtils::opArrayFromEmitArgs(opArray, o0, o1, o2, opExt);
|
||||
|
||||
sb.appendChars(' ', logger->indentation(FormatOptions::kIndentationCode));
|
||||
Formatter::formatInstruction(sb, flags, self, self->arch(), BaseInst(instId, options, self->extraReg()), opArray, Globals::kMaxOpCount);
|
||||
|
||||
if ((flags & FormatOptions::kFlagMachineCode) != 0)
|
||||
EmitterUtils::formatLine(sb, self->bufferPtr(), size_t(emittedSize), relSize, immSize, self->inlineComment());
|
||||
else
|
||||
EmitterUtils::formatLine(sb, nullptr, SIZE_MAX, 0, 0, self->inlineComment());
|
||||
logger->log(sb);
|
||||
}
|
||||
|
||||
Error logInstructionFailed(
|
||||
BaseAssembler* self,
|
||||
Error err,
|
||||
uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt) {
|
||||
|
||||
StringTmp<256> sb;
|
||||
sb.append(DebugUtils::errorAsString(err));
|
||||
sb.append(": ");
|
||||
|
||||
Operand_ opArray[Globals::kMaxOpCount];
|
||||
EmitterUtils::opArrayFromEmitArgs(opArray, o0, o1, o2, opExt);
|
||||
|
||||
Formatter::formatInstruction(sb, 0, self, self->arch(), BaseInst(instId, options, self->extraReg()), opArray, Globals::kMaxOpCount);
|
||||
|
||||
if (self->inlineComment()) {
|
||||
sb.append(" ; ");
|
||||
sb.append(self->inlineComment());
|
||||
}
|
||||
|
||||
self->resetInstOptions();
|
||||
self->resetExtraReg();
|
||||
self->resetInlineComment();
|
||||
return self->reportError(err, sb.data());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // {EmitterUtils}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
109
3rdparty/asmjit/src/asmjit/core/emitterutils_p.h
vendored
Normal file
109
3rdparty/asmjit/src/asmjit/core/emitterutils_p.h
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#ifndef ASMJIT_CORE_EMITTERUTILS_P_H_INCLUDED
|
||||
#define ASMJIT_CORE_EMITTERUTILS_P_H_INCLUDED
|
||||
|
||||
#include "../core/emitter.h"
|
||||
#include "../core/operand.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
class BaseAssembler;
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::EmitterUtils]
|
||||
// ============================================================================
|
||||
|
||||
namespace EmitterUtils {
|
||||
|
||||
static const Operand_ noExt[3] {};
|
||||
|
||||
enum kOpIndex {
|
||||
kOp3 = 0,
|
||||
kOp4 = 1,
|
||||
kOp5 = 2
|
||||
};
|
||||
|
||||
static ASMJIT_INLINE uint32_t opCountFromEmitArgs(const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt) noexcept {
|
||||
uint32_t opCount = 0;
|
||||
|
||||
if (opExt[kOp3].isNone()) {
|
||||
if (!o0.isNone()) opCount = 1;
|
||||
if (!o1.isNone()) opCount = 2;
|
||||
if (!o2.isNone()) opCount = 3;
|
||||
}
|
||||
else {
|
||||
opCount = 4;
|
||||
if (!opExt[kOp4].isNone()) {
|
||||
opCount = 5 + uint32_t(!opExt[kOp5].isNone());
|
||||
}
|
||||
}
|
||||
|
||||
return opCount;
|
||||
}
|
||||
|
||||
static ASMJIT_INLINE void opArrayFromEmitArgs(Operand_ dst[Globals::kMaxOpCount], const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt) noexcept {
|
||||
dst[0].copyFrom(o0);
|
||||
dst[1].copyFrom(o1);
|
||||
dst[2].copyFrom(o2);
|
||||
dst[3].copyFrom(opExt[kOp3]);
|
||||
dst[4].copyFrom(opExt[kOp4]);
|
||||
dst[5].copyFrom(opExt[kOp5]);
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
enum : uint32_t {
|
||||
// Has to be big to be able to hold all metadata compiler can assign to a
|
||||
// single instruction.
|
||||
kMaxInstLineSize = 44,
|
||||
kMaxBinarySize = 26
|
||||
};
|
||||
|
||||
Error formatLine(String& sb, const uint8_t* binData, size_t binSize, size_t dispSize, size_t immSize, const char* comment) noexcept;
|
||||
|
||||
void logLabelBound(BaseAssembler* self, const Label& label) noexcept;
|
||||
|
||||
void logInstructionEmitted(
|
||||
BaseAssembler* self,
|
||||
uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt,
|
||||
uint32_t relSize, uint32_t immSize, uint8_t* afterCursor);
|
||||
|
||||
Error logInstructionFailed(
|
||||
BaseAssembler* self,
|
||||
Error err, uint32_t instId, uint32_t options, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_EMITTERUTILS_P_H_INCLUDED
|
||||
|
64
3rdparty/asmjit/src/asmjit/core/environment.cpp
vendored
Normal file
64
3rdparty/asmjit/src/asmjit/core/environment.cpp
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "../core/api-build_p.h"
|
||||
#include "../core/environment.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// X86 Target
|
||||
// ----------
|
||||
//
|
||||
// - 32-bit - Linux, OSX, BSD, and apparently also Haiku guarantee 16-byte
|
||||
// stack alignment. Other operating systems are assumed to have
|
||||
// 4-byte alignment by default for safety reasons.
|
||||
// - 64-bit - stack must be aligned to 16 bytes.
|
||||
//
|
||||
// ARM Target
|
||||
// ----------
|
||||
//
|
||||
// - 32-bit - Stack must be aligned to 8 bytes.
|
||||
// - 64-bit - Stack must be aligned to 16 bytes (hardware requirement).
|
||||
uint32_t Environment::stackAlignment() const noexcept {
|
||||
if (is64Bit()) {
|
||||
// Assume 16-byte alignment on any 64-bit target.
|
||||
return 16;
|
||||
}
|
||||
else {
|
||||
// The following platforms use 16-byte alignment in 32-bit mode.
|
||||
if (isPlatformLinux() ||
|
||||
isPlatformBSD() ||
|
||||
isPlatformApple() ||
|
||||
isPlatformHaiku()) {
|
||||
return 16u;
|
||||
}
|
||||
|
||||
if (isFamilyARM())
|
||||
return 8;
|
||||
|
||||
// Bail to 4-byte alignment if we don't know.
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
591
3rdparty/asmjit/src/asmjit/core/environment.h
vendored
Normal file
591
3rdparty/asmjit/src/asmjit/core/environment.h
vendored
Normal file
@ -0,0 +1,591 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#ifndef ASMJIT_CORE_ENVIRONMENT_H_INCLUDED
|
||||
#define ASMJIT_CORE_ENVIRONMENT_H_INCLUDED
|
||||
|
||||
#include "../core/globals.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Environment]
|
||||
// ============================================================================
|
||||
|
||||
//! Represents an environment, which is usually related to a \ref Target.
|
||||
//!
|
||||
//! Environment has usually an 'arch-subarch-vendor-os-abi' format, which is
|
||||
//! sometimes called "Triple" (historically it used to be 3 only parts) or
|
||||
//! "Tuple", which is a convention used by Debian Linux.
|
||||
//!
|
||||
//! AsmJit doesn't support all possible combinations or architectures and ABIs,
|
||||
//! however, it models the environment similarly to other compilers for future
|
||||
//! extensibility.
|
||||
class Environment {
|
||||
public:
|
||||
//! Architecture type, see \ref Arch.
|
||||
uint8_t _arch;
|
||||
//! Sub-architecture type, see \ref SubArch.
|
||||
uint8_t _subArch;
|
||||
//! Vendor type, see \ref Vendor.
|
||||
uint8_t _vendor;
|
||||
//! Platform type, see \ref Platform.
|
||||
uint8_t _platform;
|
||||
//! ABI type, see \ref Abi.
|
||||
uint8_t _abi;
|
||||
//! Object format, see \ref Format.
|
||||
uint8_t _format;
|
||||
//! Reserved for future use, must be zero.
|
||||
uint16_t _reserved;
|
||||
|
||||
//! Architecture.
|
||||
enum Arch : uint32_t {
|
||||
//! Unknown or uninitialized architecture.
|
||||
kArchUnknown = 0,
|
||||
|
||||
//! Mask used by 32-bit architectures (odd are 32-bit, even are 64-bit).
|
||||
kArch32BitMask = 0x01,
|
||||
//! Mask used by big-endian architectures.
|
||||
kArchBigEndianMask = 0x80u,
|
||||
|
||||
//! 32-bit X86 architecture.
|
||||
kArchX86 = 1,
|
||||
//! 64-bit X86 architecture also known as X86_64 and AMD64.
|
||||
kArchX64 = 2,
|
||||
|
||||
//! 32-bit RISC-V architecture.
|
||||
kArchRISCV32 = 3,
|
||||
//! 64-bit RISC-V architecture.
|
||||
kArchRISCV64 = 4,
|
||||
|
||||
//! 32-bit ARM architecture (little endian).
|
||||
kArchARM = 5,
|
||||
//! 32-bit ARM architecture (big endian).
|
||||
kArchARM_BE = kArchARM | kArchBigEndianMask,
|
||||
//! 64-bit ARM architecture in (little endian).
|
||||
kArchAArch64 = 6,
|
||||
//! 64-bit ARM architecture in (big endian).
|
||||
kArchAArch64_BE = kArchAArch64 | kArchBigEndianMask,
|
||||
//! 32-bit ARM in Thumb mode (little endian).
|
||||
kArchThumb = 7,
|
||||
//! 32-bit ARM in Thumb mode (big endian).
|
||||
kArchThumb_BE = kArchThumb | kArchBigEndianMask,
|
||||
|
||||
// 8 is not used, even numbers are 64-bit architectures.
|
||||
|
||||
//! 32-bit MIPS architecture in (little endian).
|
||||
kArchMIPS_LE = 9,
|
||||
//! 32-bit MIPS architecture in (big endian).
|
||||
kArchMIPS_BE = kArchMIPS_LE | kArchBigEndianMask,
|
||||
//! 64-bit MIPS architecture in (little endian).
|
||||
kArchMIPS64_LE = 10,
|
||||
//! 64-bit MIPS architecture in (big endian).
|
||||
kArchMIPS64_BE = kArchMIPS64_LE | kArchBigEndianMask,
|
||||
|
||||
//! Count of architectures.
|
||||
kArchCount
|
||||
};
|
||||
|
||||
//! Sub-architecture.
|
||||
enum SubArch : uint32_t {
|
||||
//! Unknown or uninitialized architecture sub-type.
|
||||
kSubArchUnknown = 0,
|
||||
|
||||
//! Count of sub-architectures.
|
||||
kSubArchCount
|
||||
};
|
||||
|
||||
//! Vendor.
|
||||
//!
|
||||
//! \note AsmJit doesn't use vendor information at the moment. It's provided
|
||||
//! for future use, if required.
|
||||
enum Vendor : uint32_t {
|
||||
//! Unknown or uninitialized vendor.
|
||||
kVendorUnknown = 0,
|
||||
|
||||
//! Count of vendor identifiers.
|
||||
kVendorCount
|
||||
};
|
||||
|
||||
//! Platform / OS.
|
||||
enum Platform : uint32_t {
|
||||
//! Unknown or uninitialized platform.
|
||||
kPlatformUnknown = 0,
|
||||
|
||||
//! Windows OS.
|
||||
kPlatformWindows,
|
||||
|
||||
//! Other platform, most likely POSIX based.
|
||||
kPlatformOther,
|
||||
|
||||
//! Linux OS.
|
||||
kPlatformLinux,
|
||||
//! GNU/Hurd OS.
|
||||
kPlatformHurd,
|
||||
|
||||
//! FreeBSD OS.
|
||||
kPlatformFreeBSD,
|
||||
//! OpenBSD OS.
|
||||
kPlatformOpenBSD,
|
||||
//! NetBSD OS.
|
||||
kPlatformNetBSD,
|
||||
//! DragonFly BSD OS.
|
||||
kPlatformDragonFlyBSD,
|
||||
|
||||
//! Haiku OS.
|
||||
kPlatformHaiku,
|
||||
|
||||
//! Apple OSX.
|
||||
kPlatformOSX,
|
||||
//! Apple iOS.
|
||||
kPlatformIOS,
|
||||
//! Apple TVOS.
|
||||
kPlatformTVOS,
|
||||
//! Apple WatchOS.
|
||||
kPlatformWatchOS,
|
||||
|
||||
//! Emscripten platform.
|
||||
kPlatformEmscripten,
|
||||
|
||||
//! Count of platform identifiers.
|
||||
kPlatformCount
|
||||
};
|
||||
|
||||
//! ABI.
|
||||
enum Abi : uint32_t {
|
||||
//! Unknown or uninitialied environment.
|
||||
kAbiUnknown = 0,
|
||||
//! Microsoft ABI.
|
||||
kAbiMSVC,
|
||||
//! GNU ABI.
|
||||
kAbiGNU,
|
||||
//! Android Environment / ABI.
|
||||
kAbiAndroid,
|
||||
//! Cygwin ABI.
|
||||
kAbiCygwin,
|
||||
|
||||
//! Count of known ABI types.
|
||||
kAbiCount
|
||||
};
|
||||
|
||||
//! Object format.
|
||||
//!
|
||||
//! \note AsmJit doesn't really use anything except \ref kFormatUnknown and
|
||||
//! \ref kFormatJIT at the moment. Object file formats are provided for
|
||||
//! future extensibility and a possibility to generate object files at some
|
||||
//! point.
|
||||
enum Format : uint32_t {
|
||||
//! Unknown or uninitialized object format.
|
||||
kFormatUnknown = 0,
|
||||
|
||||
//! JIT code generation object, most likely \ref JitRuntime or a custom
|
||||
//! \ref Target implementation.
|
||||
kFormatJIT,
|
||||
|
||||
//! Executable and linkable format (ELF).
|
||||
kFormatELF,
|
||||
//! Common object file format.
|
||||
kFormatCOFF,
|
||||
//! Extended COFF object format.
|
||||
kFormatXCOFF,
|
||||
//! Mach object file format.
|
||||
kFormatMachO,
|
||||
|
||||
//! Count of object format types.
|
||||
kFormatCount
|
||||
};
|
||||
|
||||
//! \name Environment Detection
|
||||
//! \{
|
||||
|
||||
#ifdef _DOXYGEN
|
||||
//! Architecture detected at compile-time (architecture of the host).
|
||||
static constexpr Arch kArchHost = DETECTED_AT_COMPILE_TIME;
|
||||
//! Sub-architecture detected at compile-time (sub-architecture of the host).
|
||||
static constexpr SubArch kSubArchHost = DETECTED_AT_COMPILE_TIME;
|
||||
//! Vendor detected at compile-time (vendor of the host).
|
||||
static constexpr Vendor kVendorHost = DETECTED_AT_COMPILE_TIME;
|
||||
//! Platform detected at compile-time (platform of the host).
|
||||
static constexpr Platform kPlatformHost = DETECTED_AT_COMPILE_TIME;
|
||||
//! ABI detected at compile-time (ABI of the host).
|
||||
static constexpr Abi kAbiHost = DETECTED_AT_COMPILE_TIME;
|
||||
#else
|
||||
static constexpr Arch kArchHost =
|
||||
ASMJIT_ARCH_X86 == 32 ? kArchX86 :
|
||||
ASMJIT_ARCH_X86 == 64 ? kArchX64 :
|
||||
|
||||
ASMJIT_ARCH_ARM == 32 && ASMJIT_ARCH_LE ? kArchARM :
|
||||
ASMJIT_ARCH_ARM == 32 && ASMJIT_ARCH_BE ? kArchARM_BE :
|
||||
ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_LE ? kArchAArch64 :
|
||||
ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_BE ? kArchAArch64_BE :
|
||||
|
||||
ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_LE ? kArchMIPS_LE :
|
||||
ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_BE ? kArchMIPS_BE :
|
||||
ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_LE ? kArchMIPS64_LE :
|
||||
ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_BE ? kArchMIPS64_BE :
|
||||
|
||||
kArchUnknown;
|
||||
|
||||
static constexpr SubArch kSubArchHost =
|
||||
kSubArchUnknown;
|
||||
|
||||
static constexpr Vendor kVendorHost =
|
||||
kVendorUnknown;
|
||||
|
||||
static constexpr Platform kPlatformHost =
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
kPlatformEmscripten
|
||||
#elif defined(_WIN32)
|
||||
kPlatformWindows
|
||||
#elif defined(__linux__)
|
||||
kPlatformLinux
|
||||
#elif defined(__gnu_hurd__)
|
||||
kPlatformHurd
|
||||
#elif defined(__FreeBSD__)
|
||||
kPlatformFreeBSD
|
||||
#elif defined(__OpenBSD__)
|
||||
kPlatformOpenBSD
|
||||
#elif defined(__NetBSD__)
|
||||
kPlatformNetBSD
|
||||
#elif defined(__DragonFly__)
|
||||
kPlatformDragonFlyBSD
|
||||
#elif defined(__HAIKU__)
|
||||
kPlatformHaiku
|
||||
#elif defined(__APPLE__) && TARGET_OS_OSX
|
||||
kPlatformOSX
|
||||
#elif defined(__APPLE__) && TARGET_OS_TV
|
||||
kPlatformTVOS
|
||||
#elif defined(__APPLE__) && TARGET_OS_WATCH
|
||||
kPlatformWatchOS
|
||||
#elif defined(__APPLE__) && TARGET_OS_IPHONE
|
||||
kPlatformIOS
|
||||
#else
|
||||
kPlatformOther
|
||||
#endif
|
||||
;
|
||||
|
||||
static constexpr Abi kAbiHost =
|
||||
#if defined(_MSC_VER)
|
||||
kAbiMSVC
|
||||
#elif defined(__CYGWIN__)
|
||||
kAbiCygwin
|
||||
#elif defined(__MINGW32__) || defined(__GLIBC__)
|
||||
kAbiGNU
|
||||
#elif defined(__ANDROID__)
|
||||
kAbiAndroid
|
||||
#else
|
||||
kAbiUnknown
|
||||
#endif
|
||||
;
|
||||
|
||||
#endif
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Construction / Destruction
|
||||
//! \{
|
||||
|
||||
inline Environment() noexcept :
|
||||
_arch(uint8_t(kArchUnknown)),
|
||||
_subArch(uint8_t(kSubArchUnknown)),
|
||||
_vendor(uint8_t(kVendorUnknown)),
|
||||
_platform(uint8_t(kPlatformUnknown)),
|
||||
_abi(uint8_t(kAbiUnknown)),
|
||||
_format(uint8_t(kFormatUnknown)),
|
||||
_reserved(0) {}
|
||||
|
||||
inline Environment(const Environment& other) noexcept = default;
|
||||
|
||||
inline explicit Environment(uint32_t arch,
|
||||
uint32_t subArch = kSubArchUnknown,
|
||||
uint32_t vendor = kVendorUnknown,
|
||||
uint32_t platform = kPlatformUnknown,
|
||||
uint32_t abi = kAbiUnknown,
|
||||
uint32_t format = kFormatUnknown) noexcept {
|
||||
init(arch, subArch, vendor, platform, abi, format);
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Overloaded Operators
|
||||
//! \{
|
||||
|
||||
inline Environment& operator=(const Environment& other) noexcept = default;
|
||||
|
||||
inline bool operator==(const Environment& other) const noexcept { return equals(other); }
|
||||
inline bool operator!=(const Environment& other) const noexcept { return !equals(other); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Tests whether the environment is not set up.
|
||||
//!
|
||||
//! Returns true if all members are zero, and thus unknown.
|
||||
inline bool empty() const noexcept {
|
||||
// Unfortunately compilers won't optimize fields are checked one by one...
|
||||
return _packed() == 0;
|
||||
}
|
||||
|
||||
//! Tests whether the environment is intialized, which means it must have
|
||||
//! a valid architecture.
|
||||
inline bool isInitialized() const noexcept {
|
||||
return _arch != kArchUnknown;
|
||||
}
|
||||
|
||||
inline uint64_t _packed() const noexcept {
|
||||
uint64_t x;
|
||||
memcpy(&x, this, 8);
|
||||
return x;
|
||||
}
|
||||
|
||||
//! Resets all members of the environment to zero / unknown.
|
||||
inline void reset() noexcept {
|
||||
_arch = uint8_t(kArchUnknown);
|
||||
_subArch = uint8_t(kSubArchUnknown);
|
||||
_vendor = uint8_t(kVendorUnknown);
|
||||
_platform = uint8_t(kPlatformUnknown);
|
||||
_abi = uint8_t(kAbiUnknown);
|
||||
_format = uint8_t(kFormatUnknown);
|
||||
_reserved = 0;
|
||||
}
|
||||
|
||||
inline bool equals(const Environment& other) const noexcept {
|
||||
return _packed() == other._packed();
|
||||
}
|
||||
|
||||
//! Returns the architecture, see \ref Arch.
|
||||
inline uint32_t arch() const noexcept { return _arch; }
|
||||
//! Returns the sub-architecture, see \ref SubArch.
|
||||
inline uint32_t subArch() const noexcept { return _subArch; }
|
||||
//! Returns vendor, see \ref Vendor.
|
||||
inline uint32_t vendor() const noexcept { return _vendor; }
|
||||
//! Returns target's platform or operating system, see \ref Platform.
|
||||
inline uint32_t platform() const noexcept { return _platform; }
|
||||
//! Returns target's ABI, see \ref Abi.
|
||||
inline uint32_t abi() const noexcept { return _abi; }
|
||||
//! Returns target's object format, see \ref Format.
|
||||
inline uint32_t format() const noexcept { return _format; }
|
||||
|
||||
inline void init(uint32_t arch,
|
||||
uint32_t subArch = kSubArchUnknown,
|
||||
uint32_t vendor = kVendorUnknown,
|
||||
uint32_t platform = kPlatformUnknown,
|
||||
uint32_t abi = kAbiUnknown,
|
||||
uint32_t format = kFormatUnknown) noexcept {
|
||||
_arch = uint8_t(arch);
|
||||
_subArch = uint8_t(subArch);
|
||||
_vendor = uint8_t(vendor);
|
||||
_platform = uint8_t(platform);
|
||||
_abi = uint8_t(abi);
|
||||
_format = uint8_t(format);
|
||||
_reserved = 0;
|
||||
}
|
||||
|
||||
//! Tests whether the architecture is 32-bit.
|
||||
inline bool is32Bit() const noexcept { return is32Bit(_arch); }
|
||||
//! Tests whether the architecture is 64-bit.
|
||||
inline bool is64Bit() const noexcept { return is64Bit(_arch); }
|
||||
|
||||
//! Tests whether the architecture is little endian.
|
||||
inline bool isLittleEndian() const noexcept { return isLittleEndian(_arch); }
|
||||
//! Tests whether the architecture is big endian.
|
||||
inline bool isBigEndian() const noexcept { return isBigEndian(_arch); }
|
||||
|
||||
//! Tests whether this architecture is of X86 family.
|
||||
inline bool isFamilyX86() const noexcept { return isFamilyX86(_arch); }
|
||||
//! Tests whether this architecture is of ARM family.
|
||||
inline bool isFamilyRISCV() const noexcept { return isFamilyRISCV(_arch); }
|
||||
//! Tests whether this architecture is of ARM family.
|
||||
inline bool isFamilyARM() const noexcept { return isFamilyARM(_arch); }
|
||||
//! Tests whether this architecture is of ARM family.
|
||||
inline bool isFamilyMIPS() const noexcept { return isFamilyMIPS(_arch); }
|
||||
|
||||
//! Tests whether the environment platform is Windows.
|
||||
inline bool isPlatformWindows() const noexcept { return _platform == kPlatformWindows; }
|
||||
|
||||
//! Tests whether the environment platform is Linux.
|
||||
inline bool isPlatformLinux() const noexcept { return _platform == kPlatformLinux; }
|
||||
|
||||
//! Tests whether the environment platform is Hurd.
|
||||
inline bool isPlatformHurd() const noexcept { return _platform == kPlatformHurd; }
|
||||
|
||||
//! Tests whether the environment platform is Haiku.
|
||||
inline bool isPlatformHaiku() const noexcept { return _platform == kPlatformHaiku; }
|
||||
|
||||
//! Tests whether the environment platform is any BSD.
|
||||
inline bool isPlatformBSD() const noexcept {
|
||||
return _platform == kPlatformFreeBSD ||
|
||||
_platform == kPlatformOpenBSD ||
|
||||
_platform == kPlatformNetBSD ||
|
||||
_platform == kPlatformDragonFlyBSD;
|
||||
}
|
||||
|
||||
//! Tests whether the environment platform is any Apple platform (OSX, iOS, TVOS, WatchOS).
|
||||
inline bool isPlatformApple() const noexcept {
|
||||
return _platform == kPlatformOSX ||
|
||||
_platform == kPlatformIOS ||
|
||||
_platform == kPlatformTVOS ||
|
||||
_platform == kPlatformWatchOS;
|
||||
}
|
||||
|
||||
//! Tests whether the ABI is MSVC.
|
||||
inline bool isAbiMSVC() const noexcept { return _abi == kAbiMSVC; }
|
||||
//! Tests whether the ABI is GNU.
|
||||
inline bool isAbiGNU() const noexcept { return _abi == kAbiGNU; }
|
||||
|
||||
//! Returns a calculated stack alignment for this environment.
|
||||
ASMJIT_API uint32_t stackAlignment() const noexcept;
|
||||
|
||||
//! Returns a native register size of this architecture.
|
||||
uint32_t registerSize() const noexcept { return registerSizeFromArch(_arch); }
|
||||
|
||||
//! Sets the architecture to `arch`.
|
||||
inline void setArch(uint32_t arch) noexcept { _arch = uint8_t(arch); }
|
||||
//! Sets the sub-architecture to `subArch`.
|
||||
inline void setSubArch(uint32_t subArch) noexcept { _subArch = uint8_t(subArch); }
|
||||
//! Sets the vendor to `vendor`.
|
||||
inline void setVendor(uint32_t vendor) noexcept { _vendor = uint8_t(vendor); }
|
||||
//! Sets the platform to `platform`.
|
||||
inline void setPlatform(uint32_t platform) noexcept { _platform = uint8_t(platform); }
|
||||
//! Sets the ABI to `abi`.
|
||||
inline void setAbi(uint32_t abi) noexcept { _abi = uint8_t(abi); }
|
||||
//! Sets the object format to `format`.
|
||||
inline void setFormat(uint32_t format) noexcept { _format = uint8_t(format); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Static Utilities
|
||||
//! \{
|
||||
|
||||
//! Tests whether the given architecture `arch` is 32-bit.
|
||||
static inline bool is32Bit(uint32_t arch) noexcept {
|
||||
return (arch & kArch32BitMask) == kArch32BitMask;
|
||||
}
|
||||
|
||||
//! Tests whether the given architecture `arch` is 64-bit.
|
||||
static inline bool is64Bit(uint32_t arch) noexcept {
|
||||
return (arch & kArch32BitMask) == 0;
|
||||
}
|
||||
|
||||
//! Tests whether the given architecture `arch` is little endian.
|
||||
static inline bool isLittleEndian(uint32_t arch) noexcept {
|
||||
return (arch & kArchBigEndianMask) == 0;
|
||||
}
|
||||
|
||||
//! Tests whether the given architecture `arch` is big endian.
|
||||
static inline bool isBigEndian(uint32_t arch) noexcept {
|
||||
return (arch & kArchBigEndianMask) == kArchBigEndianMask;
|
||||
}
|
||||
|
||||
//! Tests whether the given architecture family is X86 or X64.
|
||||
static inline bool isFamilyX86(uint32_t arch) noexcept {
|
||||
return arch == kArchX86 ||
|
||||
arch == kArchX64;
|
||||
}
|
||||
|
||||
//! Tests whether the given architecture family is RISC-V (both 32-bit and 64-bit).
|
||||
static inline bool isFamilyRISCV(uint32_t arch) noexcept {
|
||||
return arch == kArchRISCV32 ||
|
||||
arch == kArchRISCV64;
|
||||
}
|
||||
|
||||
//! Tests whether the given architecture family is ARM, Thumb, or AArch64.
|
||||
static inline bool isFamilyARM(uint32_t arch) noexcept {
|
||||
arch &= ~kArchBigEndianMask;
|
||||
return arch == kArchARM ||
|
||||
arch == kArchAArch64 ||
|
||||
arch == kArchThumb;
|
||||
}
|
||||
|
||||
//! Tests whether the given architecture family is MISP or MIPS64.
|
||||
static inline bool isFamilyMIPS(uint32_t arch) noexcept {
|
||||
arch &= ~kArchBigEndianMask;
|
||||
return arch == kArchMIPS_LE ||
|
||||
arch == kArchMIPS64_LE;
|
||||
}
|
||||
|
||||
//! Returns a native general purpose register size from the given architecture.
|
||||
static uint32_t registerSizeFromArch(uint32_t arch) noexcept {
|
||||
return is32Bit(arch) ? 4u : 8u;
|
||||
}
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! Returns the host environment constructed from preprocessor macros defined
|
||||
//! by the compiler.
|
||||
//!
|
||||
//! The returned environment should precisely match the target host architecture,
|
||||
//! sub-architecture, platform, and ABI.
|
||||
static ASMJIT_INLINE Environment hostEnvironment() noexcept {
|
||||
return Environment(Environment::kArchHost,
|
||||
Environment::kSubArchHost,
|
||||
Environment::kVendorHost,
|
||||
Environment::kPlatformHost,
|
||||
Environment::kAbiHost,
|
||||
Environment::kFormatUnknown);
|
||||
}
|
||||
|
||||
static_assert(sizeof(Environment) == 8,
|
||||
"Environment must occupy exactly 8 bytes.");
|
||||
|
||||
//! \}
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
class ASMJIT_DEPRECATED_STRUCT("Use Environment instead") ArchInfo : public Environment {
|
||||
public:
|
||||
inline ArchInfo() noexcept : Environment() {}
|
||||
|
||||
inline ArchInfo(const Environment& other) noexcept : Environment(other) {}
|
||||
inline explicit ArchInfo(uint32_t arch, uint32_t subArch = kSubArchUnknown) noexcept
|
||||
: Environment(arch, subArch) {}
|
||||
|
||||
enum Id : uint32_t {
|
||||
kIdNone = Environment::kArchUnknown,
|
||||
kIdX86 = Environment::kArchX86,
|
||||
kIdX64 = Environment::kArchX64,
|
||||
kIdA32 = Environment::kArchARM,
|
||||
kIdA64 = Environment::kArchAArch64,
|
||||
kIdHost = Environment::kArchHost
|
||||
};
|
||||
|
||||
enum SubType : uint32_t {
|
||||
kSubIdNone = Environment::kSubArchUnknown
|
||||
};
|
||||
|
||||
static inline ArchInfo host() noexcept { return ArchInfo(hostEnvironment()); }
|
||||
};
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_ENVIRONMENT_H_INCLUDED
|
37
3rdparty/asmjit/src/asmjit/core/errorhandler.cpp
vendored
Normal file
37
3rdparty/asmjit/src/asmjit/core/errorhandler.cpp
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "../core/api-build_p.h"
|
||||
#include "../core/errorhandler.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ErrorHandler]
|
||||
// ============================================================================
|
||||
|
||||
ErrorHandler::ErrorHandler() noexcept {}
|
||||
ErrorHandler::~ErrorHandler() noexcept {}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
267
3rdparty/asmjit/src/asmjit/core/errorhandler.h
vendored
Normal file
267
3rdparty/asmjit/src/asmjit/core/errorhandler.h
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#ifndef ASMJIT_CORE_ERRORHANDLER_H_INCLUDED
|
||||
#define ASMJIT_CORE_ERRORHANDLER_H_INCLUDED
|
||||
|
||||
#include "../core/globals.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_error_handling
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [Forward Declarations]
|
||||
// ============================================================================
|
||||
|
||||
class BaseEmitter;
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ErrorHandler]
|
||||
// ============================================================================
|
||||
|
||||
//! Error handler can be used to override the default behavior of error handling.
|
||||
//!
|
||||
//! It's available to all classes that inherit `BaseEmitter`. Override
|
||||
//! \ref ErrorHandler::handleError() to implement your own error handler.
|
||||
//!
|
||||
//! The following use-cases are supported:
|
||||
//!
|
||||
//! - Record the error and continue code generation. This is the simplest
|
||||
//! approach that can be used to at least log possible errors.
|
||||
//! - Throw an exception. AsmJit doesn't use exceptions and is completely
|
||||
//! exception-safe, but it's perfectly legal to throw an exception from
|
||||
//! the error handler.
|
||||
//! - Use plain old C's `setjmp()` and `longjmp()`. Asmjit always puts Assembler,
|
||||
//! Builder and Compiler to a consistent state before calling \ref handleError(),
|
||||
//! so `longjmp()` can be used without issues to cancel the code-generation if
|
||||
//! an error occurred. This method can be used if exception handling in your
|
||||
//! project is turned off and you still want some comfort. In most cases it
|
||||
//! should be safe as AsmJit uses \ref Zone memory and the ownership of memory
|
||||
//! it allocates always ends with the instance that allocated it. If using this
|
||||
//! approach please never jump outside the life-time of \ref CodeHolder and
|
||||
//! \ref BaseEmitter.
|
||||
//!
|
||||
//! \ref ErrorHandler can be attached to \ref CodeHolder or \ref BaseEmitter,
|
||||
//! which has a priority. The example below uses error handler that just prints
|
||||
//! the error, but lets AsmJit continue:
|
||||
//!
|
||||
//! ```
|
||||
//! // Error Handling #1 - Logging and returing Error.
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! // Error handler that just prints the error and lets AsmJit ignore it.
|
||||
//! class SimpleErrorHandler : public ErrorHandler {
|
||||
//! public:
|
||||
//! Error err;
|
||||
//!
|
||||
//! inline SimpleErrorHandler() : err(kErrorOk) {}
|
||||
//!
|
||||
//! void handleError(Error err, const char* message, BaseEmitter* origin) override {
|
||||
//! this->err = err;
|
||||
//! fprintf(stderr, "ERROR: %s\n", message);
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//! int main() {
|
||||
//! JitRuntime rt;
|
||||
//! SimpleErrorHandler eh;
|
||||
//!
|
||||
//! CodeHolder code;
|
||||
//! code.init(rt.environment());
|
||||
//! code.setErrorHandler(&eh);
|
||||
//!
|
||||
//! // Try to emit instruction that doesn't exist.
|
||||
//! x86::Assembler a(&code);
|
||||
//! a.emit(x86::Inst::kIdMov, x86::xmm0, x86::xmm1);
|
||||
//!
|
||||
//! if (eh.err) {
|
||||
//! // Assembler failed!
|
||||
//! return 1;
|
||||
//! }
|
||||
//!
|
||||
//! 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:
|
||||
//!
|
||||
//! ```
|
||||
//! // Error Handling #2 - Throwing an exception.
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <exception>
|
||||
//! #include <string>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! // Error handler that throws a user-defined `AsmJitException`.
|
||||
//! class AsmJitException : public std::exception {
|
||||
//! public:
|
||||
//! Error err;
|
||||
//! std::string message;
|
||||
//!
|
||||
//! AsmJitException(Error err, const char* message) noexcept
|
||||
//! : err(err),
|
||||
//! message(message) {}
|
||||
//!
|
||||
//! const char* what() const noexcept override { return message.c_str(); }
|
||||
//! };
|
||||
//!
|
||||
//! class ThrowableErrorHandler : public ErrorHandler {
|
||||
//! public:
|
||||
//! // Throw is possible, functions that use ErrorHandler are never 'noexcept'.
|
||||
//! void handleError(Error err, const char* message, BaseEmitter* origin) override {
|
||||
//! throw AsmJitException(err, message);
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//! int main() {
|
||||
//! JitRuntime rt;
|
||||
//! ThrowableErrorHandler eh;
|
||||
//!
|
||||
//! CodeHolder code;
|
||||
//! code.init(rt.environment());
|
||||
//! code.setErrorHandler(&eh);
|
||||
//!
|
||||
//! x86::Assembler a(&code);
|
||||
//!
|
||||
//! // Try to emit instruction that doesn't exist.
|
||||
//! try {
|
||||
//! a.emit(x86::Inst::kIdMov, x86::xmm0, x86::xmm1);
|
||||
//! }
|
||||
//! catch (const AsmJitException& ex) {
|
||||
//! printf("EXCEPTION THROWN: %s\n", ex.what());
|
||||
//! return 1;
|
||||
//! }
|
||||
//!
|
||||
//! 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:
|
||||
//!
|
||||
//! ```
|
||||
//! // Error Handling #3 - Using setjmp/longjmp if exceptions are not allowed.
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <setjmp.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! class LongJmpErrorHandler : public asmjit::ErrorHandler {
|
||||
//! public:
|
||||
//! inline LongJmpErrorHandler() : err(asmjit::kErrorOk) {}
|
||||
//!
|
||||
//! void handleError(asmjit::Error err, const char* message, asmjit::BaseEmitter* origin) override {
|
||||
//! 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.rt.environment());
|
||||
//! code.setErrorHandler(&eh);
|
||||
//!
|
||||
//! x86::Assembler a(&code);
|
||||
//!
|
||||
//! if (!setjmp(eh.state)) {
|
||||
//! // Try to emit instruction that doesn't exist.
|
||||
//! a.emit(x86::Inst::kIdMov, x86::xmm0, x86::xmm1);
|
||||
//! }
|
||||
//! else {
|
||||
//! Error err = eh.err;
|
||||
//! printf("ASMJIT ERROR: 0x%08X [%s]\n", err, DebugUtils::errorAsString(err));
|
||||
//! }
|
||||
//!
|
||||
//! return 0;
|
||||
//! }
|
||||
//! ```
|
||||
class ASMJIT_VIRTAPI ErrorHandler {
|
||||
public:
|
||||
ASMJIT_BASE_CLASS(ErrorHandler)
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Construction / Destruction]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Creates a new `ErrorHandler` instance.
|
||||
ASMJIT_API ErrorHandler() noexcept;
|
||||
//! Destroys the `ErrorHandler` instance.
|
||||
ASMJIT_API virtual ~ErrorHandler() noexcept;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Handle Error]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Error handler (must be reimplemented).
|
||||
//!
|
||||
//! 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. User-based error handling without throwing exception or using C's
|
||||
//! `longjmp()`. This is for users that don't use exceptions and want
|
||||
//! customized error handling.
|
||||
//!
|
||||
//! 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.
|
||||
//!
|
||||
//! 3. Using plain old C's `setjmp()` and `longjmp()`. Asmjit always puts
|
||||
//! `BaseEmitter` to a consistent state before calling `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, however,
|
||||
//! never jump outside of `CodeHolder` and `BaseEmitter` scope as you
|
||||
//! would leak memory.
|
||||
virtual void handleError(Error err, const char* message, BaseEmitter* origin) = 0;
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_ERRORHANDLER_H_INCLUDED
|
||||
|
28
3rdparty/asmjit/src/asmjit/core/features.h
vendored
28
3rdparty/asmjit/src/asmjit/core/features.h
vendored
@ -36,9 +36,15 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::BaseFeatures]
|
||||
// ============================================================================
|
||||
|
||||
//! Base class that provides information about CPU features.
|
||||
//!
|
||||
//! Internally each feature is repreesnted by a single bit in an embedded
|
||||
//! bit-array, however, feature bits are defined by an architecture specific
|
||||
//! implementations, like \ref x86::Features.
|
||||
class BaseFeatures {
|
||||
public:
|
||||
typedef Support::BitWord BitWord;
|
||||
typedef Support::BitVectorIterator<BitWord> Iterator;
|
||||
|
||||
enum : uint32_t {
|
||||
kMaxFeatures = 128,
|
||||
@ -74,9 +80,11 @@ public:
|
||||
//! \name Cast
|
||||
//! \{
|
||||
|
||||
//! Casts this base class into a derived type `T`.
|
||||
template<typename T>
|
||||
inline T& as() noexcept { return static_cast<T&>(*this); }
|
||||
|
||||
//! Casts this base class into a derived type `T` (const).
|
||||
template<typename T>
|
||||
inline const T& as() const noexcept { return static_cast<const T&>(*this); }
|
||||
|
||||
@ -85,11 +93,27 @@ public:
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns all features as `BitWord` array.
|
||||
inline bool empty() const noexcept {
|
||||
for (uint32_t i = 0; i < kNumBitWords; i++)
|
||||
if (_bits[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Returns all features as array of bitwords (see \ref Support::BitWord).
|
||||
inline BitWord* bits() noexcept { return _bits; }
|
||||
//! Returns all features as `BitWord` array (const).
|
||||
//! Returns all features as array of bitwords (const).
|
||||
inline const BitWord* bits() const noexcept { return _bits; }
|
||||
|
||||
//! Returns the number of BitWords returned by \ref bits().
|
||||
inline size_t bitWordCount() const noexcept { return kNumBitWords; }
|
||||
|
||||
//! Returns \ref Support::BitVectorIterator, that can be used to iterate
|
||||
//! all features efficiently
|
||||
inline Iterator iterator() const noexcept {
|
||||
return Iterator(_bits, kNumBitWords);
|
||||
}
|
||||
|
||||
//! Tests whether the feature `featureId` is present.
|
||||
inline bool has(uint32_t featureId) const noexcept {
|
||||
ASMJIT_ASSERT(featureId < kMaxFeatures);
|
||||
|
469
3rdparty/asmjit/src/asmjit/core/formatter.cpp
vendored
Normal file
469
3rdparty/asmjit/src/asmjit/core/formatter.cpp
vendored
Normal file
@ -0,0 +1,469 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "../core/api-build_p.h"
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
|
||||
#include "../core/builder.h"
|
||||
#include "../core/codeholder.h"
|
||||
#include "../core/compiler.h"
|
||||
#include "../core/emitter.h"
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/string.h"
|
||||
#include "../core/support.h"
|
||||
#include "../core/type.h"
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
#include "../x86/x86formatter_p.h"
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
#include "../arm/armformatter_p.h"
|
||||
#endif
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
#if defined(ASMJIT_NO_COMPILER)
|
||||
class VirtReg;
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Formatter]
|
||||
// ============================================================================
|
||||
|
||||
namespace Formatter {
|
||||
|
||||
Error formatTypeId(String& sb, uint32_t typeId) noexcept {
|
||||
if (typeId == Type::kIdVoid)
|
||||
return sb.append("void");
|
||||
|
||||
if (!Type::isValid(typeId))
|
||||
return sb.append("unknown");
|
||||
|
||||
const char* typeName = "unknown";
|
||||
uint32_t typeSize = Type::sizeOf(typeId);
|
||||
|
||||
uint32_t baseId = Type::baseOf(typeId);
|
||||
switch (baseId) {
|
||||
case Type::kIdIntPtr : typeName = "iptr" ; break;
|
||||
case Type::kIdUIntPtr: typeName = "uptr" ; break;
|
||||
case Type::kIdI8 : typeName = "i8" ; break;
|
||||
case Type::kIdU8 : typeName = "u8" ; break;
|
||||
case Type::kIdI16 : typeName = "i16" ; break;
|
||||
case Type::kIdU16 : typeName = "u16" ; break;
|
||||
case Type::kIdI32 : typeName = "i32" ; break;
|
||||
case Type::kIdU32 : typeName = "u32" ; break;
|
||||
case Type::kIdI64 : typeName = "i64" ; break;
|
||||
case Type::kIdU64 : typeName = "u64" ; break;
|
||||
case Type::kIdF32 : typeName = "f32" ; break;
|
||||
case Type::kIdF64 : typeName = "f64" ; break;
|
||||
case Type::kIdF80 : typeName = "f80" ; break;
|
||||
case Type::kIdMask8 : typeName = "mask8" ; break;
|
||||
case Type::kIdMask16 : typeName = "mask16"; break;
|
||||
case Type::kIdMask32 : typeName = "mask32"; break;
|
||||
case Type::kIdMask64 : typeName = "mask64"; break;
|
||||
case Type::kIdMmx32 : typeName = "mmx32" ; break;
|
||||
case Type::kIdMmx64 : typeName = "mmx64" ; break;
|
||||
}
|
||||
|
||||
uint32_t baseSize = Type::sizeOf(baseId);
|
||||
if (typeSize > baseSize) {
|
||||
uint32_t count = typeSize / baseSize;
|
||||
return sb.appendFormat("%sx%u", typeName, unsigned(count));
|
||||
}
|
||||
else {
|
||||
return sb.append(typeName);
|
||||
}
|
||||
}
|
||||
|
||||
Error formatFeature(
|
||||
String& sb,
|
||||
uint32_t arch,
|
||||
uint32_t featureId) noexcept {
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (Environment::isFamilyX86(arch))
|
||||
return x86::FormatterInternal::formatFeature(sb, featureId);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (Environment::isFamilyARM(arch))
|
||||
return arm::FormatterInternal::formatFeature(sb, featureId);
|
||||
#endif
|
||||
|
||||
return kErrorInvalidArch;
|
||||
}
|
||||
|
||||
Error formatLabel(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t labelId) noexcept {
|
||||
|
||||
DebugUtils::unused(formatFlags);
|
||||
|
||||
const LabelEntry* le = emitter->code()->labelEntry(labelId);
|
||||
if (ASMJIT_UNLIKELY(!le))
|
||||
return sb.appendFormat("<InvalidLabel:%u>", labelId);
|
||||
|
||||
if (le->hasName()) {
|
||||
if (le->hasParent()) {
|
||||
uint32_t parentId = le->parentId();
|
||||
const LabelEntry* pe = emitter->code()->labelEntry(parentId);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!pe))
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("<InvalidLabel:%u>", labelId));
|
||||
else if (ASMJIT_UNLIKELY(!pe->hasName()))
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("L%u", parentId));
|
||||
else
|
||||
ASMJIT_PROPAGATE(sb.append(pe->name()));
|
||||
|
||||
ASMJIT_PROPAGATE(sb.append('.'));
|
||||
}
|
||||
return sb.append(le->name());
|
||||
}
|
||||
else {
|
||||
return sb.appendFormat("L%u", labelId);
|
||||
}
|
||||
}
|
||||
|
||||
Error formatRegister(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t arch,
|
||||
uint32_t regType,
|
||||
uint32_t regId) noexcept {
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (Environment::isFamilyX86(arch))
|
||||
return x86::FormatterInternal::formatRegister(sb, formatFlags, emitter, arch, regType, regId);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (Environment::isFamilyARM(arch))
|
||||
return arm::FormatterInternal::formatRegister(sb, formatFlags, emitter, arch, regType, regId);
|
||||
#endif
|
||||
|
||||
return kErrorInvalidArch;
|
||||
}
|
||||
|
||||
Error formatOperand(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t arch,
|
||||
const Operand_& op) noexcept {
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (Environment::isFamilyX86(arch))
|
||||
return x86::FormatterInternal::formatOperand(sb, formatFlags, emitter, arch, op);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (Environment::isFamilyARM(arch))
|
||||
return arm::FormatterInternal::formatOperand(sb, formatFlags, emitter, arch, op);
|
||||
#endif
|
||||
|
||||
return kErrorInvalidArch;
|
||||
}
|
||||
|
||||
Error formatInstruction(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t arch,
|
||||
const BaseInst& inst, const Operand_* operands, size_t opCount) noexcept {
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (Environment::isFamilyX86(arch))
|
||||
return x86::FormatterInternal::formatInstruction(sb, formatFlags, emitter, arch, inst, operands, opCount);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (Environment::isFamilyARM(arch))
|
||||
return arm::FormatterInternal::formatInstruction(sb, formatFlags, emitter, arch, inst, operands, opCount);
|
||||
#endif
|
||||
|
||||
return kErrorInvalidArch;
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_BUILDER
|
||||
static Error formatFuncValue(String& sb, uint32_t formatFlags, const BaseEmitter* emitter, FuncValue value) noexcept {
|
||||
uint32_t typeId = value.typeId();
|
||||
ASMJIT_PROPAGATE(formatTypeId(sb, typeId));
|
||||
|
||||
if (value.isAssigned()) {
|
||||
ASMJIT_PROPAGATE(sb.append('@'));
|
||||
|
||||
if (value.isIndirect())
|
||||
ASMJIT_PROPAGATE(sb.append('['));
|
||||
|
||||
// NOTE: It should be either reg or stack, but never both. We
|
||||
// use two IFs on purpose so if the FuncValue is both it would
|
||||
// show in logs.
|
||||
if (value.isReg()) {
|
||||
ASMJIT_PROPAGATE(formatRegister(sb, formatFlags, emitter, emitter->arch(), value.regType(), value.regId()));
|
||||
}
|
||||
|
||||
if (value.isStack()) {
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("[%d]", int(value.stackOffset())));
|
||||
}
|
||||
|
||||
if (value.isIndirect())
|
||||
ASMJIT_PROPAGATE(sb.append(']'));
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
static Error formatFuncRets(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseEmitter* emitter,
|
||||
const FuncDetail& fd,
|
||||
VirtReg* const* vRegs) noexcept {
|
||||
|
||||
if (!fd.hasRet())
|
||||
return sb.append("void");
|
||||
|
||||
for (uint32_t i = 0; i < fd.retCount(); i++) {
|
||||
if (i) ASMJIT_PROPAGATE(sb.append(", "));
|
||||
ASMJIT_PROPAGATE(formatFuncValue(sb, formatFlags, emitter, fd.ret(i)));
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
if (vRegs) {
|
||||
static const char nullRet[] = "<none>";
|
||||
ASMJIT_PROPAGATE(sb.appendFormat(" %s", vRegs[i] ? vRegs[i]->name() : nullRet));
|
||||
}
|
||||
#else
|
||||
DebugUtils::unused(vRegs);
|
||||
#endif
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
static Error formatFuncArgs(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseEmitter* emitter,
|
||||
const FuncDetail& fd,
|
||||
VirtReg* const* vRegs) noexcept {
|
||||
|
||||
uint32_t count = fd.argCount();
|
||||
if (!count)
|
||||
return sb.append("void");
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (i)
|
||||
ASMJIT_PROPAGATE(sb.append(", "));
|
||||
|
||||
ASMJIT_PROPAGATE(formatFuncValue(sb, formatFlags, emitter, fd.arg(i)));
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
if (vRegs) {
|
||||
static const char nullArg[] = "<none>";
|
||||
ASMJIT_PROPAGATE(sb.appendFormat(" %s", vRegs[i] ? vRegs[i]->name() : nullArg));
|
||||
}
|
||||
#else
|
||||
DebugUtils::unused(vRegs);
|
||||
#endif
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error formatNode(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseBuilder* builder,
|
||||
const BaseNode* node) noexcept {
|
||||
|
||||
if (node->hasPosition() && (formatFlags & FormatOptions::kFlagPositions) != 0)
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("<%05u> ", node->position()));
|
||||
|
||||
switch (node->type()) {
|
||||
case BaseNode::kNodeInst:
|
||||
case BaseNode::kNodeJump: {
|
||||
const InstNode* instNode = node->as<InstNode>();
|
||||
ASMJIT_PROPAGATE(
|
||||
formatInstruction(sb, formatFlags, builder,
|
||||
builder->arch(),
|
||||
instNode->baseInst(), instNode->operands(), instNode->opCount()));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeSection: {
|
||||
const SectionNode* sectionNode = node->as<SectionNode>();
|
||||
if (builder->_code->isSectionValid(sectionNode->id())) {
|
||||
const Section* section = builder->_code->sectionById(sectionNode->id());
|
||||
ASMJIT_PROPAGATE(sb.appendFormat(".section %s", section->name()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeLabel: {
|
||||
const LabelNode* labelNode = node->as<LabelNode>();
|
||||
ASMJIT_PROPAGATE(formatLabel(sb, formatFlags, builder, labelNode->labelId()));
|
||||
ASMJIT_PROPAGATE(sb.append(":"));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeAlign: {
|
||||
const AlignNode* alignNode = node->as<AlignNode>();
|
||||
ASMJIT_PROPAGATE(
|
||||
sb.appendFormat("align %u (%s)",
|
||||
alignNode->alignment(),
|
||||
alignNode->alignMode() == kAlignCode ? "code" : "data"));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeEmbedData: {
|
||||
const EmbedDataNode* embedNode = node->as<EmbedDataNode>();
|
||||
ASMJIT_PROPAGATE(sb.append("embed "));
|
||||
if (embedNode->repeatCount() != 1)
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("[repeat=%zu] ", size_t(embedNode->repeatCount())));
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("%u bytes", embedNode->dataSize()));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeEmbedLabel: {
|
||||
const EmbedLabelNode* embedNode = node->as<EmbedLabelNode>();
|
||||
ASMJIT_PROPAGATE(sb.append(".label "));
|
||||
ASMJIT_PROPAGATE(formatLabel(sb, formatFlags, builder, embedNode->labelId()));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeEmbedLabelDelta: {
|
||||
const EmbedLabelDeltaNode* embedNode = node->as<EmbedLabelDeltaNode>();
|
||||
ASMJIT_PROPAGATE(sb.append(".label ("));
|
||||
ASMJIT_PROPAGATE(formatLabel(sb, formatFlags, builder, embedNode->labelId()));
|
||||
ASMJIT_PROPAGATE(sb.append(" - "));
|
||||
ASMJIT_PROPAGATE(formatLabel(sb, formatFlags, builder, embedNode->baseLabelId()));
|
||||
ASMJIT_PROPAGATE(sb.append(")"));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeComment: {
|
||||
const CommentNode* commentNode = node->as<CommentNode>();
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("; %s", commentNode->inlineComment()));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeSentinel: {
|
||||
const SentinelNode* sentinelNode = node->as<SentinelNode>();
|
||||
const char* sentinelName = nullptr;
|
||||
|
||||
switch (sentinelNode->sentinelType()) {
|
||||
case SentinelNode::kSentinelFuncEnd:
|
||||
sentinelName = "[FuncEnd]";
|
||||
break;
|
||||
|
||||
default:
|
||||
sentinelName = "[Sentinel]";
|
||||
break;
|
||||
}
|
||||
|
||||
ASMJIT_PROPAGATE(sb.append(sentinelName));
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
case BaseNode::kNodeFunc: {
|
||||
const FuncNode* funcNode = node->as<FuncNode>();
|
||||
|
||||
ASMJIT_PROPAGATE(formatLabel(sb, formatFlags, builder, funcNode->labelId()));
|
||||
ASMJIT_PROPAGATE(sb.append(": "));
|
||||
|
||||
ASMJIT_PROPAGATE(formatFuncRets(sb, formatFlags, builder, funcNode->detail(), nullptr));
|
||||
ASMJIT_PROPAGATE(sb.append(" Func("));
|
||||
ASMJIT_PROPAGATE(formatFuncArgs(sb, formatFlags, builder, funcNode->detail(), funcNode->args()));
|
||||
ASMJIT_PROPAGATE(sb.append(")"));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeFuncRet: {
|
||||
const FuncRetNode* retNode = node->as<FuncRetNode>();
|
||||
ASMJIT_PROPAGATE(sb.append("[FuncRet]"));
|
||||
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
const Operand_& op = retNode->_opArray[i];
|
||||
if (!op.isNone()) {
|
||||
ASMJIT_PROPAGATE(sb.append(i == 0 ? " " : ", "));
|
||||
ASMJIT_PROPAGATE(formatOperand(sb, formatFlags, builder, builder->arch(), op));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeInvoke: {
|
||||
const InvokeNode* invokeNode = node->as<InvokeNode>();
|
||||
ASMJIT_PROPAGATE(
|
||||
formatInstruction(sb, formatFlags, builder,
|
||||
builder->arch(),
|
||||
invokeNode->baseInst(), invokeNode->operands(), invokeNode->opCount()));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default: {
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("[UserNode:%u]", node->type()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
|
||||
Error formatNodeList(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseBuilder* builder) noexcept {
|
||||
|
||||
return formatNodeList(sb, formatFlags, builder, builder->firstNode(), nullptr);
|
||||
}
|
||||
|
||||
Error formatNodeList(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseBuilder* builder,
|
||||
const BaseNode* begin,
|
||||
const BaseNode* end) noexcept {
|
||||
|
||||
const BaseNode* node = begin;
|
||||
while (node != end) {
|
||||
ASMJIT_PROPAGATE(formatNode(sb, formatFlags, builder, node));
|
||||
ASMJIT_PROPAGATE(sb.append('\n'));
|
||||
node = node->next();
|
||||
}
|
||||
return kErrorOk;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // {Formatter}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif
|
252
3rdparty/asmjit/src/asmjit/core/formatter.h
vendored
Normal file
252
3rdparty/asmjit/src/asmjit/core/formatter.h
vendored
Normal file
@ -0,0 +1,252 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#ifndef ASMJIT_CORE_FORMATTER_H_INCLUDED
|
||||
#define ASMJIT_CORE_FORMATTER_H_INCLUDED
|
||||
|
||||
#include "../core/inst.h"
|
||||
#include "../core/string.h"
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_logging
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [Forward Declarations]
|
||||
// ============================================================================
|
||||
|
||||
class BaseEmitter;
|
||||
struct Operand_;
|
||||
|
||||
#ifndef ASMJIT_NO_BUILDER
|
||||
class BaseBuilder;
|
||||
class BaseNode;
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::FormatOptions]
|
||||
// ============================================================================
|
||||
|
||||
//! Formatting options used by \ref Logger and \ref Formatter.
|
||||
class FormatOptions {
|
||||
public:
|
||||
//! Format flags, see \ref Flags.
|
||||
uint32_t _flags;
|
||||
//! Indentation by type, see \ref IndentationType.
|
||||
uint8_t _indentation[4];
|
||||
|
||||
//! Flags can enable a logging feature.
|
||||
enum Flags : uint32_t {
|
||||
//! No flags.
|
||||
kNoFlags = 0u,
|
||||
|
||||
//! Show also binary form of each logged instruction (Assembler).
|
||||
kFlagMachineCode = 0x00000001u,
|
||||
//! Show a text explanation of some immediate values.
|
||||
kFlagExplainImms = 0x00000002u,
|
||||
//! Use hexadecimal notation of immediate values.
|
||||
kFlagHexImms = 0x00000004u,
|
||||
//! Use hexadecimal notation of address offsets.
|
||||
kFlagHexOffsets = 0x00000008u,
|
||||
//! Show casts between virtual register types (Compiler).
|
||||
kFlagRegCasts = 0x00000010u,
|
||||
//! Show positions associated with nodes (Compiler).
|
||||
kFlagPositions = 0x00000020u,
|
||||
//! Annotate nodes that are lowered by passes.
|
||||
kFlagAnnotations = 0x00000040u,
|
||||
|
||||
// TODO: These must go, keep this only for formatting.
|
||||
//! Show an additional output from passes.
|
||||
kFlagDebugPasses = 0x00000080u,
|
||||
//! Show an additional output from RA.
|
||||
kFlagDebugRA = 0x00000100u
|
||||
};
|
||||
|
||||
//! Describes indentation type of code, label, or comment in logger output.
|
||||
enum IndentationType : uint32_t {
|
||||
//! Indentation used for instructions and directives.
|
||||
kIndentationCode = 0u,
|
||||
//! Indentation used for labels and function nodes.
|
||||
kIndentationLabel = 1u,
|
||||
//! Indentation used for comments (not inline comments).
|
||||
kIndentationComment = 2u,
|
||||
//! \cond INTERNAL
|
||||
//! Reserved for future use.
|
||||
kIndentationReserved = 3u
|
||||
//! \endcond
|
||||
};
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a default-initialized FormatOptions.
|
||||
constexpr FormatOptions() noexcept
|
||||
: _flags(0),
|
||||
_indentation { 0, 0, 0, 0 } {}
|
||||
|
||||
constexpr FormatOptions(const FormatOptions& other) noexcept = default;
|
||||
inline FormatOptions& operator=(const FormatOptions& other) noexcept = default;
|
||||
|
||||
//! Resets FormatOptions to its default initialized state.
|
||||
inline void reset() noexcept {
|
||||
_flags = 0;
|
||||
_indentation[0] = 0;
|
||||
_indentation[1] = 0;
|
||||
_indentation[2] = 0;
|
||||
_indentation[3] = 0;
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns format flags.
|
||||
constexpr uint32_t flags() const noexcept { return _flags; }
|
||||
//! Tests whether the given `flag` is set in format flags.
|
||||
constexpr bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
|
||||
//! Resets all format flags to `flags`.
|
||||
inline void setFlags(uint32_t flags) noexcept { _flags = flags; }
|
||||
//! Adds `flags` to format flags.
|
||||
inline void addFlags(uint32_t flags) noexcept { _flags |= flags; }
|
||||
//! Removes `flags` from format flags.
|
||||
inline void clearFlags(uint32_t flags) noexcept { _flags &= ~flags; }
|
||||
|
||||
//! Returns indentation for the given `type`, see \ref IndentationType.
|
||||
constexpr uint8_t indentation(uint32_t type) const noexcept { return _indentation[type]; }
|
||||
//! Sets indentation for the given `type`, see \ref IndentationType.
|
||||
inline void setIndentation(uint32_t type, uint32_t n) noexcept { _indentation[type] = uint8_t(n); }
|
||||
//! Resets indentation for the given `type` to zero.
|
||||
inline void resetIndentation(uint32_t type) noexcept { _indentation[type] = uint8_t(0); }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Formatter]
|
||||
// ============================================================================
|
||||
|
||||
//! Provides formatting functionality to format operands, instructions, and nodes.
|
||||
namespace Formatter {
|
||||
|
||||
//! Appends a formatted `typeId` to the output string `sb`.
|
||||
ASMJIT_API Error formatTypeId(
|
||||
String& sb,
|
||||
uint32_t typeId) noexcept;
|
||||
|
||||
//! Appends a formatted `featureId` to the output string `sb`.
|
||||
//!
|
||||
//! See \ref BaseFeatures.
|
||||
ASMJIT_API Error formatFeature(
|
||||
String& sb,
|
||||
uint32_t arch,
|
||||
uint32_t featureId) noexcept;
|
||||
|
||||
//! Appends a formatted register to the output string `sb`.
|
||||
//!
|
||||
//! \note Emitter is optional, but it's required to format virtual registers,
|
||||
//! which won't be formatted properly if the `emitter` is not provided.
|
||||
ASMJIT_API Error formatRegister(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t arch,
|
||||
uint32_t regType,
|
||||
uint32_t regId) noexcept;
|
||||
|
||||
//! Appends a formatted label to the output string `sb`.
|
||||
//!
|
||||
//! \note Emitter is optional, but it's required to format named labels
|
||||
//! properly, otherwise the formatted as it is an anonymous label.
|
||||
ASMJIT_API Error formatLabel(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t labelId) noexcept;
|
||||
|
||||
//! Appends a formatted operand to the output string `sb`.
|
||||
//!
|
||||
//! \note Emitter is optional, but it's required to format named labels and
|
||||
//! virtual registers. See \ref formatRegister() and \ref formatLabel() for
|
||||
//! more details.
|
||||
ASMJIT_API Error formatOperand(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t arch,
|
||||
const Operand_& op) noexcept;
|
||||
|
||||
//! Appends a formatted instruction to the output string `sb`.
|
||||
//!
|
||||
//! \note Emitter is optional, but it's required to format named labels and
|
||||
//! virtual registers. See \ref formatRegister() and \ref formatLabel() for
|
||||
//! more details.
|
||||
ASMJIT_API Error formatInstruction(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t arch,
|
||||
const BaseInst& inst, const Operand_* operands, size_t opCount) noexcept;
|
||||
|
||||
#ifndef ASMJIT_NO_BUILDER
|
||||
//! Appends a formatted node to the output string `sb`.
|
||||
//!
|
||||
//! The `node` must belong to the provided `builder`.
|
||||
ASMJIT_API Error formatNode(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseBuilder* builder,
|
||||
const BaseNode* node) noexcept;
|
||||
|
||||
//! Appends formatted nodes to the output string `sb`.
|
||||
//!
|
||||
//! All nodes that are part of the given `builder` will be appended.
|
||||
ASMJIT_API Error formatNodeList(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseBuilder* builder) noexcept;
|
||||
|
||||
//! Appends formatted nodes to the output string `sb`.
|
||||
//!
|
||||
//! This function works the same as \ref formatNode(), but appends more nodes
|
||||
//! to the output string, separating each node with a newline '\n' character.
|
||||
ASMJIT_API Error formatNodeList(
|
||||
String& sb,
|
||||
uint32_t formatFlags,
|
||||
const BaseBuilder* builder,
|
||||
const BaseNode* begin,
|
||||
const BaseNode* end) noexcept;
|
||||
#endif
|
||||
|
||||
} // {Formatter}
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
#endif // ASMJIT_CORE_FORMATTER_H_INCLUDED
|
47
3rdparty/asmjit/src/asmjit/core/func.cpp
vendored
47
3rdparty/asmjit/src/asmjit/core/func.cpp
vendored
@ -42,41 +42,41 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::FuncDetail - Init / Reset]
|
||||
// ============================================================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& sign) {
|
||||
uint32_t ccId = sign.callConv();
|
||||
CallConv& cc = _callConv;
|
||||
ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const Environment& environment) noexcept {
|
||||
uint32_t ccId = signature.callConv();
|
||||
uint32_t argCount = signature.argCount();
|
||||
|
||||
uint32_t argCount = sign.argCount();
|
||||
if (ASMJIT_UNLIKELY(argCount > Globals::kMaxFuncArgs))
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
|
||||
ASMJIT_PROPAGATE(cc.init(ccId));
|
||||
CallConv& cc = _callConv;
|
||||
ASMJIT_PROPAGATE(cc.init(ccId, environment));
|
||||
|
||||
uint32_t gpSize = (cc.archId() == ArchInfo::kIdX86) ? 4 : 8;
|
||||
uint32_t deabstractDelta = Type::deabstractDeltaOfSize(gpSize);
|
||||
uint32_t registerSize = Environment::registerSizeFromArch(cc.arch());
|
||||
uint32_t deabstractDelta = Type::deabstractDeltaOfSize(registerSize);
|
||||
|
||||
const uint8_t* args = sign.args();
|
||||
const uint8_t* args = signature.args();
|
||||
for (uint32_t i = 0; i < argCount; i++) {
|
||||
FuncValue& arg = _args[i];
|
||||
arg.initTypeId(Type::deabstract(args[i], deabstractDelta));
|
||||
}
|
||||
_argCount = uint8_t(argCount);
|
||||
_vaIndex = uint8_t(sign.vaIndex());
|
||||
_vaIndex = uint8_t(signature.vaIndex());
|
||||
|
||||
uint32_t ret = sign.ret();
|
||||
uint32_t ret = signature.ret();
|
||||
if (ret != Type::kIdVoid) {
|
||||
_rets[0].initTypeId(Type::deabstract(ret, deabstractDelta));
|
||||
_retCount = 1;
|
||||
}
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (CallConv::isX86Family(ccId))
|
||||
return x86::X86Internal::initFuncDetail(*this, sign, gpSize);
|
||||
if (environment.isFamilyX86())
|
||||
return x86::X86Internal::initFuncDetail(*this, signature, registerSize);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (CallConv::isArmFamily(ccId))
|
||||
return arm::ArmInternal::initFuncDetail(*this, sign, gpSize);
|
||||
if (environment.isFamilyARM())
|
||||
return arm::ArmInternal::initFuncDetail(*this, signature, registerSize);
|
||||
#endif
|
||||
|
||||
// We should never bubble here as if `cc.init()` succeeded then there has to
|
||||
@ -89,15 +89,13 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& sign) {
|
||||
// ============================================================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error FuncFrame::init(const FuncDetail& func) noexcept {
|
||||
uint32_t ccId = func.callConv().id();
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (CallConv::isX86Family(ccId))
|
||||
if (Environment::isFamilyX86(func.callConv().arch()))
|
||||
return x86::X86Internal::initFuncFrame(*this, func);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (CallConv::isArmFamily(ccId))
|
||||
if (Environment::isFamilyARM(func.callConv().arch()))
|
||||
return arm::ArmInternal::initFuncFrame(*this, func);
|
||||
#endif
|
||||
|
||||
@ -106,12 +104,12 @@ ASMJIT_FAVOR_SIZE Error FuncFrame::init(const FuncDetail& func) noexcept {
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error FuncFrame::finalize() noexcept {
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (ArchInfo::isX86Family(archId()))
|
||||
if (Environment::isFamilyX86(arch()))
|
||||
return x86::X86Internal::finalizeFuncFrame(*this);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (ArchInfo::isArmFamily(archId()))
|
||||
if (Environment::isFamilyARM(arch()))
|
||||
return arm::ArmInternal::finalizeFuncFrame(*this);
|
||||
#endif
|
||||
|
||||
@ -123,18 +121,19 @@ ASMJIT_FAVOR_SIZE Error FuncFrame::finalize() noexcept {
|
||||
// ============================================================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error FuncArgsAssignment::updateFuncFrame(FuncFrame& frame) const noexcept {
|
||||
uint32_t arch = frame.arch();
|
||||
const FuncDetail* func = funcDetail();
|
||||
if (!func) return DebugUtils::errored(kErrorInvalidState);
|
||||
|
||||
uint32_t ccId = func->callConv().id();
|
||||
if (!func)
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (CallConv::isX86Family(ccId))
|
||||
if (Environment::isFamilyX86(arch))
|
||||
return x86::X86Internal::argsToFuncFrame(*this, frame);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (CallConv::isArmFamily(ccId))
|
||||
if (Environment::isFamilyARM(arch))
|
||||
return arm::ArmInternal::argsToFuncFrame(*this, frame);
|
||||
#endif
|
||||
|
||||
|
48
3rdparty/asmjit/src/asmjit/core/func.h
vendored
48
3rdparty/asmjit/src/asmjit/core/func.h
vendored
@ -26,13 +26,14 @@
|
||||
|
||||
#include "../core/arch.h"
|
||||
#include "../core/callconv.h"
|
||||
#include "../core/environment.h"
|
||||
#include "../core/operand.h"
|
||||
#include "../core/type.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_func
|
||||
//! \addtogroup asmjit_function
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
@ -365,7 +366,7 @@ public:
|
||||
inline FuncDetail(const FuncDetail& other) noexcept = default;
|
||||
|
||||
//! Initializes this `FuncDetail` to the given signature.
|
||||
ASMJIT_API Error init(const FuncSignature& sign);
|
||||
ASMJIT_API Error init(const FuncSignature& signature, const Environment& environment) noexcept;
|
||||
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
|
||||
|
||||
//! \}
|
||||
@ -472,12 +473,13 @@ public:
|
||||
//! frame. The function frame in most cases won't use all of the properties
|
||||
//! illustrated (for example Spill Zone and Red Zone are never used together).
|
||||
//!
|
||||
//! ```
|
||||
//! +-----------------------------+
|
||||
//! | Arguments Passed by Stack |
|
||||
//! +-----------------------------+
|
||||
//! | Spill Zone |
|
||||
//! +-----------------------------+ <- Stack offset (args) starts from here.
|
||||
//! | Return Address if Pushed |
|
||||
//! | Return Address, if Pushed |
|
||||
//! +-----------------------------+ <- Stack pointer (SP) upon entry.
|
||||
//! | Save/Restore Stack. |
|
||||
//! +-----------------------------+-----------------------------+
|
||||
@ -487,32 +489,42 @@ public:
|
||||
//! +-----------------------------+-----------------------------+ <- SP after prolog.
|
||||
//! | Red Zone |
|
||||
//! +-----------------------------+
|
||||
//! ```
|
||||
class FuncFrame {
|
||||
public:
|
||||
enum Tag : uint32_t {
|
||||
kTagInvalidOffset = 0xFFFFFFFFu //!< Tag used to inform that some offset is invalid.
|
||||
//! Tag used to inform that some offset is invalid.
|
||||
kTagInvalidOffset = 0xFFFFFFFFu
|
||||
};
|
||||
|
||||
//! Attributes are designed in a way that all are initially false, and user
|
||||
//! or FuncFrame finalizer adds them when necessary.
|
||||
enum Attributes : uint32_t {
|
||||
kAttrHasVarArgs = 0x00000001u, //!< Function has variable number of arguments.
|
||||
kAttrHasPreservedFP = 0x00000010u, //!< Preserve frame pointer (don't omit FP).
|
||||
kAttrHasFuncCalls = 0x00000020u, //!< Function calls other functions (is not leaf).
|
||||
//! Function has variable number of arguments.
|
||||
kAttrHasVarArgs = 0x00000001u,
|
||||
//! Preserve frame pointer (don't omit FP).
|
||||
kAttrHasPreservedFP = 0x00000010u,
|
||||
//! Function calls other functions (is not leaf).
|
||||
kAttrHasFuncCalls = 0x00000020u,
|
||||
|
||||
kAttrX86AvxEnabled = 0x00010000u, //!< Use AVX instead of SSE for all operations (X86).
|
||||
kAttrX86AvxCleanup = 0x00020000u, //!< Emit VZEROUPPER instruction in epilog (X86).
|
||||
kAttrX86MmxCleanup = 0x00040000u, //!< Emit EMMS instruction in epilog (X86).
|
||||
//! Use AVX instead of SSE for all operations (X86).
|
||||
kAttrX86AvxEnabled = 0x00010000u,
|
||||
//! Emit VZEROUPPER instruction in epilog (X86).
|
||||
kAttrX86AvxCleanup = 0x00020000u,
|
||||
//! Emit EMMS instruction in epilog (X86).
|
||||
kAttrX86MmxCleanup = 0x00040000u,
|
||||
|
||||
kAttrAlignedVecSR = 0x40000000u, //!< Function has aligned save/restore of vector registers.
|
||||
kAttrIsFinalized = 0x80000000u //!< FuncFrame is finalized and can be used by PEI.
|
||||
//! Function has aligned save/restore of vector registers.
|
||||
kAttrAlignedVecSR = 0x40000000u,
|
||||
//! FuncFrame is finalized and can be used by PEI.
|
||||
kAttrIsFinalized = 0x80000000u
|
||||
};
|
||||
|
||||
//! Function attributes.
|
||||
uint32_t _attributes;
|
||||
|
||||
//! Architecture ID.
|
||||
uint8_t _archId;
|
||||
//! Architecture, see \ref Environment::Arch.
|
||||
uint8_t _arch;
|
||||
//! SP register ID (to access call stack and local stack).
|
||||
uint8_t _spRegId;
|
||||
//! SA register ID (to access stack arguments).
|
||||
@ -591,7 +603,7 @@ public:
|
||||
//! \{
|
||||
|
||||
//! Returns the target architecture of the function frame.
|
||||
inline uint32_t archId() const noexcept { return _archId; }
|
||||
inline uint32_t arch() const noexcept { return _arch; }
|
||||
|
||||
//! Returns function frame attributes, see `Attributes`.
|
||||
inline uint32_t attributes() const noexcept { return _attributes; }
|
||||
@ -784,10 +796,8 @@ public:
|
||||
}
|
||||
|
||||
inline void setAllDirty() noexcept {
|
||||
_dirtyRegs[0] = 0xFFFFFFFFu;
|
||||
_dirtyRegs[1] = 0xFFFFFFFFu;
|
||||
_dirtyRegs[2] = 0xFFFFFFFFu;
|
||||
_dirtyRegs[3] = 0xFFFFFFFFu;
|
||||
for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_dirtyRegs); i++)
|
||||
_dirtyRegs[i] = 0xFFFFFFFFu;
|
||||
}
|
||||
|
||||
inline void setAllDirty(uint32_t group) noexcept {
|
||||
|
149
3rdparty/asmjit/src/asmjit/core/globals.cpp
vendored
149
3rdparty/asmjit/src/asmjit/core/globals.cpp
vendored
@ -33,75 +33,88 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
|
||||
#ifndef ASMJIT_NO_TEXT
|
||||
static const char errorMessages[] =
|
||||
// @EnumStringBegin{"enum": "ErrorCode", "output": "sError", "strip": "kError"}@
|
||||
static const char sErrorString[] =
|
||||
"Ok\0"
|
||||
"Out of memory\0"
|
||||
"Invalid argument\0"
|
||||
"Invalid state\0"
|
||||
"Invalid architecture\0"
|
||||
"Not initialized\0"
|
||||
"Already initialized\0"
|
||||
"Feature not enabled\0"
|
||||
"Too many handles or file descriptors\0"
|
||||
"Too large (code or memory request)\0"
|
||||
"No code generated\0"
|
||||
"Invalid directive\0"
|
||||
"Invalid label\0"
|
||||
"Too many labels\0"
|
||||
"Label already bound\0"
|
||||
"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"
|
||||
"Invalid section\0"
|
||||
"Too many sections\0"
|
||||
"Invalid section name\0"
|
||||
"Too many relocations\0"
|
||||
"Invalid relocation entry\0"
|
||||
"Relocation offset out of range\0"
|
||||
"Invalid assignment\0"
|
||||
"Invalid instruction\0"
|
||||
"Invalid register type\0"
|
||||
"Invalid register group\0"
|
||||
"Invalid register physical id\0"
|
||||
"Invalid register virtual id\0"
|
||||
"Invalid prefix combination\0"
|
||||
"Invalid lock prefix\0"
|
||||
"Invalid xacquire prefix\0"
|
||||
"Invalid xrelease prefix\0"
|
||||
"Invalid rep prefix\0"
|
||||
"Invalid rex prefix\0"
|
||||
"Invalid {...} register \0"
|
||||
"Invalid use of {k}\0"
|
||||
"Invalid use of {k}{z}\0"
|
||||
"Invalid broadcast {1tox}\0"
|
||||
"Invalid {er} or {sae} option\0"
|
||||
"Invalid address\0"
|
||||
"Invalid address index\0"
|
||||
"Invalid address scale\0"
|
||||
"Invalid use of 64-bit address or offset\0"
|
||||
"Invalid use of 64-bit address or offset that requires 32-bit zero-extension\0"
|
||||
"Invalid displacement\0"
|
||||
"Invalid segment\0"
|
||||
"Invalid immediate value\0"
|
||||
"Invalid operand size\0"
|
||||
"Ambiguous operand size\0"
|
||||
"Operand size mismatch\0"
|
||||
"Invalid option\0"
|
||||
"Option already defined\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"
|
||||
"Not consecutive registers\0"
|
||||
"No more physical registers\0"
|
||||
"Overlapped registers\0"
|
||||
"Overlapping register and arguments base-address register\0"
|
||||
"Unbound label cannot be evaluated by expression\0"
|
||||
"Arithmetic overflow during expression evaluation\0"
|
||||
"Unknown error\0";
|
||||
return Support::findPackedString(errorMessages, Support::min<Error>(err, kErrorCount));
|
||||
"OutOfMemory\0"
|
||||
"InvalidArgument\0"
|
||||
"InvalidState\0"
|
||||
"InvalidArch\0"
|
||||
"NotInitialized\0"
|
||||
"AlreadyInitialized\0"
|
||||
"FeatureNotEnabled\0"
|
||||
"TooManyHandles\0"
|
||||
"TooLarge\0"
|
||||
"NoCodeGenerated\0"
|
||||
"InvalidDirective\0"
|
||||
"InvalidLabel\0"
|
||||
"TooManyLabels\0"
|
||||
"LabelAlreadyBound\0"
|
||||
"LabelAlreadyDefined\0"
|
||||
"LabelNameTooLong\0"
|
||||
"InvalidLabelName\0"
|
||||
"InvalidParentLabel\0"
|
||||
"NonLocalLabelCannotHaveParent\0"
|
||||
"InvalidSection\0"
|
||||
"TooManySections\0"
|
||||
"InvalidSectionName\0"
|
||||
"TooManyRelocations\0"
|
||||
"InvalidRelocEntry\0"
|
||||
"RelocOffsetOutOfRange\0"
|
||||
"InvalidAssignment\0"
|
||||
"InvalidInstruction\0"
|
||||
"InvalidRegType\0"
|
||||
"InvalidRegGroup\0"
|
||||
"InvalidPhysId\0"
|
||||
"InvalidVirtId\0"
|
||||
"InvalidPrefixCombination\0"
|
||||
"InvalidLockPrefix\0"
|
||||
"InvalidXAcquirePrefix\0"
|
||||
"InvalidXReleasePrefix\0"
|
||||
"InvalidRepPrefix\0"
|
||||
"InvalidRexPrefix\0"
|
||||
"InvalidExtraReg\0"
|
||||
"InvalidKMaskUse\0"
|
||||
"InvalidKZeroUse\0"
|
||||
"InvalidBroadcast\0"
|
||||
"InvalidEROrSAE\0"
|
||||
"InvalidAddress\0"
|
||||
"InvalidAddressIndex\0"
|
||||
"InvalidAddressScale\0"
|
||||
"InvalidAddress64Bit\0"
|
||||
"InvalidAddress64BitZeroExtension\0"
|
||||
"InvalidDisplacement\0"
|
||||
"InvalidSegment\0"
|
||||
"InvalidImmediate\0"
|
||||
"InvalidOperandSize\0"
|
||||
"AmbiguousOperandSize\0"
|
||||
"OperandSizeMismatch\0"
|
||||
"InvalidOption\0"
|
||||
"OptionAlreadyDefined\0"
|
||||
"InvalidTypeId\0"
|
||||
"InvalidUseOfGpbHi\0"
|
||||
"InvalidUseOfGpq\0"
|
||||
"InvalidUseOfF80\0"
|
||||
"NotConsecutiveRegs\0"
|
||||
"IllegalVirtReg\0"
|
||||
"TooManyVirtRegs\0"
|
||||
"NoMorePhysRegs\0"
|
||||
"OverlappedRegs\0"
|
||||
"OverlappingStackRegWithRegArg\0"
|
||||
"ExpressionLabelNotBound\0"
|
||||
"ExpressionOverflow\0"
|
||||
"<Unknown>\0";
|
||||
|
||||
static const uint16_t sErrorIndex[] = {
|
||||
0, 3, 15, 31, 44, 56, 71, 90, 108, 123, 132, 148, 165, 178, 192, 210, 230,
|
||||
247, 264, 283, 313, 328, 344, 363, 382, 400, 422, 440, 459, 474, 490, 504,
|
||||
518, 543, 561, 583, 605, 622, 639, 655, 671, 687, 704, 719, 734, 754, 774,
|
||||
794, 827, 847, 862, 879, 898, 919, 939, 953, 974, 988, 1006, 1022, 1038,
|
||||
1057, 1072, 1088, 1103, 1118, 1148, 1172, 1191
|
||||
};
|
||||
// @EnumStringEnd@
|
||||
|
||||
return sErrorString + sErrorIndex[Support::min<Error>(err, kErrorCount)];
|
||||
#else
|
||||
DebugUtils::unused(err);
|
||||
static const char noMessage[] = "";
|
||||
|
122
3rdparty/asmjit/src/asmjit/core/globals.h
vendored
122
3rdparty/asmjit/src/asmjit/core/globals.h
vendored
@ -33,7 +33,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// ============================================================================
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup Support
|
||||
//! \addtogroup asmjit_utilities
|
||||
//! \{
|
||||
namespace Support {
|
||||
//! Cast designed to cast between function and void* pointers.
|
||||
@ -88,9 +88,9 @@ constexpr uint32_t kAllocAlignment = 8;
|
||||
//! Aggressive growing strategy threshold.
|
||||
constexpr uint32_t kGrowThreshold = 1024 * 1024 * 16;
|
||||
|
||||
//! Maximum height of RB-Tree is:
|
||||
//! Maximum depth of RB-Tree is:
|
||||
//!
|
||||
//! `2 * log2(n + 1)`.
|
||||
//! `2 * log2(n + 1)`
|
||||
//!
|
||||
//! Size of RB node is at least two pointers (without data),
|
||||
//! so a theoretical architecture limit would be:
|
||||
@ -104,7 +104,7 @@ constexpr uint32_t kMaxTreeHeight = (ASMJIT_ARCH_BITS == 32 ? 30 : 61) + 1;
|
||||
//! Maximum number of operands per a single instruction.
|
||||
constexpr uint32_t kMaxOpCount = 6;
|
||||
|
||||
// TODO: Use this one.
|
||||
//! Maximum arguments of a function supported by the Compiler / Function API.
|
||||
constexpr uint32_t kMaxFuncArgs = 16;
|
||||
|
||||
//! Maximum number of physical registers AsmJit can use per register group.
|
||||
@ -168,15 +168,46 @@ static const constexpr NoInit_ NoInit {};
|
||||
|
||||
} // {Globals}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ByteOrder]
|
||||
// ============================================================================
|
||||
|
||||
//! Byte order.
|
||||
namespace ByteOrder {
|
||||
enum : uint32_t {
|
||||
kLE = 0,
|
||||
kBE = 1,
|
||||
kNative = ASMJIT_ARCH_LE ? kLE : kBE,
|
||||
kSwapped = ASMJIT_ARCH_LE ? kBE : kLE
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ptr_as_func / func_as_ptr]
|
||||
// ============================================================================
|
||||
|
||||
template<typename Func>
|
||||
static inline Func ptr_as_func(void* func) noexcept { return Support::ptr_cast_impl<Func, void*>(func); }
|
||||
|
||||
template<typename Func>
|
||||
static inline void* func_as_ptr(Func func) noexcept { return Support::ptr_cast_impl<void*, Func>(func); }
|
||||
|
||||
//! \}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Error]
|
||||
// ============================================================================
|
||||
|
||||
//! \addtogroup asmjit_error_handling
|
||||
//! \{
|
||||
|
||||
//! AsmJit error type (uint32_t).
|
||||
typedef uint32_t Error;
|
||||
|
||||
//! AsmJit error codes.
|
||||
enum ErrorCode : uint32_t {
|
||||
// @EnumValuesBegin{"enum": "ErrorCode"}@
|
||||
|
||||
//! No error (success).
|
||||
kErrorOk = 0,
|
||||
|
||||
@ -211,16 +242,16 @@ enum ErrorCode : uint32_t {
|
||||
|
||||
//! No code generated.
|
||||
//!
|
||||
//! Returned by runtime if the `CodeHolder` contains no code.
|
||||
//! Returned by runtime if the \ref CodeHolder contains no code.
|
||||
kErrorNoCodeGenerated,
|
||||
|
||||
//! Invalid directive.
|
||||
kErrorInvalidDirective,
|
||||
//! Attempt to use uninitialized label.
|
||||
kErrorInvalidLabel,
|
||||
//! Label index overflow - a single `Assembler` instance can hold almost
|
||||
//! 2^32 (4 billion) labels. If there is an attempt to create more labels
|
||||
//! then this error is returned.
|
||||
//! Label index overflow - a single \ref BaseAssembler instance can hold
|
||||
//! almost 2^32 (4 billion) labels. If there is an attempt to create more
|
||||
//! labels then this error is returned.
|
||||
kErrorTooManyLabels,
|
||||
//! Label is already bound.
|
||||
kErrorLabelAlreadyBound,
|
||||
@ -230,10 +261,10 @@ enum ErrorCode : uint32_t {
|
||||
kErrorLabelNameTooLong,
|
||||
//! Label must always be local if it's anonymous (without a name).
|
||||
kErrorInvalidLabelName,
|
||||
//! Parent id passed to `CodeHolder::newNamedLabelId()` was invalid.
|
||||
//! Parent id passed to \ref CodeHolder::newNamedLabelEntry() was invalid.
|
||||
kErrorInvalidParentLabel,
|
||||
//! Parent id specified for a non-local (global) label.
|
||||
kErrorNonLocalLabelCantHaveParent,
|
||||
kErrorNonLocalLabelCannotHaveParent,
|
||||
|
||||
//! Invalid section.
|
||||
kErrorInvalidSection,
|
||||
@ -257,9 +288,9 @@ enum ErrorCode : uint32_t {
|
||||
kErrorInvalidRegType,
|
||||
//! Invalid register group.
|
||||
kErrorInvalidRegGroup,
|
||||
//! Invalid register's physical id.
|
||||
//! Invalid physical register id.
|
||||
kErrorInvalidPhysId,
|
||||
//! Invalid register's virtual id.
|
||||
//! Invalid virtual register id.
|
||||
kErrorInvalidVirtId,
|
||||
//! Invalid prefix combination.
|
||||
kErrorInvalidPrefixCombination,
|
||||
@ -319,11 +350,17 @@ enum ErrorCode : uint32_t {
|
||||
kErrorInvalidUseOfGpbHi,
|
||||
//! Invalid use of a 64-bit GPQ register in 32-bit mode.
|
||||
kErrorInvalidUseOfGpq,
|
||||
//! Invalid use of an 80-bit float (Type::kIdF80).
|
||||
//! Invalid use of an 80-bit float (\ref Type::kIdF80).
|
||||
kErrorInvalidUseOfF80,
|
||||
//! Some registers in the instruction muse be consecutive (some ARM and AVX512 neural-net instructions).
|
||||
//! Some registers in the instruction muse be consecutive (some ARM and AVX512
|
||||
//! neural-net instructions).
|
||||
kErrorNotConsecutiveRegs,
|
||||
|
||||
//! Illegal virtual register - reported by instruction validation.
|
||||
kErrorIllegalVirtReg,
|
||||
//! AsmJit cannot create more virtual registers.
|
||||
kErrorTooManyVirtRegs,
|
||||
|
||||
//! AsmJit requires a physical register, but no one is available.
|
||||
kErrorNoMorePhysRegs,
|
||||
//! A variable has been assigned more than once to a function argument (BaseCompiler).
|
||||
@ -336,33 +373,12 @@ enum ErrorCode : uint32_t {
|
||||
//! Arithmetic overflow during expression evaluation.
|
||||
kErrorExpressionOverflow,
|
||||
|
||||
// @EnumValuesEnd@
|
||||
|
||||
//! Count of AsmJit error codes.
|
||||
kErrorCount
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ByteOrder]
|
||||
// ============================================================================
|
||||
|
||||
//! Byte order.
|
||||
namespace ByteOrder {
|
||||
enum : uint32_t {
|
||||
kLE = 0,
|
||||
kBE = 1,
|
||||
kNative = ASMJIT_ARCH_LE ? kLE : kBE,
|
||||
kSwapped = ASMJIT_ARCH_LE ? kBE : kLE
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ptr_as_func / func_as_ptr]
|
||||
// ============================================================================
|
||||
|
||||
template<typename Func>
|
||||
static inline Func ptr_as_func(void* func) noexcept { return Support::ptr_cast_impl<Func, void*>(func); }
|
||||
template<typename Func>
|
||||
static inline void* func_as_ptr(Func func) noexcept { return Support::ptr_cast_impl<void*, Func>(func); }
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::DebugUtils]
|
||||
// ============================================================================
|
||||
@ -370,9 +386,11 @@ static inline void* func_as_ptr(Func func) noexcept { return Support::ptr_cast_i
|
||||
//! Debugging utilities.
|
||||
namespace DebugUtils {
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! Used to silence warnings about unused arguments or variables.
|
||||
template<typename... Args>
|
||||
static ASMJIT_INLINE void unused(Args&&...) noexcept {}
|
||||
//! \endcond
|
||||
|
||||
//! Returns the error `err` passed.
|
||||
//!
|
||||
@ -392,23 +410,35 @@ ASMJIT_API void debugOutput(const char* str) noexcept;
|
||||
//! \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/core/globals.cpp) and check the call stack to locate the
|
||||
//! failing code.
|
||||
//! If you have problems with assertion failures a breakpoint can be put
|
||||
//! at \ref assertionFailed() function (asmjit/core/globals.cpp). A call stack
|
||||
//! will be available when such assertion failure is triggered. AsmJit always
|
||||
//! returns errors on failures, assertions are a last resort and usually mean
|
||||
//! unrecoverable state due to out of range array access or totally invalid
|
||||
//! arguments like nullptr where a valid pointer should be provided, etc...
|
||||
ASMJIT_API void ASMJIT_NORETURN assertionFailed(const char* file, int line, const char* msg) noexcept;
|
||||
|
||||
} // {DebugUtils}
|
||||
|
||||
//! \def ASMJIT_ASSERT(...)
|
||||
//!
|
||||
//! AsmJit's own assert macro used in AsmJit code-base.
|
||||
#if defined(ASMJIT_BUILD_DEBUG)
|
||||
#define ASMJIT_ASSERT(EXP) \
|
||||
#define ASMJIT_ASSERT(...) \
|
||||
do { \
|
||||
if (ASMJIT_LIKELY(EXP)) \
|
||||
if (ASMJIT_LIKELY(__VA_ARGS__)) \
|
||||
break; \
|
||||
::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, #EXP); \
|
||||
::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, #__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define ASMJIT_ASSERT(EXP) ((void)0)
|
||||
#define ASMJIT_ASSERT(...) ((void)0)
|
||||
#endif
|
||||
|
||||
//! Used by AsmJit to propagate a possible `Error` produced by `...` to the caller.
|
||||
//! \def ASMJIT_PROPAGATE(...)
|
||||
//!
|
||||
//! Propagates a possible `Error` produced by `...` to the caller by returning
|
||||
//! the error immediately. Used by AsmJit internally, but kept public for users
|
||||
//! that want to use the same technique to propagate errors to the caller.
|
||||
#define ASMJIT_PROPAGATE(...) \
|
||||
do { \
|
||||
::asmjit::Error _err = __VA_ARGS__; \
|
||||
@ -416,8 +446,6 @@ ASMJIT_API void ASMJIT_NORETURN assertionFailed(const char* file, int line, cons
|
||||
return _err; \
|
||||
} while (0)
|
||||
|
||||
} // {DebugUtils}
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
52
3rdparty/asmjit/src/asmjit/core/inst.cpp
vendored
52
3rdparty/asmjit/src/asmjit/core/inst.cpp
vendored
@ -42,29 +42,29 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// ============================================================================
|
||||
|
||||
#ifndef ASMJIT_NO_TEXT
|
||||
Error InstAPI::instIdToString(uint32_t archId, uint32_t instId, String& output) noexcept {
|
||||
Error InstAPI::instIdToString(uint32_t arch, uint32_t instId, String& output) noexcept {
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (ArchInfo::isX86Family(archId))
|
||||
return x86::InstInternal::instIdToString(archId, instId, output);
|
||||
if (Environment::isFamilyX86(arch))
|
||||
return x86::InstInternal::instIdToString(arch, instId, output);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (ArchInfo::isArmFamily(archId))
|
||||
return arm::InstInternal::instIdToString(archId, instId, output);
|
||||
if (Environment::isFamilyARM(arch))
|
||||
return arm::InstInternal::instIdToString(arch, instId, output);
|
||||
#endif
|
||||
|
||||
return DebugUtils::errored(kErrorInvalidArch);
|
||||
}
|
||||
|
||||
uint32_t InstAPI::stringToInstId(uint32_t archId, const char* s, size_t len) noexcept {
|
||||
uint32_t InstAPI::stringToInstId(uint32_t arch, const char* s, size_t len) noexcept {
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (ArchInfo::isX86Family(archId))
|
||||
return x86::InstInternal::stringToInstId(archId, s, len);
|
||||
if (Environment::isFamilyX86(arch))
|
||||
return x86::InstInternal::stringToInstId(arch, s, len);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (ArchInfo::isArmFamily(archId))
|
||||
return arm::InstInternal::stringToInstId(archId, s, len);
|
||||
if (Environment::isFamilyARM(arch))
|
||||
return arm::InstInternal::stringToInstId(arch, s, len);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
@ -76,15 +76,15 @@ uint32_t InstAPI::stringToInstId(uint32_t archId, const char* s, size_t len) noe
|
||||
// ============================================================================
|
||||
|
||||
#ifndef ASMJIT_NO_VALIDATION
|
||||
Error InstAPI::validate(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept {
|
||||
Error InstAPI::validate(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, uint32_t validationFlags) noexcept {
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (ArchInfo::isX86Family(archId))
|
||||
return x86::InstInternal::validate(archId, inst, operands, opCount);
|
||||
if (Environment::isFamilyX86(arch))
|
||||
return x86::InstInternal::validate(arch, inst, operands, opCount, validationFlags);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (ArchInfo::isArmFamily(archId))
|
||||
return arm::InstInternal::validate(archId, inst, operands, opCount);
|
||||
if (Environment::isFamilyARM(arch))
|
||||
return arm::InstInternal::validate(arch, inst, operands, opCount, validationFlags);
|
||||
#endif
|
||||
|
||||
return DebugUtils::errored(kErrorInvalidArch);
|
||||
@ -96,18 +96,18 @@ Error InstAPI::validate(uint32_t archId, const BaseInst& inst, const Operand_* o
|
||||
// ============================================================================
|
||||
|
||||
#ifndef ASMJIT_NO_INTROSPECTION
|
||||
Error InstAPI::queryRWInfo(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, InstRWInfo& out) noexcept {
|
||||
if (ASMJIT_UNLIKELY(opCount > 6))
|
||||
Error InstAPI::queryRWInfo(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, InstRWInfo* out) noexcept {
|
||||
if (ASMJIT_UNLIKELY(opCount > Globals::kMaxOpCount))
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (ArchInfo::isX86Family(archId))
|
||||
return x86::InstInternal::queryRWInfo(archId, inst, operands, opCount, out);
|
||||
if (Environment::isFamilyX86(arch))
|
||||
return x86::InstInternal::queryRWInfo(arch, inst, operands, opCount, out);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (ArchInfo::isArmFamily(archId))
|
||||
return arm::InstInternal::queryRWInfo(archId, inst, operands, opCount, out);
|
||||
if (Environment::isFamilyARM(arch))
|
||||
return arm::InstInternal::queryRWInfo(arch, inst, operands, opCount, out);
|
||||
#endif
|
||||
|
||||
return DebugUtils::errored(kErrorInvalidArch);
|
||||
@ -119,15 +119,15 @@ Error InstAPI::queryRWInfo(uint32_t archId, const BaseInst& inst, const Operand_
|
||||
// ============================================================================
|
||||
|
||||
#ifndef ASMJIT_NO_INTROSPECTION
|
||||
Error InstAPI::queryFeatures(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, BaseFeatures& out) noexcept {
|
||||
Error InstAPI::queryFeatures(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, BaseFeatures* out) noexcept {
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (ArchInfo::isX86Family(archId))
|
||||
return x86::InstInternal::queryFeatures(archId, inst, operands, opCount, out);
|
||||
if (Environment::isFamilyX86(arch))
|
||||
return x86::InstInternal::queryFeatures(arch, inst, operands, opCount, out);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (ArchInfo::isArmFamily(archId))
|
||||
return arm::InstInternal::queryFeatures(archId, inst, operands, opCount, out);
|
||||
if (Environment::isFamilyARM(arch))
|
||||
return arm::InstInternal::queryFeatures(arch, inst, operands, opCount, out);
|
||||
#endif
|
||||
|
||||
return DebugUtils::errored(kErrorInvalidArch);
|
||||
|
606
3rdparty/asmjit/src/asmjit/core/inst.h
vendored
606
3rdparty/asmjit/src/asmjit/core/inst.h
vendored
@ -31,243 +31,9 @@
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \addtogroup asmjit_instruction_db
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::InstInfo]
|
||||
// ============================================================================
|
||||
|
||||
// TODO: Finalize instruction info and make more x86::InstDB methods/structs private.
|
||||
|
||||
/*
|
||||
|
||||
struct InstInfo {
|
||||
//! Architecture agnostic attributes.
|
||||
enum Attributes : uint32_t {
|
||||
|
||||
|
||||
};
|
||||
|
||||
//! Instruction attributes.
|
||||
uint32_t _attributes;
|
||||
|
||||
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
|
||||
|
||||
inline uint32_t attributes() const noexcept { return _attributes; }
|
||||
inline bool hasAttribute(uint32_t attr) const noexcept { return (_attributes & attr) != 0; }
|
||||
};
|
||||
|
||||
//! Gets attributes of the given instruction.
|
||||
ASMJIT_API Error queryCommonInfo(uint32_t archId, uint32_t instId, InstInfo& out) noexcept;
|
||||
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::InstRWInfo / OpRWInfo]
|
||||
// ============================================================================
|
||||
|
||||
//! Read/Write information related to a single operand, used by `InstRWInfo`.
|
||||
struct OpRWInfo {
|
||||
//! Read/Write flags, see `OpRWInfo::Flags`.
|
||||
uint32_t _opFlags;
|
||||
//! Physical register index, if required.
|
||||
uint8_t _physId;
|
||||
//! Size of a possible memory operand that can replace a register operand.
|
||||
uint8_t _rmSize;
|
||||
//! Reserved for future use.
|
||||
uint8_t _reserved[2];
|
||||
//! Read bit-mask where each bit represents one byte read from Reg/Mem.
|
||||
uint64_t _readByteMask;
|
||||
//! Write bit-mask where each bit represents one byte written to Reg/Mem.
|
||||
uint64_t _writeByteMask;
|
||||
//! Zero/Sign extend bit-mask where each bit represents one byte written to Reg/Mem.
|
||||
uint64_t _extendByteMask;
|
||||
|
||||
//! Flags describe how the operand is accessed and some additional information.
|
||||
enum Flags : uint32_t {
|
||||
//! Operand is read.
|
||||
//!
|
||||
//! \note This flag must be `0x00000001`.
|
||||
kRead = 0x00000001u,
|
||||
|
||||
//! Operand is written.
|
||||
//!
|
||||
//! \note This flag must be `0x00000002`.
|
||||
kWrite = 0x00000002u,
|
||||
|
||||
//! Operand is both read and written.
|
||||
//!
|
||||
//! \note This combination of flags must be `0x00000003`.
|
||||
kRW = 0x00000003u,
|
||||
|
||||
//! Register operand can be replaced by a memory operand.
|
||||
kRegMem = 0x00000004u,
|
||||
|
||||
//! The `extendByteMask()` represents a zero extension.
|
||||
kZExt = 0x00000010u,
|
||||
|
||||
//! Register operand must use `physId()`.
|
||||
kRegPhysId = 0x00000100u,
|
||||
//! Base register of a memory operand must use `physId()`.
|
||||
kMemPhysId = 0x00000200u,
|
||||
|
||||
//! This memory operand is only used to encode registers and doesn't access memory.
|
||||
//!
|
||||
//! X86 Specific
|
||||
//! ------------
|
||||
//!
|
||||
//! Instructions that use such feature include BNDLDX, BNDSTX, and LEA.
|
||||
kMemFake = 0x000000400u,
|
||||
|
||||
//! Base register of the memory operand will be read.
|
||||
kMemBaseRead = 0x00001000u,
|
||||
//! Base register of the memory operand will be written.
|
||||
kMemBaseWrite = 0x00002000u,
|
||||
//! Base register of the memory operand will be read & written.
|
||||
kMemBaseRW = 0x00003000u,
|
||||
|
||||
//! Index register of the memory operand will be read.
|
||||
kMemIndexRead = 0x00004000u,
|
||||
//! Index register of the memory operand will be written.
|
||||
kMemIndexWrite = 0x00008000u,
|
||||
//! Index register of the memory operand will be read & written.
|
||||
kMemIndexRW = 0x0000C000u,
|
||||
|
||||
//! Base register of the memory operand will be modified before the operation.
|
||||
kMemBasePreModify = 0x00010000u,
|
||||
//! Base register of the memory operand will be modified after the operation.
|
||||
kMemBasePostModify = 0x00020000u
|
||||
};
|
||||
|
||||
static_assert(kRead == 0x1, "OpRWInfo::kRead flag must be 0x1");
|
||||
static_assert(kWrite == 0x2, "OpRWInfo::kWrite flag must be 0x2");
|
||||
static_assert(kRegMem == 0x4, "OpRWInfo::kRegMem flag must be 0x4");
|
||||
|
||||
//! \name Reset
|
||||
//! \{
|
||||
|
||||
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
|
||||
inline void reset(uint32_t opFlags, uint32_t regSize, uint32_t physId = BaseReg::kIdBad) noexcept {
|
||||
_opFlags = opFlags;
|
||||
_physId = uint8_t(physId);
|
||||
_rmSize = uint8_t((opFlags & kRegMem) ? regSize : uint32_t(0));
|
||||
_resetReserved();
|
||||
|
||||
uint64_t mask = Support::lsbMask<uint64_t>(regSize);
|
||||
_readByteMask = opFlags & kRead ? mask : uint64_t(0);
|
||||
_writeByteMask = opFlags & kWrite ? mask : uint64_t(0);
|
||||
_extendByteMask = 0;
|
||||
}
|
||||
|
||||
inline void _resetReserved() noexcept {
|
||||
memset(_reserved, 0, sizeof(_reserved));
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Operand Flags
|
||||
//! \{
|
||||
|
||||
inline uint32_t opFlags() const noexcept { return _opFlags; }
|
||||
inline bool hasOpFlag(uint32_t flag) const noexcept { return (_opFlags & flag) != 0; }
|
||||
|
||||
inline void addOpFlags(uint32_t flags) noexcept { _opFlags |= flags; }
|
||||
inline void clearOpFlags(uint32_t flags) noexcept { _opFlags &= ~flags; }
|
||||
|
||||
inline bool isRead() const noexcept { return hasOpFlag(kRead); }
|
||||
inline bool isWrite() const noexcept { return hasOpFlag(kWrite); }
|
||||
inline bool isReadWrite() const noexcept { return (_opFlags & kRW) == kRW; }
|
||||
inline bool isReadOnly() const noexcept { return (_opFlags & kRW) == kRead; }
|
||||
inline bool isWriteOnly() const noexcept { return (_opFlags & kRW) == kWrite; }
|
||||
inline bool isRm() const noexcept { return hasOpFlag(kRegMem); }
|
||||
inline bool isZExt() const noexcept { return hasOpFlag(kZExt); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Physical Register ID
|
||||
//! \{
|
||||
|
||||
inline uint32_t physId() const noexcept { return _physId; }
|
||||
inline bool hasPhysId() const noexcept { return _physId != BaseReg::kIdBad; }
|
||||
inline void setPhysId(uint32_t physId) noexcept { _physId = uint8_t(physId); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Reg/Mem
|
||||
//! \{
|
||||
|
||||
inline uint32_t rmSize() const noexcept { return _rmSize; }
|
||||
inline void setRmSize(uint32_t rmSize) noexcept { _rmSize = uint8_t(rmSize); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Read & Write Masks
|
||||
//! \{
|
||||
|
||||
inline uint64_t readByteMask() const noexcept { return _readByteMask; }
|
||||
inline uint64_t writeByteMask() const noexcept { return _writeByteMask; }
|
||||
inline uint64_t extendByteMask() const noexcept { return _extendByteMask; }
|
||||
|
||||
inline void setReadByteMask(uint64_t mask) noexcept { _readByteMask = mask; }
|
||||
inline void setWriteByteMask(uint64_t mask) noexcept { _writeByteMask = mask; }
|
||||
inline void setExtendByteMask(uint64_t mask) noexcept { _extendByteMask = mask; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! Read/Write information of an instruction.
|
||||
struct InstRWInfo {
|
||||
//! Instruction flags.
|
||||
uint32_t _instFlags;
|
||||
//! Mask of flags read.
|
||||
uint32_t _readFlags;
|
||||
//! Mask of flags written.
|
||||
uint32_t _writeFlags;
|
||||
//! Count of operands.
|
||||
uint8_t _opCount;
|
||||
//! CPU feature required for replacing register operand with memory operand.
|
||||
uint8_t _rmFeature;
|
||||
//! Reserved for future use.
|
||||
uint8_t _reserved[19];
|
||||
//! Read/Write onfo of extra register (rep{} or kz{}).
|
||||
OpRWInfo _extraReg;
|
||||
//! Read/Write info of instruction operands.
|
||||
OpRWInfo _operands[Globals::kMaxOpCount];
|
||||
|
||||
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
|
||||
|
||||
inline uint32_t instFlags() const noexcept { return _instFlags; }
|
||||
inline bool hasInstFlag(uint32_t flag) const noexcept { return (_instFlags & flag) != 0; }
|
||||
|
||||
inline uint32_t opCount() const noexcept { return _opCount; }
|
||||
|
||||
inline uint32_t readFlags() const noexcept { return _readFlags; }
|
||||
inline uint32_t writeFlags() const noexcept { return _writeFlags; }
|
||||
|
||||
//! Returns the CPU feature required to replace a register operand with memory
|
||||
//! operand. If the returned feature is zero (none) then this instruction
|
||||
//! either doesn't provide memory operand combination or there is no extra
|
||||
//! CPU feature required.
|
||||
//!
|
||||
//! X86 Specific
|
||||
//! ------------
|
||||
//!
|
||||
//! Some AVX+ instructions may require extra features for replacing registers
|
||||
//! with memory operands, for example VPSLLDQ instruction only supports
|
||||
//! 'reg/reg/imm' combination on AVX/AVX2 capable CPUs and requires AVX-512 for
|
||||
//! 'reg/mem/imm' combination.
|
||||
inline uint32_t rmFeature() const noexcept { return _rmFeature; }
|
||||
|
||||
inline const OpRWInfo& extraReg() const noexcept { return _extraReg; }
|
||||
inline const OpRWInfo* operands() const noexcept { return _operands; }
|
||||
|
||||
inline const OpRWInfo& operand(size_t index) const noexcept {
|
||||
ASMJIT_ASSERT(index < Globals::kMaxOpCount);
|
||||
return _operands[index];
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::BaseInst]
|
||||
// ============================================================================
|
||||
@ -277,33 +43,26 @@ struct InstRWInfo {
|
||||
//! and `Operand[]` array.
|
||||
class BaseInst {
|
||||
public:
|
||||
//! Instruction id.
|
||||
//! Instruction id, see \ref BaseInst::Id or {arch-specific}::Inst::Id.
|
||||
uint32_t _id;
|
||||
//! Instruction options.
|
||||
//! Instruction options, see \ref BaseInst::Options or {arch-specific}::Inst::Options.
|
||||
uint32_t _options;
|
||||
//! Extra register used by instruction (either REP register or AVX-512 selector).
|
||||
RegOnly _extraReg;
|
||||
|
||||
enum Id : uint32_t {
|
||||
//! Invalid or uninitialized instruction id.
|
||||
kIdNone = 0x00000000u,
|
||||
kIdNone = 0x00000000u,
|
||||
//! Abstract instruction (BaseBuilder and BaseCompiler).
|
||||
kIdAbstract = 0x80000000u
|
||||
kIdAbstract = 0x80000000u
|
||||
};
|
||||
|
||||
enum Options : uint32_t {
|
||||
//! Used internally by emitters for handling errors and rare cases.
|
||||
kOptionReserved = 0x00000001u,
|
||||
|
||||
//! Used only by Assembler to mark that `_op4` and `_op5` are used (internal).
|
||||
//!
|
||||
//! TODO: This should be removed in the future.
|
||||
kOptionOp4Op5Used = 0x00000002u,
|
||||
kOptionReserved = 0x00000001u,
|
||||
|
||||
//! Prevents following a jump during compilation (BaseCompiler).
|
||||
//!
|
||||
//! TODO: This should be renamed to kOptionNoReturn.
|
||||
kOptionUnfollow = 0x00000010u,
|
||||
kOptionUnfollow = 0x00000010u,
|
||||
|
||||
//! Overwrite the destination operand(s) (BaseCompiler).
|
||||
//!
|
||||
@ -343,17 +102,17 @@ public:
|
||||
//!
|
||||
//! - `sqrtss x, y` - only LO element of `x` is changed, if you don't
|
||||
//! use HI elements, use `compiler.overwrite().sqrtss(x, y)`.
|
||||
kOptionOverwrite = 0x00000020u,
|
||||
kOptionOverwrite = 0x00000020u,
|
||||
|
||||
//! Emit short-form of the instruction.
|
||||
kOptionShortForm = 0x00000040u,
|
||||
kOptionShortForm = 0x00000040u,
|
||||
//! Emit long-form of the instruction.
|
||||
kOptionLongForm = 0x00000080u,
|
||||
kOptionLongForm = 0x00000080u,
|
||||
|
||||
//! Conditional jump is likely to be taken.
|
||||
kOptionTaken = 0x00000100u,
|
||||
kOptionTaken = 0x00000100u,
|
||||
//! Conditional jump is unlikely to be taken.
|
||||
kOptionNotTaken = 0x00000200u
|
||||
kOptionNotTaken = 0x00000200u
|
||||
};
|
||||
|
||||
//! Control type.
|
||||
@ -373,6 +132,11 @@ public:
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a new BaseInst instance with `id` and `options` set.
|
||||
//!
|
||||
//! Default values of `id` and `options` are zero, which means none instruciton.
|
||||
//! Such instruction is guaranteed to never exist for any architecture supported
|
||||
//! by AsmJit.
|
||||
inline explicit BaseInst(uint32_t id = 0, uint32_t options = 0) noexcept
|
||||
: _id(id),
|
||||
_options(options),
|
||||
@ -393,8 +157,11 @@ public:
|
||||
//! \name Instruction ID
|
||||
//! \{
|
||||
|
||||
//! Returns the instruction id.
|
||||
inline uint32_t id() const noexcept { return _id; }
|
||||
//! Sets the instruction id to the given `id`.
|
||||
inline void setId(uint32_t id) noexcept { _id = id; }
|
||||
//! Resets the instruction id to zero, see \ref kIdNone.
|
||||
inline void resetId() noexcept { _id = 0; }
|
||||
|
||||
//! \}
|
||||
@ -403,6 +170,7 @@ public:
|
||||
//! \{
|
||||
|
||||
inline uint32_t options() const noexcept { return _options; }
|
||||
inline bool hasOption(uint32_t option) const noexcept { return (_options & option) != 0; }
|
||||
inline void setOptions(uint32_t options) noexcept { _options = options; }
|
||||
inline void addOptions(uint32_t options) noexcept { _options |= options; }
|
||||
inline void clearOptions(uint32_t options) noexcept { _options &= ~options; }
|
||||
@ -423,6 +191,316 @@ public:
|
||||
//! \}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::OpRWInfo]
|
||||
// ============================================================================
|
||||
|
||||
//! Read/Write information related to a single operand, used by \ref InstRWInfo.
|
||||
struct OpRWInfo {
|
||||
//! Read/Write flags, see \ref OpRWInfo::Flags.
|
||||
uint32_t _opFlags;
|
||||
//! Physical register index, if required.
|
||||
uint8_t _physId;
|
||||
//! Size of a possible memory operand that can replace a register operand.
|
||||
uint8_t _rmSize;
|
||||
//! Reserved for future use.
|
||||
uint8_t _reserved[2];
|
||||
//! Read bit-mask where each bit represents one byte read from Reg/Mem.
|
||||
uint64_t _readByteMask;
|
||||
//! Write bit-mask where each bit represents one byte written to Reg/Mem.
|
||||
uint64_t _writeByteMask;
|
||||
//! Zero/Sign extend bit-mask where each bit represents one byte written to Reg/Mem.
|
||||
uint64_t _extendByteMask;
|
||||
|
||||
//! Flags describe how the operand is accessed and some additional information.
|
||||
enum Flags : uint32_t {
|
||||
//! Operand is read.
|
||||
kRead = 0x00000001u,
|
||||
|
||||
//! Operand is written.
|
||||
kWrite = 0x00000002u,
|
||||
|
||||
//! Operand is both read and written.
|
||||
kRW = 0x00000003u,
|
||||
|
||||
//! Register operand can be replaced by a memory operand.
|
||||
kRegMem = 0x00000004u,
|
||||
|
||||
//! The `extendByteMask()` represents a zero extension.
|
||||
kZExt = 0x00000010u,
|
||||
|
||||
//! Register operand must use \ref physId().
|
||||
kRegPhysId = 0x00000100u,
|
||||
//! Base register of a memory operand must use \ref physId().
|
||||
kMemPhysId = 0x00000200u,
|
||||
|
||||
//! This memory operand is only used to encode registers and doesn't access memory.
|
||||
//!
|
||||
//! X86 Specific
|
||||
//! ------------
|
||||
//!
|
||||
//! Instructions that use such feature include BNDLDX, BNDSTX, and LEA.
|
||||
kMemFake = 0x000000400u,
|
||||
|
||||
//! Base register of the memory operand will be read.
|
||||
kMemBaseRead = 0x00001000u,
|
||||
//! Base register of the memory operand will be written.
|
||||
kMemBaseWrite = 0x00002000u,
|
||||
//! Base register of the memory operand will be read & written.
|
||||
kMemBaseRW = 0x00003000u,
|
||||
|
||||
//! Index register of the memory operand will be read.
|
||||
kMemIndexRead = 0x00004000u,
|
||||
//! Index register of the memory operand will be written.
|
||||
kMemIndexWrite = 0x00008000u,
|
||||
//! Index register of the memory operand will be read & written.
|
||||
kMemIndexRW = 0x0000C000u,
|
||||
|
||||
//! Base register of the memory operand will be modified before the operation.
|
||||
kMemBasePreModify = 0x00010000u,
|
||||
//! Base register of the memory operand will be modified after the operation.
|
||||
kMemBasePostModify = 0x00020000u
|
||||
};
|
||||
|
||||
// Don't remove these asserts. Read/Write flags are used extensively
|
||||
// by Compiler and they must always be compatible with constants below.
|
||||
static_assert(kRead == 0x1, "OpRWInfo::kRead flag must be 0x1");
|
||||
static_assert(kWrite == 0x2, "OpRWInfo::kWrite flag must be 0x2");
|
||||
static_assert(kRegMem == 0x4, "OpRWInfo::kRegMem flag must be 0x4");
|
||||
|
||||
//! \name Reset
|
||||
//! \{
|
||||
|
||||
//! Resets this operand information to all zeros.
|
||||
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
|
||||
|
||||
//! Resets this operand info (resets all members) and set common information
|
||||
//! to the given `opFlags`, `regSize`, and possibly `physId`.
|
||||
inline void reset(uint32_t opFlags, uint32_t regSize, uint32_t physId = BaseReg::kIdBad) noexcept {
|
||||
_opFlags = opFlags;
|
||||
_physId = uint8_t(physId);
|
||||
_rmSize = uint8_t((opFlags & kRegMem) ? regSize : uint32_t(0));
|
||||
_resetReserved();
|
||||
|
||||
uint64_t mask = Support::lsbMask<uint64_t>(regSize);
|
||||
_readByteMask = opFlags & kRead ? mask : uint64_t(0);
|
||||
_writeByteMask = opFlags & kWrite ? mask : uint64_t(0);
|
||||
_extendByteMask = 0;
|
||||
}
|
||||
|
||||
inline void _resetReserved() noexcept {
|
||||
memset(_reserved, 0, sizeof(_reserved));
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Operand Flags
|
||||
//! \{
|
||||
|
||||
//! Returns operand flags, see \ref Flags.
|
||||
inline uint32_t opFlags() const noexcept { return _opFlags; }
|
||||
//! Tests whether operand flags contain the given `flag`.
|
||||
inline bool hasOpFlag(uint32_t flag) const noexcept { return (_opFlags & flag) != 0; }
|
||||
|
||||
//! Adds the given `flags` to operand flags.
|
||||
inline void addOpFlags(uint32_t flags) noexcept { _opFlags |= flags; }
|
||||
//! Removes the given `flags` from operand flags.
|
||||
inline void clearOpFlags(uint32_t flags) noexcept { _opFlags &= ~flags; }
|
||||
|
||||
//! Tests whether this operand is read from.
|
||||
inline bool isRead() const noexcept { return hasOpFlag(kRead); }
|
||||
//! Tests whether this operand is written to.
|
||||
inline bool isWrite() const noexcept { return hasOpFlag(kWrite); }
|
||||
//! Tests whether this operand is both read and write.
|
||||
inline bool isReadWrite() const noexcept { return (_opFlags & kRW) == kRW; }
|
||||
//! Tests whether this operand is read only.
|
||||
inline bool isReadOnly() const noexcept { return (_opFlags & kRW) == kRead; }
|
||||
//! Tests whether this operand is write only.
|
||||
inline bool isWriteOnly() const noexcept { return (_opFlags & kRW) == kWrite; }
|
||||
|
||||
//! Tests whether this operand is Reg/Mem
|
||||
//!
|
||||
//! Reg/Mem operands can use either register or memory.
|
||||
inline bool isRm() const noexcept { return hasOpFlag(kRegMem); }
|
||||
|
||||
//! Tests whether the operand will be zero extended.
|
||||
inline bool isZExt() const noexcept { return hasOpFlag(kZExt); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Memory Flags
|
||||
//! \{
|
||||
|
||||
//! Tests whether this is a fake memory operand, which is only used, because
|
||||
//! of encoding. Fake memory operands do not access any memory, they are only
|
||||
//! used to encode registers.
|
||||
inline bool isMemFake() const noexcept { return hasOpFlag(kMemFake); }
|
||||
|
||||
//! Tests whether the instruction reads from its BASE registers.
|
||||
inline bool isMemBaseRead() const noexcept { return hasOpFlag(kMemBaseRead); }
|
||||
//! Tests whether the instruction writes to its BASE registers.
|
||||
inline bool isMemBaseWrite() const noexcept { return hasOpFlag(kMemBaseWrite); }
|
||||
//! Tests whether the instruction reads and writes from/to its BASE registers.
|
||||
inline bool isMemBaseReadWrite() const noexcept { return (_opFlags & kMemBaseRW) == kMemBaseRW; }
|
||||
//! Tests whether the instruction only reads from its BASE registers.
|
||||
inline bool isMemBaseReadOnly() const noexcept { return (_opFlags & kMemBaseRW) == kMemBaseRead; }
|
||||
//! Tests whether the instruction only writes to its BASE registers.
|
||||
inline bool isMemBaseWriteOnly() const noexcept { return (_opFlags & kMemBaseRW) == kMemBaseWrite; }
|
||||
|
||||
//! Tests whether the instruction modifies the BASE register before it uses
|
||||
//! it to calculate the target address.
|
||||
inline bool isMemBasePreModify() const noexcept { return hasOpFlag(kMemBasePreModify); }
|
||||
//! Tests whether the instruction modifies the BASE register after it uses
|
||||
//! it to calculate the target address.
|
||||
inline bool isMemBasePostModify() const noexcept { return hasOpFlag(kMemBasePostModify); }
|
||||
|
||||
//! Tests whether the instruction reads the INDEX registers.
|
||||
inline bool isMemIndexRead() const noexcept { return hasOpFlag(kMemIndexRead); }
|
||||
//! Tests whether the instruction writes to its INDEX registers.
|
||||
inline bool isMemIndexWrite() const noexcept { return hasOpFlag(kMemIndexWrite); }
|
||||
//! Tests whether the instruction reads and writes from/to its INDEX registers.
|
||||
inline bool isMemIndexReadWrite() const noexcept { return (_opFlags & kMemIndexRW) == kMemIndexRW; }
|
||||
//! Tests whether the instruction only reads from its INDEX registers.
|
||||
inline bool isMemIndexReadOnly() const noexcept { return (_opFlags & kMemIndexRW) == kMemIndexRead; }
|
||||
//! Tests whether the instruction only writes to its INDEX registers.
|
||||
inline bool isMemIndexWriteOnly() const noexcept { return (_opFlags & kMemIndexRW) == kMemIndexWrite; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Physical Register ID
|
||||
//! \{
|
||||
|
||||
//! Returns a physical id of the register that is fixed for this operand.
|
||||
//!
|
||||
//! Returns \ref BaseReg::kIdBad if any register can be used.
|
||||
inline uint32_t physId() const noexcept { return _physId; }
|
||||
//! Tests whether \ref physId() would return a valid physical register id.
|
||||
inline bool hasPhysId() const noexcept { return _physId != BaseReg::kIdBad; }
|
||||
//! Sets physical register id, which would be fixed for this operand.
|
||||
inline void setPhysId(uint32_t physId) noexcept { _physId = uint8_t(physId); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Reg/Mem Information
|
||||
//! \{
|
||||
|
||||
//! Returns Reg/Mem size of the operand.
|
||||
inline uint32_t rmSize() const noexcept { return _rmSize; }
|
||||
//! Sets Reg/Mem size of the operand.
|
||||
inline void setRmSize(uint32_t rmSize) noexcept { _rmSize = uint8_t(rmSize); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Read & Write Masks
|
||||
//! \{
|
||||
|
||||
//! Returns read mask.
|
||||
inline uint64_t readByteMask() const noexcept { return _readByteMask; }
|
||||
//! Returns write mask.
|
||||
inline uint64_t writeByteMask() const noexcept { return _writeByteMask; }
|
||||
//! Returns extend mask.
|
||||
inline uint64_t extendByteMask() const noexcept { return _extendByteMask; }
|
||||
|
||||
//! Sets read mask.
|
||||
inline void setReadByteMask(uint64_t mask) noexcept { _readByteMask = mask; }
|
||||
//! Sets write mask.
|
||||
inline void setWriteByteMask(uint64_t mask) noexcept { _writeByteMask = mask; }
|
||||
//! Sets externd mask.
|
||||
inline void setExtendByteMask(uint64_t mask) noexcept { _extendByteMask = mask; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::InstRWInfo]
|
||||
// ============================================================================
|
||||
|
||||
//! Read/Write information of an instruction.
|
||||
struct InstRWInfo {
|
||||
//! Instruction flags (there are no flags at the moment, this field is reserved).
|
||||
uint32_t _instFlags;
|
||||
//! Mask of CPU flags read.
|
||||
uint32_t _readFlags;
|
||||
//! Mask of CPU flags written.
|
||||
uint32_t _writeFlags;
|
||||
//! Count of operands.
|
||||
uint8_t _opCount;
|
||||
//! CPU feature required for replacing register operand with memory operand.
|
||||
uint8_t _rmFeature;
|
||||
//! Reserved for future use.
|
||||
uint8_t _reserved[18];
|
||||
//! Read/Write onfo of extra register (rep{} or kz{}).
|
||||
OpRWInfo _extraReg;
|
||||
//! Read/Write info of instruction operands.
|
||||
OpRWInfo _operands[Globals::kMaxOpCount];
|
||||
|
||||
//! \name Commons
|
||||
//! \{
|
||||
|
||||
//! Resets this RW information to all zeros.
|
||||
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Instruction Flags
|
||||
//!
|
||||
//! \{
|
||||
|
||||
inline uint32_t instFlags() const noexcept { return _instFlags; }
|
||||
inline bool hasInstFlag(uint32_t flag) const noexcept { return (_instFlags & flag) != 0; }
|
||||
|
||||
//! }
|
||||
|
||||
//! \name CPU Flags Read/Write Information
|
||||
//! \{
|
||||
|
||||
//! Returns read flags of the instruction.
|
||||
inline uint32_t readFlags() const noexcept { return _readFlags; }
|
||||
//! Returns write flags of the instruction.
|
||||
inline uint32_t writeFlags() const noexcept { return _writeFlags; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Reg/Mem Information
|
||||
//! \{
|
||||
|
||||
//! Returns the CPU feature required to replace a register operand with memory
|
||||
//! operand. If the returned feature is zero (none) then this instruction
|
||||
//! either doesn't provide memory operand combination or there is no extra
|
||||
//! CPU feature required.
|
||||
//!
|
||||
//! X86 Specific
|
||||
//! ------------
|
||||
//!
|
||||
//! Some AVX+ instructions may require extra features for replacing registers
|
||||
//! with memory operands, for example VPSLLDQ instruction only supports
|
||||
//! 'reg/reg/imm' combination on AVX/AVX2 capable CPUs and requires AVX-512 for
|
||||
//! 'reg/mem/imm' combination.
|
||||
inline uint32_t rmFeature() const noexcept { return _rmFeature; }
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Operand Read/Write Information
|
||||
//! \{
|
||||
|
||||
//! Returns RW information of extra register operand (extraReg).
|
||||
inline const OpRWInfo& extraReg() const noexcept { return _extraReg; }
|
||||
|
||||
//! Returns RW information of all instruction's operands.
|
||||
inline const OpRWInfo* operands() const noexcept { return _operands; }
|
||||
|
||||
//! Returns RW information of the operand at the given `index`.
|
||||
inline const OpRWInfo& operand(size_t index) const noexcept {
|
||||
ASMJIT_ASSERT(index < Globals::kMaxOpCount);
|
||||
return _operands[index];
|
||||
}
|
||||
|
||||
//! Returns the number of operands this instruction has.
|
||||
inline uint32_t opCount() const noexcept { return _opCount; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::InstAPI]
|
||||
// ============================================================================
|
||||
@ -430,6 +508,12 @@ public:
|
||||
//! Instruction API.
|
||||
namespace InstAPI {
|
||||
|
||||
//! Validation flags that can be used with \ref InstAPI::validate().
|
||||
enum ValidationFlags : uint32_t {
|
||||
//! Allow virtual registers in the instruction.
|
||||
kValidationFlagVirtRegs = 0x01u
|
||||
};
|
||||
|
||||
#ifndef ASMJIT_NO_TEXT
|
||||
//! Appends the name of the instruction specified by `instId` and `instOptions`
|
||||
//! into the `output` string.
|
||||
@ -437,27 +521,29 @@ namespace InstAPI {
|
||||
//! \note Instruction options would only affect instruction prefix & suffix,
|
||||
//! other options would be ignored. If `instOptions` is zero then only raw
|
||||
//! instruction name (without any additional text) will be appended.
|
||||
ASMJIT_API Error instIdToString(uint32_t archId, uint32_t instId, String& output) noexcept;
|
||||
ASMJIT_API Error instIdToString(uint32_t arch, uint32_t instId, String& output) noexcept;
|
||||
|
||||
//! Parses an instruction name in the given string `s`. Length is specified
|
||||
//! by `len` argument, which can be `SIZE_MAX` if `s` is known to be null
|
||||
//! terminated.
|
||||
//!
|
||||
//! The output is stored in `instId`.
|
||||
ASMJIT_API uint32_t stringToInstId(uint32_t archId, const char* s, size_t len) noexcept;
|
||||
//! Returns the parsed instruction id or \ref BaseInst::kIdNone if no such
|
||||
//! instruction exists.
|
||||
ASMJIT_API uint32_t stringToInstId(uint32_t arch, const char* s, size_t len) noexcept;
|
||||
#endif // !ASMJIT_NO_TEXT
|
||||
|
||||
#ifndef ASMJIT_NO_VALIDATION
|
||||
//! Validates the given instruction.
|
||||
ASMJIT_API Error validate(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept;
|
||||
//! Validates the given instruction considering the validation `flags`, see
|
||||
//! \ref ValidationFlags.
|
||||
ASMJIT_API Error validate(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, uint32_t validationFlags = 0) noexcept;
|
||||
#endif // !ASMJIT_NO_VALIDATION
|
||||
|
||||
#ifndef ASMJIT_NO_INTROSPECTION
|
||||
//! Gets Read/Write information of the given instruction.
|
||||
ASMJIT_API Error queryRWInfo(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, InstRWInfo& out) noexcept;
|
||||
ASMJIT_API Error queryRWInfo(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, InstRWInfo* out) noexcept;
|
||||
|
||||
//! Gets CPU features required by the given instruction.
|
||||
ASMJIT_API Error queryFeatures(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, BaseFeatures& out) noexcept;
|
||||
ASMJIT_API Error queryFeatures(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, BaseFeatures* out) noexcept;
|
||||
#endif // !ASMJIT_NO_INTROSPECTION
|
||||
|
||||
} // {InstAPI}
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
#include "../core/arch.h"
|
||||
#include "../core/jitallocator.h"
|
||||
#include "../core/osutils.h"
|
||||
#include "../core/osutils_p.h"
|
||||
#include "../core/support.h"
|
||||
#include "../core/virtmem.h"
|
||||
#include "../core/zone.h"
|
||||
@ -938,7 +938,7 @@ public:
|
||||
// Helper class to verify that JitAllocator doesn't return addresses that overlap.
|
||||
class JitAllocatorWrapper {
|
||||
public:
|
||||
explicit inline JitAllocatorWrapper(const JitAllocator::CreateParams* params) noexcept
|
||||
inline explicit JitAllocatorWrapper(const JitAllocator::CreateParams* params) noexcept
|
||||
: _zone(1024 * 1024),
|
||||
_heap(&_zone),
|
||||
_allocator(params) {}
|
||||
|
26
3rdparty/asmjit/src/asmjit/core/jitallocator.h
vendored
26
3rdparty/asmjit/src/asmjit/core/jitallocator.h
vendored
@ -32,7 +32,7 @@
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_jit
|
||||
//! \addtogroup asmjit_virtual_memory
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
@ -132,9 +132,6 @@ public:
|
||||
//! JitAllocator allocator(¶ms);
|
||||
//! ```
|
||||
struct CreateParams {
|
||||
// Reset the content of `CreateParams`.
|
||||
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
|
||||
|
||||
//! Allocator options, see \ref JitAllocator::Options.
|
||||
//!
|
||||
//! No options are used by default.
|
||||
@ -161,6 +158,9 @@ public:
|
||||
//!
|
||||
//! Only used if \ref kOptionCustomFillPattern is set.
|
||||
uint32_t fillPattern;
|
||||
|
||||
// Reset the content of `CreateParams`.
|
||||
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
//! Creates a `JitAllocator` instance.
|
||||
@ -221,6 +221,15 @@ public:
|
||||
|
||||
//! Statistics about `JitAllocator`.
|
||||
struct Statistics {
|
||||
//! Number of blocks `JitAllocator` maintains.
|
||||
size_t _blockCount;
|
||||
//! How many bytes are currently used / allocated.
|
||||
size_t _usedSize;
|
||||
//! How many bytes are currently reserved by the allocator.
|
||||
size_t _reservedSize;
|
||||
//! Allocation overhead (in bytes) required to maintain all blocks.
|
||||
size_t _overheadSize;
|
||||
|
||||
inline void reset() noexcept {
|
||||
_blockCount = 0;
|
||||
_usedSize = 0;
|
||||
@ -251,15 +260,6 @@ public:
|
||||
inline double overheadSizeAsPercent() const noexcept {
|
||||
return (double(overheadSize()) / (double(reservedSize()) + 1e-16)) * 100.0;
|
||||
}
|
||||
|
||||
//! Number of blocks `JitAllocator` maintains.
|
||||
size_t _blockCount;
|
||||
//! How many bytes are currently used / allocated.
|
||||
size_t _usedSize;
|
||||
//! How many bytes are currently reserved by the allocator.
|
||||
size_t _reservedSize;
|
||||
//! Allocation overhead (in bytes) required to maintain all blocks.
|
||||
size_t _overheadSize;
|
||||
};
|
||||
|
||||
//! Returns JIT allocator statistics.
|
||||
|
42
3rdparty/asmjit/src/asmjit/core/jitruntime.cpp
vendored
42
3rdparty/asmjit/src/asmjit/core/jitruntime.cpp
vendored
@ -43,52 +43,16 @@ static inline void JitRuntime_flushInstructionCache(const void* p, size_t size)
|
||||
#endif
|
||||
}
|
||||
|
||||
// X86 Target
|
||||
// ----------
|
||||
//
|
||||
// - 32-bit - Linux, OSX, BSD, and apparently also Haiku guarantee 16-byte
|
||||
// stack alignment. Other operating systems are assumed to have
|
||||
// 4-byte alignment by default for safety reasons.
|
||||
// - 64-bit - stack must be aligned to 16 bytes.
|
||||
//
|
||||
// ARM Target
|
||||
// ----------
|
||||
//
|
||||
// - 32-bit - Stack must be aligned to 8 bytes.
|
||||
// - 64-bit - Stack must be aligned to 16 bytes (hardware requirement).
|
||||
static inline uint32_t JitRuntime_detectNaturalStackAlignment() noexcept {
|
||||
#if ASMJIT_ARCH_BITS == 64 || \
|
||||
defined(__APPLE__ ) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__HAIKU__ ) || \
|
||||
defined(__FreeBSD__ ) || \
|
||||
defined(__NetBSD__ ) || \
|
||||
defined(__OpenBSD__ ) || \
|
||||
defined(__bsdi__ ) || \
|
||||
defined(__linux__ )
|
||||
return 16;
|
||||
#elif ASMJIT_ARCH_ARM
|
||||
return 8;
|
||||
#else
|
||||
return uint32_t(sizeof(uintptr_t));
|
||||
#endif
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::JitRuntime - Construction / Destruction]
|
||||
// ============================================================================
|
||||
|
||||
JitRuntime::JitRuntime(const JitAllocator::CreateParams* params) noexcept
|
||||
: _allocator(params) {
|
||||
|
||||
// Setup target properties.
|
||||
_targetType = kTargetJit;
|
||||
_codeInfo._archInfo = CpuInfo::host().archInfo();
|
||||
_codeInfo._stackAlignment = uint8_t(JitRuntime_detectNaturalStackAlignment());
|
||||
_codeInfo._cdeclCallConv = CallConv::kIdHostCDecl;
|
||||
_codeInfo._stdCallConv = CallConv::kIdHostStdCall;
|
||||
_codeInfo._fastCallConv = CallConv::kIdHostFastCall;
|
||||
_environment = hostEnvironment();
|
||||
_environment.setFormat(Environment::kFormatJIT);
|
||||
}
|
||||
|
||||
JitRuntime::~JitRuntime() noexcept {}
|
||||
|
||||
// ============================================================================
|
||||
|
2
3rdparty/asmjit/src/asmjit/core/jitruntime.h
vendored
2
3rdparty/asmjit/src/asmjit/core/jitruntime.h
vendored
@ -35,7 +35,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
class CodeHolder;
|
||||
|
||||
//! \addtogroup asmjit_jit
|
||||
//! \addtogroup asmjit_virtual_memory
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
|
124
3rdparty/asmjit/src/asmjit/core/logger.cpp
vendored
Normal file
124
3rdparty/asmjit/src/asmjit/core/logger.cpp
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "../core/api-build_p.h"
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
|
||||
#include "../core/logger.h"
|
||||
#include "../core/string.h"
|
||||
#include "../core/support.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Logger - Construction / Destruction]
|
||||
// ============================================================================
|
||||
|
||||
Logger::Logger() noexcept
|
||||
: _options() {}
|
||||
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 {
|
||||
StringTmp<2048> sb;
|
||||
ASMJIT_PROPAGATE(sb.appendVFormat(fmt, ap));
|
||||
return log(sb);
|
||||
}
|
||||
|
||||
Error Logger::logBinary(const void* data, size_t size) noexcept {
|
||||
static const char prefix[] = "db ";
|
||||
|
||||
StringTmp<256> sb;
|
||||
sb.append(prefix, ASMJIT_ARRAY_SIZE(prefix) - 1);
|
||||
|
||||
size_t i = size;
|
||||
const uint8_t* s = static_cast<const uint8_t*>(data);
|
||||
|
||||
while (i) {
|
||||
uint32_t n = uint32_t(Support::min<size_t>(i, 16));
|
||||
sb.truncate(ASMJIT_ARRAY_SIZE(prefix) - 1);
|
||||
sb.appendHex(s, n);
|
||||
sb.append('\n');
|
||||
ASMJIT_PROPAGATE(log(sb));
|
||||
s += n;
|
||||
i -= n;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::FileLogger - Construction / Destruction]
|
||||
// ============================================================================
|
||||
|
||||
FileLogger::FileLogger(FILE* file) noexcept
|
||||
: _file(file) {}
|
||||
FileLogger::~FileLogger() noexcept {}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::FileLogger - Logging]
|
||||
// ============================================================================
|
||||
|
||||
Error FileLogger::_log(const char* data, size_t size) noexcept {
|
||||
if (!_file)
|
||||
return kErrorOk;
|
||||
|
||||
if (size == SIZE_MAX)
|
||||
size = strlen(data);
|
||||
|
||||
fwrite(data, 1, size, _file);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::StringLogger - Construction / Destruction]
|
||||
// ============================================================================
|
||||
|
||||
StringLogger::StringLogger() noexcept {}
|
||||
StringLogger::~StringLogger() noexcept {}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::StringLogger - Logging]
|
||||
// ============================================================================
|
||||
|
||||
Error StringLogger::_log(const char* data, size_t size) noexcept {
|
||||
return _content.append(data, size);
|
||||
}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif
|
@ -26,119 +26,28 @@
|
||||
|
||||
#include "../core/inst.h"
|
||||
#include "../core/string.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \{
|
||||
#include "../core/formatter.h"
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
|
||||
// ============================================================================
|
||||
// [Forward Declarations]
|
||||
// ============================================================================
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
class BaseEmitter;
|
||||
class BaseReg;
|
||||
class Logger;
|
||||
struct Operand_;
|
||||
|
||||
#ifndef ASMJIT_NO_BUILDER
|
||||
class BaseBuilder;
|
||||
class BaseNode;
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::FormatOptions]
|
||||
// ============================================================================
|
||||
|
||||
class FormatOptions {
|
||||
public:
|
||||
uint32_t _flags;
|
||||
uint8_t _indentation[4];
|
||||
|
||||
enum Flags : uint32_t {
|
||||
//! Show also binary form of each logged instruction (assembler).
|
||||
kFlagMachineCode = 0x00000001u,
|
||||
//! Show a text explanation of some immediate values.
|
||||
kFlagExplainImms = 0x00000002u,
|
||||
//! Use hexadecimal notation of immediate values.
|
||||
kFlagHexImms = 0x00000004u,
|
||||
//! Use hexadecimal notation of address offsets.
|
||||
kFlagHexOffsets = 0x00000008u,
|
||||
//! Show casts between virtual register types (compiler).
|
||||
kFlagRegCasts = 0x00000010u,
|
||||
//! Show positions associated with nodes (compiler).
|
||||
kFlagPositions = 0x00000020u,
|
||||
//! Annotate nodes that are lowered by passes.
|
||||
kFlagAnnotations = 0x00000040u,
|
||||
|
||||
// TODO: These must go, keep this only for formatting.
|
||||
//! Show an additional output from passes.
|
||||
kFlagDebugPasses = 0x00000080u,
|
||||
//! Show an additional output from RA.
|
||||
kFlagDebugRA = 0x00000100u
|
||||
};
|
||||
|
||||
enum IndentationType : uint32_t {
|
||||
//! Indentation used for instructions and directives.
|
||||
kIndentationCode = 0u,
|
||||
//! Indentation used for labels and function nodes.
|
||||
kIndentationLabel = 1u,
|
||||
//! Indentation used for comments (not inline comments).
|
||||
kIndentationComment = 2u,
|
||||
kIndentationReserved = 3u
|
||||
};
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
constexpr FormatOptions() noexcept
|
||||
: _flags(0),
|
||||
_indentation { 0, 0, 0, 0 } {}
|
||||
|
||||
constexpr FormatOptions(const FormatOptions& other) noexcept = default;
|
||||
inline FormatOptions& operator=(const FormatOptions& other) noexcept = default;
|
||||
|
||||
inline void reset() noexcept {
|
||||
_flags = 0;
|
||||
_indentation[0] = 0;
|
||||
_indentation[1] = 0;
|
||||
_indentation[2] = 0;
|
||||
_indentation[3] = 0;
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
constexpr uint32_t flags() const noexcept { return _flags; }
|
||||
constexpr bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
|
||||
inline void setFlags(uint32_t flags) noexcept { _flags = flags; }
|
||||
inline void addFlags(uint32_t flags) noexcept { _flags |= flags; }
|
||||
inline void clearFlags(uint32_t flags) noexcept { _flags &= ~flags; }
|
||||
|
||||
constexpr uint8_t indentation(uint32_t type) const noexcept { return _indentation[type]; }
|
||||
inline void setIndentation(uint32_t type, uint32_t n) noexcept { _indentation[type] = uint8_t(n); }
|
||||
inline void resetIndentation(uint32_t type) noexcept { _indentation[type] = uint8_t(0); }
|
||||
|
||||
//! \}
|
||||
};
|
||||
//! \addtogroup asmjit_logging
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Logger]
|
||||
// ============================================================================
|
||||
|
||||
//! Abstract logging interface and helpers.
|
||||
//! Logging interface.
|
||||
//!
|
||||
//! This class can be inherited and reimplemented to fit into your logging
|
||||
//! subsystem. When reimplementing use `Logger::_log()` method to log into
|
||||
//! a custom stream.
|
||||
//! This class can be inherited and reimplemented to fit into your own logging
|
||||
//! needs. When reimplementing a logger use \ref Logger::_log() method to log
|
||||
//! customize the output.
|
||||
//!
|
||||
//! There are two `Logger` implementations offered by AsmJit:
|
||||
//! - `FileLogger` - allows to log into `FILE*`.
|
||||
//! - `StringLogger` - logs into a `String`.
|
||||
//! - \ref FileLogger - logs into a `FILE*`.
|
||||
//! - \ref StringLogger - concatenates all logs into a \ref String.
|
||||
class ASMJIT_VIRTAPI Logger {
|
||||
public:
|
||||
ASMJIT_BASE_CLASS(Logger)
|
||||
@ -160,17 +69,28 @@ public:
|
||||
//! \name Format Options
|
||||
//! \{
|
||||
|
||||
//! Returns \ref FormatOptions of this logger.
|
||||
inline FormatOptions& options() noexcept { return _options; }
|
||||
//! \overload
|
||||
inline const FormatOptions& options() const noexcept { return _options; }
|
||||
|
||||
//! Returns formatting flags, see \ref FormatOptions::Flags.
|
||||
inline uint32_t flags() const noexcept { return _options.flags(); }
|
||||
//! Tests whether the logger has the given `flag` enabled.
|
||||
inline bool hasFlag(uint32_t flag) const noexcept { return _options.hasFlag(flag); }
|
||||
//! Sets formatting flags to `flags`, see \ref FormatOptions::Flags.
|
||||
inline void setFlags(uint32_t flags) noexcept { _options.setFlags(flags); }
|
||||
//! Enables the given formatting `flags`, see \ref FormatOptions::Flags.
|
||||
inline void addFlags(uint32_t flags) noexcept { _options.addFlags(flags); }
|
||||
//! Disables the given formatting `flags`, see \ref FormatOptions::Flags.
|
||||
inline void clearFlags(uint32_t flags) noexcept { _options.clearFlags(flags); }
|
||||
|
||||
//! Returns indentation of `type`, see \ref FormatOptions::IndentationType.
|
||||
inline uint32_t indentation(uint32_t type) const noexcept { return _options.indentation(type); }
|
||||
//! Sets indentation of the given indentation `type` to `n` spaces, see \ref
|
||||
//! FormatOptions::IndentationType.
|
||||
inline void setIndentation(uint32_t type, uint32_t n) noexcept { _options.setIndentation(type, n); }
|
||||
//! Resets indentation of the given indentation `type` to 0 spaces.
|
||||
inline void resetIndentation(uint32_t type) noexcept { _options.resetIndentation(type); }
|
||||
|
||||
//! \}
|
||||
@ -179,6 +99,11 @@ public:
|
||||
//! \{
|
||||
|
||||
//! Logs `str` - must be reimplemented.
|
||||
//!
|
||||
//! The function can accept either a null terminated string if `size` is
|
||||
//! `SIZE_MAX` or a non-null terminated string of the given `size`. The
|
||||
//! function cannot assume that the data is null terminated and must handle
|
||||
//! non-null terminated inputs.
|
||||
virtual Error _log(const char* data, size_t size) noexcept = 0;
|
||||
|
||||
//! Logs string `str`, which is either null terminated or having size `size`.
|
||||
@ -186,15 +111,15 @@ public:
|
||||
//! Logs content of a string `str`.
|
||||
inline Error log(const String& str) noexcept { return _log(str.data(), str.size()); }
|
||||
|
||||
//! Formats the message by using `snprintf()` and then sends the result
|
||||
//! to `log()`.
|
||||
//! Formats the message by using `snprintf()` and then passes the formatted
|
||||
//! string to \ref _log().
|
||||
ASMJIT_API Error logf(const char* fmt, ...) noexcept;
|
||||
|
||||
//! Formats the message by using `vsnprintf()` and then sends the result
|
||||
//! to `log()`.
|
||||
//! Formats the message by using `vsnprintf()` and then passes the formatted
|
||||
//! string to \ref _log().
|
||||
ASMJIT_API Error logv(const char* fmt, va_list ap) noexcept;
|
||||
|
||||
//! Logs binary data.
|
||||
//! Logs binary `data` of the given `size`.
|
||||
ASMJIT_API Error logBinary(const void* data, size_t size) noexcept;
|
||||
|
||||
//! \}
|
||||
@ -267,6 +192,13 @@ public:
|
||||
//! \name Logger Data Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the content of the logger as \ref String.
|
||||
//!
|
||||
//! It can be moved, if desired.
|
||||
inline String& content() noexcept { return _content; }
|
||||
//! \overload
|
||||
inline const String& content() const noexcept { return _content; }
|
||||
|
||||
//! Returns aggregated logger data as `char*` pointer.
|
||||
//!
|
||||
//! The pointer is owned by `StringLogger`, it can't be modified or freed.
|
||||
@ -287,69 +219,10 @@ public:
|
||||
ASMJIT_API Error _log(const char* data, size_t size = SIZE_MAX) noexcept override;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Logging]
|
||||
// ============================================================================
|
||||
|
||||
struct Logging {
|
||||
ASMJIT_API static Error formatRegister(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t archId,
|
||||
uint32_t regType,
|
||||
uint32_t regId) noexcept;
|
||||
|
||||
ASMJIT_API static Error formatLabel(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t labelId) noexcept;
|
||||
|
||||
ASMJIT_API static Error formatOperand(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t archId,
|
||||
const Operand_& op) noexcept;
|
||||
|
||||
ASMJIT_API static Error formatInstruction(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t archId,
|
||||
const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept;
|
||||
|
||||
ASMJIT_API static Error formatTypeId(
|
||||
String& sb,
|
||||
uint32_t typeId) noexcept;
|
||||
|
||||
#ifndef ASMJIT_NO_BUILDER
|
||||
ASMJIT_API static Error formatNode(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseBuilder* cb,
|
||||
const BaseNode* node_) noexcept;
|
||||
#endif
|
||||
|
||||
// Only used by AsmJit internals, not available to users.
|
||||
#ifdef ASMJIT_EXPORTS
|
||||
enum {
|
||||
// Has to be big to be able to hold all metadata compiler can assign to a
|
||||
// single instruction.
|
||||
kMaxInstLineSize = 44,
|
||||
kMaxBinarySize = 26
|
||||
};
|
||||
|
||||
static Error formatLine(
|
||||
String& sb,
|
||||
const uint8_t* binData, size_t binSize, size_t dispSize, size_t immSize, const char* comment) noexcept;
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
#endif // ASMJIT_CORE_LOGGER_H_INCLUDED
|
535
3rdparty/asmjit/src/asmjit/core/logging.cpp
vendored
535
3rdparty/asmjit/src/asmjit/core/logging.cpp
vendored
@ -1,535 +0,0 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "../core/api-build_p.h"
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
|
||||
#include "../core/builder.h"
|
||||
#include "../core/codeholder.h"
|
||||
#include "../core/compiler.h"
|
||||
#include "../core/emitter.h"
|
||||
#include "../core/logging.h"
|
||||
#include "../core/string.h"
|
||||
#include "../core/support.h"
|
||||
#include "../core/type.h"
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
#include "../x86/x86logging_p.h"
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
#include "../arm/armlogging_p.h"
|
||||
#endif
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
#if defined(ASMJIT_NO_COMPILER)
|
||||
class VirtReg;
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Logger - Construction / Destruction]
|
||||
// ============================================================================
|
||||
|
||||
Logger::Logger() noexcept
|
||||
: _options() {}
|
||||
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 {
|
||||
StringTmp<2048> sb;
|
||||
ASMJIT_PROPAGATE(sb.appendVFormat(fmt, ap));
|
||||
return log(sb);
|
||||
}
|
||||
|
||||
Error Logger::logBinary(const void* data, size_t size) noexcept {
|
||||
static const char prefix[] = "db ";
|
||||
|
||||
StringTmp<256> sb;
|
||||
sb.appendString(prefix, ASMJIT_ARRAY_SIZE(prefix) - 1);
|
||||
|
||||
size_t i = size;
|
||||
const uint8_t* s = static_cast<const uint8_t*>(data);
|
||||
|
||||
while (i) {
|
||||
uint32_t n = uint32_t(Support::min<size_t>(i, 16));
|
||||
sb.truncate(ASMJIT_ARRAY_SIZE(prefix) - 1);
|
||||
sb.appendHex(s, n);
|
||||
sb.appendChar('\n');
|
||||
ASMJIT_PROPAGATE(log(sb));
|
||||
s += n;
|
||||
i -= n;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::FileLogger - Construction / Destruction]
|
||||
// ============================================================================
|
||||
|
||||
FileLogger::FileLogger(FILE* file) noexcept
|
||||
: _file(nullptr) { setFile(file); }
|
||||
FileLogger::~FileLogger() noexcept {}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::FileLogger - Logging]
|
||||
// ============================================================================
|
||||
|
||||
Error FileLogger::_log(const char* data, size_t size) noexcept {
|
||||
if (!_file)
|
||||
return kErrorOk;
|
||||
|
||||
if (size == SIZE_MAX)
|
||||
size = strlen(data);
|
||||
|
||||
fwrite(data, 1, size, _file);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::StringLogger - Construction / Destruction]
|
||||
// ============================================================================
|
||||
|
||||
StringLogger::StringLogger() noexcept {}
|
||||
StringLogger::~StringLogger() noexcept {}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::StringLogger - Logging]
|
||||
// ============================================================================
|
||||
|
||||
Error StringLogger::_log(const char* data, size_t size) noexcept {
|
||||
return _content.appendString(data, size);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Logging]
|
||||
// ============================================================================
|
||||
|
||||
Error Logging::formatLabel(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t labelId) noexcept {
|
||||
|
||||
DebugUtils::unused(flags);
|
||||
|
||||
const LabelEntry* le = emitter->code()->labelEntry(labelId);
|
||||
if (ASMJIT_UNLIKELY(!le))
|
||||
return sb.appendFormat("InvalidLabel[Id=%u]", labelId);
|
||||
|
||||
if (le->hasName()) {
|
||||
if (le->hasParent()) {
|
||||
uint32_t parentId = le->parentId();
|
||||
const LabelEntry* pe = emitter->code()->labelEntry(parentId);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!pe))
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("InvalidLabel[Id=%u]", labelId));
|
||||
else if (ASMJIT_UNLIKELY(!pe->hasName()))
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("L%u", parentId));
|
||||
else
|
||||
ASMJIT_PROPAGATE(sb.appendString(pe->name()));
|
||||
|
||||
ASMJIT_PROPAGATE(sb.appendChar('.'));
|
||||
}
|
||||
return sb.appendString(le->name());
|
||||
}
|
||||
else {
|
||||
return sb.appendFormat("L%u", labelId);
|
||||
}
|
||||
}
|
||||
|
||||
Error Logging::formatRegister(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t archId,
|
||||
uint32_t regType,
|
||||
uint32_t regId) noexcept {
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (ArchInfo::isX86Family(archId))
|
||||
return x86::LoggingInternal::formatRegister(sb, flags, emitter, archId, regType, regId);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (ArchInfo::isArmFamily(archId))
|
||||
return arm::LoggingInternal::formatRegister(sb, flags, emitter, archId, regType, regId);
|
||||
#endif
|
||||
|
||||
return kErrorInvalidArch;
|
||||
}
|
||||
|
||||
Error Logging::formatOperand(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t archId,
|
||||
const Operand_& op) noexcept {
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (ArchInfo::isX86Family(archId))
|
||||
return x86::LoggingInternal::formatOperand(sb, flags, emitter, archId, op);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (ArchInfo::isArmFamily(archId))
|
||||
return arm::LoggingInternal::formatOperand(sb, flags, emitter, archId, op);
|
||||
#endif
|
||||
|
||||
return kErrorInvalidArch;
|
||||
}
|
||||
|
||||
Error Logging::formatInstruction(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t archId,
|
||||
const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept {
|
||||
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
if (ArchInfo::isX86Family(archId))
|
||||
return x86::LoggingInternal::formatInstruction(sb, flags, emitter, archId, inst, operands, opCount);
|
||||
#endif
|
||||
|
||||
#ifdef ASMJIT_BUILD_ARM
|
||||
if (ArchInfo::isArmFamily(archId))
|
||||
return arm::LoggingInternal::formatInstruction(sb, flags, emitter, archId, inst, operands, opCount);
|
||||
#endif
|
||||
|
||||
return kErrorInvalidArch;
|
||||
}
|
||||
|
||||
Error Logging::formatTypeId(String& sb, uint32_t typeId) noexcept {
|
||||
if (typeId == Type::kIdVoid)
|
||||
return sb.appendString("void");
|
||||
|
||||
if (!Type::isValid(typeId))
|
||||
return sb.appendString("unknown");
|
||||
|
||||
const char* typeName = "unknown";
|
||||
uint32_t typeSize = Type::sizeOf(typeId);
|
||||
|
||||
uint32_t baseId = Type::baseOf(typeId);
|
||||
switch (baseId) {
|
||||
case Type::kIdIntPtr : typeName = "iptr" ; break;
|
||||
case Type::kIdUIntPtr: typeName = "uptr" ; break;
|
||||
case Type::kIdI8 : typeName = "i8" ; break;
|
||||
case Type::kIdU8 : typeName = "u8" ; break;
|
||||
case Type::kIdI16 : typeName = "i16" ; break;
|
||||
case Type::kIdU16 : typeName = "u16" ; break;
|
||||
case Type::kIdI32 : typeName = "i32" ; break;
|
||||
case Type::kIdU32 : typeName = "u32" ; break;
|
||||
case Type::kIdI64 : typeName = "i64" ; break;
|
||||
case Type::kIdU64 : typeName = "u64" ; break;
|
||||
case Type::kIdF32 : typeName = "f32" ; break;
|
||||
case Type::kIdF64 : typeName = "f64" ; break;
|
||||
case Type::kIdF80 : typeName = "f80" ; break;
|
||||
case Type::kIdMask8 : typeName = "mask8" ; break;
|
||||
case Type::kIdMask16 : typeName = "mask16"; break;
|
||||
case Type::kIdMask32 : typeName = "mask32"; break;
|
||||
case Type::kIdMask64 : typeName = "mask64"; break;
|
||||
case Type::kIdMmx32 : typeName = "mmx32" ; break;
|
||||
case Type::kIdMmx64 : typeName = "mmx64" ; break;
|
||||
}
|
||||
|
||||
uint32_t baseSize = Type::sizeOf(baseId);
|
||||
if (typeSize > baseSize) {
|
||||
uint32_t count = typeSize / baseSize;
|
||||
return sb.appendFormat("%sx%u", typeName, unsigned(count));
|
||||
}
|
||||
else {
|
||||
return sb.appendString(typeName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_BUILDER
|
||||
static Error formatFuncValue(String& sb, uint32_t flags, const BaseEmitter* emitter, FuncValue value) noexcept {
|
||||
uint32_t typeId = value.typeId();
|
||||
ASMJIT_PROPAGATE(Logging::formatTypeId(sb, typeId));
|
||||
|
||||
if (value.isReg()) {
|
||||
ASMJIT_PROPAGATE(sb.appendChar('@'));
|
||||
ASMJIT_PROPAGATE(Logging::formatRegister(sb, flags, emitter, emitter->archId(), value.regType(), value.regId()));
|
||||
}
|
||||
|
||||
if (value.isStack()) {
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("@[%d]", int(value.stackOffset())));
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
static Error formatFuncRets(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
const FuncDetail& fd,
|
||||
VirtReg* const* vRegs) noexcept {
|
||||
|
||||
if (!fd.hasRet())
|
||||
return sb.appendString("void");
|
||||
|
||||
for (uint32_t i = 0; i < fd.retCount(); i++) {
|
||||
if (i) ASMJIT_PROPAGATE(sb.appendString(", "));
|
||||
ASMJIT_PROPAGATE(formatFuncValue(sb, flags, emitter, fd.ret(i)));
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
if (vRegs) {
|
||||
static const char nullRet[] = "<none>";
|
||||
ASMJIT_PROPAGATE(sb.appendFormat(" %s", vRegs[i] ? vRegs[i]->name() : nullRet));
|
||||
}
|
||||
#else
|
||||
DebugUtils::unused(vRegs);
|
||||
#endif
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
static Error formatFuncArgs(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
const FuncDetail& fd,
|
||||
VirtReg* const* vRegs) noexcept {
|
||||
|
||||
uint32_t count = fd.argCount();
|
||||
if (!count)
|
||||
return sb.appendString("void");
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (i) ASMJIT_PROPAGATE(sb.appendString(", "));
|
||||
ASMJIT_PROPAGATE(formatFuncValue(sb, flags, emitter, fd.arg(i)));
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
if (vRegs) {
|
||||
static const char nullArg[] = "<none>";
|
||||
ASMJIT_PROPAGATE(sb.appendFormat(" %s", vRegs[i] ? vRegs[i]->name() : nullArg));
|
||||
}
|
||||
#else
|
||||
DebugUtils::unused(vRegs);
|
||||
#endif
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error Logging::formatNode(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseBuilder* cb,
|
||||
const BaseNode* node_) noexcept {
|
||||
|
||||
if (node_->hasPosition() && (flags & FormatOptions::kFlagPositions) != 0)
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("<%05u> ", node_->position()));
|
||||
|
||||
switch (node_->type()) {
|
||||
case BaseNode::kNodeInst:
|
||||
case BaseNode::kNodeJump: {
|
||||
const InstNode* node = node_->as<InstNode>();
|
||||
ASMJIT_PROPAGATE(
|
||||
Logging::formatInstruction(sb, flags, cb,
|
||||
cb->archId(),
|
||||
node->baseInst(), node->operands(), node->opCount()));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeSection: {
|
||||
const SectionNode* node = node_->as<SectionNode>();
|
||||
if (cb->_code->isSectionValid(node->id())) {
|
||||
const Section* section = cb->_code->sectionById(node->id());
|
||||
ASMJIT_PROPAGATE(sb.appendFormat(".section %s", section->name()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeLabel: {
|
||||
const LabelNode* node = node_->as<LabelNode>();
|
||||
ASMJIT_PROPAGATE(formatLabel(sb, flags, cb, node->id()));
|
||||
ASMJIT_PROPAGATE(sb.appendString(":"));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeAlign: {
|
||||
const AlignNode* node = node_->as<AlignNode>();
|
||||
ASMJIT_PROPAGATE(
|
||||
sb.appendFormat(".align %u (%s)",
|
||||
node->alignment(),
|
||||
node->alignMode() == kAlignCode ? "code" : "data"));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeEmbedData: {
|
||||
const EmbedDataNode* node = node_->as<EmbedDataNode>();
|
||||
ASMJIT_PROPAGATE(sb.appendFormat(".embed (%u bytes)", node->size()));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeEmbedLabel: {
|
||||
const EmbedLabelNode* node = node_->as<EmbedLabelNode>();
|
||||
ASMJIT_PROPAGATE(sb.appendString(".label "));
|
||||
ASMJIT_PROPAGATE(formatLabel(sb, flags, cb, node->id()));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeEmbedLabelDelta: {
|
||||
const EmbedLabelDeltaNode* node = node_->as<EmbedLabelDeltaNode>();
|
||||
ASMJIT_PROPAGATE(sb.appendString(".label ("));
|
||||
ASMJIT_PROPAGATE(formatLabel(sb, flags, cb, node->id()));
|
||||
ASMJIT_PROPAGATE(sb.appendString(" - "));
|
||||
ASMJIT_PROPAGATE(formatLabel(sb, flags, cb, node->baseId()));
|
||||
ASMJIT_PROPAGATE(sb.appendString(")"));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeComment: {
|
||||
const CommentNode* node = node_->as<CommentNode>();
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("; %s", node->inlineComment()));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeSentinel: {
|
||||
const SentinelNode* node = node_->as<SentinelNode>();
|
||||
const char* sentinelName = nullptr;
|
||||
|
||||
switch (node->sentinelType()) {
|
||||
case SentinelNode::kSentinelFuncEnd:
|
||||
sentinelName = "[FuncEnd]";
|
||||
break;
|
||||
|
||||
default:
|
||||
sentinelName = "[Sentinel]";
|
||||
break;
|
||||
}
|
||||
|
||||
ASMJIT_PROPAGATE(sb.appendString(sentinelName));
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
case BaseNode::kNodeFunc: {
|
||||
const FuncNode* node = node_->as<FuncNode>();
|
||||
|
||||
ASMJIT_PROPAGATE(formatLabel(sb, flags, cb, node->id()));
|
||||
ASMJIT_PROPAGATE(sb.appendString(": "));
|
||||
|
||||
ASMJIT_PROPAGATE(formatFuncRets(sb, flags, cb, node->detail(), nullptr));
|
||||
ASMJIT_PROPAGATE(sb.appendString(" Func("));
|
||||
ASMJIT_PROPAGATE(formatFuncArgs(sb, flags, cb, node->detail(), node->args()));
|
||||
ASMJIT_PROPAGATE(sb.appendString(")"));
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeFuncRet: {
|
||||
const FuncRetNode* node = node_->as<FuncRetNode>();
|
||||
ASMJIT_PROPAGATE(sb.appendString("[FuncRet]"));
|
||||
|
||||
for (uint32_t i = 0; i < 2; i++) {
|
||||
const Operand_& op = node->_opArray[i];
|
||||
if (!op.isNone()) {
|
||||
ASMJIT_PROPAGATE(sb.appendString(i == 0 ? " " : ", "));
|
||||
ASMJIT_PROPAGATE(formatOperand(sb, flags, cb, cb->archId(), op));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BaseNode::kNodeFuncCall: {
|
||||
const FuncCallNode* node = node_->as<FuncCallNode>();
|
||||
ASMJIT_PROPAGATE(
|
||||
Logging::formatInstruction(sb, flags, cb,
|
||||
cb->archId(),
|
||||
node->baseInst(), node->operands(), node->opCount()));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default: {
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("[User:%u]", node_->type()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
#endif
|
||||
|
||||
Error Logging::formatLine(String& sb, const uint8_t* binData, size_t binSize, size_t dispSize, size_t immSize, const char* comment) noexcept {
|
||||
size_t currentSize = sb.size();
|
||||
size_t commentSize = comment ? Support::strLen(comment, Globals::kMaxCommentSize) : 0;
|
||||
|
||||
ASMJIT_ASSERT(binSize >= dispSize);
|
||||
const size_t kNoBinSize = std::numeric_limits<size_t>::max();
|
||||
|
||||
if ((binSize != 0 && binSize != kNoBinSize) || commentSize) {
|
||||
size_t align = kMaxInstLineSize;
|
||||
char sep = ';';
|
||||
|
||||
for (size_t i = (binSize == kNoBinSize); i < 2; i++) {
|
||||
size_t begin = sb.size();
|
||||
ASMJIT_PROPAGATE(sb.padEnd(align));
|
||||
|
||||
if (sep) {
|
||||
ASMJIT_PROPAGATE(sb.appendChar(sep));
|
||||
ASMJIT_PROPAGATE(sb.appendChar(' '));
|
||||
}
|
||||
|
||||
// Append binary data or comment.
|
||||
if (i == 0) {
|
||||
ASMJIT_PROPAGATE(sb.appendHex(binData, binSize - dispSize - immSize));
|
||||
ASMJIT_PROPAGATE(sb.appendChars('.', dispSize * 2));
|
||||
ASMJIT_PROPAGATE(sb.appendHex(binData + binSize - immSize, immSize));
|
||||
if (commentSize == 0) break;
|
||||
}
|
||||
else {
|
||||
ASMJIT_PROPAGATE(sb.appendString(comment, commentSize));
|
||||
}
|
||||
|
||||
currentSize += sb.size() - begin;
|
||||
align += kMaxBinarySize;
|
||||
sep = '|';
|
||||
}
|
||||
}
|
||||
|
||||
return sb.appendChar('\n');
|
||||
}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif
|
2
3rdparty/asmjit/src/asmjit/core/misc_p.h
vendored
2
3rdparty/asmjit/src/asmjit/core/misc_p.h
vendored
@ -29,7 +29,7 @@
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_support
|
||||
//! \addtogroup asmjit_utilities
|
||||
//! \{
|
||||
|
||||
#define ASMJIT_LOOKUP_TABLE_8(T, I) T((I)), T((I+1)), T((I+2)), T((I+3)), T((I+4)), T((I+5)), T((I+6)), T((I+7))
|
||||
|
8
3rdparty/asmjit/src/asmjit/core/operand.cpp
vendored
8
3rdparty/asmjit/src/asmjit/core/operand.cpp
vendored
@ -126,10 +126,10 @@ UNIT(operand) {
|
||||
|
||||
INFO("Checking basic functionality of Imm");
|
||||
Imm immValue(-42);
|
||||
EXPECT(Imm(-1).i64() == int64_t(-1));
|
||||
EXPECT(imm(-1).i64() == int64_t(-1));
|
||||
EXPECT(immValue.i64() == int64_t(-42));
|
||||
EXPECT(imm(0xFFFFFFFF).i64() == int64_t(0xFFFFFFFF));
|
||||
EXPECT(Imm(-1).value() == -1);
|
||||
EXPECT(imm(-1).value() == -1);
|
||||
EXPECT(immValue.value() == -42);
|
||||
EXPECT(imm(0xFFFFFFFF).value() == int64_t(0xFFFFFFFF));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
295
3rdparty/asmjit/src/asmjit/core/operand.h
vendored
295
3rdparty/asmjit/src/asmjit/core/operand.h
vendored
@ -103,7 +103,7 @@ public: \
|
||||
constexpr explicit REG(uint32_t rId) noexcept \
|
||||
: BASE(kSignature, rId) {}
|
||||
|
||||
//! \addtogroup asmjit_core
|
||||
//! \addtogroup asmjit_assembler
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
@ -162,6 +162,16 @@ struct Operand_ {
|
||||
};
|
||||
static_assert(kOpMem == kOpReg + 1, "asmjit::Operand requires `kOpMem` to be `kOpReg+1`.");
|
||||
|
||||
//! Label tag.
|
||||
enum LabelTag {
|
||||
//! Label tag is used as a sub-type, forming a unique signature across all
|
||||
//! operand types as 0x1 is never associated with any register 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
|
||||
//! (register type or label-tag) and operand id.
|
||||
kLabelTag = 0x1
|
||||
};
|
||||
|
||||
// \cond INTERNAL
|
||||
enum SignatureBits : uint32_t {
|
||||
// Operand type (3 least significant bits).
|
||||
@ -211,7 +221,6 @@ struct Operand_ {
|
||||
};
|
||||
//! \endcond
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! Constants useful for VirtId <-> Index translation.
|
||||
enum VirtIdConstants : uint32_t {
|
||||
//! Minimum valid packed-id.
|
||||
@ -225,14 +234,12 @@ struct Operand_ {
|
||||
//! Tests whether the given `id` is a valid virtual register id. Since AsmJit
|
||||
//! supports both physical and virtual registers it must be able to distinguish
|
||||
//! between these two. The idea is that physical registers are always limited
|
||||
//! in size, so virtual identifiers start from `kVirtIdMin` and end at
|
||||
//! `kVirtIdMax`.
|
||||
//! in size, so virtual identifiers start from `kVirtIdMin` and end at `kVirtIdMax`.
|
||||
static ASMJIT_INLINE bool isVirtId(uint32_t id) noexcept { return id - kVirtIdMin < uint32_t(kVirtIdCount); }
|
||||
//! Converts a real-id into a packed-id that can be stored in Operand.
|
||||
static ASMJIT_INLINE uint32_t indexToVirtId(uint32_t id) noexcept { return id + kVirtIdMin; }
|
||||
//! Converts a packed-id back to real-id.
|
||||
static ASMJIT_INLINE uint32_t virtIdToIndex(uint32_t id) noexcept { return id - kVirtIdMin; }
|
||||
//! \endcond
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
@ -245,11 +252,11 @@ struct Operand_ {
|
||||
_data[0] = 0;
|
||||
_data[1] = 0;
|
||||
}
|
||||
|
||||
//! Initializes the operand from `other` (used by operator overloads).
|
||||
inline void copyFrom(const Operand_& other) noexcept { memcpy(this, &other, sizeof(Operand_)); }
|
||||
//! \endcond
|
||||
|
||||
//! Initializes the operand from `other` operand (used by operator overloads).
|
||||
inline void copyFrom(const Operand_& other) noexcept { memcpy(this, &other, sizeof(Operand_)); }
|
||||
|
||||
//! Resets the `Operand` to none.
|
||||
//!
|
||||
//! None operand is defined the following way:
|
||||
@ -290,8 +297,10 @@ struct Operand_ {
|
||||
//! \name Operator Overloads
|
||||
//! \{
|
||||
|
||||
constexpr bool operator==(const Operand_& other) const noexcept { return isEqual(other); }
|
||||
constexpr bool operator!=(const Operand_& other) const noexcept { return !isEqual(other); }
|
||||
//! Tests whether this operand is the same as `other`.
|
||||
constexpr bool operator==(const Operand_& other) const noexcept { return equals(other); }
|
||||
//! Tests whether this operand is not the same as `other`.
|
||||
constexpr bool operator!=(const Operand_& other) const noexcept { return !equals(other); }
|
||||
|
||||
//! \}
|
||||
|
||||
@ -395,14 +404,22 @@ struct Operand_ {
|
||||
//! initialized.
|
||||
constexpr uint32_t id() const noexcept { return _baseId; }
|
||||
|
||||
//! Tests whether the operand is 100% equal to `other`.
|
||||
constexpr bool isEqual(const Operand_& other) const noexcept {
|
||||
//! Tests whether the operand is 100% equal to `other` operand.
|
||||
//!
|
||||
//! \note This basically performs a binary comparison, if aby bit is
|
||||
//! different the operands are not equal.
|
||||
constexpr bool equals(const Operand_& other) const noexcept {
|
||||
return (_signature == other._signature) &
|
||||
(_baseId == other._baseId ) &
|
||||
(_data[0] == other._data[0] ) &
|
||||
(_data[1] == other._data[1] ) ;
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("Use equals() instead")
|
||||
constexpr bool isEqual(const Operand_& other) const noexcept { return equals(other); }
|
||||
#endif //!ASMJIT_NO_DEPRECATED
|
||||
|
||||
//! Tests whether the operand is a register matching `rType`.
|
||||
constexpr bool isReg(uint32_t rType) const noexcept {
|
||||
return (_signature & (kSignatureOpMask | kSignatureRegTypeMask)) ==
|
||||
@ -471,11 +488,6 @@ public:
|
||||
|
||||
static_assert(sizeof(Operand) == 16, "asmjit::Operand must be exactly 16 bytes long");
|
||||
|
||||
namespace Globals {
|
||||
//! A default-constructed operand of `Operand_::kOpNone` type.
|
||||
static constexpr const Operand none;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Label]
|
||||
// ============================================================================
|
||||
@ -519,16 +531,6 @@ public:
|
||||
kTypeCount = 3
|
||||
};
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
@ -536,7 +538,7 @@ public:
|
||||
constexpr Label() noexcept
|
||||
: Operand(Globals::Init, kOpLabel, Globals::kInvalidId, 0, 0) {}
|
||||
|
||||
//! Creates a cloned label operand of `other` .
|
||||
//! Creates a cloned label operand of `other`.
|
||||
constexpr Label(const Label& other) noexcept
|
||||
: Operand(other) {}
|
||||
|
||||
@ -610,7 +612,7 @@ struct BaseRegTraits {
|
||||
//! This information is compatible with operand's signature (32-bit integer)
|
||||
//! and `RegInfo` just provides easy way to access it.
|
||||
struct RegInfo {
|
||||
inline void reset() noexcept { _signature = 0; }
|
||||
inline void reset(uint32_t signature = 0) noexcept { _signature = signature; }
|
||||
inline void setSignature(uint32_t signature) noexcept { _signature = signature; }
|
||||
|
||||
template<uint32_t mask>
|
||||
@ -628,7 +630,7 @@ struct RegInfo {
|
||||
uint32_t _signature;
|
||||
};
|
||||
|
||||
//! Physical/Virtual register operand.
|
||||
//! Physical or virtual register operand.
|
||||
class BaseReg : public Operand {
|
||||
public:
|
||||
//! Architecture neutral register types.
|
||||
@ -638,7 +640,7 @@ public:
|
||||
//! of a memory operand.
|
||||
enum RegType : uint32_t {
|
||||
//! No register - unused, invalid, multiple meanings.
|
||||
kTypeNone = 0,
|
||||
kTypeNone = 0,
|
||||
|
||||
// (1 is used as a LabelTag)
|
||||
|
||||
@ -736,15 +738,14 @@ public:
|
||||
//! Tests whether 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
|
||||
//! 8 bytes of Operand data, 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 one of the two operand contains a garbage or other metadata in the
|
||||
//! upper 8 bytes then `isSame()` may return `true` in cases where `isEqual()`
|
||||
//! both \ref equals() and \ref isSame() should give the same answer, however,
|
||||
//! if any of these two contains garbage or other metadata in the upper 8
|
||||
//! bytes then \ref isSame() may return `true` in cases in which \ref equals()
|
||||
//! returns false.
|
||||
constexpr bool isSame(const BaseReg& other) const noexcept {
|
||||
return (_signature == other._signature) &
|
||||
(_baseId == other._baseId ) ;
|
||||
return (_signature == other._signature) & (_baseId == other._baseId);
|
||||
}
|
||||
|
||||
//! Tests whether the register is valid (either virtual or physical).
|
||||
@ -810,6 +811,7 @@ public:
|
||||
//! \name Static Functions
|
||||
//! \{
|
||||
|
||||
//! Tests whether the `op` operand is a general purpose register.
|
||||
static inline bool isGp(const Operand_& op) noexcept {
|
||||
// Check operand type and register group. Not interested in register type and size.
|
||||
const uint32_t kSgn = (kOpReg << kSignatureOpShift ) |
|
||||
@ -817,7 +819,7 @@ public:
|
||||
return (op.signature() & (kSignatureOpMask | kSignatureRegGroupMask)) == kSgn;
|
||||
}
|
||||
|
||||
//! Tests whether the `op` operand is either a low or high 8-bit GPB register.
|
||||
//! Tests whether the `op` operand is a vector register.
|
||||
static inline bool isVec(const Operand_& op) noexcept {
|
||||
// Check operand type and register group. Not interested in register type and size.
|
||||
const uint32_t kSgn = (kOpReg << kSignatureOpShift ) |
|
||||
@ -825,7 +827,9 @@ public:
|
||||
return (op.signature() & (kSignatureOpMask | kSignatureRegGroupMask)) == kSgn;
|
||||
}
|
||||
|
||||
//! Tests whether the `op` is a general purpose register of the given `rId`.
|
||||
static inline bool isGp(const Operand_& op, uint32_t rId) noexcept { return isGp(op) & (op.id() == rId); }
|
||||
//! Tests whether the `op` is a vector register of the given `rId`.
|
||||
static inline bool isVec(const Operand_& op, uint32_t rId) noexcept { return isVec(op) & (op.id() == rId); }
|
||||
|
||||
//! \}
|
||||
@ -947,10 +951,14 @@ struct RegOnly {
|
||||
//! index shift (scale).
|
||||
class BaseMem : public Operand {
|
||||
public:
|
||||
//! Address type.
|
||||
enum AddrType : uint32_t {
|
||||
//! Default address type, Assembler will select the best type when necessary.
|
||||
kAddrTypeDefault = 0,
|
||||
kAddrTypeAbs = 1,
|
||||
kAddrTypeRel = 2
|
||||
//! Absolute address type.
|
||||
kAddrTypeAbs = 1,
|
||||
//! Relative address type.
|
||||
kAddrTypeRel = 2
|
||||
};
|
||||
|
||||
// Shortcuts.
|
||||
@ -1028,25 +1036,37 @@ public:
|
||||
//! Clones the memory operand.
|
||||
constexpr BaseMem clone() const noexcept { return BaseMem(*this); }
|
||||
|
||||
//! Returns the address type (see \ref AddrType) of the memory operand.
|
||||
//!
|
||||
//! By default, address type of newly created memory operands is always \ref kAddrTypeDefault.
|
||||
constexpr uint32_t addrType() const noexcept { return _getSignaturePart<kSignatureMemAddrTypeMask>(); }
|
||||
//! Sets the address type to `addrType`, see \ref AddrType.
|
||||
inline void setAddrType(uint32_t addrType) noexcept { _setSignaturePart<kSignatureMemAddrTypeMask>(addrType); }
|
||||
//! Resets the address type to \ref kAddrTypeDefault.
|
||||
inline void resetAddrType() noexcept { _setSignaturePart<kSignatureMemAddrTypeMask>(0); }
|
||||
|
||||
//! Tests whether the address type is \ref kAddrTypeAbs.
|
||||
constexpr bool isAbs() const noexcept { return addrType() == kAddrTypeAbs; }
|
||||
//! Sets the address type to \ref kAddrTypeAbs.
|
||||
inline void setAbs() noexcept { setAddrType(kAddrTypeAbs); }
|
||||
|
||||
//! Tests whether the address type is \ref kAddrTypeRel.
|
||||
constexpr bool isRel() const noexcept { return addrType() == kAddrTypeRel; }
|
||||
//! Sets the address type to \ref kAddrTypeRel.
|
||||
inline void setRel() noexcept { setAddrType(kAddrTypeRel); }
|
||||
|
||||
//! Tests whether this memory operand is a register home (only used by \ref asmjit_compiler)
|
||||
constexpr bool isRegHome() const noexcept { return _hasSignaturePart<kSignatureMemRegHomeFlag>(); }
|
||||
//! Mark this memory operand as register home (only used by \ref asmjit_compiler).
|
||||
inline void setRegHome() noexcept { _signature |= kSignatureMemRegHomeFlag; }
|
||||
//! Marks this operand to not be a register home (only used by \ref asmjit_compiler).
|
||||
inline void clearRegHome() noexcept { _signature &= ~kSignatureMemRegHomeFlag; }
|
||||
|
||||
//! Tests whether the memory operand has a BASE register or label specified.
|
||||
constexpr bool hasBase() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0; }
|
||||
//! Tests whether the memory operand has an INDEX register specified.
|
||||
constexpr bool hasIndex() const noexcept { return (_signature & kSignatureMemIndexTypeMask) != 0; }
|
||||
//! Tests whether the memory operand has BASE and INDEX register.
|
||||
//! Tests whether the memory operand has BASE or INDEX register.
|
||||
constexpr bool hasBaseOrIndex() const noexcept { return (_signature & kSignatureMemBaseIndexMask) != 0; }
|
||||
//! Tests whether the memory operand has BASE and INDEX register.
|
||||
constexpr bool hasBaseAndIndex() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0 && (_signature & kSignatureMemIndexTypeMask) != 0; }
|
||||
@ -1093,6 +1113,7 @@ public:
|
||||
//! Sets the index register to type and id of the given `index` operand.
|
||||
inline void setIndex(const BaseReg& index) noexcept { return _setIndex(index.type(), index.id()); }
|
||||
|
||||
//! \cond INTERNAL
|
||||
inline void _setBase(uint32_t rType, uint32_t rId) noexcept {
|
||||
_setSignaturePart<kSignatureMemBaseTypeMask>(rType);
|
||||
_baseId = rId;
|
||||
@ -1102,6 +1123,7 @@ public:
|
||||
_setSignaturePart<kSignatureMemIndexTypeMask>(rType);
|
||||
_data[kDataMemIndexId] = rId;
|
||||
}
|
||||
//! \endcond
|
||||
|
||||
//! Resets the memory operand's BASE register or label.
|
||||
inline void resetBase() noexcept { _setBase(0, 0); }
|
||||
@ -1159,7 +1181,7 @@ public:
|
||||
//! 64-bit offset. Use it only if you know that there is a BASE register
|
||||
//! and the offset is only 32 bits anyway.
|
||||
|
||||
//! Adjusts the offset by a 64-bit `offset`.
|
||||
//! Adjusts the memory operand offset by a `offset`.
|
||||
inline void addOffset(int64_t offset) noexcept {
|
||||
if (isOffset64Bit()) {
|
||||
int64_t result = offset + int64_t(uint64_t(_data[kDataMemOffsetLo]) | (uint64_t(_baseId) << 32));
|
||||
@ -1230,77 +1252,54 @@ public:
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns immediate value as 8-bit signed integer, possibly cropped.
|
||||
constexpr int8_t i8() const noexcept { return int8_t(_data[kDataImmValueLo] & 0xFFu); }
|
||||
//! Returns immediate value as 8-bit unsigned integer, possibly cropped.
|
||||
constexpr uint8_t u8() const noexcept { return uint8_t(_data[kDataImmValueLo] & 0xFFu); }
|
||||
//! Returns immediate value as 16-bit signed integer, possibly cropped.
|
||||
constexpr int16_t i16() const noexcept { return int16_t(_data[kDataImmValueLo] & 0xFFFFu);}
|
||||
//! Returns immediate value as 16-bit unsigned integer, possibly cropped.
|
||||
constexpr uint16_t u16() const noexcept { return uint16_t(_data[kDataImmValueLo] & 0xFFFFu);}
|
||||
//! Returns immediate value as 32-bit signed integer, possibly cropped.
|
||||
constexpr int32_t i32() const noexcept { return int32_t(_data[kDataImmValueLo]); }
|
||||
//! Returns low 32-bit signed integer.
|
||||
constexpr int32_t i32Lo() const noexcept { return int32_t(_data[kDataImmValueLo]); }
|
||||
//! Returns high 32-bit signed integer.
|
||||
constexpr int32_t i32Hi() const noexcept { return int32_t(_data[kDataImmValueHi]); }
|
||||
//! Returns immediate value as 32-bit unsigned integer, possibly cropped.
|
||||
constexpr uint32_t u32() const noexcept { return _data[kDataImmValueLo]; }
|
||||
//! Returns low 32-bit signed integer.
|
||||
constexpr uint32_t u32Lo() const noexcept { return _data[kDataImmValueLo]; }
|
||||
//! Returns high 32-bit signed integer.
|
||||
constexpr uint32_t u32Hi() const noexcept { return _data[kDataImmValueHi]; }
|
||||
//! Returns immediate value as 64-bit signed integer.
|
||||
constexpr int64_t i64() const noexcept { return int64_t((uint64_t(_data[kDataImmValueHi]) << 32) | _data[kDataImmValueLo]); }
|
||||
//! Returns immediate value as 64-bit unsigned integer.
|
||||
constexpr uint64_t u64() const noexcept { return uint64_t(i64()); }
|
||||
//! Returns immediate value as `intptr_t`, possibly cropped if size of `intptr_t` is 32 bits.
|
||||
constexpr intptr_t iptr() const noexcept { return (sizeof(intptr_t) == sizeof(int64_t)) ? intptr_t(i64()) : intptr_t(i32()); }
|
||||
//! Returns immediate value as `uintptr_t`, possibly cropped if size of `uintptr_t` is 32 bits.
|
||||
constexpr uintptr_t uptr() const noexcept { return (sizeof(uintptr_t) == sizeof(uint64_t)) ? uintptr_t(u64()) : uintptr_t(u32()); }
|
||||
//! Returns the immediate value as `int64_t`, which is the internal format Imm uses.
|
||||
constexpr int64_t value() const noexcept {
|
||||
return int64_t((uint64_t(_data[kDataImmValueHi]) << 32) | _data[kDataImmValueLo]);
|
||||
}
|
||||
|
||||
//! Tests whether the immediate can be casted to 8-bit signed integer.
|
||||
constexpr bool isInt8() const noexcept { return Support::isInt8(i64()); }
|
||||
constexpr bool isInt8() const noexcept { return Support::isInt8(value()); }
|
||||
//! Tests whether the immediate can be casted to 8-bit unsigned integer.
|
||||
constexpr bool isUInt8() const noexcept { return Support::isUInt8(i64()); }
|
||||
constexpr bool isUInt8() const noexcept { return Support::isUInt8(value()); }
|
||||
//! Tests whether the immediate can be casted to 16-bit signed integer.
|
||||
constexpr bool isInt16() const noexcept { return Support::isInt16(i64()); }
|
||||
constexpr bool isInt16() const noexcept { return Support::isInt16(value()); }
|
||||
//! Tests whether the immediate can be casted to 16-bit unsigned integer.
|
||||
constexpr bool isUInt16() const noexcept { return Support::isUInt16(i64()); }
|
||||
constexpr bool isUInt16() const noexcept { return Support::isUInt16(value()); }
|
||||
//! Tests whether the immediate can be casted to 32-bit signed integer.
|
||||
constexpr bool isInt32() const noexcept { return Support::isInt32(i64()); }
|
||||
constexpr bool isInt32() const noexcept { return Support::isInt32(value()); }
|
||||
//! Tests whether the immediate can be casted to 32-bit unsigned integer.
|
||||
constexpr bool isUInt32() const noexcept { return _data[kDataImmValueHi] == 0; }
|
||||
|
||||
//! Sets immediate value to 8-bit signed integer `val`.
|
||||
inline void setI8(int8_t val) noexcept { setI64(val); }
|
||||
//! Sets immediate value to 8-bit unsigned integer `val`.
|
||||
inline void setU8(uint8_t val) noexcept { setU64(val); }
|
||||
//! Sets immediate value to 16-bit signed integer `val`.
|
||||
inline void setI16(int16_t val) noexcept { setI64(val); }
|
||||
//! Sets immediate value to 16-bit unsigned integer `val`.
|
||||
inline void setU16(uint16_t val) noexcept { setU64(val); }
|
||||
//! Sets immediate value to 32-bit signed integer `val`.
|
||||
inline void setI32(int32_t val) noexcept { setI64(val); }
|
||||
//! Sets immediate value to 32-bit unsigned integer `val`.
|
||||
inline void setU32(uint32_t val) noexcept { setU64(val); }
|
||||
//! Sets immediate value to 64-bit signed integer `val`.
|
||||
inline void setI64(int64_t val) noexcept {
|
||||
_data[kDataImmValueHi] = uint32_t(uint64_t(val) >> 32);
|
||||
_data[kDataImmValueLo] = uint32_t(uint64_t(val) & 0xFFFFFFFFu);
|
||||
}
|
||||
//! Sets immediate value to 64-bit unsigned integer `val`.
|
||||
inline void setU64(uint64_t val) noexcept { setI64(int64_t(val)); }
|
||||
//! Sets immediate value to intptr_t `val`.
|
||||
inline void setIPtr(intptr_t val) noexcept { setI64(val); }
|
||||
//! Sets immediate value to uintptr_t `val`.
|
||||
inline void setUPtr(uintptr_t val) noexcept { setU64(val); }
|
||||
|
||||
//! Sets immediate value to `val`.
|
||||
//! Returns the immediate value casted to `T`.
|
||||
//!
|
||||
//! The value is masked before it's casted to `T` so the returned value is
|
||||
//! simply the representation of `T` considering the original value's lowest
|
||||
//! bits.
|
||||
template<typename T>
|
||||
inline void setValue(T val) noexcept { setI64(int64_t(Support::asNormalized(val))); }
|
||||
constexpr T valueAs() const noexcept {
|
||||
return T(uint64_t(value()) & Support::allOnes<typename std::make_unsigned<T>::type>());
|
||||
}
|
||||
|
||||
inline void setDouble(double d) noexcept { setU64(Support::bitCast<uint64_t>(d)); }
|
||||
//! Returns low 32-bit signed integer.
|
||||
constexpr int32_t int32Lo() const noexcept { return int32_t(_data[kDataImmValueLo]); }
|
||||
//! Returns high 32-bit signed integer.
|
||||
constexpr int32_t int32Hi() const noexcept { return int32_t(_data[kDataImmValueHi]); }
|
||||
//! Returns low 32-bit signed integer.
|
||||
constexpr uint32_t uint32Lo() const noexcept { return _data[kDataImmValueLo]; }
|
||||
//! Returns high 32-bit signed integer.
|
||||
constexpr uint32_t uint32Hi() const noexcept { return _data[kDataImmValueHi]; }
|
||||
|
||||
//! Sets immediate value to `val`, the value is casted to a signed 64-bit integer.
|
||||
template<typename T>
|
||||
inline void setValue(const T& val) noexcept {
|
||||
int64_t val64 = int64_t(Support::asNormalized(val));
|
||||
_data[kDataImmValueHi] = uint32_t(uint64_t(val64) >> 32);
|
||||
_data[kDataImmValueLo] = uint32_t(uint64_t(val64) & 0xFFFFFFFFu);
|
||||
}
|
||||
|
||||
inline void setDouble(double d) noexcept {
|
||||
setValue(Support::bitCast<uint64_t>(d));
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
@ -1310,15 +1309,59 @@ public:
|
||||
//! Clones the immediate operand.
|
||||
constexpr Imm clone() const noexcept { return Imm(*this); }
|
||||
|
||||
inline void signExtend8Bits() noexcept { setI64(int64_t(i8())); }
|
||||
inline void signExtend16Bits() noexcept { setI64(int64_t(i16())); }
|
||||
inline void signExtend32Bits() noexcept { setI64(int64_t(i32())); }
|
||||
inline void signExtend8Bits() noexcept { setValue(int64_t(valueAs<int8_t>())); }
|
||||
inline void signExtend16Bits() noexcept { setValue(int64_t(valueAs<int16_t>())); }
|
||||
inline void signExtend32Bits() noexcept { setValue(int64_t(valueAs<int32_t>())); }
|
||||
|
||||
inline void zeroExtend8Bits() noexcept { setU64(u8()); }
|
||||
inline void zeroExtend16Bits() noexcept { setU64(u16()); }
|
||||
inline void zeroExtend8Bits() noexcept { setValue(valueAs<uint8_t>()); }
|
||||
inline void zeroExtend16Bits() noexcept { setValue(valueAs<uint16_t>()); }
|
||||
inline void zeroExtend32Bits() noexcept { _data[kDataImmValueHi] = 0u; }
|
||||
|
||||
//! \}
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("Use valueAs<int8_t>() instead")
|
||||
inline int8_t i8() const noexcept { return valueAs<int8_t>(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use valueAs<uint8_t>() instead")
|
||||
inline uint8_t u8() const noexcept { return valueAs<uint8_t>(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use valueAs<int16_t>() instead")
|
||||
inline int16_t i16() const noexcept { return valueAs<int16_t>(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use valueAs<uint16_t>() instead")
|
||||
inline uint16_t u16() const noexcept { return valueAs<uint16_t>(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use valueAs<int32_t>() instead")
|
||||
inline int32_t i32() const noexcept { return valueAs<int32_t>(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use valueAs<uint32_t>() instead")
|
||||
inline uint32_t u32() const noexcept { return valueAs<uint32_t>(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use value() instead")
|
||||
inline int64_t i64() const noexcept { return value(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use valueAs<uint64_t>() instead")
|
||||
inline uint64_t u64() const noexcept { return valueAs<uint64_t>(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use valueAs<intptr_t>() instead")
|
||||
inline intptr_t iptr() const noexcept { return valueAs<intptr_t>(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use valueAs<uintptr_t>() instead")
|
||||
inline uintptr_t uptr() const noexcept { return valueAs<uintptr_t>(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use int32Lo() instead")
|
||||
inline int32_t i32Lo() const noexcept { return int32Lo(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use uint32Lo() instead")
|
||||
inline uint32_t u32Lo() const noexcept { return uint32Lo(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use int32Hi() instead")
|
||||
inline int32_t i32Hi() const noexcept { return int32Hi(); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use uint32Hi() instead")
|
||||
inline uint32_t u32Hi() const noexcept { return uint32Hi(); }
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
};
|
||||
|
||||
//! Creates a new immediate operand.
|
||||
@ -1332,6 +1375,44 @@ static constexpr Imm imm(T val) noexcept {
|
||||
|
||||
//! \}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Globals::none]
|
||||
// ============================================================================
|
||||
|
||||
namespace Globals {
|
||||
//! \ingroup asmjit_assembler
|
||||
//!
|
||||
//! A default-constructed operand of `Operand_::kOpNone` type.
|
||||
static constexpr const Operand none;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Support::ForwardOp]
|
||||
// ============================================================================
|
||||
|
||||
//! \cond INTERNAL
|
||||
namespace Support {
|
||||
|
||||
template<typename T, bool IsIntegral>
|
||||
struct ForwardOpImpl {
|
||||
static ASMJIT_INLINE const T& forward(const T& value) noexcept { return value; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ForwardOpImpl<T, true> {
|
||||
static ASMJIT_INLINE Imm forward(const T& value) noexcept { return Imm(value); }
|
||||
};
|
||||
|
||||
//! Either forwards operand T or returns a new operand for T if T is a type
|
||||
//! convertible to operand. At the moment this is only used to convert integers
|
||||
//! to \ref Imm operands.
|
||||
template<typename T>
|
||||
struct ForwardOp : public ForwardOpImpl<T, std::is_integral<typename std::decay<T>::type>::value> {};
|
||||
|
||||
}
|
||||
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_OPERAND_H_INCLUDED
|
||||
|
56
3rdparty/asmjit/src/asmjit/core/osutils.h
vendored
56
3rdparty/asmjit/src/asmjit/core/osutils.h
vendored
@ -28,7 +28,7 @@
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_support
|
||||
//! \addtogroup asmjit_utilities
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
@ -50,7 +50,7 @@ namespace OSUtils {
|
||||
//! Lock.
|
||||
//!
|
||||
//! Lock is internal, it cannot be used outside of AsmJit, however, its internal
|
||||
//! layout is exposed as it's used by some other public classes.
|
||||
//! layout is exposed as it's used by some other classes, which are public.
|
||||
class Lock {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(Lock)
|
||||
@ -78,60 +78,8 @@ public:
|
||||
inline void lock() noexcept;
|
||||
inline void unlock() noexcept;
|
||||
};
|
||||
|
||||
#ifdef ASMJIT_EXPORTS
|
||||
#if defined(_WIN32)
|
||||
|
||||
// Win32 implementation.
|
||||
static_assert(sizeof(Lock::Handle) == sizeof(CRITICAL_SECTION), "asmjit::Lock::Handle layout must match CRITICAL_SECTION");
|
||||
static_assert(alignof(Lock::Handle) == alignof(CRITICAL_SECTION), "asmjit::Lock::Handle alignment must match CRITICAL_SECTION");
|
||||
|
||||
inline Lock::Lock() noexcept { InitializeCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&_handle)); }
|
||||
inline Lock::~Lock() noexcept { DeleteCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&_handle)); }
|
||||
inline void Lock::lock() noexcept { EnterCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&_handle)); }
|
||||
inline void Lock::unlock() noexcept { LeaveCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&_handle)); }
|
||||
|
||||
#elif !defined(__EMSCRIPTEN__)
|
||||
|
||||
// PThread implementation.
|
||||
inline Lock::Lock() noexcept { pthread_mutex_init(&_handle, nullptr); }
|
||||
inline Lock::~Lock() noexcept { pthread_mutex_destroy(&_handle); }
|
||||
inline void Lock::lock() noexcept { pthread_mutex_lock(&_handle); }
|
||||
inline void Lock::unlock() noexcept { pthread_mutex_unlock(&_handle); }
|
||||
|
||||
#else
|
||||
|
||||
// Dummy implementation - Emscripten or other unsupported platform.
|
||||
inline Lock::Lock() noexcept {}
|
||||
inline Lock::~Lock() noexcept {}
|
||||
inline void Lock::lock() noexcept {}
|
||||
inline void Lock::unlock() noexcept {}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//! \endcond
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::LockGuard]
|
||||
// ============================================================================
|
||||
|
||||
#ifdef ASMJIT_EXPORTS
|
||||
//! \cond INTERNAL
|
||||
|
||||
//! Scoped lock.
|
||||
struct LockGuard {
|
||||
ASMJIT_NONCOPYABLE(LockGuard)
|
||||
|
||||
Lock& _target;
|
||||
|
||||
inline LockGuard(Lock& target) noexcept : _target(target) { _target.lock(); }
|
||||
inline ~LockGuard() noexcept { _target.unlock(); }
|
||||
};
|
||||
|
||||
//! \endcond
|
||||
#endif
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
94
3rdparty/asmjit/src/asmjit/core/osutils_p.h
vendored
Normal file
94
3rdparty/asmjit/src/asmjit/core/osutils_p.h
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#ifndef ASMJIT_CORE_OSUTILS_P_H_INCLUDED
|
||||
#define ASMJIT_CORE_OSUTILS_P_H_INCLUDED
|
||||
|
||||
#include "../core/osutils.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_utilities
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Lock]
|
||||
// ============================================================================
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
// Windows implementation.
|
||||
static_assert(sizeof(Lock::Handle) == sizeof(CRITICAL_SECTION), "asmjit::Lock::Handle layout must match CRITICAL_SECTION");
|
||||
static_assert(alignof(Lock::Handle) == alignof(CRITICAL_SECTION), "asmjit::Lock::Handle alignment must match CRITICAL_SECTION");
|
||||
|
||||
inline Lock::Lock() noexcept { InitializeCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&_handle)); }
|
||||
inline Lock::~Lock() noexcept { DeleteCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&_handle)); }
|
||||
inline void Lock::lock() noexcept { EnterCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&_handle)); }
|
||||
inline void Lock::unlock() noexcept { LeaveCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&_handle)); }
|
||||
|
||||
#elif !defined(__EMSCRIPTEN__)
|
||||
|
||||
// PThread implementation.
|
||||
#ifdef PTHREAD_MUTEX_INITIALIZER
|
||||
inline Lock::Lock() noexcept : _handle(PTHREAD_MUTEX_INITIALIZER) {}
|
||||
#else
|
||||
inline Lock::Lock() noexcept { pthread_mutex_init(&_handle, nullptr); }
|
||||
#endif
|
||||
inline Lock::~Lock() noexcept { pthread_mutex_destroy(&_handle); }
|
||||
inline void Lock::lock() noexcept { pthread_mutex_lock(&_handle); }
|
||||
inline void Lock::unlock() noexcept { pthread_mutex_unlock(&_handle); }
|
||||
|
||||
#else
|
||||
|
||||
// Dummy implementation - Emscripten or other unsupported platform.
|
||||
inline Lock::Lock() noexcept {}
|
||||
inline Lock::~Lock() noexcept {}
|
||||
inline void Lock::lock() noexcept {}
|
||||
inline void Lock::unlock() noexcept {}
|
||||
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::LockGuard]
|
||||
// ============================================================================
|
||||
|
||||
//! Scoped lock.
|
||||
class LockGuard {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(LockGuard)
|
||||
|
||||
Lock& _target;
|
||||
|
||||
inline LockGuard(Lock& target) noexcept
|
||||
: _target(target) { _target.lock(); }
|
||||
inline ~LockGuard() noexcept { _target.unlock(); }
|
||||
};
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_CORE_OSUTILS_P_H_INCLUDED
|
||||
|
31
3rdparty/asmjit/src/asmjit/core/rabuilders_p.h
vendored
31
3rdparty/asmjit/src/asmjit/core/rabuilders_p.h
vendored
@ -27,6 +27,7 @@
|
||||
#include "../core/api-config.h"
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/rapass_p.h"
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
@ -66,7 +67,7 @@ public:
|
||||
static constexpr uint32_t kCodeIndentation = 4;
|
||||
|
||||
// NOTE: This is a bit hacky. There are some nodes which are processed twice
|
||||
// (see `onBeforeCall()` and `onBeforeRet()`) as they can insert some nodes
|
||||
// (see `onBeforeInvoke()` and `onBeforeRet()`) as they can insert some nodes
|
||||
// around them. Since we don't have any flags to mark these we just use their
|
||||
// position that is [at that time] unassigned.
|
||||
static constexpr uint32_t kNodePositionDidOnBefore = 0xFFFFFFFFu;
|
||||
@ -122,7 +123,7 @@ public:
|
||||
// Instruction | Jump | Invoke | Return
|
||||
// ------------------------------------
|
||||
|
||||
// Handle `InstNode`, `FuncCallNode`, and `FuncRetNode`. All of them
|
||||
// Handle `InstNode`, `InvokeNode`, and `FuncRetNode`. All of them
|
||||
// share the same interface that provides operands that have read/write
|
||||
// semantics.
|
||||
if (ASMJIT_UNLIKELY(!_curBlock)) {
|
||||
@ -135,18 +136,18 @@ public:
|
||||
|
||||
_hasCode = true;
|
||||
|
||||
if (node->isFuncCall() || node->isFuncRet()) {
|
||||
if (node->isInvoke() || node->isFuncRet()) {
|
||||
if (node->position() != kNodePositionDidOnBefore) {
|
||||
// Call and Reg are complicated as they may insert some surrounding
|
||||
// code around them. The simplest approach is to get the previous
|
||||
// node, call the `onBefore()` handlers and then check whether
|
||||
// anything changed and restart if so. By restart we mean that the
|
||||
// current `node` would go back to the first possible inserted node
|
||||
// by `onBeforeCall()` or `onBeforeRet()`.
|
||||
// by `onBeforeInvoke()` or `onBeforeRet()`.
|
||||
BaseNode* prev = node->prev();
|
||||
|
||||
if (node->type() == BaseNode::kNodeFuncCall)
|
||||
ASMJIT_PROPAGATE(static_cast<This*>(this)->onBeforeCall(node->as<FuncCallNode>()));
|
||||
if (node->type() == BaseNode::kNodeInvoke)
|
||||
ASMJIT_PROPAGATE(static_cast<This*>(this)->onBeforeInvoke(node->as<InvokeNode>()));
|
||||
else
|
||||
ASMJIT_PROPAGATE(static_cast<This*>(this)->onBeforeRet(node->as<FuncRetNode>()));
|
||||
|
||||
@ -159,7 +160,7 @@ public:
|
||||
node->setPosition(kNodePositionDidOnBefore);
|
||||
node = prev->next();
|
||||
|
||||
// `onBeforeCall()` and `onBeforeRet()` can only insert instructions.
|
||||
// `onBeforeInvoke()` and `onBeforeRet()` can only insert instructions.
|
||||
ASMJIT_ASSERT(node->isInst());
|
||||
}
|
||||
|
||||
@ -179,8 +180,8 @@ public:
|
||||
ib.reset();
|
||||
ASMJIT_PROPAGATE(static_cast<This*>(this)->onInst(inst, controlType, ib));
|
||||
|
||||
if (node->isFuncCall()) {
|
||||
ASMJIT_PROPAGATE(static_cast<This*>(this)->onCall(inst->as<FuncCallNode>(), ib));
|
||||
if (node->isInvoke()) {
|
||||
ASMJIT_PROPAGATE(static_cast<This*>(this)->onInvoke(inst->as<InvokeNode>(), ib));
|
||||
}
|
||||
|
||||
if (node->isFuncRet()) {
|
||||
@ -409,7 +410,7 @@ public:
|
||||
logNode(node, kRootIndentation);
|
||||
|
||||
// Unlikely: Assume that the exit label is reached only once per function.
|
||||
if (ASMJIT_UNLIKELY(node->as<LabelNode>()->id() == _exitLabelId)) {
|
||||
if (ASMJIT_UNLIKELY(node->as<LabelNode>()->labelId() == _exitLabelId)) {
|
||||
_curBlock->setLast(node);
|
||||
_curBlock->makeConstructed(_blockRegStats);
|
||||
ASMJIT_PROPAGATE(_pass->addExitBlock(_curBlock));
|
||||
@ -492,7 +493,7 @@ public:
|
||||
|
||||
// Reset everything we may need.
|
||||
_blockRegStats.reset();
|
||||
_exitLabelId = func->exitNode()->id();
|
||||
_exitLabelId = func->exitNode()->labelId();
|
||||
|
||||
// Initially we assume there is no code in the function body.
|
||||
_hasCode = false;
|
||||
@ -599,11 +600,11 @@ public:
|
||||
_sb.clear();
|
||||
_sb.appendChars(' ', indentation);
|
||||
if (action) {
|
||||
_sb.appendString(action);
|
||||
_sb.appendChar(' ');
|
||||
_sb.append(action);
|
||||
_sb.append(' ');
|
||||
}
|
||||
Logging::formatNode(_sb, _logFlags, cc(), node);
|
||||
_sb.appendChar('\n');
|
||||
Formatter::formatNode(_sb, _logFlags, cc(), node);
|
||||
_sb.append('\n');
|
||||
_logger->log(_sb);
|
||||
}
|
||||
#else
|
||||
|
9
3rdparty/asmjit/src/asmjit/core/radefs_p.h
vendored
9
3rdparty/asmjit/src/asmjit/core/radefs_p.h
vendored
@ -28,7 +28,7 @@
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
|
||||
#include "../core/compiler.h"
|
||||
#include "../core/logging.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/support.h"
|
||||
#include "../core/zone.h"
|
||||
#include "../core/zonevector.h"
|
||||
@ -984,10 +984,7 @@ public:
|
||||
//! Stack allocation is preferred.
|
||||
kFlagStackPreferred = 0x00000004u,
|
||||
//! Marked for stack argument reassignment.
|
||||
kFlagStackArgToStack = 0x00000008u,
|
||||
|
||||
// TODO: Used?
|
||||
kFlagDirtyStats = 0x80000000u
|
||||
kFlagStackArgToStack = 0x00000008u
|
||||
};
|
||||
|
||||
enum ArgIndex : uint32_t {
|
||||
@ -1004,7 +1001,7 @@ public:
|
||||
_tiedReg(nullptr),
|
||||
_stackSlot(nullptr),
|
||||
_info(vReg->info()),
|
||||
_flags(kFlagDirtyStats),
|
||||
_flags(0),
|
||||
_allocatedMask(0),
|
||||
_clobberSurvivalMask(0),
|
||||
_regByteMask(0),
|
||||
|
29
3rdparty/asmjit/src/asmjit/core/ralocal.cpp
vendored
29
3rdparty/asmjit/src/asmjit/core/ralocal.cpp
vendored
@ -596,7 +596,7 @@ Error RALocalAllocator::allocInst(InstNode* node) noexcept {
|
||||
|
||||
// DECIDE whether to MOVE or SPILL.
|
||||
if (allocableRegs) {
|
||||
uint32_t reassignedId = decideOnUnassignment(group, workId, assignedId, allocableRegs);
|
||||
uint32_t reassignedId = decideOnReassignment(group, workId, assignedId, allocableRegs);
|
||||
if (reassignedId != RAAssignment::kPhysNone) {
|
||||
ASMJIT_PROPAGATE(onMoveReg(group, workId, reassignedId, assignedId));
|
||||
allocableRegs ^= Support::bitMask(reassignedId);
|
||||
@ -897,7 +897,7 @@ Error RALocalAllocator::allocBranch(InstNode* node, RABlock* target, RABlock* co
|
||||
// Additional instructions emitted to switch from the current state to
|
||||
// the `target` state. This means that we have to move these instructions
|
||||
// into an independent code block and patch the jump location.
|
||||
Operand& targetOp(node->opType(node->opCount() - 1));
|
||||
Operand& targetOp = node->op(node->opCount() - 1);
|
||||
if (ASMJIT_UNLIKELY(!targetOp.isLabel()))
|
||||
return DebugUtils::errored(kErrorInvalidState);
|
||||
|
||||
@ -970,38 +970,39 @@ Error RALocalAllocator::allocJumpTable(InstNode* node, const RABlocks& targets,
|
||||
// ============================================================================
|
||||
|
||||
uint32_t RALocalAllocator::decideOnAssignment(uint32_t group, uint32_t workId, uint32_t physId, uint32_t allocableRegs) const noexcept {
|
||||
DebugUtils::unused(group, physId);
|
||||
ASMJIT_ASSERT(allocableRegs != 0);
|
||||
DebugUtils::unused(group, physId);
|
||||
|
||||
RAWorkReg* workReg = workRegById(workId);
|
||||
|
||||
// HIGHEST PRIORITY: Home register id.
|
||||
// Prefer home register id, if possible.
|
||||
if (workReg->hasHomeRegId()) {
|
||||
uint32_t homeId = workReg->homeRegId();
|
||||
if (Support::bitTest(allocableRegs, homeId))
|
||||
return homeId;
|
||||
}
|
||||
|
||||
// HIGH PRIORITY: Register IDs used upon block entries.
|
||||
// Prefer registers used upon block entries.
|
||||
uint32_t previouslyAssignedRegs = workReg->allocatedMask();
|
||||
if (allocableRegs & previouslyAssignedRegs)
|
||||
allocableRegs &= previouslyAssignedRegs;
|
||||
|
||||
if (Support::isPowerOf2(allocableRegs))
|
||||
return Support::ctz(allocableRegs);
|
||||
|
||||
// TODO: This is not finished.
|
||||
return Support::ctz(allocableRegs);
|
||||
}
|
||||
|
||||
uint32_t RALocalAllocator::decideOnUnassignment(uint32_t group, uint32_t workId, uint32_t physId, uint32_t allocableRegs) const noexcept {
|
||||
uint32_t RALocalAllocator::decideOnReassignment(uint32_t group, uint32_t workId, uint32_t physId, uint32_t allocableRegs) const noexcept {
|
||||
ASMJIT_ASSERT(allocableRegs != 0);
|
||||
DebugUtils::unused(group, physId);
|
||||
|
||||
// TODO:
|
||||
DebugUtils::unused(allocableRegs, group, workId, physId);
|
||||
RAWorkReg* workReg = workRegById(workId);
|
||||
|
||||
// if (!_curAssignment.isPhysDirty(group, physId)) {
|
||||
// }
|
||||
// Prefer allocating back to HomeId, if possible.
|
||||
if (workReg->hasHomeRegId()) {
|
||||
if (Support::bitTest(allocableRegs, workReg->homeRegId()))
|
||||
return workReg->homeRegId();
|
||||
}
|
||||
|
||||
// TODO: [Register Allocator] This could be improved.
|
||||
|
||||
// Decided to SPILL.
|
||||
return RAAssignment::kPhysNone;
|
||||
|
9
3rdparty/asmjit/src/asmjit/core/ralocal_p.h
vendored
9
3rdparty/asmjit/src/asmjit/core/ralocal_p.h
vendored
@ -198,12 +198,13 @@ public:
|
||||
//! Decides on register assignment.
|
||||
uint32_t decideOnAssignment(uint32_t group, uint32_t workId, uint32_t assignedId, uint32_t allocableRegs) const noexcept;
|
||||
|
||||
//! Decides on whether to MOVE or SPILL the given WorkReg.
|
||||
//! Decides on whether to MOVE or SPILL the given WorkReg, because it's allocated
|
||||
//! in a physical register that have to be used by another WorkReg.
|
||||
//!
|
||||
//! The function must return either `RAAssignment::kPhysNone`, which means that
|
||||
//! the WorkReg should be spilled, or a valid physical register ID, which means
|
||||
//! that the register should be moved to that physical register instead.
|
||||
uint32_t decideOnUnassignment(uint32_t group, uint32_t workId, uint32_t assignedId, uint32_t allocableRegs) const noexcept;
|
||||
//! the WorkReg of `workId` should be spilled, or a valid physical register ID,
|
||||
//! which means that the register should be moved to that physical register instead.
|
||||
uint32_t decideOnReassignment(uint32_t group, uint32_t workId, uint32_t assignedId, uint32_t allocableRegs) const noexcept;
|
||||
|
||||
//! Decides on best spill given a register mask `spillableRegs`
|
||||
uint32_t decideOnSpillFor(uint32_t group, uint32_t workId, uint32_t spillableRegs, uint32_t* spillWorkId) const noexcept;
|
||||
|
65
3rdparty/asmjit/src/asmjit/core/rapass.cpp
vendored
65
3rdparty/asmjit/src/asmjit/core/rapass.cpp
vendored
@ -24,6 +24,7 @@
|
||||
#include "../core/api-build_p.h"
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/ralocal_p.h"
|
||||
#include "../core/rapass_p.h"
|
||||
#include "../core/support.h"
|
||||
@ -161,7 +162,7 @@ static void RAPass_resetVirtRegData(RAPass* self) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
Error RAPass::runOnFunction(Zone* zone, Logger* logger, FuncNode* func) noexcept {
|
||||
Error RAPass::runOnFunction(Zone* zone, Logger* logger, FuncNode* func) {
|
||||
_allocator.reset(zone);
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
@ -194,7 +195,6 @@ Error RAPass::runOnFunction(Zone* zone, Logger* logger, FuncNode* func) noexcept
|
||||
// Must be called regardless of the allocation status.
|
||||
onDone();
|
||||
|
||||
// TODO: I don't like this...
|
||||
// Reset possible connections introduced by the register allocator.
|
||||
RAPass_resetVirtRegData(this);
|
||||
|
||||
@ -472,7 +472,7 @@ Error RAPass::buildViews() noexcept {
|
||||
if (block->hasSuccessors()) {
|
||||
sb.appendFormat(" #%u -> {", block->blockId());
|
||||
_dumpBlockIds(sb, block->successors());
|
||||
sb.appendString("}\n");
|
||||
sb.append("}\n");
|
||||
}
|
||||
else {
|
||||
sb.appendFormat(" #%u -> {Exit}\n", block->blockId());
|
||||
@ -1133,11 +1133,11 @@ static void RAPass_dumpSpans(String& sb, uint32_t index, const LiveRegSpans& liv
|
||||
|
||||
for (uint32_t i = 0; i < liveSpans.size(); i++) {
|
||||
const LiveRegSpan& liveSpan = liveSpans[i];
|
||||
if (i) sb.appendString(", ");
|
||||
if (i) sb.append(", ");
|
||||
sb.appendFormat("[%u:%u@%u]", liveSpan.a, liveSpan.b, liveSpan.id);
|
||||
}
|
||||
|
||||
sb.appendChar('\n');
|
||||
sb.append('\n');
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1296,10 +1296,10 @@ ASMJIT_FAVOR_SPEED Error RAPass::binPack(uint32_t group) noexcept {
|
||||
sb.appendFormat(" Unassigned (%u): ", count);
|
||||
for (i = 0; i < numWorkRegs; i++) {
|
||||
RAWorkReg* workReg = workRegs[i];
|
||||
if (i) sb.appendString(", ");
|
||||
sb.appendString(workReg->name());
|
||||
if (i) sb.append(", ");
|
||||
sb.append(workReg->name());
|
||||
}
|
||||
sb.appendChar('\n');
|
||||
sb.append('\n');
|
||||
logger->log(sb);
|
||||
});
|
||||
}
|
||||
@ -1380,8 +1380,8 @@ Error RAPass::runLocalAllocator() noexcept {
|
||||
}
|
||||
|
||||
ASMJIT_PROPAGATE(lra.allocInst(inst));
|
||||
if (inst->type() == BaseNode::kNodeFuncCall)
|
||||
ASMJIT_PROPAGATE(onEmitPreCall(inst->as<FuncCallNode>()));
|
||||
if (inst->type() == BaseNode::kNodeInvoke)
|
||||
ASMJIT_PROPAGATE(onEmitPreCall(inst->as<InvokeNode>()));
|
||||
else
|
||||
ASMJIT_PROPAGATE(lra.spillAfterAllocation(inst));
|
||||
}
|
||||
@ -1595,7 +1595,7 @@ Error RAPass::useTemporaryMem(BaseMem& out, uint32_t size, uint32_t alignment) n
|
||||
ASMJIT_ASSERT(alignment <= 64);
|
||||
|
||||
if (_temporaryMem.isNone()) {
|
||||
ASMJIT_PROPAGATE(cc()->_newStack(_temporaryMem.as<BaseMem>(), size, alignment));
|
||||
ASMJIT_PROPAGATE(cc()->_newStack(&_temporaryMem.as<BaseMem>(), size, alignment));
|
||||
}
|
||||
else {
|
||||
ASMJIT_ASSERT(_temporaryMem.as<BaseMem>().isRegHome());
|
||||
@ -1852,27 +1852,31 @@ static void RAPass_dumpRAInst(RAPass* pass, String& sb, const RAInst* raInst) no
|
||||
for (uint32_t i = 0; i < tiedCount; i++) {
|
||||
const RATiedReg& tiedReg = tiedRegs[i];
|
||||
|
||||
if (i != 0) sb.appendChar(' ');
|
||||
if (i != 0)
|
||||
sb.append(' ');
|
||||
|
||||
sb.appendFormat("%s{", pass->workRegById(tiedReg.workId())->name());
|
||||
sb.appendChar(tiedReg.isReadWrite() ? 'X' :
|
||||
tiedReg.isRead() ? 'R' :
|
||||
tiedReg.isWrite() ? 'W' : '?');
|
||||
sb.append(tiedReg.isReadWrite() ? 'X' :
|
||||
tiedReg.isRead() ? 'R' :
|
||||
tiedReg.isWrite() ? 'W' : '?');
|
||||
|
||||
if (tiedReg.hasUseId())
|
||||
sb.appendFormat("|Use=%u", tiedReg.useId());
|
||||
else if (tiedReg.isUse())
|
||||
sb.appendString("|Use");
|
||||
sb.append("|Use");
|
||||
|
||||
if (tiedReg.hasOutId())
|
||||
sb.appendFormat("|Out=%u", tiedReg.outId());
|
||||
else if (tiedReg.isOut())
|
||||
sb.appendString("|Out");
|
||||
sb.append("|Out");
|
||||
|
||||
if (tiedReg.isLast()) sb.appendString("|Last");
|
||||
if (tiedReg.isKill()) sb.appendString("|Kill");
|
||||
if (tiedReg.isLast())
|
||||
sb.append("|Last");
|
||||
|
||||
sb.appendString("}");
|
||||
if (tiedReg.isKill())
|
||||
sb.append("|Kill");
|
||||
|
||||
sb.append("}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1887,13 +1891,13 @@ ASMJIT_FAVOR_SIZE Error RAPass::annotateCode() noexcept {
|
||||
BaseNode* last = block->last();
|
||||
for (;;) {
|
||||
sb.clear();
|
||||
Logging::formatNode(sb, loggerFlags, cc(), node);
|
||||
Formatter::formatNode(sb, loggerFlags, cc(), node);
|
||||
|
||||
if ((loggerFlags & FormatOptions::kFlagDebugRA) != 0 && node->isInst() && node->hasPassData()) {
|
||||
const RAInst* raInst = node->passData<RAInst>();
|
||||
if (raInst->tiedCount() > 0) {
|
||||
sb.padEnd(40);
|
||||
sb.appendString(" | ");
|
||||
sb.append(" | ");
|
||||
RAPass_dumpRAInst(this, sb, raInst);
|
||||
}
|
||||
}
|
||||
@ -1940,15 +1944,15 @@ ASMJIT_FAVOR_SIZE Error RAPass::_dumpBlockLiveness(String& sb, const RABlock* bl
|
||||
if (!n)
|
||||
sb.appendFormat(" %s [", bitsName);
|
||||
else
|
||||
sb.appendString(", ");
|
||||
sb.append(", ");
|
||||
|
||||
sb.appendString(wReg->name());
|
||||
sb.append(wReg->name());
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
if (n)
|
||||
sb.appendString("]\n");
|
||||
sb.append("]\n");
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
@ -1961,10 +1965,10 @@ ASMJIT_FAVOR_SIZE Error RAPass::_dumpLiveSpans(String& sb) noexcept {
|
||||
for (uint32_t workId = 0; workId < numWorkRegs; workId++) {
|
||||
RAWorkReg* workReg = _workRegs[workId];
|
||||
|
||||
sb.appendString(" ");
|
||||
sb.append(" ");
|
||||
|
||||
size_t oldSize = sb.size();
|
||||
sb.appendString(workReg->name());
|
||||
sb.append(workReg->name());
|
||||
sb.padEnd(oldSize + maxSize);
|
||||
|
||||
RALiveStats& stats = workReg->liveStats();
|
||||
@ -1973,16 +1977,17 @@ ASMJIT_FAVOR_SIZE Error RAPass::_dumpLiveSpans(String& sb) noexcept {
|
||||
stats.width(),
|
||||
stats.freq(),
|
||||
stats.priority());
|
||||
sb.appendString(": ");
|
||||
sb.append(": ");
|
||||
|
||||
LiveRegSpans& liveSpans = workReg->liveSpans();
|
||||
for (uint32_t x = 0; x < liveSpans.size(); x++) {
|
||||
const LiveRegSpan& liveSpan = liveSpans[x];
|
||||
if (x) sb.appendString(", ");
|
||||
if (x)
|
||||
sb.append(", ");
|
||||
sb.appendFormat("[%u:%u]", liveSpan.a, liveSpan.b);
|
||||
}
|
||||
|
||||
sb.appendChar('\n');
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
|
37
3rdparty/asmjit/src/asmjit/core/rapass_p.h
vendored
37
3rdparty/asmjit/src/asmjit/core/rapass_p.h
vendored
@ -42,6 +42,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::RABlock]
|
||||
// ============================================================================
|
||||
|
||||
//! Basic block used by register allocator pass.
|
||||
class RABlock {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(RABlock)
|
||||
@ -53,6 +54,7 @@ public:
|
||||
kUnassignedId = 0xFFFFFFFFu
|
||||
};
|
||||
|
||||
//! Basic block flags.
|
||||
enum Flags : uint32_t {
|
||||
//! Block has been constructed from nodes.
|
||||
kFlagIsConstructed = 0x00000001u,
|
||||
@ -111,9 +113,6 @@ public:
|
||||
//! Block successors.
|
||||
RABlocks _successors;
|
||||
|
||||
// TODO: Used?
|
||||
RABlocks _doms;
|
||||
|
||||
enum LiveType : uint32_t {
|
||||
kLiveIn = 0,
|
||||
kLiveOut = 1,
|
||||
@ -157,7 +156,6 @@ public:
|
||||
_idom(nullptr),
|
||||
_predecessors(),
|
||||
_successors(),
|
||||
_doms(),
|
||||
_sharedAssignmentId(Globals::kInvalidId),
|
||||
_entryScratchGpRegs(0),
|
||||
_exitScratchGpRegs(0),
|
||||
@ -470,7 +468,7 @@ public:
|
||||
//! \name Utilities
|
||||
//! \{
|
||||
|
||||
ASMJIT_INLINE Error add(RAWorkReg* workReg, uint32_t flags, uint32_t allocable, uint32_t useId, uint32_t useRewriteMask, uint32_t outId, uint32_t outRewriteMask, uint32_t rmSize = 0) noexcept {
|
||||
Error add(RAWorkReg* workReg, uint32_t flags, uint32_t allocable, uint32_t useId, uint32_t useRewriteMask, uint32_t outId, uint32_t outRewriteMask, uint32_t rmSize = 0) noexcept {
|
||||
uint32_t group = workReg->group();
|
||||
RATiedReg* tiedReg = workReg->tiedReg();
|
||||
|
||||
@ -510,7 +508,6 @@ public:
|
||||
if (ASMJIT_UNLIKELY(tiedReg->hasOutId()))
|
||||
return DebugUtils::errored(kErrorOverlappedRegs);
|
||||
tiedReg->setOutId(outId);
|
||||
// TODO: ? _used[group] |= Support::bitMask(outId);
|
||||
}
|
||||
|
||||
tiedReg->addRefCount();
|
||||
@ -523,7 +520,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
ASMJIT_INLINE Error addCallArg(RAWorkReg* workReg, uint32_t useId) noexcept {
|
||||
Error addCallArg(RAWorkReg* workReg, uint32_t useId) noexcept {
|
||||
ASMJIT_ASSERT(useId != BaseReg::kIdBad);
|
||||
|
||||
uint32_t flags = RATiedReg::kUse | RATiedReg::kRead | RATiedReg::kUseFixed;
|
||||
@ -563,7 +560,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
ASMJIT_INLINE Error addCallRet(RAWorkReg* workReg, uint32_t outId) noexcept {
|
||||
Error addCallRet(RAWorkReg* workReg, uint32_t outId) noexcept {
|
||||
ASMJIT_ASSERT(outId != BaseReg::kIdBad);
|
||||
|
||||
uint32_t flags = RATiedReg::kOut | RATiedReg::kWrite | RATiedReg::kOutFixed;
|
||||
@ -761,14 +758,14 @@ public:
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns `Logger` passed to `runOnFunction()`.
|
||||
//! Returns \ref Logger passed to \ref runOnFunction().
|
||||
inline Logger* logger() const noexcept { return _logger; }
|
||||
//! Returns `Logger` passed to `runOnFunction()` or null if `kOptionDebugPasses` is not set.
|
||||
//! Returns \ref Logger passed to \ref runOnFunction() or null if `kOptionDebugPasses` is not set.
|
||||
inline Logger* debugLogger() const noexcept { return _debugLogger; }
|
||||
|
||||
//! Returns `Zone` passed to `runOnFunction()`.
|
||||
//! Returns \ref Zone passed to \ref runOnFunction().
|
||||
inline Zone* zone() const noexcept { return _allocator.zone(); }
|
||||
//! Returns `ZoneAllocator` used by the register allocator.
|
||||
//! Returns \ref ZoneAllocator used by the register allocator.
|
||||
inline ZoneAllocator* allocator() const noexcept { return const_cast<ZoneAllocator*>(&_allocator); }
|
||||
|
||||
inline const ZoneVector<RASharedAssignment>& sharedAssignments() const { return _sharedAssignments; }
|
||||
@ -800,7 +797,7 @@ public:
|
||||
}
|
||||
|
||||
//! Runs the register allocator for the given `func`.
|
||||
Error runOnFunction(Zone* zone, Logger* logger, FuncNode* func) noexcept override;
|
||||
Error runOnFunction(Zone* zone, Logger* logger, FuncNode* func) override;
|
||||
|
||||
//! Performs all allocation steps sequentially, called by `runOnFunction()`.
|
||||
Error onPerformAllSteps() noexcept;
|
||||
@ -810,11 +807,11 @@ public:
|
||||
//! \name Events
|
||||
//! \{
|
||||
|
||||
//! Called by `runOnFunction()` before the register allocation to initialize
|
||||
//! Called by \ref runOnFunction() before the register allocation to initialize
|
||||
//! architecture-specific data and constraints.
|
||||
virtual void onInit() noexcept = 0;
|
||||
|
||||
//! Called by `runOnFunction()` after register allocation to clean everything
|
||||
//! Called by \ref runOnFunction(` after register allocation to clean everything
|
||||
//! up. Called even if the register allocation failed.
|
||||
virtual void onDone() noexcept = 0;
|
||||
|
||||
@ -1009,7 +1006,7 @@ public:
|
||||
//! \{
|
||||
|
||||
//! Returns a native size of the general-purpose register of the target architecture.
|
||||
inline uint32_t gpSize() const noexcept { return _sp.size(); }
|
||||
inline uint32_t registerSize() const noexcept { return _sp.size(); }
|
||||
inline uint32_t availableRegCount(uint32_t group) const noexcept { return _availableRegCount[group]; }
|
||||
|
||||
inline RAWorkReg* workRegById(uint32_t workId) const noexcept { return _workRegs[workId]; }
|
||||
@ -1049,9 +1046,11 @@ public:
|
||||
|
||||
inline RAStackSlot* getOrCreateStackSlot(RAWorkReg* workReg) noexcept {
|
||||
RAStackSlot* slot = workReg->stackSlot();
|
||||
if (slot) return slot;
|
||||
|
||||
slot = _stackAllocator.newSlot(_sp.id(), workReg->virtReg()->virtSize(), workReg->virtReg()->alignment(), 0);
|
||||
if (slot)
|
||||
return slot;
|
||||
|
||||
slot = _stackAllocator.newSlot(_sp.id(), workReg->virtReg()->virtSize(), workReg->virtReg()->alignment(), RAStackSlot::kFlagRegHome);
|
||||
workReg->_stackSlot = slot;
|
||||
workReg->markStackUsed();
|
||||
return slot;
|
||||
@ -1166,7 +1165,7 @@ public:
|
||||
virtual Error onEmitSave(uint32_t workId, uint32_t srcPhysId) noexcept = 0;
|
||||
|
||||
virtual Error onEmitJump(const Label& label) noexcept = 0;
|
||||
virtual Error onEmitPreCall(FuncCallNode* call) noexcept = 0;
|
||||
virtual Error onEmitPreCall(InvokeNode* invokeNode) noexcept = 0;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
9
3rdparty/asmjit/src/asmjit/core/rastack.cpp
vendored
9
3rdparty/asmjit/src/asmjit/core/rastack.cpp
vendored
@ -43,11 +43,9 @@ RAStackSlot* RAStackAllocator::newSlot(uint32_t baseRegId, uint32_t size, uint32
|
||||
|
||||
slot->_baseRegId = uint8_t(baseRegId);
|
||||
slot->_alignment = uint8_t(Support::max<uint32_t>(alignment, 1));
|
||||
slot->_reserved[0] = 0;
|
||||
slot->_reserved[1] = 0;
|
||||
slot->_flags = uint16_t(flags);
|
||||
slot->_useCount = 0;
|
||||
slot->_size = size;
|
||||
slot->_flags = flags;
|
||||
|
||||
slot->_weight = 0;
|
||||
slot->_offset = 0;
|
||||
@ -92,7 +90,7 @@ Error RAStackAllocator::calculateStackFrame() noexcept {
|
||||
uint32_t alignment = slot->alignment();
|
||||
ASMJIT_ASSERT(alignment > 0);
|
||||
|
||||
uint32_t power = Support::ctz(alignment);
|
||||
uint32_t power = Support::min<uint32_t>(Support::ctz(alignment), 6);
|
||||
uint64_t weight;
|
||||
|
||||
if (slot->isRegHome())
|
||||
@ -128,7 +126,8 @@ Error RAStackAllocator::calculateStackFrame() noexcept {
|
||||
ZoneVector<RAStackGap> gaps[kSizeCount - 1];
|
||||
|
||||
for (RAStackSlot* slot : _slots) {
|
||||
if (slot->isStackArg()) continue;
|
||||
if (slot->isStackArg())
|
||||
continue;
|
||||
|
||||
uint32_t slotAlignment = slot->alignment();
|
||||
uint32_t alignedOffset = Support::alignUp(offset, slotAlignment);
|
||||
|
26
3rdparty/asmjit/src/asmjit/core/rastack_p.h
vendored
26
3rdparty/asmjit/src/asmjit/core/rastack_p.h
vendored
@ -41,10 +41,14 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! Stack slot.
|
||||
struct RAStackSlot {
|
||||
//! Stack slot flags.
|
||||
//!
|
||||
//! TODO: kFlagStackArg is not used by the current implementation, do we need to keep it?
|
||||
enum Flags : uint32_t {
|
||||
// TODO: kFlagRegHome is apparently not used, but isRegHome() is.
|
||||
kFlagRegHome = 0x00000001u, //!< Stack slot is register home slot.
|
||||
kFlagStackArg = 0x00000002u //!< Stack slot position matches argument passed via stack.
|
||||
//! Stack slot is register home slot.
|
||||
kFlagRegHome = 0x0001u,
|
||||
//! Stack slot position matches argument passed via stack.
|
||||
kFlagStackArg = 0x0002u
|
||||
};
|
||||
|
||||
enum ArgIndex : uint32_t {
|
||||
@ -56,17 +60,15 @@ struct RAStackSlot {
|
||||
//! Minimum alignment required by the slot.
|
||||
uint8_t _alignment;
|
||||
//! Reserved for future use.
|
||||
uint8_t _reserved[2];
|
||||
uint16_t _flags;
|
||||
//! Size of memory required by the slot.
|
||||
uint32_t _size;
|
||||
//! Slot flags.
|
||||
uint32_t _flags;
|
||||
|
||||
//! Usage counter (one unit equals one memory access).
|
||||
uint32_t _useCount;
|
||||
//! Weight of the slot (calculated by `calculateStackFrame()`).
|
||||
//! Weight of the slot, calculated by \ref RAStackAllocator::calculateStackFrame().
|
||||
uint32_t _weight;
|
||||
//! Stack offset (calculated by `calculateStackFrame()`).
|
||||
//! Stack offset, calculated by \ref RAStackAllocator::calculateStackFrame().
|
||||
int32_t _offset;
|
||||
|
||||
//! \name Accessors
|
||||
@ -79,9 +81,11 @@ struct RAStackSlot {
|
||||
inline uint32_t alignment() const noexcept { return _alignment; }
|
||||
|
||||
inline uint32_t flags() const noexcept { return _flags; }
|
||||
inline void addFlags(uint32_t flags) noexcept { _flags |= flags; }
|
||||
inline bool isRegHome() const noexcept { return (_flags & kFlagRegHome) != 0; }
|
||||
inline bool isStackArg() const noexcept { return (_flags & kFlagStackArg) != 0; }
|
||||
inline bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
|
||||
inline void addFlags(uint32_t flags) noexcept { _flags = uint16_t(_flags | flags); }
|
||||
|
||||
inline bool isRegHome() const noexcept { return hasFlag(kFlagRegHome); }
|
||||
inline bool isStackArg() const noexcept { return hasFlag(kFlagStackArg); }
|
||||
|
||||
inline uint32_t useCount() const noexcept { return _useCount; }
|
||||
inline void addUseCount(uint32_t n = 1) noexcept { _useCount += n; }
|
||||
|
30
3rdparty/asmjit/src/asmjit/core/string.cpp
vendored
30
3rdparty/asmjit/src/asmjit/core/string.cpp
vendored
@ -34,7 +34,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
static const char String_baseN[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
constexpr size_t kMinAllocSize = 64;
|
||||
constexpr size_t kMaxAllocSize = std::numeric_limits<size_t>::max() - Globals::kGrowThreshold;
|
||||
constexpr size_t kMaxAllocSize = SIZE_MAX - Globals::kGrowThreshold;
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::String]
|
||||
@ -150,7 +150,7 @@ char* String::prepare(uint32_t op, size_t size) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
Error String::assignString(const char* data, size_t size) noexcept {
|
||||
Error String::assign(const char* data, size_t size) noexcept {
|
||||
char* dst = nullptr;
|
||||
|
||||
// Null terminated string without `size` specified.
|
||||
@ -222,7 +222,8 @@ Error String::_opString(uint32_t op, const char* str, size_t size) noexcept {
|
||||
return kErrorOk;
|
||||
|
||||
char* p = prepare(op, size);
|
||||
if (!p) return DebugUtils::errored(kErrorOutOfMemory);
|
||||
if (!p)
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
|
||||
memcpy(p, str, size);
|
||||
return kErrorOk;
|
||||
@ -230,7 +231,8 @@ Error String::_opString(uint32_t op, const char* str, size_t size) noexcept {
|
||||
|
||||
Error String::_opChar(uint32_t op, char c) noexcept {
|
||||
char* p = prepare(op, 1);
|
||||
if (!p) return DebugUtils::errored(kErrorOutOfMemory);
|
||||
if (!p)
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
|
||||
*p = c;
|
||||
return kErrorOk;
|
||||
@ -241,7 +243,8 @@ Error String::_opChars(uint32_t op, char c, size_t n) noexcept {
|
||||
return kErrorOk;
|
||||
|
||||
char* p = prepare(op, n);
|
||||
if (!p) return DebugUtils::errored(kErrorOutOfMemory);
|
||||
if (!p)
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
|
||||
memset(p, c, n);
|
||||
return kErrorOk;
|
||||
@ -349,7 +352,7 @@ Error String::_opHex(uint32_t op, const void* data, size_t size, char separator)
|
||||
return kErrorOk;
|
||||
|
||||
if (separator) {
|
||||
if (ASMJIT_UNLIKELY(size >= std::numeric_limits<size_t>::max() / 3))
|
||||
if (ASMJIT_UNLIKELY(size >= SIZE_MAX / 3))
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
|
||||
dst = prepare(op, size * 3 - 1);
|
||||
@ -369,7 +372,7 @@ Error String::_opHex(uint32_t op, const void* data, size_t size, char separator)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ASMJIT_UNLIKELY(size >= std::numeric_limits<size_t>::max() / 2))
|
||||
if (ASMJIT_UNLIKELY(size >= SIZE_MAX / 2))
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
|
||||
dst = prepare(op, size * 2);
|
||||
@ -404,6 +407,9 @@ Error String::_opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept {
|
||||
int fmtResult;
|
||||
size_t outputSize;
|
||||
|
||||
va_list apCopy;
|
||||
va_copy(apCopy, ap);
|
||||
|
||||
if (remainingCapacity >= 128) {
|
||||
fmtResult = vsnprintf(data() + startAt, remainingCapacity, fmt, ap);
|
||||
outputSize = size_t(fmtResult);
|
||||
@ -428,7 +434,7 @@ Error String::_opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept {
|
||||
if (ASMJIT_UNLIKELY(!p))
|
||||
return DebugUtils::errored(kErrorOutOfMemory);
|
||||
|
||||
fmtResult = vsnprintf(p, outputSize + 1, fmt, ap);
|
||||
fmtResult = vsnprintf(p, outputSize + 1, fmt, apCopy);
|
||||
ASMJIT_ASSERT(size_t(fmtResult) == outputSize);
|
||||
|
||||
return kErrorOk;
|
||||
@ -483,7 +489,7 @@ UNIT(core_string) {
|
||||
EXPECT(s.isLarge() == false);
|
||||
EXPECT(s.isExternal() == false);
|
||||
|
||||
EXPECT(s.assignChar('a') == kErrorOk);
|
||||
EXPECT(s.assign('a') == kErrorOk);
|
||||
EXPECT(s.size() == 1);
|
||||
EXPECT(s.capacity() == String::kSSOCapacity);
|
||||
EXPECT(s.data()[0] == 'a');
|
||||
@ -502,7 +508,7 @@ UNIT(core_string) {
|
||||
EXPECT(s.eq("bbbb") == true);
|
||||
EXPECT(s.eq("bbbb", 4) == true);
|
||||
|
||||
EXPECT(s.assignString("abc") == kErrorOk);
|
||||
EXPECT(s.assign("abc") == kErrorOk);
|
||||
EXPECT(s.size() == 3);
|
||||
EXPECT(s.capacity() == String::kSSOCapacity);
|
||||
EXPECT(s.data()[0] == 'a');
|
||||
@ -513,7 +519,7 @@ UNIT(core_string) {
|
||||
EXPECT(s.eq("abc", 3) == true);
|
||||
|
||||
const char* large = "Large string that will not fit into SSO buffer";
|
||||
EXPECT(s.assignString(large) == kErrorOk);
|
||||
EXPECT(s.assign(large) == kErrorOk);
|
||||
EXPECT(s.isLarge() == true);
|
||||
EXPECT(s.size() == strlen(large));
|
||||
EXPECT(s.capacity() > String::kSSOCapacity);
|
||||
@ -522,7 +528,7 @@ UNIT(core_string) {
|
||||
|
||||
const char* additional = " (additional content)";
|
||||
EXPECT(s.isLarge() == true);
|
||||
EXPECT(s.appendString(additional) == kErrorOk);
|
||||
EXPECT(s.append(additional) == kErrorOk);
|
||||
EXPECT(s.size() == strlen(large) + strlen(additional));
|
||||
|
||||
EXPECT(s.clear() == kErrorOk);
|
||||
|
232
3rdparty/asmjit/src/asmjit/core/string.h
vendored
232
3rdparty/asmjit/src/asmjit/core/string.h
vendored
@ -29,9 +29,33 @@
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_support
|
||||
//! \addtogroup asmjit_utilities
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::FixedString]
|
||||
// ============================================================================
|
||||
|
||||
//! A fixed string - only useful for strings that would never exceed `N - 1`
|
||||
//! characters; always null-terminated.
|
||||
template<size_t N>
|
||||
union FixedString {
|
||||
enum : uint32_t {
|
||||
kNumU32 = uint32_t((N + sizeof(uint32_t) - 1) / sizeof(uint32_t))
|
||||
};
|
||||
|
||||
char str[kNumU32 * sizeof(uint32_t)];
|
||||
uint32_t u32[kNumU32];
|
||||
|
||||
//! \name Utilities
|
||||
//! \{
|
||||
|
||||
inline bool eq(const char* other) const noexcept {
|
||||
return strcmp(str, other) == 0;
|
||||
}
|
||||
|
||||
//! \}
|
||||
};
|
||||
// ============================================================================
|
||||
// [asmjit::String]
|
||||
// ============================================================================
|
||||
@ -60,8 +84,10 @@ public:
|
||||
|
||||
//! String operation.
|
||||
enum Op : uint32_t {
|
||||
kOpAssign = 0,
|
||||
kOpAppend = 1
|
||||
//! Assignment - a new content replaces the current one.
|
||||
kOpAssign = 0,
|
||||
//! Append - a new content is appended to the string.
|
||||
kOpAppend = 1
|
||||
};
|
||||
|
||||
//! String format flags.
|
||||
@ -114,12 +140,13 @@ public:
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
|
||||
//! Creates a default-initialized string if zero length.
|
||||
inline String() noexcept
|
||||
: _small {} {}
|
||||
|
||||
//! Creates a string that takes ownership of the content of the `other` string.
|
||||
inline String(String&& other) noexcept {
|
||||
for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_raw.uptr); i++)
|
||||
_raw.uptr[i] = other._raw.uptr[i];
|
||||
_raw = other._raw;
|
||||
other._resetInternal();
|
||||
}
|
||||
|
||||
@ -135,6 +162,12 @@ public:
|
||||
//! \name Overloaded Operators
|
||||
//! \{
|
||||
|
||||
inline String& operator=(String&& other) noexcept {
|
||||
swap(other);
|
||||
other.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool operator==(const char* other) const noexcept { return eq(other); }
|
||||
inline bool operator!=(const char* other) const noexcept { return !eq(other); }
|
||||
|
||||
@ -149,13 +182,21 @@ public:
|
||||
inline bool isLarge() const noexcept { return _type >= kTypeLarge; }
|
||||
inline bool isExternal() const noexcept { return _type == kTypeExternal; }
|
||||
|
||||
//! Tests whether the string is empty.
|
||||
inline bool empty() const noexcept { return size() == 0; }
|
||||
//! Returns the size of the string.
|
||||
inline size_t size() const noexcept { return isLarge() ? size_t(_large.size) : size_t(_type); }
|
||||
//! Returns the capacity of the string.
|
||||
inline size_t capacity() const noexcept { return isLarge() ? _large.capacity : size_t(kSSOCapacity); }
|
||||
|
||||
//! Returns the data of the string.
|
||||
inline char* data() noexcept { return isLarge() ? _large.data : _small.data; }
|
||||
//! \overload
|
||||
inline const char* data() const noexcept { return isLarge() ? _large.data : _small.data; }
|
||||
|
||||
inline char* start() noexcept { return data(); }
|
||||
inline const char* start() const noexcept { return data(); }
|
||||
|
||||
inline char* end() noexcept { return data() + size(); }
|
||||
inline const char* end() const noexcept { return data() + size(); }
|
||||
|
||||
@ -164,102 +205,120 @@ public:
|
||||
//! \name String Operations
|
||||
//! \{
|
||||
|
||||
//! Clear the content of the string.
|
||||
//! Swaps the content of this string with `other`.
|
||||
inline void swap(String& other) noexcept {
|
||||
std::swap(_raw, other._raw);
|
||||
}
|
||||
|
||||
//! Clears the content of the string.
|
||||
ASMJIT_API Error clear() noexcept;
|
||||
|
||||
ASMJIT_API char* prepare(uint32_t op, size_t size) noexcept;
|
||||
|
||||
ASMJIT_API Error _opString(uint32_t op, const char* str, size_t size = SIZE_MAX) noexcept;
|
||||
ASMJIT_API Error _opFormat(uint32_t op, const char* fmt, ...) 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 size, char separator = '\0') noexcept;
|
||||
ASMJIT_API Error _opFormat(uint32_t op, const char* fmt, ...) noexcept;
|
||||
ASMJIT_API Error _opVFormat(uint32_t op, const char* fmt, va_list ap) noexcept;
|
||||
|
||||
//! Replace the string content to a string specified by `data` and `size`. If
|
||||
//! `size` is `SIZE_MAX` then it's considered null-terminated and its length
|
||||
//! will be obtained through `strlen()`.
|
||||
ASMJIT_API Error assignString(const char* data, size_t size = SIZE_MAX) noexcept;
|
||||
//! Replaces the current of the string with `data` of the given `size`.
|
||||
//!
|
||||
//! Null terminated strings can set `size` to `SIZE_MAX`.
|
||||
ASMJIT_API Error assign(const char* data, size_t size = SIZE_MAX) noexcept;
|
||||
|
||||
//! Replace the current content by a formatted string `fmt`.
|
||||
//! Replaces the current of the string with `other` string.
|
||||
inline Error assign(const String& other) noexcept {
|
||||
return assign(other.data(), other.size());
|
||||
}
|
||||
|
||||
//! Replaces the current of the string by a single `c` character.
|
||||
inline Error assign(char c) noexcept {
|
||||
return _opChar(kOpAssign, c);
|
||||
}
|
||||
|
||||
//! Replaces the current of the string by a `c` character, repeated `n` times.
|
||||
inline Error assignChars(char c, size_t n) noexcept {
|
||||
return _opChars(kOpAssign, c, n);
|
||||
}
|
||||
|
||||
//! Replaces the current of the string by a formatted integer `i` (signed).
|
||||
inline Error assignInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
|
||||
return _opNumber(kOpAssign, uint64_t(i), base, width, flags | kFormatSigned);
|
||||
}
|
||||
|
||||
//! Replaces the current of the string by a formatted integer `i` (unsigned).
|
||||
inline Error assignUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
|
||||
return _opNumber(kOpAssign, i, base, width, flags);
|
||||
}
|
||||
|
||||
//! Replaces the current of the string by the given `data` converted to a HEX string.
|
||||
inline Error assignHex(const void* data, size_t size, char separator = '\0') noexcept {
|
||||
return _opHex(kOpAssign, data, size, separator);
|
||||
}
|
||||
|
||||
//! Replaces the current of the string by a formatted string `fmt`.
|
||||
template<typename... Args>
|
||||
inline Error assignFormat(const char* fmt, Args&&... args) noexcept {
|
||||
return _opFormat(kOpAssign, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
//! Replace the current content by a formatted string `fmt` (va_list version).
|
||||
//! Replaces the current of the string by a formatted string `fmt` (va_list version).
|
||||
inline Error assignVFormat(const char* fmt, va_list ap) noexcept {
|
||||
return _opVFormat(kOpAssign, fmt, ap);
|
||||
}
|
||||
|
||||
//! Replace the current content by a single `c` character.
|
||||
inline Error assignChar(char c) noexcept {
|
||||
return _opChar(kOpAssign, c);
|
||||
}
|
||||
|
||||
//! Replace the current content by `c` character `n` times.
|
||||
inline Error assignChars(char c, size_t n) noexcept {
|
||||
return _opChars(kOpAssign, c, n);
|
||||
}
|
||||
|
||||
//! Replace the current content by a formatted integer `i` (signed).
|
||||
inline Error assignInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
|
||||
return _opNumber(kOpAssign, uint64_t(i), base, width, flags | kFormatSigned);
|
||||
}
|
||||
|
||||
//! Replace the current content by a formatted integer `i` (unsigned).
|
||||
inline Error assignUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
|
||||
return _opNumber(kOpAssign, i, base, width, flags);
|
||||
}
|
||||
|
||||
//! Replace the current content by the given `data` converted to a HEX string.
|
||||
inline Error assignHex(const void* data, size_t size, char separator = '\0') noexcept {
|
||||
return _opHex(kOpAssign, data, size, separator);
|
||||
}
|
||||
|
||||
//! Append string `str` of size `size` (or possibly null terminated).
|
||||
inline Error appendString(const char* str, size_t size = SIZE_MAX) noexcept {
|
||||
//! Appends `str` having the given size `size` to the string.
|
||||
//!
|
||||
//! Null terminated strings can set `size` to `SIZE_MAX`.
|
||||
inline Error append(const char* str, size_t size = SIZE_MAX) noexcept {
|
||||
return _opString(kOpAppend, str, size);
|
||||
}
|
||||
|
||||
//! Appends `other` string to this string.
|
||||
inline Error append(const String& other) noexcept {
|
||||
return append(other.data(), other.size());
|
||||
}
|
||||
|
||||
//! Appends a single `c` character.
|
||||
inline Error append(char c) noexcept {
|
||||
return _opChar(kOpAppend, c);
|
||||
}
|
||||
|
||||
//! Appends `c` character repeated `n` times.
|
||||
inline Error appendChars(char c, size_t n) noexcept {
|
||||
return _opChars(kOpAppend, c, n);
|
||||
}
|
||||
|
||||
//! Appends a formatted integer `i` (signed).
|
||||
inline Error appendInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
|
||||
return _opNumber(kOpAppend, uint64_t(i), base, width, flags | kFormatSigned);
|
||||
}
|
||||
|
||||
//! Appends a formatted integer `i` (unsigned).
|
||||
inline Error appendUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
|
||||
return _opNumber(kOpAppend, i, base, width, flags);
|
||||
}
|
||||
|
||||
//! Appends the given `data` converted to a HEX string.
|
||||
inline Error appendHex(const void* data, size_t size, char separator = '\0') noexcept {
|
||||
return _opHex(kOpAppend, data, size, separator);
|
||||
}
|
||||
|
||||
//! Appends a formatted string `fmt` with `args`.
|
||||
template<typename... Args>
|
||||
inline Error appendFormat(const char* fmt, Args&&... args) noexcept {
|
||||
return _opFormat(kOpAppend, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
//! Append a formatted string `fmt` (va_list version).
|
||||
//! Appends a formatted string `fmt` (va_list version).
|
||||
inline Error appendVFormat(const char* fmt, va_list ap) noexcept {
|
||||
return _opVFormat(kOpAppend, fmt, ap);
|
||||
}
|
||||
|
||||
//! Append a single `c` character.
|
||||
inline Error appendChar(char c) noexcept {
|
||||
return _opChar(kOpAppend, c);
|
||||
}
|
||||
|
||||
//! Append `c` character `n` times.
|
||||
inline Error appendChars(char c, size_t n) noexcept {
|
||||
return _opChars(kOpAppend, c, n);
|
||||
}
|
||||
|
||||
ASMJIT_API Error padEnd(size_t n, char c = ' ') noexcept;
|
||||
|
||||
//! Append `i`.
|
||||
inline Error appendInt(int64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
|
||||
return _opNumber(kOpAppend, uint64_t(i), base, width, flags | kFormatSigned);
|
||||
}
|
||||
|
||||
//! Append `i`.
|
||||
inline Error appendUInt(uint64_t i, uint32_t base = 0, size_t width = 0, uint32_t flags = 0) noexcept {
|
||||
return _opNumber(kOpAppend, i, base, width, flags);
|
||||
}
|
||||
|
||||
//! Append the given `data` converted to a HEX string.
|
||||
inline Error appendHex(const void* data, size_t size, char separator = '\0') noexcept {
|
||||
return _opHex(kOpAppend, data, size, separator);
|
||||
}
|
||||
|
||||
//! Truncate the string length into `newSize`.
|
||||
ASMJIT_API Error truncate(size_t newSize) noexcept;
|
||||
|
||||
@ -288,6 +347,20 @@ public:
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("Use assign() instead of assignString()")
|
||||
inline Error assignString(const char* data, size_t size = SIZE_MAX) noexcept { return assign(data, size); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use assign() instead of assignChar()")
|
||||
inline Error assignChar(char c) noexcept { return assign(c); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use append() instead of appendString()")
|
||||
inline Error appendString(const char* data, size_t size = SIZE_MAX) noexcept { return append(data, size); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use append() instead of appendChar()")
|
||||
inline Error appendChar(char c) noexcept { return append(c); }
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@ -320,31 +393,6 @@ public:
|
||||
//! \}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::FixedString]
|
||||
// ============================================================================
|
||||
|
||||
//! A fixed string - only useful for strings that would never exceed `N - 1`
|
||||
//! characters; always null-terminated.
|
||||
template<size_t N>
|
||||
union FixedString {
|
||||
enum : uint32_t {
|
||||
kNumU32 = uint32_t((N + sizeof(uint32_t) - 1) / sizeof(uint32_t))
|
||||
};
|
||||
|
||||
char str[kNumU32 * sizeof(uint32_t)];
|
||||
uint32_t u32[kNumU32];
|
||||
|
||||
//! \name Utilities
|
||||
//! \{
|
||||
|
||||
inline bool eq(const char* other) const noexcept {
|
||||
return strcmp(str, other) == 0;
|
||||
}
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
10
3rdparty/asmjit/src/asmjit/core/support.cpp
vendored
10
3rdparty/asmjit/src/asmjit/core/support.cpp
vendored
@ -100,7 +100,7 @@ static void testBitUtils() noexcept {
|
||||
EXPECT(Support::bitTest((1 << i), i) == true, "Support::bitTest(%X, %u) should return true", (1 << i), i);
|
||||
}
|
||||
|
||||
INFO("Support::lsbMask()");
|
||||
INFO("Support::lsbMask<uint32_t>()");
|
||||
for (i = 0; i < 32; i++) {
|
||||
uint32_t expectedBits = 0;
|
||||
for (uint32_t b = 0; b < i; b++)
|
||||
@ -108,6 +108,14 @@ static void testBitUtils() noexcept {
|
||||
EXPECT(Support::lsbMask<uint32_t>(i) == expectedBits);
|
||||
}
|
||||
|
||||
INFO("Support::lsbMask<uint64_t>()");
|
||||
for (i = 0; i < 64; i++) {
|
||||
uint64_t expectedBits = 0;
|
||||
for (uint32_t b = 0; b < i; b++)
|
||||
expectedBits |= uint64_t(1) << b;
|
||||
EXPECT(Support::lsbMask<uint64_t>(i) == expectedBits);
|
||||
}
|
||||
|
||||
INFO("Support::popcnt()");
|
||||
for (i = 0; i < 32; i++) EXPECT(Support::popcnt((uint32_t(1) << i)) == 1);
|
||||
for (i = 0; i < 64; i++) EXPECT(Support::popcnt((uint64_t(1) << i)) == 1);
|
||||
|
209
3rdparty/asmjit/src/asmjit/core/support.h
vendored
209
3rdparty/asmjit/src/asmjit/core/support.h
vendored
@ -32,7 +32,7 @@
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_support
|
||||
//! \addtogroup asmjit_utilities
|
||||
//! \{
|
||||
|
||||
//! Contains support classes and functions that may be used by AsmJit source
|
||||
@ -69,27 +69,34 @@ namespace Internal {
|
||||
template<> struct AlignedInt<uint64_t, 4> { typedef uint64_t ASMJIT_ALIGN_TYPE(T, 4); };
|
||||
template<> struct AlignedInt<uint64_t, 8> { typedef uint64_t T; };
|
||||
|
||||
// IntBySize - Make an int-type by size (signed or unsigned) that is the
|
||||
// StdInt - Make an int-type by size (signed or unsigned) that is the
|
||||
// same as types defined by <stdint.h>.
|
||||
// Int32Or64 - Make an int-type that has at least 32 bits: [u]int[32|64]_t.
|
||||
|
||||
template<size_t SIZE, int IS_SIGNED>
|
||||
struct IntBySize {}; // Fail if not specialized.
|
||||
template<size_t Size, unsigned Unsigned>
|
||||
struct StdInt {}; // Fail if not specialized.
|
||||
|
||||
template<> struct IntBySize<1, 0> { typedef uint8_t Type; };
|
||||
template<> struct IntBySize<1, 1> { typedef int8_t Type; };
|
||||
template<> struct IntBySize<2, 0> { typedef uint16_t Type; };
|
||||
template<> struct IntBySize<2, 1> { typedef int16_t Type; };
|
||||
template<> struct IntBySize<4, 0> { typedef uint32_t Type; };
|
||||
template<> struct IntBySize<4, 1> { typedef int32_t Type; };
|
||||
template<> struct IntBySize<8, 0> { typedef uint64_t Type; };
|
||||
template<> struct IntBySize<8, 1> { typedef int64_t Type; };
|
||||
template<> struct StdInt<1, 0> { typedef int8_t Type; };
|
||||
template<> struct StdInt<1, 1> { typedef uint8_t Type; };
|
||||
template<> struct StdInt<2, 0> { typedef int16_t Type; };
|
||||
template<> struct StdInt<2, 1> { typedef uint16_t Type; };
|
||||
template<> struct StdInt<4, 0> { typedef int32_t Type; };
|
||||
template<> struct StdInt<4, 1> { typedef uint32_t Type; };
|
||||
template<> struct StdInt<8, 0> { typedef int64_t Type; };
|
||||
template<> struct StdInt<8, 1> { typedef uint64_t Type; };
|
||||
|
||||
template<typename T, int IS_SIGNED = std::is_signed<T>::value>
|
||||
struct Int32Or64 : public IntBySize<sizeof(T) <= 4 ? size_t(4) : sizeof(T), IS_SIGNED> {};
|
||||
template<typename T, int Unsigned = std::is_unsigned<T>::value>
|
||||
struct Int32Or64 : public StdInt<sizeof(T) <= 4 ? size_t(4) : sizeof(T), Unsigned> {};
|
||||
}
|
||||
//! \endcond
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Support - Basic Traits]
|
||||
// ============================================================================
|
||||
|
||||
template<typename T>
|
||||
static constexpr bool isUnsigned() noexcept { return std::is_unsigned<T>::value; }
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Support - FastUInt8]
|
||||
// ============================================================================
|
||||
@ -101,20 +108,32 @@ typedef unsigned int FastUInt8;
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Support - IntBySize / Int32Or64]
|
||||
// [asmjit::Support - asInt / asUInt / asNormalized]
|
||||
// ============================================================================
|
||||
|
||||
//! Casts an integer `x` to either `int32_t` or `int64_t` depending on `T`.
|
||||
template<typename T>
|
||||
static constexpr typename Internal::Int32Or64<T, 1>::Type asInt(T x) noexcept { return (typename Internal::Int32Or64<T, 1>::Type)x; }
|
||||
static constexpr typename Internal::Int32Or64<T, 0>::Type asInt(const T& x) noexcept {
|
||||
return (typename Internal::Int32Or64<T, 0>::Type)x;
|
||||
}
|
||||
|
||||
//! Casts an integer `x` to either `uint32_t` or `uint64_t` depending on `T`.
|
||||
template<typename T>
|
||||
static constexpr typename Internal::Int32Or64<T, 0>::Type asUInt(T x) noexcept { return (typename Internal::Int32Or64<T, 0>::Type)x; }
|
||||
static constexpr typename Internal::Int32Or64<T, 1>::Type asUInt(const T& x) noexcept {
|
||||
return (typename Internal::Int32Or64<T, 1>::Type)x;
|
||||
}
|
||||
|
||||
//! Casts an integer `x` to either `int32_t`, uint32_t`, `int64_t`, or `uint64_t` depending on `T`.
|
||||
template<typename T>
|
||||
static constexpr typename Internal::Int32Or64<T>::Type asNormalized(T x) noexcept { return (typename Internal::Int32Or64<T>::Type)x; }
|
||||
static constexpr typename Internal::Int32Or64<T>::Type asNormalized(const T& x) noexcept {
|
||||
return (typename Internal::Int32Or64<T>::Type)x;
|
||||
}
|
||||
|
||||
//! Casts an integer `x` to the same type as defined by `<stdint.h>`.
|
||||
template<typename T>
|
||||
static constexpr typename Internal::StdInt<sizeof(T), isUnsigned<T>()>::Type asStdInt(const T& x) noexcept {
|
||||
return (typename Internal::StdInt<sizeof(T), isUnsigned<T>()>::Type)x;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Support - BitCast]
|
||||
@ -142,7 +161,7 @@ static inline Dst bitCast(const Src& x) noexcept { return Internal::BitCastUnion
|
||||
// ============================================================================
|
||||
|
||||
//! Storage used to store a pack of bits (should by compatible with a machine word).
|
||||
typedef Internal::IntBySize<sizeof(uintptr_t), 0>::Type BitWord;
|
||||
typedef Internal::StdInt<sizeof(uintptr_t), 1>::Type BitWord;
|
||||
|
||||
template<typename T>
|
||||
static constexpr uint32_t bitSizeOf() noexcept { return uint32_t(sizeof(T) * 8u); }
|
||||
@ -194,14 +213,14 @@ static constexpr T blsi(T x) noexcept {
|
||||
|
||||
//! Generate a trailing bit-mask that has `n` least significant (trailing) bits set.
|
||||
template<typename T, typename CountT>
|
||||
static constexpr T lsbMask(CountT n) noexcept {
|
||||
static constexpr T lsbMask(const CountT& n) noexcept {
|
||||
typedef typename std::make_unsigned<T>::type U;
|
||||
return (sizeof(U) < sizeof(uintptr_t))
|
||||
// Prevent undefined behavior by using a larger type than T.
|
||||
? T(U((uintptr_t(1) << n) - uintptr_t(1)))
|
||||
// Shifting more bits than the type provides is UNDEFINED BEHAVIOR.
|
||||
// In such case we trash the result by ORing it with a mask that has
|
||||
// all bits set and discards the UNDEFINED RESULT of the shift.
|
||||
: T(((U(1) << n) - U(1u)) | neg(U(n >= CountT(bitSizeOf<T>()))));
|
||||
// Prevent undefined behavior by performing `n & (nBits - 1)` so it's always within the range.
|
||||
: shr(sar(neg(T(n)), bitSizeOf<T>() - 1u),
|
||||
neg(T(n)) & CountT(bitSizeOf<T>() - 1u));
|
||||
}
|
||||
|
||||
//! Tests whether the given value `x` has `n`th bit set.
|
||||
@ -398,30 +417,113 @@ static constexpr T max(const T& a, const T& b, Args&&... args) noexcept { return
|
||||
//! \cond
|
||||
namespace Internal {
|
||||
template<typename T>
|
||||
static ASMJIT_INLINE T addOverflowImpl(T x, T y, FastUInt8* of) noexcept {
|
||||
ASMJIT_INLINE T addOverflowFallback(T x, T y, FastUInt8* of) noexcept {
|
||||
typedef typename std::make_unsigned<T>::type U;
|
||||
|
||||
U result = U(x) + U(y);
|
||||
*of = FastUInt8(*of | FastUInt8(std::is_unsigned<T>::value ? result < U(x) : T((U(x) ^ ~U(y)) & (U(x) ^ result)) < 0));
|
||||
*of = FastUInt8(*of | FastUInt8(isUnsigned<T>() ? result < U(x) : T((U(x) ^ ~U(y)) & (U(x) ^ result)) < 0));
|
||||
return T(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static ASMJIT_INLINE T subOverflowImpl(T x, T y, FastUInt8* of) noexcept {
|
||||
ASMJIT_INLINE T subOverflowFallback(T x, T y, FastUInt8* of) noexcept {
|
||||
typedef typename std::make_unsigned<T>::type U;
|
||||
|
||||
U result = U(x) - U(y);
|
||||
*of = FastUInt8(*of | FastUInt8(std::is_unsigned<T>::value ? result > U(x) : T((U(x) ^ U(y)) & (U(x) ^ result)) < 0));
|
||||
*of = FastUInt8(*of | FastUInt8(isUnsigned<T>() ? result > U(x) : T((U(x) ^ U(y)) & (U(x) ^ result)) < 0));
|
||||
return T(result);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ASMJIT_INLINE T mulOverflowFallback(T x, T y, FastUInt8* of) noexcept {
|
||||
typedef typename Internal::StdInt<sizeof(T) * 2, isUnsigned<T>()>::Type I;
|
||||
typedef typename std::make_unsigned<I>::type U;
|
||||
|
||||
U mask = allOnes<U>();
|
||||
if (std::is_signed<T>::value) {
|
||||
U prod = U(I(x)) * U(I(y));
|
||||
*of = FastUInt8(*of | FastUInt8(I(prod) < I(std::numeric_limits<T>::lowest()) || I(prod) > I(std::numeric_limits<T>::max())));
|
||||
return T(I(prod & mask));
|
||||
}
|
||||
else {
|
||||
U prod = U(x) * U(y);
|
||||
*of = FastUInt8(*of | FastUInt8((prod & ~mask) != 0));
|
||||
return T(prod & mask);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
ASMJIT_INLINE int64_t mulOverflowFallback(int64_t x, int64_t y, FastUInt8* of) noexcept {
|
||||
int64_t result = int64_t(uint64_t(x) * uint64_t(y));
|
||||
*of = FastUInt8(*of | FastUInt8(x && (result / x != y)));
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
ASMJIT_INLINE uint64_t mulOverflowFallback(uint64_t x, uint64_t y, FastUInt8* of) noexcept {
|
||||
uint64_t result = x * y;
|
||||
*of = FastUInt8(*of | FastUInt8(y != 0 && allOnes<uint64_t>() / y < x));
|
||||
return result;
|
||||
}
|
||||
|
||||
// These can be specialized.
|
||||
template<typename T> ASMJIT_INLINE T addOverflowImpl(const T& x, const T& y, FastUInt8* of) noexcept { return addOverflowFallback(x, y, of); }
|
||||
template<typename T> ASMJIT_INLINE T subOverflowImpl(const T& x, const T& y, FastUInt8* of) noexcept { return subOverflowFallback(x, y, of); }
|
||||
template<typename T> ASMJIT_INLINE T mulOverflowImpl(const T& x, const T& y, FastUInt8* of) noexcept { return mulOverflowFallback(x, y, of); }
|
||||
|
||||
#if defined(__GNUC__) && !defined(ASMJIT_NO_INTRINSICS)
|
||||
#if defined(__clang__) || __GNUC__ >= 5
|
||||
#define ASMJIT_ARITH_OVERFLOW_SPECIALIZE(FUNC, T, RESULT_T, BUILTIN) \
|
||||
template<> \
|
||||
ASMJIT_INLINE T FUNC(const T& x, const T& y, FastUInt8* of) noexcept { \
|
||||
RESULT_T result; \
|
||||
*of = FastUInt8(*of | (BUILTIN((RESULT_T)x, (RESULT_T)y, &result))); \
|
||||
return T(result); \
|
||||
}
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(addOverflowImpl, int32_t , int , __builtin_sadd_overflow )
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(addOverflowImpl, uint32_t, unsigned int , __builtin_uadd_overflow )
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(addOverflowImpl, int64_t , long long , __builtin_saddll_overflow)
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(addOverflowImpl, uint64_t, unsigned long long, __builtin_uaddll_overflow)
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(subOverflowImpl, int32_t , int , __builtin_ssub_overflow )
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(subOverflowImpl, uint32_t, unsigned int , __builtin_usub_overflow )
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(subOverflowImpl, int64_t , long long , __builtin_ssubll_overflow)
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(subOverflowImpl, uint64_t, unsigned long long, __builtin_usubll_overflow)
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(mulOverflowImpl, int32_t , int , __builtin_smul_overflow )
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(mulOverflowImpl, uint32_t, unsigned int , __builtin_umul_overflow )
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(mulOverflowImpl, int64_t , long long , __builtin_smulll_overflow)
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(mulOverflowImpl, uint64_t, unsigned long long, __builtin_umulll_overflow)
|
||||
#undef ASMJIT_ARITH_OVERFLOW_SPECIALIZE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// There is a bug in MSVC that makes these specializations unusable, maybe in the future...
|
||||
#if defined(_MSC_VER) && 0
|
||||
#define ASMJIT_ARITH_OVERFLOW_SPECIALIZE(FUNC, T, ALT_T, BUILTIN) \
|
||||
template<> \
|
||||
ASMJIT_INLINE T FUNC(T x, T y, FastUInt8* of) noexcept { \
|
||||
ALT_T result; \
|
||||
*of = FastUInt8(*of | BUILTIN(0, (ALT_T)x, (ALT_T)y, &result)); \
|
||||
return T(result); \
|
||||
}
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(addOverflowImpl, uint32_t, unsigned int , _addcarry_u32 )
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(subOverflowImpl, uint32_t, unsigned int , _subborrow_u32)
|
||||
#if ARCH_BITS >= 64
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(addOverflowImpl, uint64_t, unsigned __int64 , _addcarry_u64 )
|
||||
ASMJIT_ARITH_OVERFLOW_SPECIALIZE(subOverflowImpl, uint64_t, unsigned __int64 , _subborrow_u64)
|
||||
#endif
|
||||
#undef ASMJIT_ARITH_OVERFLOW_SPECIALIZE
|
||||
#endif
|
||||
} // {Internal}
|
||||
//! \endcond
|
||||
|
||||
template<typename T>
|
||||
static ASMJIT_INLINE T addOverflow(const T& x, const T& y, FastUInt8* of) noexcept { return T(Internal::addOverflowImpl(x, y, of)); }
|
||||
static ASMJIT_INLINE T addOverflow(const T& x, const T& y, FastUInt8* of) noexcept { return T(Internal::addOverflowImpl(asStdInt(x), asStdInt(y), of)); }
|
||||
|
||||
template<typename T>
|
||||
static ASMJIT_INLINE T subOverflow(const T& x, const T& y, FastUInt8* of) noexcept { return T(Internal::subOverflowImpl(x, y, of)); }
|
||||
static ASMJIT_INLINE T subOverflow(const T& x, const T& y, FastUInt8* of) noexcept { return T(Internal::subOverflowImpl(asStdInt(x), asStdInt(y), of)); }
|
||||
|
||||
template<typename T>
|
||||
static ASMJIT_INLINE T mulOverflow(const T& x, const T& y, FastUInt8* of) noexcept { return T(Internal::mulOverflowImpl(asStdInt(x), asStdInt(y), of)); }
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Support - Alignment]
|
||||
@ -429,7 +531,7 @@ static ASMJIT_INLINE T subOverflow(const T& x, const T& y, FastUInt8* of) noexce
|
||||
|
||||
template<typename X, typename Y>
|
||||
static constexpr bool isAligned(X base, Y alignment) noexcept {
|
||||
typedef typename Internal::IntBySize<sizeof(X), 0>::Type U;
|
||||
typedef typename Internal::StdInt<sizeof(X), 1>::Type U;
|
||||
return ((U)base % (U)alignment) == 0;
|
||||
}
|
||||
|
||||
@ -442,27 +544,27 @@ static constexpr bool isPowerOf2(T x) noexcept {
|
||||
|
||||
template<typename X, typename Y>
|
||||
static constexpr X alignUp(X x, Y alignment) noexcept {
|
||||
typedef typename Internal::IntBySize<sizeof(X), 0>::Type U;
|
||||
typedef typename Internal::StdInt<sizeof(X), 1>::Type U;
|
||||
return (X)( ((U)x + ((U)(alignment) - 1u)) & ~((U)(alignment) - 1u) );
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr T alignUpPowerOf2(T x) noexcept {
|
||||
typedef typename Internal::IntBySize<sizeof(T), 0>::Type U;
|
||||
typedef typename Internal::StdInt<sizeof(T), 1>::Type U;
|
||||
return (T)(fillTrailingBits(U(x) - 1u) + 1u);
|
||||
}
|
||||
|
||||
//! Returns either zero or a positive difference between `base` and `base` when
|
||||
//! aligned to `alignment`.
|
||||
template<typename X, typename Y>
|
||||
static constexpr typename Internal::IntBySize<sizeof(X), 0>::Type alignUpDiff(X base, Y alignment) noexcept {
|
||||
typedef typename Internal::IntBySize<sizeof(X), 0>::Type U;
|
||||
static constexpr typename Internal::StdInt<sizeof(X), 1>::Type alignUpDiff(X base, Y alignment) noexcept {
|
||||
typedef typename Internal::StdInt<sizeof(X), 1>::Type U;
|
||||
return alignUp(U(base), alignment) - U(base);
|
||||
}
|
||||
|
||||
template<typename X, typename Y>
|
||||
static constexpr X alignDown(X x, Y alignment) noexcept {
|
||||
typedef typename Internal::IntBySize<sizeof(X), 0>::Type U;
|
||||
typedef typename Internal::StdInt<sizeof(X), 1>::Type U;
|
||||
return (X)( (U)x & ~((U)(alignment) - 1u) );
|
||||
}
|
||||
|
||||
@ -475,7 +577,7 @@ static constexpr X alignDown(X x, Y alignment) noexcept {
|
||||
//! the number of BitWords to represent N bits, for example.
|
||||
template<typename X, typename Y>
|
||||
static constexpr X numGranularized(X base, Y granularity) noexcept {
|
||||
typedef typename Internal::IntBySize<sizeof(X), 0>::Type U;
|
||||
typedef typename Internal::StdInt<sizeof(X), 1>::Type U;
|
||||
return X((U(base) + U(granularity) - 1) / U(granularity));
|
||||
}
|
||||
|
||||
@ -620,10 +722,10 @@ static inline uint32_t byteShiftOfDWordStruct(uint32_t index) noexcept {
|
||||
// ============================================================================
|
||||
|
||||
template<typename T>
|
||||
static constexpr T asciiToLower(T c) noexcept { return c ^ (T(c >= T('A') && c <= T('Z')) << 5); }
|
||||
static constexpr T asciiToLower(T c) noexcept { return T(c ^ T(T(c >= T('A') && c <= T('Z')) << 5)); }
|
||||
|
||||
template<typename T>
|
||||
static constexpr T asciiToUpper(T c) noexcept { return c ^ (T(c >= T('a') && c <= T('z')) << 5); }
|
||||
static constexpr T asciiToUpper(T c) noexcept { return T(c ^ T(T(c >= T('a') && c <= T('z')) << 5)); }
|
||||
|
||||
static ASMJIT_INLINE size_t strLen(const char* s, size_t maxSize) noexcept {
|
||||
size_t i = 0;
|
||||
@ -920,6 +1022,7 @@ static inline void writeU64uBE(void* p, uint64_t x) noexcept { writeU64xBE<1>(p,
|
||||
// [asmjit::Support - Operators]
|
||||
// ============================================================================
|
||||
|
||||
//! \cond INTERNAL
|
||||
struct Set { template<typename T> static inline T op(T x, T y) noexcept { DebugUtils::unused(x); return y; } };
|
||||
struct SetNot { template<typename T> static inline T op(T x, T y) noexcept { DebugUtils::unused(x); return ~y; } };
|
||||
struct And { template<typename T> static inline T op(T x, T y) noexcept { return x & y; } };
|
||||
@ -931,6 +1034,7 @@ struct Add { template<typename T> static inline T op(T x, T y) noexcept { ret
|
||||
struct Sub { template<typename T> static inline T op(T x, T y) noexcept { return x - y; } };
|
||||
struct Min { template<typename T> static inline T op(T x, T y) noexcept { return min<T>(x, y); } };
|
||||
struct Max { template<typename T> static inline T op(T x, T y) noexcept { return max<T>(x, y); } };
|
||||
//! \endcond
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Support - BitWordIterator]
|
||||
@ -1081,6 +1185,13 @@ static inline size_t bitVectorIndexOf(T* buf, size_t start, bool value) noexcept
|
||||
template<typename T>
|
||||
class BitVectorIterator {
|
||||
public:
|
||||
const T* _ptr;
|
||||
size_t _idx;
|
||||
size_t _end;
|
||||
T _current;
|
||||
|
||||
ASMJIT_INLINE BitVectorIterator(const BitVectorIterator& other) noexcept = default;
|
||||
|
||||
ASMJIT_INLINE BitVectorIterator(const T* data, size_t numBitWords, size_t start = 0) noexcept {
|
||||
init(data, numBitWords, start);
|
||||
}
|
||||
@ -1126,11 +1237,6 @@ public:
|
||||
ASMJIT_ASSERT(_current != T(0));
|
||||
return _idx + ctz(_current);
|
||||
}
|
||||
|
||||
const T* _ptr;
|
||||
size_t _idx;
|
||||
size_t _end;
|
||||
T _current;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@ -1142,6 +1248,12 @@ class BitVectorOpIterator {
|
||||
public:
|
||||
static constexpr uint32_t kTSizeInBits = bitSizeOf<T>();
|
||||
|
||||
const T* _aPtr;
|
||||
const T* _bPtr;
|
||||
size_t _idx;
|
||||
size_t _end;
|
||||
T _current;
|
||||
|
||||
ASMJIT_INLINE BitVectorOpIterator(const T* aData, const T* bData, size_t numBitWords, size_t start = 0) noexcept {
|
||||
init(aData, bData, numBitWords, start);
|
||||
}
|
||||
@ -1184,12 +1296,6 @@ public:
|
||||
_current = bitWord;
|
||||
return n;
|
||||
}
|
||||
|
||||
const T* _aPtr;
|
||||
const T* _bPtr;
|
||||
size_t _idx;
|
||||
size_t _end;
|
||||
T _current;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
@ -1290,7 +1396,6 @@ namespace Internal {
|
||||
}
|
||||
//! \endcond
|
||||
|
||||
|
||||
//! Quick sort implementation.
|
||||
//!
|
||||
//! The main reason to provide a custom qsort implementation is that we needed
|
||||
|
3
3rdparty/asmjit/src/asmjit/core/target.cpp
vendored
3
3rdparty/asmjit/src/asmjit/core/target.cpp
vendored
@ -31,8 +31,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// ============================================================================
|
||||
|
||||
Target::Target() noexcept
|
||||
: _targetType(kTargetNone),
|
||||
_codeInfo() {}
|
||||
: _environment() {}
|
||||
Target::~Target() noexcept {}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
119
3rdparty/asmjit/src/asmjit/core/target.h
vendored
119
3rdparty/asmjit/src/asmjit/core/target.h
vendored
@ -36,20 +36,13 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::CodeInfo]
|
||||
// ============================================================================
|
||||
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
//! Basic information about a code (or target). It describes its architecture,
|
||||
//! code generation mode (or optimization level), and base address.
|
||||
class CodeInfo {
|
||||
class ASMJIT_DEPRECATED_STRUCT("Use Environment instead of CodeInfo") CodeInfo {
|
||||
public:
|
||||
//!< Architecture information.
|
||||
ArchInfo _archInfo;
|
||||
//! Natural stack alignment (ARCH+OS).
|
||||
uint8_t _stackAlignment;
|
||||
//! Default CDECL calling convention.
|
||||
uint8_t _cdeclCallConv;
|
||||
//! Default STDCALL calling convention.
|
||||
uint8_t _stdCallConv;
|
||||
//! Default FASTCALL calling convention.
|
||||
uint8_t _fastCallConv;
|
||||
//!< Environment information.
|
||||
Environment _environment;
|
||||
//! Base address.
|
||||
uint64_t _baseAddress;
|
||||
|
||||
@ -57,46 +50,35 @@ public:
|
||||
//! \{
|
||||
|
||||
inline CodeInfo() noexcept
|
||||
: _archInfo(),
|
||||
_stackAlignment(0),
|
||||
_cdeclCallConv(CallConv::kIdNone),
|
||||
_stdCallConv(CallConv::kIdNone),
|
||||
_fastCallConv(CallConv::kIdNone),
|
||||
: _environment(),
|
||||
_baseAddress(Globals::kNoBaseAddress) {}
|
||||
|
||||
inline explicit CodeInfo(uint32_t archId, uint32_t archMode = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept
|
||||
: _archInfo(archId, archMode),
|
||||
_stackAlignment(0),
|
||||
_cdeclCallConv(CallConv::kIdNone),
|
||||
_stdCallConv(CallConv::kIdNone),
|
||||
_fastCallConv(CallConv::kIdNone),
|
||||
inline explicit CodeInfo(uint32_t arch, uint32_t subArch = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept
|
||||
: _environment(arch, subArch),
|
||||
_baseAddress(baseAddress) {}
|
||||
|
||||
inline explicit CodeInfo(const Environment& environment, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept
|
||||
: _environment(environment),
|
||||
_baseAddress(baseAddress) {}
|
||||
|
||||
|
||||
inline CodeInfo(const CodeInfo& other) noexcept { init(other); }
|
||||
|
||||
inline bool isInitialized() const noexcept {
|
||||
return _archInfo.archId() != ArchInfo::kIdNone;
|
||||
return _environment.arch() != Environment::kArchUnknown;
|
||||
}
|
||||
|
||||
inline void init(const CodeInfo& other) noexcept {
|
||||
*this = other;
|
||||
}
|
||||
|
||||
inline void init(uint32_t archId, uint32_t archMode = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept {
|
||||
_archInfo.init(archId, archMode);
|
||||
_stackAlignment = 0;
|
||||
_cdeclCallConv = CallConv::kIdNone;
|
||||
_stdCallConv = CallConv::kIdNone;
|
||||
_fastCallConv = CallConv::kIdNone;
|
||||
inline void init(uint32_t arch, uint32_t subArch = 0, uint64_t baseAddress = Globals::kNoBaseAddress) noexcept {
|
||||
_environment.init(arch, subArch);
|
||||
_baseAddress = baseAddress;
|
||||
}
|
||||
|
||||
inline void reset() noexcept {
|
||||
_archInfo.reset();
|
||||
_stackAlignment = 0;
|
||||
_cdeclCallConv = CallConv::kIdNone;
|
||||
_stdCallConv = CallConv::kIdNone;
|
||||
_fastCallConv = CallConv::kIdNone;
|
||||
_environment.reset();
|
||||
_baseAddress = Globals::kNoBaseAddress;
|
||||
}
|
||||
|
||||
@ -115,39 +97,28 @@ public:
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
//! Returns the target architecture information, see `ArchInfo`.
|
||||
inline const ArchInfo& archInfo() const noexcept { return _archInfo; }
|
||||
//! Returns the target environment information, see \ref Environment.
|
||||
inline const Environment& environment() const noexcept { return _environment; }
|
||||
|
||||
//! Returns the target architecture id, see `ArchInfo::Id`.
|
||||
inline uint32_t archId() const noexcept { return _archInfo.archId(); }
|
||||
//! Returns the target architecture sub-type, see `ArchInfo::SubId`.
|
||||
inline uint32_t archSubId() const noexcept { return _archInfo.archSubId(); }
|
||||
//! Returns the target architecture, see \ref Environment::Arch.
|
||||
inline uint32_t arch() const noexcept { return _environment.arch(); }
|
||||
//! Returns the target sub-architecture, see \ref Environment::SubArch.
|
||||
inline uint32_t subArch() const noexcept { return _environment.subArch(); }
|
||||
//! Returns the native size of the target's architecture GP register.
|
||||
inline uint32_t gpSize() const noexcept { return _archInfo.gpSize(); }
|
||||
//! Returns the number of GP registers of the target's architecture.
|
||||
inline uint32_t gpCount() const noexcept { return _archInfo.gpCount(); }
|
||||
|
||||
//! Returns a natural stack alignment that must be honored (or 0 if not known).
|
||||
inline uint32_t stackAlignment() const noexcept { return _stackAlignment; }
|
||||
//! Sets a natural stack alignment that must be honored.
|
||||
inline void setStackAlignment(uint32_t sa) noexcept { _stackAlignment = uint8_t(sa); }
|
||||
|
||||
inline uint32_t cdeclCallConv() const noexcept { return _cdeclCallConv; }
|
||||
inline void setCdeclCallConv(uint32_t cc) noexcept { _cdeclCallConv = uint8_t(cc); }
|
||||
|
||||
inline uint32_t stdCallConv() const noexcept { return _stdCallConv; }
|
||||
inline void setStdCallConv(uint32_t cc) noexcept { _stdCallConv = uint8_t(cc); }
|
||||
|
||||
inline uint32_t fastCallConv() const noexcept { return _fastCallConv; }
|
||||
inline void setFastCallConv(uint32_t cc) noexcept { _fastCallConv = uint8_t(cc); }
|
||||
inline uint32_t gpSize() const noexcept { return _environment.registerSize(); }
|
||||
|
||||
//! Tests whether this CodeInfo has a base address set.
|
||||
inline bool hasBaseAddress() const noexcept { return _baseAddress != Globals::kNoBaseAddress; }
|
||||
//! Returns the base address or \ref Globals::kNoBaseAddress if it's not set.
|
||||
inline uint64_t baseAddress() const noexcept { return _baseAddress; }
|
||||
//! Sets base address to `p`.
|
||||
inline void setBaseAddress(uint64_t p) noexcept { _baseAddress = p; }
|
||||
//! Resets base address (implicitly sets it to \ref Globals::kNoBaseAddress).
|
||||
inline void resetBaseAddress() noexcept { _baseAddress = Globals::kNoBaseAddress; }
|
||||
|
||||
//! \}
|
||||
};
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Target]
|
||||
@ -159,19 +130,8 @@ public:
|
||||
ASMJIT_BASE_CLASS(Target)
|
||||
ASMJIT_NONCOPYABLE(Target)
|
||||
|
||||
//! Tartget type, see `TargetType`.
|
||||
uint8_t _targetType;
|
||||
//! Reserved for future use.
|
||||
uint8_t _reserved[7];
|
||||
//! Basic information about the Runtime's code.
|
||||
CodeInfo _codeInfo;
|
||||
|
||||
enum TargetType : uint32_t {
|
||||
//! Uninitialized target or unknown target type.
|
||||
kTargetNone = 0,
|
||||
//! JIT target type, see `JitRuntime`.
|
||||
kTargetJit = 1
|
||||
};
|
||||
//! Target environment information.
|
||||
Environment _environment;
|
||||
|
||||
//! \name Construction & Destruction
|
||||
//! \{
|
||||
@ -190,15 +150,20 @@ public:
|
||||
//!
|
||||
//! CodeInfo can be used to setup a CodeHolder in case you plan to generate a
|
||||
//! code compatible and executable by this Runtime.
|
||||
inline const CodeInfo& codeInfo() const noexcept { return _codeInfo; }
|
||||
inline const Environment& environment() const noexcept { return _environment; }
|
||||
|
||||
//! Returns the target architecture id, see `ArchInfo::Id`.
|
||||
inline uint32_t archId() const noexcept { return _codeInfo.archId(); }
|
||||
//! Returns the target architecture sub-id, see `ArchInfo::SubId`.
|
||||
inline uint32_t archSubId() const noexcept { return _codeInfo.archSubId(); }
|
||||
//! Returns the target architecture, see \ref Environment::Arch.
|
||||
inline uint32_t arch() const noexcept { return _environment.arch(); }
|
||||
//! Returns the target sub-architecture, see \ref Environment::SubArch.
|
||||
inline uint32_t subArch() const noexcept { return _environment.subArch(); }
|
||||
|
||||
//! Returns the target type, see `TargetType`.
|
||||
inline uint32_t targetType() const noexcept { return _targetType; }
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("Use environment() instead")
|
||||
inline CodeInfo codeInfo() const noexcept { return CodeInfo(_environment); }
|
||||
|
||||
ASMJIT_DEPRECATED("Use environment().format() instead")
|
||||
inline uint32_t targetType() const noexcept { return _environment.format(); }
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
54
3rdparty/asmjit/src/asmjit/core/type.cpp
vendored
54
3rdparty/asmjit/src/asmjit/core/type.cpp
vendored
@ -31,14 +31,62 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::Type]
|
||||
// ============================================================================
|
||||
|
||||
const Type::TypeData Type::_typeData = {
|
||||
#define VALUE(X) Type::BaseOfTypeId<X>::kTypeId
|
||||
namespace Type {
|
||||
|
||||
template<uint32_t TYPE_ID>
|
||||
struct BaseOfTypeId {
|
||||
static constexpr uint32_t kTypeId =
|
||||
isBase (TYPE_ID) ? TYPE_ID :
|
||||
isMask8 (TYPE_ID) ? kIdU8 :
|
||||
isMask16(TYPE_ID) ? kIdU16 :
|
||||
isMask32(TYPE_ID) ? kIdU32 :
|
||||
isMask64(TYPE_ID) ? kIdU64 :
|
||||
isMmx32 (TYPE_ID) ? kIdI32 :
|
||||
isMmx64 (TYPE_ID) ? kIdI64 :
|
||||
isVec32 (TYPE_ID) ? TYPE_ID + kIdI8 - _kIdVec32Start :
|
||||
isVec64 (TYPE_ID) ? TYPE_ID + kIdI8 - _kIdVec64Start :
|
||||
isVec128(TYPE_ID) ? TYPE_ID + kIdI8 - _kIdVec128Start :
|
||||
isVec256(TYPE_ID) ? TYPE_ID + kIdI8 - _kIdVec256Start :
|
||||
isVec512(TYPE_ID) ? TYPE_ID + kIdI8 - _kIdVec512Start : 0;
|
||||
};
|
||||
|
||||
template<uint32_t TYPE_ID>
|
||||
struct SizeOfTypeId {
|
||||
static constexpr uint32_t kTypeSize =
|
||||
isInt8 (TYPE_ID) ? 1 :
|
||||
isUInt8 (TYPE_ID) ? 1 :
|
||||
isInt16 (TYPE_ID) ? 2 :
|
||||
isUInt16 (TYPE_ID) ? 2 :
|
||||
isInt32 (TYPE_ID) ? 4 :
|
||||
isUInt32 (TYPE_ID) ? 4 :
|
||||
isInt64 (TYPE_ID) ? 8 :
|
||||
isUInt64 (TYPE_ID) ? 8 :
|
||||
isFloat32(TYPE_ID) ? 4 :
|
||||
isFloat64(TYPE_ID) ? 8 :
|
||||
isFloat80(TYPE_ID) ? 10 :
|
||||
isMask8 (TYPE_ID) ? 1 :
|
||||
isMask16 (TYPE_ID) ? 2 :
|
||||
isMask32 (TYPE_ID) ? 4 :
|
||||
isMask64 (TYPE_ID) ? 8 :
|
||||
isMmx32 (TYPE_ID) ? 4 :
|
||||
isMmx64 (TYPE_ID) ? 8 :
|
||||
isVec32 (TYPE_ID) ? 4 :
|
||||
isVec64 (TYPE_ID) ? 8 :
|
||||
isVec128 (TYPE_ID) ? 16 :
|
||||
isVec256 (TYPE_ID) ? 32 :
|
||||
isVec512 (TYPE_ID) ? 64 : 0;
|
||||
};
|
||||
|
||||
const TypeData _typeData = {
|
||||
#define VALUE(X) BaseOfTypeId<X>::kTypeId
|
||||
{ ASMJIT_LOOKUP_TABLE_256(VALUE, 0) },
|
||||
#undef VALUE
|
||||
|
||||
#define VALUE(X) Type::SizeOfTypeId<X>::kTypeSize
|
||||
#define VALUE(X) SizeOfTypeId<X>::kTypeSize
|
||||
{ ASMJIT_LOOKUP_TABLE_256(VALUE, 0) }
|
||||
#undef VALUE
|
||||
};
|
||||
|
||||
} // {Type}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
215
3rdparty/asmjit/src/asmjit/core/type.h
vendored
215
3rdparty/asmjit/src/asmjit/core/type.h
vendored
@ -35,7 +35,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::Type]
|
||||
// ============================================================================
|
||||
|
||||
//! Provides minimum type-system that is used by \ref asmjit_func and \ref asmjit_compiler.
|
||||
//! Provides a minimalist type-system that is used by Asmjit library.
|
||||
namespace Type {
|
||||
|
||||
//! TypeId.
|
||||
@ -46,7 +46,7 @@ namespace Type {
|
||||
//! width used) and it's also used by APIs that allow to describe and work with
|
||||
//! function signatures.
|
||||
enum Id : uint32_t {
|
||||
kIdVoid = 0,
|
||||
kIdVoid = 0, //!< Void type.
|
||||
|
||||
_kIdBaseStart = 32,
|
||||
_kIdBaseEnd = 44,
|
||||
@ -54,38 +54,38 @@ enum Id : uint32_t {
|
||||
_kIdIntStart = 32,
|
||||
_kIdIntEnd = 41,
|
||||
|
||||
kIdIntPtr = 32,
|
||||
kIdUIntPtr = 33,
|
||||
kIdIntPtr = 32, //!< Abstract signed integer type that has a native size.
|
||||
kIdUIntPtr = 33, //!< Abstract unsigned integer type that has a native size.
|
||||
|
||||
kIdI8 = 34,
|
||||
kIdU8 = 35,
|
||||
kIdI16 = 36,
|
||||
kIdU16 = 37,
|
||||
kIdI32 = 38,
|
||||
kIdU32 = 39,
|
||||
kIdI64 = 40,
|
||||
kIdU64 = 41,
|
||||
kIdI8 = 34, //!< 8-bit signed integer type.
|
||||
kIdU8 = 35, //!< 8-bit unsigned integer type.
|
||||
kIdI16 = 36, //!< 16-bit signed integer type.
|
||||
kIdU16 = 37, //!< 16-bit unsigned integer type.
|
||||
kIdI32 = 38, //!< 32-bit signed integer type.
|
||||
kIdU32 = 39, //!< 32-bit unsigned integer type.
|
||||
kIdI64 = 40, //!< 64-bit signed integer type.
|
||||
kIdU64 = 41, //!< 64-bit unsigned integer type.
|
||||
|
||||
_kIdFloatStart = 42,
|
||||
_kIdFloatEnd = 44,
|
||||
|
||||
kIdF32 = 42,
|
||||
kIdF64 = 43,
|
||||
kIdF80 = 44,
|
||||
kIdF32 = 42, //!< 32-bit floating point type.
|
||||
kIdF64 = 43, //!< 64-bit floating point type.
|
||||
kIdF80 = 44, //!< 80-bit floating point type.
|
||||
|
||||
_kIdMaskStart = 45,
|
||||
_kIdMaskEnd = 48,
|
||||
|
||||
kIdMask8 = 45,
|
||||
kIdMask16 = 46,
|
||||
kIdMask32 = 47,
|
||||
kIdMask64 = 48,
|
||||
kIdMask8 = 45, //!< 8-bit opmask register (K).
|
||||
kIdMask16 = 46, //!< 16-bit opmask register (K).
|
||||
kIdMask32 = 47, //!< 32-bit opmask register (K).
|
||||
kIdMask64 = 48, //!< 64-bit opmask register (K).
|
||||
|
||||
_kIdMmxStart = 49,
|
||||
_kIdMmxEnd = 50,
|
||||
|
||||
kIdMmx32 = 49,
|
||||
kIdMmx64 = 50,
|
||||
kIdMmx32 = 49, //!< 64-bit MMX register only used for 32 bits.
|
||||
kIdMmx64 = 50, //!< 64-bit MMX register.
|
||||
|
||||
_kIdVec32Start = 51,
|
||||
_kIdVec32End = 60,
|
||||
@ -206,72 +206,74 @@ static constexpr bool isVec128(uint32_t typeId) noexcept { return typeId >= _kId
|
||||
static constexpr bool isVec256(uint32_t typeId) noexcept { return typeId >= _kIdVec256Start && typeId <= _kIdVec256End; }
|
||||
static constexpr bool isVec512(uint32_t typeId) noexcept { return typeId >= _kIdVec512Start && typeId <= _kIdVec512End; }
|
||||
|
||||
//! IdOfT<> template allows to get a TypeId of a C++ `T` type.
|
||||
template<typename T> struct IdOfT { /* Fail if not specialized. */ };
|
||||
|
||||
//! \cond
|
||||
template<typename T> struct IdOfT<T*> {
|
||||
enum : uint32_t { kTypeId = kIdUIntPtr };
|
||||
enum TypeCategory : uint32_t {
|
||||
kTypeCategoryUnknown = 0,
|
||||
kTypeCategoryEnum = 1,
|
||||
kTypeCategoryIntegral = 2,
|
||||
kTypeCategoryFloatingPoint = 3,
|
||||
kTypeCategoryFunction = 4
|
||||
};
|
||||
|
||||
template<typename T> struct IdOfT<T&> {
|
||||
enum : uint32_t { kTypeId = kIdUIntPtr };
|
||||
template<typename T, uint32_t Category>
|
||||
struct IdOfT_ByCategory {}; // Fails if not specialized.
|
||||
|
||||
template<typename T>
|
||||
struct IdOfT_ByCategory<T, kTypeCategoryIntegral> {
|
||||
enum : uint32_t {
|
||||
kTypeId = (sizeof(T) == 1 && std::is_signed<T>::value) ? kIdI8 :
|
||||
(sizeof(T) == 1 && !std::is_signed<T>::value) ? kIdU8 :
|
||||
(sizeof(T) == 2 && std::is_signed<T>::value) ? kIdI16 :
|
||||
(sizeof(T) == 2 && !std::is_signed<T>::value) ? kIdU16 :
|
||||
(sizeof(T) == 4 && std::is_signed<T>::value) ? kIdI32 :
|
||||
(sizeof(T) == 4 && !std::is_signed<T>::value) ? kIdU32 :
|
||||
(sizeof(T) == 8 && std::is_signed<T>::value) ? kIdI64 :
|
||||
(sizeof(T) == 8 && !std::is_signed<T>::value) ? kIdU64 : kIdVoid
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IdOfIntT {
|
||||
static constexpr uint32_t kTypeId =
|
||||
sizeof(T) == 1 ? (std::is_signed<T>::value ? kIdI8 : kIdU8 ) :
|
||||
sizeof(T) == 2 ? (std::is_signed<T>::value ? kIdI16 : kIdU16) :
|
||||
sizeof(T) == 4 ? (std::is_signed<T>::value ? kIdI32 : kIdU32) :
|
||||
sizeof(T) == 8 ? (std::is_signed<T>::value ? kIdI64 : kIdU64) : kIdVoid;
|
||||
struct IdOfT_ByCategory<T, kTypeCategoryFloatingPoint> {
|
||||
enum : uint32_t {
|
||||
kTypeId = (sizeof(T) == 4 ) ? kIdF32 :
|
||||
(sizeof(T) == 8 ) ? kIdF64 :
|
||||
(sizeof(T) >= 10) ? kIdF80 : kIdVoid
|
||||
};
|
||||
};
|
||||
|
||||
template<uint32_t TYPE_ID>
|
||||
struct BaseOfTypeId {
|
||||
static constexpr uint32_t kTypeId =
|
||||
isBase (TYPE_ID) ? TYPE_ID :
|
||||
isMask8 (TYPE_ID) ? kIdU8 :
|
||||
isMask16(TYPE_ID) ? kIdU16 :
|
||||
isMask32(TYPE_ID) ? kIdU32 :
|
||||
isMask64(TYPE_ID) ? kIdU64 :
|
||||
isMmx32 (TYPE_ID) ? kIdI32 :
|
||||
isMmx64 (TYPE_ID) ? kIdI64 :
|
||||
isVec32 (TYPE_ID) ? TYPE_ID + kIdI8 - _kIdVec32Start :
|
||||
isVec64 (TYPE_ID) ? TYPE_ID + kIdI8 - _kIdVec64Start :
|
||||
isVec128(TYPE_ID) ? TYPE_ID + kIdI8 - _kIdVec128Start :
|
||||
isVec256(TYPE_ID) ? TYPE_ID + kIdI8 - _kIdVec256Start :
|
||||
isVec512(TYPE_ID) ? TYPE_ID + kIdI8 - _kIdVec512Start : 0;
|
||||
};
|
||||
template<typename T>
|
||||
struct IdOfT_ByCategory<T, kTypeCategoryEnum>
|
||||
: public IdOfT_ByCategory<typename std::underlying_type<T>::type, kTypeCategoryIntegral> {};
|
||||
|
||||
template<uint32_t TYPE_ID>
|
||||
struct SizeOfTypeId {
|
||||
static constexpr uint32_t kTypeSize =
|
||||
isInt8 (TYPE_ID) ? 1 :
|
||||
isUInt8 (TYPE_ID) ? 1 :
|
||||
isInt16 (TYPE_ID) ? 2 :
|
||||
isUInt16 (TYPE_ID) ? 2 :
|
||||
isInt32 (TYPE_ID) ? 4 :
|
||||
isUInt32 (TYPE_ID) ? 4 :
|
||||
isInt64 (TYPE_ID) ? 8 :
|
||||
isUInt64 (TYPE_ID) ? 8 :
|
||||
isFloat32(TYPE_ID) ? 4 :
|
||||
isFloat64(TYPE_ID) ? 8 :
|
||||
isFloat80(TYPE_ID) ? 10 :
|
||||
isMask8 (TYPE_ID) ? 1 :
|
||||
isMask16 (TYPE_ID) ? 2 :
|
||||
isMask32 (TYPE_ID) ? 4 :
|
||||
isMask64 (TYPE_ID) ? 8 :
|
||||
isMmx32 (TYPE_ID) ? 4 :
|
||||
isMmx64 (TYPE_ID) ? 8 :
|
||||
isVec32 (TYPE_ID) ? 4 :
|
||||
isVec64 (TYPE_ID) ? 8 :
|
||||
isVec128 (TYPE_ID) ? 16 :
|
||||
isVec256 (TYPE_ID) ? 32 :
|
||||
isVec512 (TYPE_ID) ? 64 : 0;
|
||||
template<typename T>
|
||||
struct IdOfT_ByCategory<T, kTypeCategoryFunction> {
|
||||
enum: uint32_t { kTypeId = kIdUIntPtr };
|
||||
};
|
||||
//! \endcond
|
||||
|
||||
//! IdOfT<> template allows to get a TypeId from a C++ type `T`.
|
||||
template<typename T>
|
||||
struct IdOfT
|
||||
#ifdef _DOXYGEN
|
||||
//! TypeId of C++ type `T`.
|
||||
static constexpr uint32_t kTypeId = _TypeIdDeducedAtCompileTime_;
|
||||
#else
|
||||
: public IdOfT_ByCategory<T,
|
||||
std::is_enum<T>::value ? kTypeCategoryEnum :
|
||||
std::is_integral<T>::value ? kTypeCategoryIntegral :
|
||||
std::is_floating_point<T>::value ? kTypeCategoryFloatingPoint :
|
||||
std::is_function<T>::value ? kTypeCategoryFunction : kTypeCategoryUnknown>
|
||||
#endif
|
||||
{};
|
||||
|
||||
//! \cond
|
||||
template<typename T>
|
||||
struct IdOfT<T*> { enum : uint32_t { kTypeId = kIdUIntPtr }; };
|
||||
|
||||
template<typename T>
|
||||
struct IdOfT<T&> { enum : uint32_t { kTypeId = kIdUIntPtr }; };
|
||||
//! \endcond
|
||||
|
||||
static inline uint32_t baseOf(uint32_t typeId) noexcept {
|
||||
ASMJIT_ASSERT(typeId <= kIdMax);
|
||||
return _typeData.baseOf[typeId];
|
||||
@ -283,14 +285,14 @@ static inline uint32_t sizeOf(uint32_t typeId) noexcept {
|
||||
}
|
||||
|
||||
//! Returns offset needed to convert a `kIntPtr` and `kUIntPtr` TypeId
|
||||
//! into a type that matches `gpSize` (general-purpose register size).
|
||||
//! into a type that matches `registerSize` (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 = Type::deabstractDeltaOfSize(gpSize);
|
||||
//! uint32_t registerSize = '4' or '8';
|
||||
//! uint32_t deabstractDelta = Type::deabstractDeltaOfSize(registerSize);
|
||||
//!
|
||||
//! uint32_t typeId = 'some type-id';
|
||||
//!
|
||||
@ -300,8 +302,8 @@ static inline uint32_t sizeOf(uint32_t typeId) noexcept {
|
||||
//! // The same, but by using Type::deabstract() function.
|
||||
//! typeId = Type::deabstract(typeId, deabstractDelta);
|
||||
//! ```
|
||||
static constexpr uint32_t deabstractDeltaOfSize(uint32_t gpSize) noexcept {
|
||||
return gpSize >= 8 ? kIdI64 - kIdIntPtr : kIdI32 - kIdIntPtr;
|
||||
static constexpr uint32_t deabstractDeltaOfSize(uint32_t registerSize) noexcept {
|
||||
return registerSize >= 8 ? kIdI64 - kIdIntPtr : kIdI32 - kIdIntPtr;
|
||||
}
|
||||
|
||||
static constexpr uint32_t deabstract(uint32_t typeId, uint32_t deabstractDelta) noexcept {
|
||||
@ -350,45 +352,20 @@ namespace Type { \
|
||||
}; \
|
||||
}
|
||||
|
||||
ASMJIT_DEFINE_TYPE_ID(bool , IdOfIntT<bool >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(char , IdOfIntT<char >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(signed char , IdOfIntT<signed char >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(unsigned char , IdOfIntT<unsigned char >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(short , IdOfIntT<short >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(unsigned short , IdOfIntT<unsigned short >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(int , IdOfIntT<int >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(unsigned int , IdOfIntT<unsigned int >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(long , IdOfIntT<long >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(unsigned long , IdOfIntT<unsigned long >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(long long , IdOfIntT<long long >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(unsigned long long, IdOfIntT<unsigned long long>::kTypeId);
|
||||
|
||||
#if ASMJIT_CXX_HAS_NATIVE_WCHAR_T
|
||||
ASMJIT_DEFINE_TYPE_ID(wchar_t , IdOfIntT<wchar_t >::kTypeId);
|
||||
#endif
|
||||
|
||||
#if ASMJIT_CXX_HAS_UNICODE_LITERALS
|
||||
ASMJIT_DEFINE_TYPE_ID(char16_t , IdOfIntT<char16_t >::kTypeId);
|
||||
ASMJIT_DEFINE_TYPE_ID(char32_t , IdOfIntT<char32_t >::kTypeId);
|
||||
#endif
|
||||
|
||||
ASMJIT_DEFINE_TYPE_ID(void , kIdVoid);
|
||||
ASMJIT_DEFINE_TYPE_ID(float , kIdF32);
|
||||
ASMJIT_DEFINE_TYPE_ID(double , kIdF64);
|
||||
|
||||
ASMJIT_DEFINE_TYPE_ID(Bool , kIdU8);
|
||||
ASMJIT_DEFINE_TYPE_ID(I8 , kIdI8);
|
||||
ASMJIT_DEFINE_TYPE_ID(U8 , kIdU8);
|
||||
ASMJIT_DEFINE_TYPE_ID(I16 , kIdI16);
|
||||
ASMJIT_DEFINE_TYPE_ID(U16 , kIdU16);
|
||||
ASMJIT_DEFINE_TYPE_ID(I32 , kIdI32);
|
||||
ASMJIT_DEFINE_TYPE_ID(U32 , kIdU32);
|
||||
ASMJIT_DEFINE_TYPE_ID(I64 , kIdI64);
|
||||
ASMJIT_DEFINE_TYPE_ID(U64 , kIdU64);
|
||||
ASMJIT_DEFINE_TYPE_ID(IPtr , kIdIntPtr);
|
||||
ASMJIT_DEFINE_TYPE_ID(UPtr , kIdUIntPtr);
|
||||
ASMJIT_DEFINE_TYPE_ID(F32 , kIdF32);
|
||||
ASMJIT_DEFINE_TYPE_ID(F64 , kIdF64);
|
||||
ASMJIT_DEFINE_TYPE_ID(void, kIdVoid);
|
||||
ASMJIT_DEFINE_TYPE_ID(Bool, kIdU8);
|
||||
ASMJIT_DEFINE_TYPE_ID(I8 , kIdI8);
|
||||
ASMJIT_DEFINE_TYPE_ID(U8 , kIdU8);
|
||||
ASMJIT_DEFINE_TYPE_ID(I16 , kIdI16);
|
||||
ASMJIT_DEFINE_TYPE_ID(U16 , kIdU16);
|
||||
ASMJIT_DEFINE_TYPE_ID(I32 , kIdI32);
|
||||
ASMJIT_DEFINE_TYPE_ID(U32 , kIdU32);
|
||||
ASMJIT_DEFINE_TYPE_ID(I64 , kIdI64);
|
||||
ASMJIT_DEFINE_TYPE_ID(U64 , kIdU64);
|
||||
ASMJIT_DEFINE_TYPE_ID(IPtr, kIdIntPtr);
|
||||
ASMJIT_DEFINE_TYPE_ID(UPtr, kIdUIntPtr);
|
||||
ASMJIT_DEFINE_TYPE_ID(F32 , kIdF32);
|
||||
ASMJIT_DEFINE_TYPE_ID(F64 , kIdF64);
|
||||
//! \endcond
|
||||
|
||||
//! \}
|
||||
|
16
3rdparty/asmjit/src/asmjit/core/virtmem.cpp
vendored
16
3rdparty/asmjit/src/asmjit/core/virtmem.cpp
vendored
@ -45,7 +45,7 @@
|
||||
// Apple recently introduced MAP_JIT flag, which we want to use.
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#if defined(TARGET_OS_OSX)
|
||||
#if TARGET_OS_OSX
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
// Older SDK doesn't define `MAP_JIT`.
|
||||
@ -316,10 +316,10 @@ static ASMJIT_INLINE bool VirtMem_isHardened() noexcept {
|
||||
// MAP_JIT flag required to run unsigned JIT code is only supported by kernel
|
||||
// version 10.14+ (Mojave) and IOS.
|
||||
static ASMJIT_INLINE bool VirtMem_hasMapJitSupport() noexcept {
|
||||
#if defined(TARGET_OS_OSX)
|
||||
static volatile uint32_t globalVersion;
|
||||
#if TARGET_OS_OSX
|
||||
static volatile int globalVersion;
|
||||
|
||||
uint32_t ver = globalVersion;
|
||||
int ver = globalVersion;
|
||||
if (!ver) {
|
||||
struct utsname osname;
|
||||
uname(&osname);
|
||||
@ -333,19 +333,19 @@ static ASMJIT_INLINE bool VirtMem_hasMapJitSupport() noexcept {
|
||||
#endif
|
||||
}
|
||||
|
||||
static ASMJIT_INLINE uint32_t VirtMem_appleSpecificMMapFlags(uint32_t flags) {
|
||||
static ASMJIT_INLINE int VirtMem_appleSpecificMMapFlags(uint32_t flags) {
|
||||
// Always use MAP_JIT flag if user asked for it (could be used for testing
|
||||
// on non-hardened processes) and detect whether it must be used when the
|
||||
// process is actually hardened (in that case it doesn't make sense to rely
|
||||
// on user `flags`).
|
||||
bool useMapJit = ((flags & VirtMem::kMMapEnableMapJit) != 0) || VirtMem_isHardened();
|
||||
if (useMapJit)
|
||||
return VirtMem_hasMapJitSupport() ? MAP_JIT : 0u;
|
||||
return VirtMem_hasMapJitSupport() ? int(MAP_JIT) : 0;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static ASMJIT_INLINE uint32_t VirtMem_appleSpecificMMapFlags(uint32_t flags) {
|
||||
static ASMJIT_INLINE int VirtMem_appleSpecificMMapFlags(uint32_t flags) {
|
||||
DebugUtils::unused(flags);
|
||||
return 0;
|
||||
}
|
||||
@ -406,7 +406,7 @@ static Error VirtMem_openAnonymousMemory(int* fd, bool preferTmpOverDevShm) noex
|
||||
bits = ((bits >> 14) ^ (bits << 6)) + uint64_t(++internalCounter) * 10619863;
|
||||
|
||||
if (!ASMJIT_VM_SHM_DETECT || preferTmpOverDevShm) {
|
||||
uniqueName.assignString(VirtMem_getTmpDir());
|
||||
uniqueName.assign(VirtMem_getTmpDir());
|
||||
uniqueName.appendFormat(kShmFormat, (unsigned long long)bits);
|
||||
*fd = open(uniqueName.data(), O_RDWR | O_CREAT | O_EXCL, 0);
|
||||
if (ASMJIT_LIKELY(*fd >= 0)) {
|
||||
|
2
3rdparty/asmjit/src/asmjit/core/virtmem.h
vendored
2
3rdparty/asmjit/src/asmjit/core/virtmem.h
vendored
@ -31,7 +31,7 @@
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_jit
|
||||
//! \addtogroup asmjit_virtual_memory
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
|
6
3rdparty/asmjit/src/asmjit/core/zone.cpp
vendored
6
3rdparty/asmjit/src/asmjit/core/zone.cpp
vendored
@ -144,7 +144,7 @@ void* Zone::_alloc(size_t size, size_t alignment) noexcept {
|
||||
size_t newSize = Support::max(blockSize(), size);
|
||||
|
||||
// Prevent arithmetic overflow.
|
||||
if (ASMJIT_UNLIKELY(newSize > std::numeric_limits<size_t>::max() - kBlockSize - blockAlignmentOverhead))
|
||||
if (ASMJIT_UNLIKELY(newSize > SIZE_MAX - kBlockSize - blockAlignmentOverhead))
|
||||
return nullptr;
|
||||
|
||||
// Allocate new block - we add alignment overhead to `newSize`, which becomes the
|
||||
@ -200,7 +200,7 @@ void* Zone::dup(const void* data, size_t size, bool nullTerminate) noexcept {
|
||||
if (ASMJIT_UNLIKELY(!data || !size))
|
||||
return nullptr;
|
||||
|
||||
ASMJIT_ASSERT(size != std::numeric_limits<size_t>::max());
|
||||
ASMJIT_ASSERT(size != SIZE_MAX);
|
||||
uint8_t* m = allocT<uint8_t>(size + nullTerminate);
|
||||
if (ASMJIT_UNLIKELY(!m)) return nullptr;
|
||||
|
||||
@ -318,7 +318,7 @@ void* ZoneAllocator::_alloc(size_t size, size_t& allocatedSize) noexcept {
|
||||
size_t kBlockOverhead = sizeof(DynamicBlock) + sizeof(DynamicBlock*) + kBlockAlignment;
|
||||
|
||||
// Handle a possible overflow.
|
||||
if (ASMJIT_UNLIKELY(kBlockOverhead >= std::numeric_limits<size_t>::max() - size))
|
||||
if (ASMJIT_UNLIKELY(kBlockOverhead >= SIZE_MAX - size))
|
||||
return nullptr;
|
||||
|
||||
void* p = ::malloc(size + kBlockOverhead);
|
||||
|
7
3rdparty/asmjit/src/asmjit/core/zone.h
vendored
7
3rdparty/asmjit/src/asmjit/core/zone.h
vendored
@ -383,15 +383,22 @@ public:
|
||||
// [b2d::ZoneTmp]
|
||||
// ============================================================================
|
||||
|
||||
//! \ref Zone with `N` bytes of a static storage, used for the initial block.
|
||||
//!
|
||||
//! Temporary zones are used in cases where it's known that some memory will be
|
||||
//! required, but in many cases it won't exceed N bytes, so the whole operation
|
||||
//! can be performed without a dynamic memory allocation.
|
||||
template<size_t N>
|
||||
class ZoneTmp : public Zone {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(ZoneTmp<N>)
|
||||
|
||||
//! Temporary storage, embedded after \ref Zone.
|
||||
struct Storage {
|
||||
char data[N];
|
||||
} _storage;
|
||||
|
||||
//! Creates a temporary zone. Dynamic block size is specified by `blockSize`.
|
||||
ASMJIT_INLINE explicit ZoneTmp(size_t blockSize, size_t blockAlignment = 1) noexcept
|
||||
: Zone(blockSize, blockAlignment, Support::Temporary(_storage.data, N)) {}
|
||||
};
|
||||
|
3
3rdparty/asmjit/src/asmjit/core/zonehash.h
vendored
3
3rdparty/asmjit/src/asmjit/core/zonehash.h
vendored
@ -35,7 +35,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::ZoneHashNode]
|
||||
// ============================================================================
|
||||
|
||||
//! Node used by `ZoneHash<>` template.
|
||||
//! Node used by \ref ZoneHash template.
|
||||
//!
|
||||
//! You must provide function `bool eq(const Key& key)` in order to make
|
||||
//! `ZoneHash::get()` working.
|
||||
@ -60,6 +60,7 @@ public:
|
||||
// [asmjit::ZoneHashBase]
|
||||
// ============================================================================
|
||||
|
||||
//! Base class used by \ref ZoneHash template
|
||||
class ZoneHashBase {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(ZoneHashBase)
|
||||
|
2
3rdparty/asmjit/src/asmjit/core/zonelist.h
vendored
2
3rdparty/asmjit/src/asmjit/core/zonelist.h
vendored
@ -35,6 +35,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::ZoneListNode]
|
||||
// ============================================================================
|
||||
|
||||
//! Node used by \ref ZoneList template.
|
||||
template<typename NodeT>
|
||||
class ZoneListNode {
|
||||
public:
|
||||
@ -69,6 +70,7 @@ public:
|
||||
// [asmjit::ZoneList<T>]
|
||||
// ============================================================================
|
||||
|
||||
//! Zone allocated list container that uses nodes of `NodeT` type.
|
||||
template <typename NodeT>
|
||||
class ZoneList {
|
||||
public:
|
||||
|
2
3rdparty/asmjit/src/asmjit/core/zonestack.h
vendored
2
3rdparty/asmjit/src/asmjit/core/zonestack.h
vendored
@ -35,7 +35,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::ZoneStackBase]
|
||||
// ============================================================================
|
||||
|
||||
//! Base class used by `ZoneStack<T>`.
|
||||
//! Base class used by \ref ZoneStack.
|
||||
class ZoneStackBase {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(ZoneStackBase)
|
||||
|
24
3rdparty/asmjit/src/asmjit/core/zonestring.h
vendored
24
3rdparty/asmjit/src/asmjit/core/zonestring.h
vendored
@ -36,6 +36,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::ZoneStringBase]
|
||||
// ============================================================================
|
||||
|
||||
//! A helper class used by \ref ZoneString implementation.
|
||||
struct ZoneStringBase {
|
||||
union {
|
||||
struct {
|
||||
@ -77,11 +78,12 @@ struct ZoneStringBase {
|
||||
// [asmjit::ZoneString<N>]
|
||||
// ============================================================================
|
||||
|
||||
//! 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 size exceeds the limit. The `N` represents the size of the whole
|
||||
//! `ZoneString` structure, based on that size the maximum size of the internal
|
||||
//! buffer is determined.
|
||||
//! A string template that can be zone allocated.
|
||||
//!
|
||||
//! Helps with creating strings that can be either statically allocated if they
|
||||
//! are small, or externally allocated in case their size exceeds the limit.
|
||||
//! The `N` represents the size of the whole `ZoneString` structure, based on
|
||||
//! that size the maximum size of the internal buffer is determined.
|
||||
template<size_t N>
|
||||
class ZoneString {
|
||||
public:
|
||||
@ -105,12 +107,22 @@ public:
|
||||
//! \name Accessors
|
||||
//! \{
|
||||
|
||||
inline const char* data() const noexcept { return _base._size <= kMaxEmbeddedSize ? _base._embedded : _base._external; }
|
||||
//! Tests whether the string is empty.
|
||||
inline bool empty() const noexcept { return _base._size == 0; }
|
||||
|
||||
//! Returns the string data.
|
||||
inline const char* data() const noexcept { return _base._size <= kMaxEmbeddedSize ? _base._embedded : _base._external; }
|
||||
//! Returns the string size.
|
||||
inline uint32_t size() const noexcept { return _base._size; }
|
||||
|
||||
//! Tests whether the string is embedded (e.g. no dynamically allocated).
|
||||
inline bool isEmbedded() const noexcept { return _base._size <= kMaxEmbeddedSize; }
|
||||
|
||||
//! Copies a new `data` of the given `size` to the string.
|
||||
//!
|
||||
//! If the `size` exceeds the internal buffer the given `zone` will be
|
||||
//! used to duplicate the data, otherwise the internal buffer will be
|
||||
//! used as a storage.
|
||||
inline Error setData(Zone* zone, const char* data, size_t size) noexcept {
|
||||
return _base.setData(zone, kMaxEmbeddedSize, data, size);
|
||||
}
|
||||
|
2
3rdparty/asmjit/src/asmjit/core/zonetree.h
vendored
2
3rdparty/asmjit/src/asmjit/core/zonetree.h
vendored
@ -99,7 +99,7 @@ public:
|
||||
//! \endcond
|
||||
};
|
||||
|
||||
//! RB-Tree typed to `NodeT`.
|
||||
//! RB-Tree node casted to `NodeT`.
|
||||
template<typename NodeT>
|
||||
class ZoneTreeNodeT : public ZoneTreeNode {
|
||||
public:
|
||||
|
29
3rdparty/asmjit/src/asmjit/core/zonevector.h
vendored
29
3rdparty/asmjit/src/asmjit/core/zonevector.h
vendored
@ -36,9 +36,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// [asmjit::ZoneVectorBase]
|
||||
// ============================================================================
|
||||
|
||||
//! \cond INTERNAL
|
||||
|
||||
//! Base class implementing core `ZoneVector<>` functionality.
|
||||
//! Base class used by \ref ZoneVector template.
|
||||
class ZoneVectorBase {
|
||||
public:
|
||||
ASMJIT_NONCOPYABLE(ZoneVectorBase)
|
||||
@ -93,6 +91,7 @@ protected:
|
||||
}
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
public:
|
||||
//! \name Accessors
|
||||
@ -133,8 +132,6 @@ public:
|
||||
//! \}
|
||||
};
|
||||
|
||||
//! \endcond
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::ZoneVector<T>]
|
||||
// ============================================================================
|
||||
@ -180,7 +177,7 @@ public:
|
||||
inline const T* data() const noexcept { return static_cast<const T*>(_data); }
|
||||
|
||||
//! Returns item at the given index `i` (const).
|
||||
inline const T& at(uint32_t i) const noexcept {
|
||||
inline const T& at(size_t i) const noexcept {
|
||||
ASMJIT_ASSERT(i < _size);
|
||||
return data()[i];
|
||||
}
|
||||
@ -259,6 +256,7 @@ public:
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
//! Appends `other` vector at the end of this vector.
|
||||
inline Error concat(ZoneAllocator* allocator, const ZoneVector<T>& other) noexcept {
|
||||
uint32_t size = other._size;
|
||||
if (_capacity - _size < size)
|
||||
@ -338,6 +336,7 @@ public:
|
||||
::memmove(data, data + 1, size_t(size) * sizeof(T));
|
||||
}
|
||||
|
||||
//! Pops the last element from the vector and returns it.
|
||||
inline T pop() noexcept {
|
||||
ASMJIT_ASSERT(_size > 0);
|
||||
|
||||
@ -351,21 +350,33 @@ public:
|
||||
}
|
||||
|
||||
//! Returns item at index `i`.
|
||||
inline T& operator[](uint32_t i) noexcept {
|
||||
inline T& operator[](size_t i) noexcept {
|
||||
ASMJIT_ASSERT(i < _size);
|
||||
return data()[i];
|
||||
}
|
||||
|
||||
//! Returns item at index `i`.
|
||||
inline const T& operator[](uint32_t i) const noexcept {
|
||||
inline const T& operator[](size_t i) const noexcept {
|
||||
ASMJIT_ASSERT(i < _size);
|
||||
return data()[i];
|
||||
}
|
||||
|
||||
//! Returns a reference to the first element of the vector.
|
||||
//!
|
||||
//! \note The vector must have at least one element. Attempting to use
|
||||
//! `first()` on empty vector will trigger an assertion failure in debug
|
||||
//! builds.
|
||||
inline T& first() noexcept { return operator[](0); }
|
||||
//! \overload
|
||||
inline const T& first() const noexcept { return operator[](0); }
|
||||
|
||||
//! Returns a reference to the last element of the vector.
|
||||
//!
|
||||
//! \note The vector must have at least one element. Attempting to use
|
||||
//! `last()` on empty vector will trigger an assertion failure in debug
|
||||
//! builds.
|
||||
inline T& last() noexcept { return operator[](_size - 1); }
|
||||
//! \overload
|
||||
inline const T& last() const noexcept { return operator[](_size - 1); }
|
||||
|
||||
//! \}
|
||||
@ -408,6 +419,7 @@ public:
|
||||
// [asmjit::ZoneBitVector]
|
||||
// ============================================================================
|
||||
|
||||
//! Zone-allocated bit vector.
|
||||
class ZoneBitVector {
|
||||
public:
|
||||
typedef Support::BitWord BitWord;
|
||||
@ -689,7 +701,6 @@ public:
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
78
3rdparty/asmjit/src/asmjit/x86.h
vendored
78
3rdparty/asmjit/src/asmjit/x86.h
vendored
@ -24,9 +24,83 @@
|
||||
#ifndef ASMJIT_X86_H_INCLUDED
|
||||
#define ASMJIT_X86_H_INCLUDED
|
||||
|
||||
//! \defgroup asmjit_x86 X86
|
||||
//! \addtogroup asmjit_x86
|
||||
//!
|
||||
//! \brief X86/X64 Backend.
|
||||
//! ### Namespace
|
||||
//!
|
||||
//! - \ref x86 - x86 namespace provides support for X86/X64 code generation.
|
||||
//!
|
||||
//! ### Emitters
|
||||
//!
|
||||
//! - \ref x86::Assembler - X86/X64 assembler (must read, provides examples).
|
||||
//! - \ref x86::Builder - X86/X64 builder.
|
||||
//! - \ref x86::Compiler - X86/X64 compiler.
|
||||
//! - \ref x86::Emitter - X86/X64 emitter (abstract).
|
||||
//!
|
||||
//! ### Supported Instructions
|
||||
//!
|
||||
//! - Emitters:
|
||||
//! - \ref x86::EmitterExplicitT - Provides all instructions that use
|
||||
//! explicit operands, provides also utility functions. The member
|
||||
//! functions provided are part of all X86 emitters.
|
||||
//! - \ref x86::EmitterImplicitT - Provides all instructions that use
|
||||
//! implicit operands, these cannot be used with \ref x86::Compiler.
|
||||
//! - Instruction representation:
|
||||
//! - \ref x86::Inst::Id - instruction identifiers.
|
||||
//! - \ref x86::Inst::Options - instruction options.
|
||||
//!
|
||||
//! ### Register Operands
|
||||
//!
|
||||
//! - \ref x86::Reg - Base class for any X86 register.
|
||||
//! - \ref x86::Gp - General purpose register:
|
||||
//! - \ref x86::GpbLo - 8-bit low register.
|
||||
//! - \ref x86::GpbHi - 8-bit high register.
|
||||
//! - \ref x86::Gpw - 16-bit register.
|
||||
//! - \ref x86::Gpd - 32-bit register.
|
||||
//! - \ref x86::Gpq - 64-bit register (X64 only).
|
||||
//! - \ref x86::Vec - Vector (SIMD) register:
|
||||
//! - \ref x86::Xmm - 128-bit SIMD register (SSE+).
|
||||
//! - \ref x86::Ymm - 256-bit SIMD register (AVX+).
|
||||
//! - \ref x86::Zmm - 512-bit SIMD register (AVX512+).
|
||||
//! - \ref x86::Mm - 64-bit MMX register.
|
||||
//! - \ref x86::St - 80-bit FPU register.
|
||||
//! - \ref x86::KReg - opmask registers (AVX512+).
|
||||
//! - \ref x86::SReg - segment register.
|
||||
//! - \ref x86::CReg - control register.
|
||||
//! - \ref x86::DReg - debug register.
|
||||
//! - \ref x86::Bnd - bound register (discontinued).
|
||||
//! - \ref x86::Rip - relative instruction pointer.
|
||||
//!
|
||||
//! ### Memory Operands
|
||||
//!
|
||||
//! - \ref x86::Mem - X86/X64 memory operand that provides support for all
|
||||
//! X86 and X64 addressing features, including absolute addresses, index
|
||||
//! scales, and segment override prefixes.
|
||||
//!
|
||||
//! ### Other
|
||||
//!
|
||||
//! - \ref x86::Features - X86/X64 CPU features on top of \ref BaseFeatures.
|
||||
//!
|
||||
//! ### Status and Control Words
|
||||
//!
|
||||
//! - \ref asmjit::x86::FpuWord::Status - FPU status word.
|
||||
//! - \ref asmjit::x86::FpuWord::Control - FPU control word.
|
||||
//!
|
||||
//! ### Predicates
|
||||
//!
|
||||
//! - \ref x86::Predicate - namespace that provides X86/X64 predicates.
|
||||
//! - \ref x86::Predicate::Cmp - `CMP[PD|PS|SD|SS]` predicate (SSE+).
|
||||
//! - \ref x86::Predicate::PCmpStr - `[V]PCMP[I|E]STR[I|M]` predicate (SSE4.1+).
|
||||
//! - \ref x86::Predicate::Round - `ROUND[PD|PS|SD|SS]` predicate (SSE+).
|
||||
//! - \ref x86::Predicate::VCmp - `VCMP[PD|PS|SD|SS]` predicate (AVX+).
|
||||
//! - \ref x86::Predicate::VFixupImm - `VFIXUPIMM[PD|PS|SD|SS]` predicate (AVX512+).
|
||||
//! - \ref x86::Predicate::VFPClass - `VFPCLASS[PD|PS|SD|SS]` predicate (AVX512+).
|
||||
//! - \ref x86::Predicate::VGetMant - `VGETMANT[PD|PS|SD|SS]` predicate (AVX512+).
|
||||
//! - \ref x86::Predicate::VPCmp - `VPCMP[U][B|W|D|Q]` predicate (AVX512+).
|
||||
//! - \ref x86::Predicate::VPCom - `VPCOM[U][B|W|D|Q]` predicate (XOP).
|
||||
//! - \ref x86::Predicate::VRange - `VRANGE[PD|PS|SD|SS]` predicate (AVX512+).
|
||||
//! - \ref x86::Predicate::VReduce - `REDUCE[PD|PS|SD|SS]` predicate (AVX512+).
|
||||
//! - \ref x86::TLog - namespace that provides `VPTERNLOG[D|Q]` predicate / operations.
|
||||
|
||||
#include "./core.h"
|
||||
|
||||
|
137
3rdparty/asmjit/src/asmjit/x86/x86archdata.cpp
vendored
Normal file
137
3rdparty/asmjit/src/asmjit/x86/x86archdata.cpp
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "../core/api-build_p.h"
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
|
||||
#include "../core/support.h"
|
||||
#include "../core/type.h"
|
||||
#include "../x86/x86archdata_p.h"
|
||||
#include "../x86/x86operand.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::x86::ArchInternal]
|
||||
// ============================================================================
|
||||
|
||||
namespace ArchInternal {
|
||||
|
||||
Error typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfoOut) noexcept {
|
||||
// Passed RegType instead of TypeId?
|
||||
if (typeId <= BaseReg::kTypeMax)
|
||||
typeId = opData.archRegs.regTypeToTypeId[typeId];
|
||||
|
||||
if (ASMJIT_UNLIKELY(!Type::isValid(typeId)))
|
||||
return DebugUtils::errored(kErrorInvalidTypeId);
|
||||
|
||||
// First normalize architecture dependent types.
|
||||
if (Type::isAbstract(typeId)) {
|
||||
bool is32Bit = arch == Environment::kArchX86;
|
||||
if (typeId == Type::kIdIntPtr)
|
||||
typeId = is32Bit ? Type::kIdI32 : Type::kIdI64;
|
||||
else
|
||||
typeId = is32Bit ? Type::kIdU32 : Type::kIdU64;
|
||||
}
|
||||
|
||||
// Type size helps to construct all groups of registers.
|
||||
// TypeId is invalid if the size is zero.
|
||||
uint32_t size = Type::sizeOf(typeId);
|
||||
if (ASMJIT_UNLIKELY(!size))
|
||||
return DebugUtils::errored(kErrorInvalidTypeId);
|
||||
|
||||
if (ASMJIT_UNLIKELY(typeId == Type::kIdF80))
|
||||
return DebugUtils::errored(kErrorInvalidUseOfF80);
|
||||
|
||||
uint32_t regType = 0;
|
||||
|
||||
switch (typeId) {
|
||||
case Type::kIdI8:
|
||||
case Type::kIdU8:
|
||||
regType = Reg::kTypeGpbLo;
|
||||
break;
|
||||
|
||||
case Type::kIdI16:
|
||||
case Type::kIdU16:
|
||||
regType = Reg::kTypeGpw;
|
||||
break;
|
||||
|
||||
case Type::kIdI32:
|
||||
case Type::kIdU32:
|
||||
regType = Reg::kTypeGpd;
|
||||
break;
|
||||
|
||||
case Type::kIdI64:
|
||||
case Type::kIdU64:
|
||||
if (arch == Environment::kArchX86)
|
||||
return DebugUtils::errored(kErrorInvalidUseOfGpq);
|
||||
|
||||
regType = Reg::kTypeGpq;
|
||||
break;
|
||||
|
||||
// F32 and F64 are always promoted to use vector registers.
|
||||
case Type::kIdF32:
|
||||
typeId = Type::kIdF32x1;
|
||||
regType = Reg::kTypeXmm;
|
||||
break;
|
||||
|
||||
case Type::kIdF64:
|
||||
typeId = Type::kIdF64x1;
|
||||
regType = Reg::kTypeXmm;
|
||||
break;
|
||||
|
||||
// Mask registers {k}.
|
||||
case Type::kIdMask8:
|
||||
case Type::kIdMask16:
|
||||
case Type::kIdMask32:
|
||||
case Type::kIdMask64:
|
||||
regType = Reg::kTypeKReg;
|
||||
break;
|
||||
|
||||
// MMX registers.
|
||||
case Type::kIdMmx32:
|
||||
case Type::kIdMmx64:
|
||||
regType = Reg::kTypeMm;
|
||||
break;
|
||||
|
||||
// XMM|YMM|ZMM registers.
|
||||
default:
|
||||
if (size <= 16)
|
||||
regType = Reg::kTypeXmm;
|
||||
else if (size == 32)
|
||||
regType = Reg::kTypeYmm;
|
||||
else
|
||||
regType = Reg::kTypeZmm;
|
||||
break;
|
||||
}
|
||||
|
||||
*typeIdOut = typeId;
|
||||
regInfoOut->reset(opData.archRegs.regInfo[regType].signature());
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
} // {ArchInternal}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_BUILD_X86
|
51
3rdparty/asmjit/src/asmjit/x86/x86archdata_p.h
vendored
Normal file
51
3rdparty/asmjit/src/asmjit/x86/x86archdata_p.h
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#ifndef ASMJIT_X86_X86ARCHDATA_P_H_INCLUDED
|
||||
#define ASMJIT_X86_X86ARCHDATA_P_H_INCLUDED
|
||||
|
||||
#include "../core/arch.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_x86
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::x86::ArchInternal]
|
||||
// ============================================================================
|
||||
|
||||
//! X86-specific function API (calling conventions and other utilities).
|
||||
namespace ArchInternal {
|
||||
|
||||
Error typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfoOut) noexcept;
|
||||
|
||||
} // {ArchInternal}
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_X86_X86ARCHDATA_P_H_INCLUDED
|
267
3rdparty/asmjit/src/asmjit/x86/x86assembler.cpp
vendored
267
3rdparty/asmjit/src/asmjit/x86/x86assembler.cpp
vendored
@ -24,14 +24,17 @@
|
||||
#include "../core/api-build_p.h"
|
||||
#ifdef ASMJIT_BUILD_X86
|
||||
|
||||
#include "../core/assembler.h"
|
||||
#include "../core/codebufferwriter_p.h"
|
||||
#include "../core/cpuinfo.h"
|
||||
#include "../core/logging.h"
|
||||
#include "../core/emitterutils_p.h"
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/logger.h"
|
||||
#include "../core/misc_p.h"
|
||||
#include "../core/support.h"
|
||||
#include "../x86/x86assembler.h"
|
||||
#include "../x86/x86instdb_p.h"
|
||||
#include "../x86/x86logging_p.h"
|
||||
#include "../x86/x86formatter_p.h"
|
||||
#include "../x86/x86opcode_p.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
@ -508,7 +511,7 @@ static ASMJIT_INLINE uint32_t x86GetMovAbsAddrType(Assembler* self, X86BufferWri
|
||||
|
||||
if (addrType == BaseMem::kAddrTypeDefault && !(options & Inst::kOptionModMR)) {
|
||||
if (self->is64Bit()) {
|
||||
uint64_t baseAddress = self->codeInfo().baseAddress();
|
||||
uint64_t baseAddress = self->code()->baseAddress();
|
||||
if (baseAddress != Globals::kNoBaseAddress && !rmRel.hasSegment()) {
|
||||
uint32_t instructionSize = x86GetMovAbsInstSize64Bit(regSize, options, rmRel);
|
||||
uint64_t virtualOffset = uint64_t(writer.offsetFrom(self->_bufferData));
|
||||
@ -545,18 +548,18 @@ Assembler::~Assembler() noexcept {}
|
||||
// [asmjit::x86::Assembler - Emit (Low-Level)]
|
||||
// ============================================================================
|
||||
|
||||
ASMJIT_FAVOR_SPEED Error Assembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) {
|
||||
ASMJIT_FAVOR_SPEED Error Assembler::_emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt) {
|
||||
constexpr uint32_t kVSHR_W = Opcode::kW_Shift - 23;
|
||||
constexpr uint32_t kVSHR_PP = Opcode::kPP_Shift - 16;
|
||||
constexpr uint32_t kVSHR_PP_EW = Opcode::kPP_Shift - 16;
|
||||
|
||||
constexpr uint32_t kRequiresSpecialHandling =
|
||||
Inst::kOptionReserved | // Logging/Validation/Error.
|
||||
Inst::kOptionRep | // REP/REPE prefix.
|
||||
Inst::kOptionRepne | // REPNE prefix.
|
||||
Inst::kOptionLock | // LOCK prefix.
|
||||
Inst::kOptionXAcquire | // XACQUIRE prefix.
|
||||
Inst::kOptionXRelease ; // XRELEASE prefix.
|
||||
uint32_t(Inst::kOptionReserved) | // Logging/Validation/Error.
|
||||
uint32_t(Inst::kOptionRep ) | // REP/REPE prefix.
|
||||
uint32_t(Inst::kOptionRepne ) | // REPNE prefix.
|
||||
uint32_t(Inst::kOptionLock ) | // LOCK prefix.
|
||||
uint32_t(Inst::kOptionXAcquire) | // XACQUIRE prefix.
|
||||
uint32_t(Inst::kOptionXRelease) ; // XRELEASE prefix.
|
||||
|
||||
Error err;
|
||||
|
||||
@ -595,12 +598,12 @@ ASMJIT_FAVOR_SPEED Error Assembler::_emit(uint32_t instId, const Operand_& o0, c
|
||||
// instruction) are handled by the next branch.
|
||||
options = uint32_t(instId == 0);
|
||||
options |= uint32_t((size_t)(_bufferEnd - writer.cursor()) < 16);
|
||||
options |= uint32_t(instOptions() | globalInstOptions());
|
||||
options |= uint32_t(instOptions() | forcedInstOptions());
|
||||
|
||||
// Handle failure and rare cases first.
|
||||
if (ASMJIT_UNLIKELY(options & kRequiresSpecialHandling)) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return DebugUtils::errored(kErrorNotInitialized);
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
// Unknown instruction.
|
||||
if (ASMJIT_UNLIKELY(instId == 0))
|
||||
@ -613,25 +616,13 @@ ASMJIT_FAVOR_SPEED Error Assembler::_emit(uint32_t instId, const Operand_& o0, c
|
||||
|
||||
#ifndef ASMJIT_NO_VALIDATION
|
||||
// Strict validation.
|
||||
if (hasEmitterOption(kOptionStrictValidation)) {
|
||||
if (hasValidationOption(kValidationOptionAssembler)) {
|
||||
Operand_ opArray[Globals::kMaxOpCount];
|
||||
EmitterUtils::opArrayFromEmitArgs(opArray, o0, o1, o2, opExt);
|
||||
|
||||
opArray[0].copyFrom(o0);
|
||||
opArray[1].copyFrom(o1);
|
||||
opArray[2].copyFrom(o2);
|
||||
opArray[3].copyFrom(o3);
|
||||
|
||||
if (options & Inst::kOptionOp4Op5Used) {
|
||||
opArray[4].copyFrom(_op4);
|
||||
opArray[5].copyFrom(_op5);
|
||||
}
|
||||
else {
|
||||
opArray[4].reset();
|
||||
opArray[5].reset();
|
||||
}
|
||||
|
||||
err = InstAPI::validate(archId(), BaseInst(instId, options, _extraReg), opArray, Globals::kMaxOpCount);
|
||||
if (ASMJIT_UNLIKELY(err)) goto Failed;
|
||||
err = InstAPI::validate(arch(), BaseInst(instId, options, _extraReg), opArray, Globals::kMaxOpCount);
|
||||
if (ASMJIT_UNLIKELY(err))
|
||||
goto Failed;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -693,7 +684,7 @@ ASMJIT_FAVOR_SPEED Error Assembler::_emit(uint32_t instId, const Operand_& o0, c
|
||||
if (ASMJIT_UNLIKELY(isign3 != ENC_OPS1(Imm)))
|
||||
goto InvalidInstruction;
|
||||
|
||||
immValue = o0.as<Imm>().u8();
|
||||
immValue = o0.as<Imm>().valueAs<uint8_t>();
|
||||
immSize = 1;
|
||||
ASMJIT_FALLTHROUGH;
|
||||
|
||||
@ -738,14 +729,14 @@ ASMJIT_FAVOR_SPEED Error Assembler::_emit(uint32_t instId, const Operand_& o0, c
|
||||
case InstDB::kEncodingX86I_xAX:
|
||||
// Implicit form.
|
||||
if (isign3 == ENC_OPS1(Imm)) {
|
||||
immValue = o0.as<Imm>().u8();
|
||||
immValue = o0.as<Imm>().valueAs<uint8_t>();
|
||||
immSize = 1;
|
||||
goto EmitX86Op;
|
||||
}
|
||||
|
||||
// Explicit form.
|
||||
if (isign3 == ENC_OPS2(Reg, Imm) && o0.id() == Gp::kIdAx) {
|
||||
immValue = o1.as<Imm>().u8();
|
||||
immValue = o1.as<Imm>().valueAs<uint8_t>();
|
||||
immSize = 1;
|
||||
goto EmitX86Op;
|
||||
}
|
||||
@ -996,7 +987,7 @@ CaseX86M_GPB_MulDiv:
|
||||
uint32_t size = o0.size();
|
||||
|
||||
rbReg = o0.id();
|
||||
immValue = o1.as<Imm>().i64();
|
||||
immValue = o1.as<Imm>().value();
|
||||
|
||||
if (size == 1) {
|
||||
FIXUP_GPB(o0, rbReg);
|
||||
@ -1022,8 +1013,7 @@ CaseX86M_GPB_MulDiv:
|
||||
else
|
||||
goto InvalidImmediate;
|
||||
}
|
||||
else if (canTransformTo32Bit && hasEmitterOption(kOptionOptimizedForSize)) {
|
||||
// This is a code-size optimization.
|
||||
else if (canTransformTo32Bit && hasEncodingOption(kEncodingOptionOptimizeForSize)) {
|
||||
size = 4;
|
||||
}
|
||||
|
||||
@ -1053,7 +1043,7 @@ CaseX86M_GPB_MulDiv:
|
||||
if (ASMJIT_UNLIKELY(memSize == 0))
|
||||
goto AmbiguousOperandSize;
|
||||
|
||||
immValue = o1.as<Imm>().i64();
|
||||
immValue = o1.as<Imm>().value();
|
||||
immSize = FastUInt8(Support::min<uint32_t>(memSize, 4));
|
||||
|
||||
// Sign extend so isInt8 returns the right result.
|
||||
@ -1098,7 +1088,7 @@ CaseX86M_GPB_MulDiv:
|
||||
}
|
||||
|
||||
// The remaining instructions use the secondary opcode/r.
|
||||
immValue = o1.as<Imm>().i64();
|
||||
immValue = o1.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
opcode = x86AltOpcodeOf(instInfo);
|
||||
@ -1175,8 +1165,11 @@ CaseX86M_GPB_MulDiv:
|
||||
}
|
||||
|
||||
case InstDB::kEncodingX86Cmpxchg8b_16b: {
|
||||
const Operand_& o3 = opExt[EmitterUtils::kOp3];
|
||||
const Operand_& o4 = opExt[EmitterUtils::kOp4];
|
||||
|
||||
if (isign3 == ENC_OPS3(Mem, Reg, Reg)) {
|
||||
if (o3.isReg() && _op4.isReg()) {
|
||||
if (o3.isReg() && o4.isReg()) {
|
||||
rmRel = &o0;
|
||||
goto EmitX86M;
|
||||
}
|
||||
@ -1224,8 +1217,8 @@ CaseX86M_GPB_MulDiv:
|
||||
|
||||
case InstDB::kEncodingX86Enter:
|
||||
if (isign3 == ENC_OPS2(Imm, Imm)) {
|
||||
uint32_t iw = o0.as<Imm>().u16();
|
||||
uint32_t ib = o1.as<Imm>().u8();
|
||||
uint32_t iw = o0.as<Imm>().valueAs<uint16_t>();
|
||||
uint32_t ib = o1.as<Imm>().valueAs<uint8_t>();
|
||||
|
||||
immValue = iw | (ib << 16);
|
||||
immSize = 3;
|
||||
@ -1239,7 +1232,7 @@ CaseX86M_GPB_MulDiv:
|
||||
opcode = 0x6B;
|
||||
opcode.addPrefixBySize(o0.size());
|
||||
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
if (!Support::isInt8(immValue) || (options & Inst::kOptionLongForm)) {
|
||||
@ -1257,7 +1250,7 @@ CaseX86M_GPB_MulDiv:
|
||||
opcode = 0x6B;
|
||||
opcode.addPrefixBySize(o0.size());
|
||||
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
// Sign extend so isInt8 returns the right result.
|
||||
@ -1309,7 +1302,7 @@ CaseX86M_GPB_MulDiv:
|
||||
opcode = 0x6B;
|
||||
opcode.addPrefixBySize(o0.size());
|
||||
|
||||
immValue = o1.as<Imm>().i64();
|
||||
immValue = o1.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
// Sign extend so isInt8 returns the right result.
|
||||
@ -1333,7 +1326,7 @@ CaseX86M_GPB_MulDiv:
|
||||
if (ASMJIT_UNLIKELY(o0.id() != Gp::kIdAx))
|
||||
goto InvalidInstruction;
|
||||
|
||||
immValue = o1.as<Imm>().u8();
|
||||
immValue = o1.as<Imm>().valueAs<uint8_t>();
|
||||
immSize = 1;
|
||||
|
||||
opcode = x86AltOpcodeOf(instInfo) + (o0.size() != 1);
|
||||
@ -1398,18 +1391,16 @@ CaseX86M_GPB_MulDiv:
|
||||
|
||||
case InstDB::kEncodingX86Int:
|
||||
if (isign3 == ENC_OPS1(Imm)) {
|
||||
immValue = o0.as<Imm>().i64();
|
||||
immValue = o0.as<Imm>().value();
|
||||
immSize = 1;
|
||||
goto EmitX86Op;
|
||||
}
|
||||
break;
|
||||
|
||||
case InstDB::kEncodingX86Jcc:
|
||||
if (_emitterOptions & kOptionPredictedJumps) {
|
||||
if (options & Inst::kOptionTaken)
|
||||
writer.emit8(0x3E);
|
||||
if (options & Inst::kOptionNotTaken)
|
||||
writer.emit8(0x2E);
|
||||
if ((options & (Inst::kOptionTaken | Inst::kOptionNotTaken)) && hasEncodingOption(kEncodingOptionPredictedJumps)) {
|
||||
uint8_t prefix = (options & Inst::kOptionTaken) ? uint8_t(0x3E) : uint8_t(0x2E);
|
||||
writer.emit8(prefix);
|
||||
}
|
||||
|
||||
rmRel = &o0;
|
||||
@ -1649,16 +1640,16 @@ CaseX86M_GPB_MulDiv:
|
||||
FIXUP_GPB(o0, opReg);
|
||||
|
||||
opcode = 0xB0;
|
||||
immValue = o1.as<Imm>().u8();
|
||||
immValue = o1.as<Imm>().valueAs<uint8_t>();
|
||||
goto EmitX86OpReg;
|
||||
}
|
||||
else {
|
||||
// 64-bit immediate in 64-bit mode is allowed.
|
||||
immValue = o1.as<Imm>().i64();
|
||||
immValue = o1.as<Imm>().value();
|
||||
|
||||
// Optimize the instruction size by using a 32-bit immediate if possible.
|
||||
if (immSize == 8 && !(options & Inst::kOptionLongForm)) {
|
||||
if (Support::isUInt32(immValue) && hasEmitterOption(kOptionOptimizedForSize)) {
|
||||
if (Support::isUInt32(immValue) && hasEncodingOption(kEncodingOptionOptimizeForSize)) {
|
||||
// Zero-extend by using a 32-bit GPD destination instead of a 64-bit GPQ.
|
||||
immSize = 4;
|
||||
}
|
||||
@ -1690,7 +1681,7 @@ CaseX86M_GPB_MulDiv:
|
||||
opReg = 0;
|
||||
rmRel = &o0;
|
||||
|
||||
immValue = o1.as<Imm>().i64();
|
||||
immValue = o1.as<Imm>().value();
|
||||
immSize = FastUInt8(Support::min<uint32_t>(memSize, 4));
|
||||
goto EmitX86M;
|
||||
}
|
||||
@ -1753,7 +1744,7 @@ CaseX86M_GPB_MulDiv:
|
||||
opcode = x86AltOpcodeOf(instInfo) + (o1.size() != 1);
|
||||
opcode.add66hBySize(o1.size());
|
||||
|
||||
immValue = o0.as<Imm>().u8();
|
||||
immValue = o0.as<Imm>().valueAs<uint8_t>();
|
||||
immSize = 1;
|
||||
goto EmitX86Op;
|
||||
}
|
||||
@ -1800,7 +1791,7 @@ CaseX86M_GPB_MulDiv:
|
||||
}
|
||||
|
||||
if (isign3 == ENC_OPS1(Imm)) {
|
||||
immValue = o0.as<Imm>().i64();
|
||||
immValue = o0.as<Imm>().value();
|
||||
immSize = 4;
|
||||
|
||||
if (Support::isInt8(immValue) && !(options & Inst::kOptionLongForm))
|
||||
@ -1840,7 +1831,7 @@ CaseX86PushPop_Gp:
|
||||
if (ASMJIT_UNLIKELY(o0.size() == 0))
|
||||
goto AmbiguousOperandSize;
|
||||
|
||||
if (ASMJIT_UNLIKELY(o0.size() != 2 && o0.size() != gpSize()))
|
||||
if (ASMJIT_UNLIKELY(o0.size() != 2 && o0.size() != registerSize()))
|
||||
goto InvalidInstruction;
|
||||
|
||||
opcode.add66hBySize(o0.size());
|
||||
@ -1857,7 +1848,7 @@ CaseX86PushPop_Gp:
|
||||
}
|
||||
|
||||
if (isign3 == ENC_OPS1(Imm)) {
|
||||
immValue = o0.as<Imm>().i64();
|
||||
immValue = o0.as<Imm>().value();
|
||||
if (immValue == 0 && !(options & Inst::kOptionLongForm)) {
|
||||
// 'ret' without immediate, change C2 to C3.
|
||||
opcode.add(1);
|
||||
@ -1887,7 +1878,7 @@ CaseX86PushPop_Gp:
|
||||
}
|
||||
|
||||
if (isign3 == ENC_OPS2(Reg, Imm)) {
|
||||
immValue = o1.as<Imm>().i64() & 0xFF;
|
||||
immValue = o1.as<Imm>().value() & 0xFF;
|
||||
immSize = 0;
|
||||
|
||||
if (immValue == 1 && !(options & Inst::kOptionLongForm))
|
||||
@ -1915,7 +1906,7 @@ CaseX86PushPop_Gp:
|
||||
goto AmbiguousOperandSize;
|
||||
|
||||
rmRel = &o0;
|
||||
immValue = o1.as<Imm>().i64() & 0xFF;
|
||||
immValue = o1.as<Imm>().value() & 0xFF;
|
||||
immSize = 0;
|
||||
|
||||
if (immValue == 1 && !(options & Inst::kOptionLongForm))
|
||||
@ -1947,7 +1938,7 @@ CaseX86PushPop_Gp:
|
||||
opReg = o1.id();
|
||||
rbReg = o0.id();
|
||||
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
goto EmitX86R;
|
||||
}
|
||||
@ -1957,7 +1948,7 @@ CaseX86PushPop_Gp:
|
||||
opReg = o1.id();
|
||||
rmRel = &o0;
|
||||
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
goto EmitX86M;
|
||||
}
|
||||
@ -2077,11 +2068,11 @@ CaseX86PushPop_Gp:
|
||||
|
||||
if (o0.size() == 1) {
|
||||
FIXUP_GPB(o0, rbReg);
|
||||
immValue = o1.as<Imm>().u8();
|
||||
immValue = o1.as<Imm>().valueAs<uint8_t>();
|
||||
immSize = 1;
|
||||
}
|
||||
else {
|
||||
immValue = o1.as<Imm>().i64();
|
||||
immValue = o1.as<Imm>().value();
|
||||
immSize = FastUInt8(Support::min<uint32_t>(o0.size(), 4));
|
||||
}
|
||||
|
||||
@ -2102,7 +2093,7 @@ CaseX86PushPop_Gp:
|
||||
opcode.addArithBySize(o0.size());
|
||||
rmRel = &o0;
|
||||
|
||||
immValue = o1.as<Imm>().i64();
|
||||
immValue = o1.as<Imm>().value();
|
||||
immSize = FastUInt8(Support::min<uint32_t>(o0.size(), 4));
|
||||
goto EmitX86M;
|
||||
}
|
||||
@ -2345,7 +2336,7 @@ CaseFpuArith_Mem:
|
||||
if (isign3 == ENC_OPS3(Reg, Reg, Imm)) {
|
||||
opcode.add66hIf(Reg::isXmm(o1));
|
||||
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
opReg = o0.id();
|
||||
@ -2358,7 +2349,7 @@ CaseFpuArith_Mem:
|
||||
opcode = x86AltOpcodeOf(instInfo);
|
||||
opcode.add66hIf(Reg::isXmm(o1));
|
||||
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
opReg = o1.id();
|
||||
@ -2371,7 +2362,7 @@ CaseFpuArith_Mem:
|
||||
if (isign3 == ENC_OPS3(Reg, Reg, Imm)) {
|
||||
opcode.add66hIf(Reg::isXmm(o1));
|
||||
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
opReg = o1.id();
|
||||
@ -2382,7 +2373,7 @@ CaseFpuArith_Mem:
|
||||
if (isign3 == ENC_OPS3(Mem, Reg, Imm)) {
|
||||
opcode.add66hIf(Reg::isXmm(o1));
|
||||
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
opReg = o1.id();
|
||||
@ -2621,7 +2612,7 @@ CaseExtRm:
|
||||
opReg = opcode.extractO();
|
||||
|
||||
if (isign3 == ENC_OPS2(Reg, Imm)) {
|
||||
immValue = o1.as<Imm>().i64();
|
||||
immValue = o1.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
rbReg = o0.id();
|
||||
@ -2653,7 +2644,7 @@ CaseExtRm:
|
||||
if (isign3 == ENC_OPS2(Reg, Imm)) {
|
||||
opcode.add66hIf(Reg::isXmm(o0));
|
||||
|
||||
immValue = o1.as<Imm>().i64();
|
||||
immValue = o1.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
rbReg = o0.id();
|
||||
@ -2662,7 +2653,7 @@ CaseExtRm:
|
||||
break;
|
||||
|
||||
case InstDB::kEncodingExtRmi:
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
if (isign3 == ENC_OPS3(Reg, Reg, Imm)) {
|
||||
@ -2679,7 +2670,7 @@ CaseExtRm:
|
||||
break;
|
||||
|
||||
case InstDB::kEncodingExtRmi_P:
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
if (isign3 == ENC_OPS3(Reg, Reg, Imm)) {
|
||||
@ -2714,8 +2705,8 @@ CaseExtRm:
|
||||
opcode = x86AltOpcodeOf(instInfo);
|
||||
|
||||
if (isign3 == ENC_OPS3(Reg, Imm, Imm)) {
|
||||
immValue = (o1.as<Imm>().u32() ) +
|
||||
(o2.as<Imm>().u32() << 8) ;
|
||||
immValue = (uint32_t(o1.as<Imm>().valueAs<uint8_t>()) ) +
|
||||
(uint32_t(o2.as<Imm>().valueAs<uint8_t>()) << 8) ;
|
||||
immSize = 2;
|
||||
|
||||
rbReg = opcode.extractO();
|
||||
@ -2724,7 +2715,9 @@ CaseExtRm:
|
||||
break;
|
||||
|
||||
case InstDB::kEncodingExtInsertq: {
|
||||
const Operand_& o3 = opExt[EmitterUtils::kOp3];
|
||||
const uint32_t isign4 = isign3 + (o3.opType() << 9);
|
||||
|
||||
opReg = o0.id();
|
||||
rbReg = o1.id();
|
||||
|
||||
@ -2735,8 +2728,8 @@ CaseExtRm:
|
||||
opcode = x86AltOpcodeOf(instInfo);
|
||||
|
||||
if (isign4 == ENC_OPS4(Reg, Reg, Imm, Imm)) {
|
||||
immValue = (o2.as<Imm>().u32() ) +
|
||||
(o3.as<Imm>().u32() << 8) ;
|
||||
immValue = (uint32_t(o2.as<Imm>().valueAs<uint8_t>()) ) +
|
||||
(uint32_t(o3.as<Imm>().valueAs<uint8_t>()) << 8) ;
|
||||
immSize = 2;
|
||||
goto EmitX86R;
|
||||
}
|
||||
@ -2869,7 +2862,7 @@ CaseExtRm:
|
||||
ASMJIT_FALLTHROUGH;
|
||||
|
||||
case InstDB::kEncodingVexMri:
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
if (isign3 == ENC_OPS3(Reg, Reg, Imm)) {
|
||||
@ -2934,24 +2927,22 @@ CaseVexRm:
|
||||
break;
|
||||
|
||||
case InstDB::kEncodingVexRm_T1_4X: {
|
||||
if (!(options & Inst::kOptionOp4Op5Used))
|
||||
goto InvalidInstruction;
|
||||
const Operand_& o3 = opExt[EmitterUtils::kOp3];
|
||||
const Operand_& o4 = opExt[EmitterUtils::kOp4];
|
||||
const Operand_& o5 = opExt[EmitterUtils::kOp5];
|
||||
|
||||
if (Reg::isZmm(o0 ) && Reg::isZmm(o1) &&
|
||||
Reg::isZmm(o2 ) && Reg::isZmm(o3) &&
|
||||
Reg::isZmm(_op4) && _op5.isMem()) {
|
||||
|
||||
// Registers [o1, o2, o3, _op4] must start aligned and must be consecutive.
|
||||
if (Reg::isZmm(o0) && Reg::isZmm(o1) && Reg::isZmm(o2) && Reg::isZmm(o3) && Reg::isZmm(o4) && o5.isMem()) {
|
||||
// Registers [o1, o2, o3, o4] must start aligned and must be consecutive.
|
||||
uint32_t i1 = o1.id();
|
||||
uint32_t i2 = o2.id();
|
||||
uint32_t i3 = o3.id();
|
||||
uint32_t i4 = _op4.id();
|
||||
uint32_t i4 = o4.id();
|
||||
|
||||
if (ASMJIT_UNLIKELY((i1 & 0x3) != 0 || i2 != i1 + 1 || i3 != i1 + 2 || i4 != i1 + 3))
|
||||
goto NotConsecutiveRegs;
|
||||
|
||||
opReg = o0.id();
|
||||
rmRel = &_op5;
|
||||
rmRel = &o5;
|
||||
goto EmitVexEvexM;
|
||||
}
|
||||
break;
|
||||
@ -2967,7 +2958,7 @@ CaseVexRm:
|
||||
|
||||
case InstDB::kEncodingVexRmi:
|
||||
CaseVexRmi:
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
if (isign3 == ENC_OPS3(Reg, Reg, Imm)) {
|
||||
@ -2999,25 +2990,32 @@ CaseVexRvm_R:
|
||||
}
|
||||
break;
|
||||
|
||||
case InstDB::kEncodingVexRvm_ZDX_Wx:
|
||||
case InstDB::kEncodingVexRvm_ZDX_Wx: {
|
||||
const Operand_& o3 = opExt[EmitterUtils::kOp3];
|
||||
if (ASMJIT_UNLIKELY(!o3.isNone() && !Reg::isGp(o3, Gp::kIdDx)))
|
||||
goto InvalidInstruction;
|
||||
ASMJIT_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case InstDB::kEncodingVexRvm_Wx:
|
||||
case InstDB::kEncodingVexRvm_Wx: {
|
||||
opcode.addWIf(Reg::isGpq(o0) | (o2.size() == 8));
|
||||
goto CaseVexRvm;
|
||||
}
|
||||
|
||||
case InstDB::kEncodingVexRvm_Lx:
|
||||
case InstDB::kEncodingVexRvm_Lx: {
|
||||
opcode |= x86OpcodeLBySize(o0.size() | o1.size());
|
||||
goto CaseVexRvm;
|
||||
}
|
||||
|
||||
case InstDB::kEncodingVexRvmr_Lx:
|
||||
case InstDB::kEncodingVexRvmr_Lx: {
|
||||
opcode |= x86OpcodeLBySize(o0.size() | o1.size());
|
||||
ASMJIT_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case InstDB::kEncodingVexRvmr: {
|
||||
const Operand_& o3 = opExt[EmitterUtils::kOp3];
|
||||
const uint32_t isign4 = isign3 + (o3.opType() << 9);
|
||||
|
||||
immValue = o3.id() << 4;
|
||||
immSize = 1;
|
||||
|
||||
@ -3040,8 +3038,10 @@ CaseVexRvm_R:
|
||||
ASMJIT_FALLTHROUGH;
|
||||
|
||||
case InstDB::kEncodingVexRvmi: {
|
||||
const Operand_& o3 = opExt[EmitterUtils::kOp3];
|
||||
const uint32_t isign4 = isign3 + (o3.opType() << 9);
|
||||
immValue = o3.as<Imm>().i64();
|
||||
|
||||
immValue = o3.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
if (isign4 == ENC_OPS4(Reg, Reg, Reg, Imm)) {
|
||||
@ -3100,8 +3100,10 @@ CaseVexRvm_R:
|
||||
|
||||
|
||||
case InstDB::kEncodingVexRmvi: {
|
||||
const Operand_& o3 = opExt[EmitterUtils::kOp3];
|
||||
const uint32_t isign4 = isign3 + (o3.opType() << 9);
|
||||
immValue = o3.as<Imm>().i64();
|
||||
|
||||
immValue = o3.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
if (isign4 == ENC_OPS4(Reg, Reg, Reg, Imm)) {
|
||||
@ -3249,7 +3251,7 @@ CaseVexRvm_R:
|
||||
opcode &= Opcode::kLL_Mask;
|
||||
opcode |= x86AltOpcodeOf(instInfo);
|
||||
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
if (isign3 == ENC_OPS3(Reg, Reg, Imm)) {
|
||||
@ -3295,7 +3297,7 @@ CaseVexRvm_R:
|
||||
// The following instructions use the secondary opcode.
|
||||
opcode = x86AltOpcodeOf(instInfo);
|
||||
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
if (isign3 == ENC_OPS3(Reg, Reg, Imm)) {
|
||||
@ -3390,7 +3392,7 @@ CaseVexRvm_R:
|
||||
opcode |= x86AltOpcodeOf(instInfo);
|
||||
opReg = opcode.extractO();
|
||||
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
if (isign3 == ENC_OPS3(Reg, Reg, Imm)) {
|
||||
@ -3434,7 +3436,7 @@ CaseVexRvm_R:
|
||||
ASMJIT_FALLTHROUGH;
|
||||
|
||||
case InstDB::kEncodingVexVmi:
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 1;
|
||||
|
||||
CaseVexVmi_AfterImm:
|
||||
@ -3453,7 +3455,7 @@ CaseVexVmi_AfterImm:
|
||||
|
||||
case InstDB::kEncodingVexVmi4_Wx:
|
||||
opcode.addWIf(Reg::isGpq(o0) || o1.size() == 8);
|
||||
immValue = o2.as<Imm>().i64();
|
||||
immValue = o2.as<Imm>().value();
|
||||
immSize = 4;
|
||||
goto CaseVexVmi_AfterImm;
|
||||
|
||||
@ -3462,6 +3464,7 @@ CaseVexVmi_AfterImm:
|
||||
ASMJIT_FALLTHROUGH;
|
||||
|
||||
case InstDB::kEncodingVexRvrmRvmr: {
|
||||
const Operand_& o3 = opExt[EmitterUtils::kOp3];
|
||||
const uint32_t isign4 = isign3 + (o3.opType() << 9);
|
||||
|
||||
if (isign4 == ENC_OPS4(Reg, Reg, Reg, Reg)) {
|
||||
@ -3495,13 +3498,16 @@ CaseVexVmi_AfterImm:
|
||||
}
|
||||
|
||||
case InstDB::kEncodingVexRvrmiRvmri_Lx: {
|
||||
if (!(options & Inst::kOptionOp4Op5Used) || !_op4.isImm())
|
||||
const Operand_& o3 = opExt[EmitterUtils::kOp3];
|
||||
const Operand_& o4 = opExt[EmitterUtils::kOp4];
|
||||
|
||||
if (ASMJIT_UNLIKELY(!o4.isImm()))
|
||||
goto InvalidInstruction;
|
||||
|
||||
const uint32_t isign4 = isign3 + (o3.opType() << 9);
|
||||
opcode |= x86OpcodeLBySize(o0.size() | o1.size() | o2.size() | o3.size());
|
||||
|
||||
immValue = _op4.as<Imm>().u8() & 0x0F;
|
||||
immValue = o4.as<Imm>().valueAs<uint8_t>() & 0x0F;
|
||||
immSize = 1;
|
||||
|
||||
if (isign4 == ENC_OPS4(Reg, Reg, Reg, Reg)) {
|
||||
@ -3560,6 +3566,7 @@ CaseVexVmi_AfterImm:
|
||||
ASMJIT_FALLTHROUGH;
|
||||
|
||||
case InstDB::kEncodingFma4: {
|
||||
const Operand_& o3 = opExt[EmitterUtils::kOp3];
|
||||
const uint32_t isign4 = isign3 + (o3.opType() << 9);
|
||||
|
||||
if (isign4 == ENC_OPS4(Reg, Reg, Reg, Reg)) {
|
||||
@ -3600,7 +3607,7 @@ CaseVexVmi_AfterImm:
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
EmitX86OpMovAbs:
|
||||
immSize = FastUInt8(gpSize());
|
||||
immSize = FastUInt8(registerSize());
|
||||
writer.emitSegmentOverride(rmRel->as<Mem>().segmentId());
|
||||
|
||||
EmitX86Op:
|
||||
@ -3805,7 +3812,7 @@ EmitModSib:
|
||||
else {
|
||||
bool isOffsetI32 = rmRel->as<Mem>().offsetHi32() == (relOffset >> 31);
|
||||
bool isOffsetU32 = rmRel->as<Mem>().offsetHi32() == 0;
|
||||
uint64_t baseAddress = codeInfo().baseAddress();
|
||||
uint64_t baseAddress = code()->baseAddress();
|
||||
|
||||
// If relative addressing was not explicitly set then we can try to guess.
|
||||
// By guessing we check some properties of the memory operand and try to
|
||||
@ -4241,7 +4248,7 @@ EmitVexEvexR:
|
||||
|
||||
rbReg &= 0x7;
|
||||
writer.emit8(x86EncodeMod(3, opReg, rbReg));
|
||||
writer.emitImmByteOrDWord(immValue, immSize);
|
||||
writer.emitImmByteOrDWord(uint64_t(immValue), immSize);
|
||||
goto EmitDone;
|
||||
}
|
||||
|
||||
@ -4262,7 +4269,7 @@ EmitVexEvexR:
|
||||
|
||||
rbReg &= 0x7;
|
||||
writer.emit8(x86EncodeMod(3, opReg, rbReg));
|
||||
writer.emitImmByteOrDWord(immValue, immSize);
|
||||
writer.emitImmByteOrDWord(uint64_t(immValue), immSize);
|
||||
goto EmitDone;
|
||||
}
|
||||
else {
|
||||
@ -4276,7 +4283,7 @@ EmitVexEvexR:
|
||||
|
||||
rbReg &= 0x7;
|
||||
writer.emit8(x86EncodeMod(3, opReg, rbReg));
|
||||
writer.emitImmByteOrDWord(immValue, immSize);
|
||||
writer.emitImmByteOrDWord(uint64_t(immValue), immSize);
|
||||
goto EmitDone;
|
||||
}
|
||||
}
|
||||
@ -4459,15 +4466,15 @@ EmitJmpCall:
|
||||
}
|
||||
|
||||
if (rmRel->isImm()) {
|
||||
uint64_t baseAddress = codeInfo().baseAddress();
|
||||
uint64_t jumpAddress = rmRel->as<Imm>().u64();
|
||||
uint64_t baseAddress = code()->baseAddress();
|
||||
uint64_t jumpAddress = rmRel->as<Imm>().valueAs<uint64_t>();
|
||||
|
||||
// If the base-address is known calculate a relative displacement and
|
||||
// check if it fits in 32 bits (which is always true in 32-bit mode).
|
||||
// Emit relative displacement as it was a bound label if all checks are ok.
|
||||
if (baseAddress != Globals::kNoBaseAddress) {
|
||||
uint64_t rel64 = jumpAddress - (ip + baseAddress) - inst32Size;
|
||||
if (archId() == ArchInfo::kIdX86 || Support::isInt32(int64_t(rel64))) {
|
||||
if (Environment::is32Bit(arch()) || Support::isInt32(int64_t(rel64))) {
|
||||
rel32 = uint32_t(rel64 & 0xFFFFFFFFu);
|
||||
goto EmitJmpCallRel;
|
||||
}
|
||||
@ -4492,7 +4499,7 @@ EmitJmpCall:
|
||||
// REX prefix does nothing if not patched, but allows to patch the
|
||||
// instruction to use MOD/M and to point to a memory where the final
|
||||
// 64-bit address is stored.
|
||||
if (archId() != ArchInfo::kIdX86 && x86IsJmpOrCall(instId)) {
|
||||
if (Environment::is64Bit(arch()) && x86IsJmpOrCall(instId)) {
|
||||
if (!rex)
|
||||
writer.emit8(kX86ByteRex);
|
||||
|
||||
@ -4582,13 +4589,13 @@ EmitRel:
|
||||
EmitDone:
|
||||
if (ASMJIT_UNLIKELY(options & Inst::kOptionReserved)) {
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (hasEmitterOption(kOptionLoggingEnabled))
|
||||
_emitLog(instId, options, o0, o1, o2, o3, relSize, immSize, writer.cursor());
|
||||
if (_logger)
|
||||
EmitterUtils::logInstructionEmitted(this, instId, options, o0, o1, o2, opExt, relSize, immSize, writer.cursor());
|
||||
#endif
|
||||
}
|
||||
|
||||
resetInstOptions();
|
||||
resetExtraReg();
|
||||
resetInstOptions();
|
||||
resetInlineComment();
|
||||
|
||||
writer.done(this);
|
||||
@ -4625,7 +4632,14 @@ EmitDone:
|
||||
#undef ERROR_HANDLER
|
||||
|
||||
Failed:
|
||||
return _emitFailed(err, instId, options, o0, o1, o2, o3);
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
return EmitterUtils::logInstructionFailed(this, err, instId, options, o0, o1, o2, opExt);
|
||||
#else
|
||||
resetExtraReg();
|
||||
resetInstOptions();
|
||||
resetInlineComment();
|
||||
return reportError(err);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@ -4633,6 +4647,9 @@ Failed:
|
||||
// ============================================================================
|
||||
|
||||
Error Assembler::align(uint32_t alignMode, uint32_t alignment) {
|
||||
if (ASMJIT_UNLIKELY(!_code))
|
||||
return reportError(DebugUtils::errored(kErrorNotInitialized));
|
||||
|
||||
if (ASMJIT_UNLIKELY(alignMode >= kAlignCount))
|
||||
return reportError(DebugUtils::errored(kErrorInvalidArgument));
|
||||
|
||||
@ -4650,7 +4667,7 @@ Error Assembler::align(uint32_t alignMode, uint32_t alignment) {
|
||||
uint8_t pattern = 0x00;
|
||||
switch (alignMode) {
|
||||
case kAlignCode: {
|
||||
if (hasEmitterOption(kOptionOptimizedAlign)) {
|
||||
if (hasEncodingOption(kEncodingOptionOptimizedAlign)) {
|
||||
// Intel 64 and IA-32 Architectures Software Developer's Manual - Volume 2B (NOP).
|
||||
enum { kMaxNopSize = 9 };
|
||||
|
||||
@ -4699,12 +4716,11 @@ Error Assembler::align(uint32_t alignMode, uint32_t alignment) {
|
||||
}
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
if (hasEmitterOption(kOptionLoggingEnabled)) {
|
||||
Logger* logger = _code->logger();
|
||||
if (_logger) {
|
||||
StringTmp<128> sb;
|
||||
sb.appendChars(' ', logger->indentation(FormatOptions::kIndentationCode));
|
||||
sb.appendChars(' ', _logger->indentation(FormatOptions::kIndentationCode));
|
||||
sb.appendFormat("align %u\n", alignment);
|
||||
logger->log(sb);
|
||||
_logger->log(sb);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -4716,22 +4732,22 @@ Error Assembler::align(uint32_t alignMode, uint32_t alignment) {
|
||||
// ============================================================================
|
||||
|
||||
Error Assembler::onAttach(CodeHolder* code) noexcept {
|
||||
uint32_t archId = code->archId();
|
||||
if (!ArchInfo::isX86Family(archId))
|
||||
uint32_t arch = code->arch();
|
||||
if (!Environment::isFamilyX86(arch))
|
||||
return DebugUtils::errored(kErrorInvalidArch);
|
||||
|
||||
ASMJIT_PROPAGATE(Base::onAttach(code));
|
||||
|
||||
if (archId == ArchInfo::kIdX86) {
|
||||
if (Environment::is32Bit(arch)) {
|
||||
// 32 bit architecture - X86.
|
||||
_gpRegInfo.setSignature(Gpd::kSignature);
|
||||
_globalInstOptions |= Inst::_kOptionInvalidRex;
|
||||
_forcedInstOptions |= Inst::_kOptionInvalidRex;
|
||||
_setAddressOverrideMask(kX86MemInfo_67H_X86);
|
||||
}
|
||||
else {
|
||||
// 64 bit architecture - X64.
|
||||
_gpRegInfo.setSignature(Gpq::kSignature);
|
||||
_globalInstOptions &= ~Inst::_kOptionInvalidRex;
|
||||
_forcedInstOptions &= ~Inst::_kOptionInvalidRex;
|
||||
_setAddressOverrideMask(kX86MemInfo_67H_X64);
|
||||
}
|
||||
|
||||
@ -4739,6 +4755,9 @@ Error Assembler::onAttach(CodeHolder* code) noexcept {
|
||||
}
|
||||
|
||||
Error Assembler::onDetach(CodeHolder* code) noexcept {
|
||||
_forcedInstOptions &= ~Inst::_kOptionInvalidRex;
|
||||
_setAddressOverrideMask(0);
|
||||
|
||||
return Base::onDetach(code);
|
||||
}
|
||||
|
||||
|
651
3rdparty/asmjit/src/asmjit/x86/x86assembler.h
vendored
651
3rdparty/asmjit/src/asmjit/x86/x86assembler.h
vendored
@ -37,9 +37,652 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
// [asmjit::Assembler]
|
||||
// ============================================================================
|
||||
|
||||
//! Assembler (X86).
|
||||
//! X86/X64 assembler implementation.
|
||||
//!
|
||||
//! Emits X86 machine-code into buffers managed by `CodeHolder`.
|
||||
//! x86::Assembler is a code emitter that emits machine code directly into the
|
||||
//! \ref CodeBuffer. The assembler is capable of targeting both 32-bit and 64-bit
|
||||
//! instruction sets, the instruction set can be configured through \ref CodeHolder.
|
||||
//!
|
||||
//! ### Basics
|
||||
//!
|
||||
//! The following example shows a basic use of `x86::Assembler`, how to generate
|
||||
//! a function that works in both 32-bit and 64-bit modes, and how to connect
|
||||
//! \ref JitRuntime, \ref CodeHolder, and `x86::Assembler`.
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! // Signature of the generated function.
|
||||
//! typedef int (*SumFunc)(const int* arr, size_t count);
|
||||
//!
|
||||
//! int main() {
|
||||
//! JitRuntime rt; // Create a runtime specialized for JIT.
|
||||
//! CodeHolder code; // Create a CodeHolder.
|
||||
//!
|
||||
//! code.init(rt.environment()); // Initialize code to match the JIT environment.
|
||||
//! x86::Assembler a(&code); // Create and attach x86::Assembler 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.
|
||||
//! x86::Gp arr, cnt;
|
||||
//! x86::Gp sum = x86::eax; // Use EAX as 'sum' as it's a return register.
|
||||
//!
|
||||
//! if (ASMJIT_ARCH_BITS == 64) {
|
||||
//! #if defined(_WIN32)
|
||||
//! arr = x86::rcx; // First argument (array ptr).
|
||||
//! cnt = x86::rdx; // Second argument (number of elements)
|
||||
//! #else
|
||||
//! arr = x86::rdi; // First argument (array ptr).
|
||||
//! cnt = x86::rsi; // Second argument (number of elements)
|
||||
//! #endif
|
||||
//! }
|
||||
//! else {
|
||||
//! arr = x86::edx; // Use EDX to hold the array pointer.
|
||||
//! cnt = x86::ecx; // Use ECX to hold the counter.
|
||||
//! // Fetch first and second arguments from [ESP + 4] and [ESP + 8].
|
||||
//! a.mov(arr, x86::ptr(x86::esp, 4));
|
||||
//! a.mov(cnt, x86::ptr(x86::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').
|
||||
//! // ----> x86::Assembler 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); // Explicitly remove the function from the runtime
|
||||
//! return 0; // Everything successful...
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! 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/X64 environments.
|
||||
//!
|
||||
//! Although functions prologs / epilogs can be implemented manually, AsmJit
|
||||
//! provides utilities that can be used to create function prologs and epilogs
|
||||
//! automatically, see \ref asmjit_function for more details.
|
||||
//!
|
||||
//! ### Instruction Validation
|
||||
//!
|
||||
//! Assembler prefers speed over strictness by default. The implementation checks
|
||||
//! the type of operands and fails if the signature of types is invalid, however,
|
||||
//! it does only basic checks regarding registers and their groups used in
|
||||
//! instructions. It's possible to pass operands that don't form any valid
|
||||
//! signature to the implementation and succeed. This is usually not a problem
|
||||
//! as Assembler provides typed API so operand types are normally checked by C++
|
||||
//! compiler at compile time, however, Assembler is fully dynamic and its \ref
|
||||
//! emit() function can be called with any instruction id, options, and operands.
|
||||
//! Moreover, it's also possible to form instructions that will be accepted by
|
||||
//! the typed API, for example by calling `mov(x86::eax, x86::al)` - the C++
|
||||
//! compiler won't see a problem as both EAX and AL are \ref Gp registers.
|
||||
//!
|
||||
//! To help with common mistakes AsmJit allows to activate instruction validation.
|
||||
//! This feature instruments the Assembler to call \ref InstAPI::validate() before
|
||||
//! it attempts to encode any instruction.
|
||||
//!
|
||||
//! The example below illustrates how validation can be turned on:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! int main(int argc, char* argv[]) {
|
||||
//! JitRuntime rt; // Create a runtime specialized for JIT.
|
||||
//! CodeHolder code; // Create a CodeHolder.
|
||||
//!
|
||||
//! code.init(rt.environment()); // Initialize code to match the JIT environment.
|
||||
//! x86::Assembler a(&code); // Create and attach x86::Assembler to code.
|
||||
//!
|
||||
//! // Enable strict validation.
|
||||
//! a.addValidationOptions(BaseEmitter::kValidationOptionAssembler);
|
||||
//!
|
||||
//! // Try to encode invalid or ill-formed instructions.
|
||||
//! Error err;
|
||||
//!
|
||||
//! // Invalid instruction.
|
||||
//! err = a.mov(x86::eax, x86::al);
|
||||
//! printf("Status: %s\n", DebugUtils::errorAsString(err));
|
||||
//!
|
||||
//! // Invalid instruction.
|
||||
//! err = a.emit(x86::Inst::kIdMovss, x86::eax, x86::xmm0);
|
||||
//! printf("Status: %s\n", DebugUtils::errorAsString(err));
|
||||
//!
|
||||
//! // Ambiguous operand size - the pointer requires size.
|
||||
//! err = a.inc(x86::ptr(x86::rax), 1);
|
||||
//! printf("Status: %s\n", DebugUtils::errorAsString(err));
|
||||
//!
|
||||
//! return 0;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Native Registers
|
||||
//!
|
||||
//! All emitters provide functions to construct machine-size registers depending
|
||||
//! on the target. This feature is for users that want to write code targeting
|
||||
//! both 32-bit and 64-bit architectures at the same time. In AsmJit terminology
|
||||
//! such registers have prefix `z`, so for example on X86 architecture the
|
||||
//! following native registers are provided:
|
||||
//!
|
||||
//! - `zax` - mapped to either `eax` or `rax`
|
||||
//! - `zbx` - mapped to either `ebx` or `rbx`
|
||||
//! - `zcx` - mapped to either `ecx` or `rcx`
|
||||
//! - `zdx` - mapped to either `edx` or `rdx`
|
||||
//! - `zsp` - mapped to either `esp` or `rsp`
|
||||
//! - `zbp` - mapped to either `ebp` or `rbp`
|
||||
//! - `zsi` - mapped to either `esi` or `rsi`
|
||||
//! - `zdi` - mapped to either `edi` or `rdi`
|
||||
//!
|
||||
//! They are accessible through \ref x86::Assembler, \ref x86::Builder, and
|
||||
//! \ref x86::Compiler. The example below illustrates how to use this feature:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! 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.environment()); // Initialize code to match the JIT environment.
|
||||
//! x86::Assembler a(&code); // Create and attach x86::Assembler to code.
|
||||
//!
|
||||
//! // Let's get these registers from x86::Assembler.
|
||||
//! x86::Gp zbp = a.zbp();
|
||||
//! x86::Gp 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:
|
||||
//!
|
||||
//! ```
|
||||
//! void example(x86::Assembler& a) {
|
||||
//! x86::Gp zax = a.gpz(x86::Gp::kIdAx);
|
||||
//! x86::Gp zbx = a.gpz(x86::Gp::kIdBx);
|
||||
//! x86::Gp zcx = a.gpz(x86::Gp::kIdCx);
|
||||
//! x86::Gp zdx = a.gpz(x86::Gp::kIdDx);
|
||||
//!
|
||||
//! // You can also change register's id easily.
|
||||
//! x86::Gp zsp = zax;
|
||||
//! zsp.setId(4); // or x86::Gp::kIdSp.
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Data Embedding
|
||||
//!
|
||||
//! x86::Assembler extends the standard \ref BaseAssembler with X86/X64 specific
|
||||
//! conventions that are often used by assemblers to embed data next to the code.
|
||||
//! The following functions can be used to embed data:
|
||||
//!
|
||||
//! - \ref x86::Assembler::db() - embeds byte (8 bits) (x86 naming).
|
||||
//! - \ref x86::Assembler::dw() - embeds word (16 bits) (x86 naming).
|
||||
//! - \ref x86::Assembler::dd() - embeds dword (32 bits) (x86 naming).
|
||||
//! - \ref x86::Assembler::dq() - embeds qword (64 bits) (x86 naming).
|
||||
//!
|
||||
//! - \ref BaseAssembler::embedInt8() - embeds int8_t (portable naming).
|
||||
//! - \ref BaseAssembler::embedUInt8() - embeds uint8_t (portable naming).
|
||||
//! - \ref BaseAssembler::embedInt16() - embeds int16_t (portable naming).
|
||||
//! - \ref BaseAssembler::embedUInt16() - embeds uint16_t (portable naming).
|
||||
//! - \ref BaseAssembler::embedInt32() - embeds int32_t (portable naming).
|
||||
//! - \ref BaseAssembler::embedUInt32() - embeds uint32_t (portable naming).
|
||||
//! - \ref BaseAssembler::embedInt64() - embeds int64_t (portable naming).
|
||||
//! - \ref BaseAssembler::embedUInt64() - embeds uint64_t (portable naming).
|
||||
//! - \ref BaseAssembler::embedFloat() - embeds float (portable naming).
|
||||
//! - \ref BaseAssembler::embedDouble() - embeds double (portable naming).
|
||||
//!
|
||||
//! The following example illustrates how embed works:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! void embedData(x86::Assembler& a) {
|
||||
//! a.db(0xFF); // Embeds 0xFF byte.
|
||||
//! a.dw(0xFF00); // Embeds 0xFF00 word (little-endian).
|
||||
//! a.dd(0xFF000000); // Embeds 0xFF000000 dword (little-endian).
|
||||
//! a.embedFloat(0.4f); // Embeds 0.4f (32-bit float, little-endian).
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Sometimes it's required to read the data that is embedded after code, for
|
||||
//! example. This can be done through \ref Label as shown below:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! void embedData(x86::Assembler& a, const Label& L_Data) {
|
||||
//! x86::Gp addr = a.zax(); // EAX or RAX.
|
||||
//! x86::Gp val = x86::edi; // Where to store some value...
|
||||
//!
|
||||
//! // Approach 1 - Load the address to register through LEA. This approach
|
||||
//! // is flexible as the address can be then manipulated, for
|
||||
//! // example if you have a data array, which would need index.
|
||||
//! a.lea(addr, L_Data); // Loads the address of the label to EAX or RAX.
|
||||
//! a.mov(val, dword_ptr(addr));
|
||||
//!
|
||||
//! // Approach 2 - Load the data directly by using L_Data in address. It's
|
||||
//! // worth noting that this doesn't work with indexes in X64
|
||||
//! // mode. It will use absolute address in 32-bit mode and
|
||||
//! // relative address (RIP) in 64-bit mode.
|
||||
//! a.mov(val, dword_ptr(L_Data));
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Label Embedding
|
||||
//!
|
||||
//! It's also possible to embed labels. In general AsmJit provides the following
|
||||
//! options:
|
||||
//!
|
||||
//! - \ref BaseEmitter::embedLabel() - Embeds absolute address of a label.
|
||||
//! This is target dependent and would embed either 32-bit or 64-bit data
|
||||
//! that embeds absolute label address. This kind of embedding cannot be
|
||||
//! used in a position independent code.
|
||||
//!
|
||||
//! - \ref BaseEmitter::embedLabelDelta() - Embeds a difference between two
|
||||
//! labels. The size of the difference can be specified so it's possible to
|
||||
//! embed 8-bit, 16-bit, 32-bit, and 64-bit difference, which is sufficient
|
||||
//! for most purposes.
|
||||
//!
|
||||
//! The following example demonstrates how to embed labels and their differences:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! void embedLabel(x86::Assembler& a, const Label& L_Data) {
|
||||
//! // [1] Embed L_Data - the size of the data will be dependent on the target.
|
||||
//! a.embedLabel(L_Data);
|
||||
//!
|
||||
//! // [2] Embed a 32-bit difference of two labels.
|
||||
//! Label L_Here = a.newLabel();
|
||||
//! a.bind(L_Here);
|
||||
//! // Embeds int32_t(L_Data - L_Here).
|
||||
//! a.embedLabelDelta(L_Data, L_Here, 4);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Using FuncFrame and FuncDetail with x86::Assembler
|
||||
//!
|
||||
//! The example below demonstrates how \ref FuncFrame and \ref FuncDetail can be
|
||||
//! used together with \ref x86::Assembler to generate a function that will use
|
||||
//! platform dependent calling conventions automatically depending on the target:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! 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.environment()); // Initialize code to match the JIT environment.
|
||||
//! x86::Assembler a(&code); // Create and attach x86::Assembler 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.
|
||||
//! x86::Gp dst = a.zax();
|
||||
//! x86::Gp src_a = a.zcx();
|
||||
//! x86::Gp src_b = a.zdx();
|
||||
//!
|
||||
//! X86::Xmm vec0 = x86::xmm0;
|
||||
//! X86::Xmm vec1 = x86::xmm1;
|
||||
//!
|
||||
//! // Create/initialize FuncDetail and FuncFrame.
|
||||
//! FuncDetail func;
|
||||
//! func.init(FuncSignatureT<void, int*, const int*, const int*>(CallConv::kIdHost));
|
||||
//!
|
||||
//! FuncFrame frame;
|
||||
//! frame.init(func);
|
||||
//!
|
||||
//! // Make XMM0 and XMM1 dirty - kGroupVec describes XMM|YMM|ZMM registers.
|
||||
//! frame.setDirtyRegs(x86::Reg::kGroupVec, IntUtils::mask(0, 1));
|
||||
//!
|
||||
//! // Alternatively, if you don't want to use register masks you can pass BaseReg
|
||||
//! // to addDirtyRegs(). The following code would add both xmm0 and xmm1.
|
||||
//! frame.addDirtyRegs(x86::xmm0, x86::xmm1);
|
||||
//!
|
||||
//! FuncArgsAssignment args(&func); // Create arguments assignment context.
|
||||
//! args.assignAll(dst, src_a, src_b);// Assign our registers to arguments.
|
||||
//! args.updateFrameInfo(frame); // Reflect our args in FuncFrame.
|
||||
//! frame.finalize(); // Finalize the FuncFrame (updates it).
|
||||
//!
|
||||
//! a.emitProlog(frame); // Emit function prolog.
|
||||
//! a.emitArgsAssignment(frame, args);// Assign 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].
|
||||
//! a.emitEpilog(frame); // 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);
|
||||
//! return 0;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Using x86::Assembler as Code-Patcher
|
||||
//!
|
||||
//! This is an advanced topic that is sometimes unavoidable. AsmJit by default
|
||||
//! appends machine code it generates into a \ref CodeBuffer, however, it also
|
||||
//! allows to set the offset in \ref CodeBuffer explicitly and to overwrite its
|
||||
//! content. This technique is extremely dangerous as X86 instructions have
|
||||
//! variable length (see below), so you should in general only patch code to
|
||||
//! change instruction's immediate values or some other details not known the
|
||||
//! at a time the instruction was emitted. 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 longest form. The `short_()` is not that
|
||||
//! useful as it's automatic (except jumps to non-bound labels). Note that
|
||||
//! the underscore after each function name 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`.
|
||||
//!
|
||||
//! It should be obvious that patching an existing instruction into an instruction
|
||||
//! having a different size may create various problems. So it's recommended to be
|
||||
//! careful and to only patch instructions into instructions having the same size.
|
||||
//! The example below demonstrates how instruction options can be used to guarantee
|
||||
//! the size of an instruction by forcing the assembler to use long-form encoding:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! 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.environment()); // Initialize code to match the JIT environment.
|
||||
//! x86::Assembler a(&code); // Create and attach x86::Assembler to code.
|
||||
//!
|
||||
//! // Let's get these registers from x86::Assembler.
|
||||
//! x86::Gp zbp = a.zbp();
|
||||
//! x86::Gp 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.offset();
|
||||
//! // 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 will just work, because both instructions have
|
||||
//! the same size. 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).
|
||||
//!
|
||||
//! ### More Prefixes
|
||||
//!
|
||||
//! X86 architecture is known for its prefixes. AsmJit supports all prefixes
|
||||
//! that can affect how the instruction is encoded:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! void prefixesExample(x86::Assembler& a) {
|
||||
//! // Lock prefix for implementing atomics:
|
||||
//! // lock add dword ptr [dst], 1
|
||||
//! a.lock().add(x86::dword_ptr(dst), 1);
|
||||
//!
|
||||
//! // Similarly, XAcquire/XRelease prefixes are also available:
|
||||
//! // xacquire add dword ptr [dst], 1
|
||||
//! a.xacquire().add(x86::dword_ptr(dst), 1);
|
||||
//!
|
||||
//! // Rep prefix (see also repe/repz and repne/repnz):
|
||||
//! // rep movs byte ptr [dst], byte ptr [src]
|
||||
//! a.rep().movs(x86::byte_ptr(dst), x86::byte_ptr(src));
|
||||
//!
|
||||
//! // Forcing REX prefix in 64-bit mode.
|
||||
//! // rex mov eax, 1
|
||||
//! a.rex().mov(x86::eax, 1);
|
||||
//!
|
||||
//! // AVX instruction without forced prefix uses the shortest encoding:
|
||||
//! // vaddpd xmm0, xmm1, xmm2 -> [C5|F1|58|C2]
|
||||
//! a.vaddpd(x86::xmm0, x86::xmm1, x86::xmm2);
|
||||
//!
|
||||
//! // Forcing VEX3 prefix (AVX):
|
||||
//! // vex3 vaddpd xmm0, xmm1, xmm2 -> [C4|E1|71|58|C2]
|
||||
//! a.vex3().vaddpd(x86::xmm0, x86::xmm1, x86::xmm2);
|
||||
//!
|
||||
//! // Forcing EVEX prefix (AVX512):
|
||||
//! // evex vaddpd xmm0, xmm1, xmm2 -> [62|F1|F5|08|58|C2]
|
||||
//! a.evex().vaddpd(x86::xmm0, x86::xmm1, x86::xmm2);
|
||||
//!
|
||||
//! // Some instructions accept prefixes not originally intended to:
|
||||
//! // rep ret
|
||||
//! a.rep().ret();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! It's important to understand that prefixes are part of instruction options.
|
||||
//! When a member function that involves adding a prefix is called the prefix
|
||||
//! is combined with existing instruction options, which will affect the next
|
||||
//! instruction generated.
|
||||
//!
|
||||
//! ### Generating AVX512 code.
|
||||
//!
|
||||
//! x86::Assembler can generate AVX512+ code including the use of opmask
|
||||
//! registers. Opmask can be specified through \ref x86::Assembler::k()
|
||||
//! function, which stores it as an extra register, which will be used
|
||||
//! by the next instruction. AsmJit uses such concept for manipulating
|
||||
//! instruction options as well.
|
||||
//!
|
||||
//! The following AVX512 features are supported:
|
||||
//!
|
||||
//! - Opmask selector {k} and zeroing {z}.
|
||||
//! - Rounding modes {rn|rd|ru|rz} and suppress-all-exceptions {sae} option.
|
||||
//! - AVX512 broadcasts {1toN}.
|
||||
//!
|
||||
//! The following example demonstrates how AVX512 features can be used:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! void generateAVX512Code(x86::Assembler& a) {
|
||||
//! using namespace x86;
|
||||
//!
|
||||
//! // Opmask Selectors
|
||||
//! // ----------------
|
||||
//! //
|
||||
//! // - Opmask / zeroing is part of the instruction options / extraReg.
|
||||
//! // - k(reg) is like {kreg} in Intel syntax.
|
||||
//! // - z() is like {z} in Intel syntax.
|
||||
//!
|
||||
//! // vaddpd zmm {k1} {z}, zmm1, zmm2
|
||||
//! a.k(k1).z().vaddpd(zmm0, zmm1, zmm2);
|
||||
//!
|
||||
//! // Memory Broadcasts
|
||||
//! // -----------------
|
||||
//! //
|
||||
//! // - Broadcast data is part of memory operand.
|
||||
//! // - Use x86::Mem::_1toN(), which returns a new x86::Mem operand.
|
||||
//!
|
||||
//! // vaddpd zmm0 {k1} {z}, zmm1, [rcx] {1to8}
|
||||
//! a.k(k1).z().vaddpd(zmm0, zmm1, x86::mem(rcx)._1to8());
|
||||
//!
|
||||
//! // Embedded Rounding & Suppress-All-Exceptoins
|
||||
//! // -------------------------------------------
|
||||
//! //
|
||||
//! // - Rounding mode and {sae} are part of instruction options.
|
||||
//! // - Use sae() to enable exception suppression.
|
||||
//! // - Use rn_sae(), rd_sae(), ru_sae(), and rz_sae() - to enable rounding.
|
||||
//! // - Embedded rounding implicitly sets {sae} as well, that's why the API
|
||||
//! // also has sae() suffix, to make it clear.
|
||||
//!
|
||||
//! // vcmppd k1, zmm1, zmm2, 0x00 {sae}
|
||||
//! a.sae().vcmppd(k1, zmm1, zmm2, 0);
|
||||
//!
|
||||
//! // vaddpd zmm0, zmm1, zmm2 {rz}
|
||||
//! a.rz_sae().vaddpd(zmm0, zmm1, zmm2);
|
||||
//! }
|
||||
//! ```
|
||||
class ASMJIT_VIRTAPI Assembler
|
||||
: public BaseAssembler,
|
||||
public EmitterImplicitT<Assembler> {
|
||||
@ -69,12 +712,10 @@ public:
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \name Emit
|
||||
//! \{
|
||||
|
||||
using BaseEmitter::_emit;
|
||||
ASMJIT_API Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) override;
|
||||
ASMJIT_API Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt) override;
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
@ -46,6 +46,8 @@ Builder::~Builder() noexcept {}
|
||||
Error Builder::finalize() {
|
||||
ASMJIT_PROPAGATE(runPasses());
|
||||
Assembler a(_code);
|
||||
a.addEncodingOptions(encodingOptions());
|
||||
a.addValidationOptions(validationOptions());
|
||||
return serialize(&a);
|
||||
}
|
||||
|
||||
@ -54,13 +56,13 @@ Error Builder::finalize() {
|
||||
// ============================================================================
|
||||
|
||||
Error Builder::onAttach(CodeHolder* code) noexcept {
|
||||
uint32_t archId = code->archId();
|
||||
if (!ArchInfo::isX86Family(archId))
|
||||
uint32_t arch = code->arch();
|
||||
if (!Environment::isFamilyX86(arch))
|
||||
return DebugUtils::errored(kErrorInvalidArch);
|
||||
|
||||
ASMJIT_PROPAGATE(Base::onAttach(code));
|
||||
|
||||
_gpRegInfo.setSignature(archId == ArchInfo::kIdX86 ? uint32_t(Gpd::kSignature) : uint32_t(Gpq::kSignature));
|
||||
_gpRegInfo.setSignature(Environment::is32Bit(arch) ? uint32_t(Gpd::kSignature) : uint32_t(Gpq::kSignature));
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
|
310
3rdparty/asmjit/src/asmjit/x86/x86builder.h
vendored
310
3rdparty/asmjit/src/asmjit/x86/x86builder.h
vendored
@ -40,7 +40,315 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
// [asmjit::x86::Builder]
|
||||
// ============================================================================
|
||||
|
||||
//! Architecture-dependent asm-builder (X86).
|
||||
//! X86/X64 builder implementation.
|
||||
//!
|
||||
//! The code representation used by \ref BaseBuilder is compatible with everything
|
||||
//! AsmJit provides. Each instruction is stored as \ref InstNode, which contains
|
||||
//! instruction id, options, and operands. Each instruction emitted will create
|
||||
//! a new \ref InstNode instance and add it to the current cursor in the double-linked
|
||||
//! list of nodes. Since the instruction stream used by \ref BaseBuilder can be
|
||||
//! manipulated, we can rewrite the SumInts example from \ref asmjit_assembler
|
||||
//! into the following:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! 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(BaseBuilder& builder, const char* phase) {
|
||||
//! String sb;
|
||||
//! builder.dump(sb);
|
||||
//! printf("%s:\n%s\n", phase, sb.data());
|
||||
//! }
|
||||
//!
|
||||
//! int main() {
|
||||
//! JitRuntime rt; // Create JIT Runtime.
|
||||
//! CodeHolder code; // Create a CodeHolder.
|
||||
//!
|
||||
//! code.init(rt.environment()); // Initialize code to match the JIT environment.
|
||||
//! x86::Builder cb(&code); // Create and attach x86::Builder to `code`.
|
||||
//!
|
||||
//! // Decide which registers will be mapped to function arguments. Try changing
|
||||
//! // registers of `dst`, `srcA`, and `srcB` and see what happens in function's
|
||||
//! // prolog and epilog.
|
||||
//! x86::Gp dst = cb.zax();
|
||||
//! x86::Gp srcA = cb.zcx();
|
||||
//! x86::Gp srcB = cb.zdx();
|
||||
//!
|
||||
//! X86::Xmm vec0 = x86::xmm0;
|
||||
//! X86::Xmm vec1 = x86::xmm1;
|
||||
//!
|
||||
//! // Create and initialize `FuncDetail`.
|
||||
//! FuncDetail func;
|
||||
//! func.init(FuncSignatureT<void, int*, const int*, const int*>(CallConv::kIdHost));
|
||||
//!
|
||||
//! // Remember prolog insertion point.
|
||||
//! BaseNode* prologInsertionPoint = cb.cursor();
|
||||
//!
|
||||
//! // Emit function body:
|
||||
//! cb.movdqu(vec0, x86::ptr(srcA)); // Load 4 ints from [srcA] to XMM0.
|
||||
//! cb.movdqu(vec1, x86::ptr(srcB)); // Load 4 ints from [srcB] 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.
|
||||
//! BaseNode* epilogInsertionPoint = cb.cursor();
|
||||
//!
|
||||
//! // 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 x86::Assembler.
|
||||
//! FuncFrame frame;
|
||||
//! frame.init(func);
|
||||
//!
|
||||
//! // Make XMM0 and XMM1 dirty; `kGroupVec` describes XMM|YMM|ZMM registers.
|
||||
//! frame.setDirtyRegs(x86::Reg::kGroupVec, IntUtils::mask(0, 1));
|
||||
//!
|
||||
//! FuncArgsAssignment args(&func); // Create arguments assignment context.
|
||||
//! args.assignAll(dst, srcA, srcB); // Assign our registers to arguments.
|
||||
//! args.updateFrame(frame); // Reflect our args in FuncFrame.
|
||||
//! frame.finalize(); // Finalize the FuncFrame (updates it).
|
||||
//!
|
||||
//! // Insert function prolog and allocate arguments to registers.
|
||||
//! cb.setCursor(prologInsertionPoint);
|
||||
//! cb.emitProlog(frame);
|
||||
//! cb.emitArgsAssignment(frame, args);
|
||||
//!
|
||||
//! // Insert function epilog.
|
||||
//! cb.setCursor(epilogInsertionPoint);
|
||||
//! cb.emitEpilog(frame);
|
||||
//!
|
||||
//! // Let's see how the function's prolog and epilog looks.
|
||||
//! dumpCode(cb, "Prolog & Epilog");
|
||||
//!
|
||||
//! // IMPORTANT: Builder requires finalize() to be called to serialize its
|
||||
//! // 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); // Explicitly 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 \ref BaseBuilder 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 \ref FuncFrame::setDirtyRegs().
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! // NOTE: This function doesn't cover all possible constructs. 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 to implement.
|
||||
//! static void collectDirtyRegs(const BaseNode* first,
|
||||
//! const BaseNode* last,
|
||||
//! uint32_t regMask[BaseReg::kGroupVirt]) {
|
||||
//! const BaseNode* node = first;
|
||||
//! while (node) {
|
||||
//! if (node->actsAsInst()) {
|
||||
//! const InstNode* inst = node->as<InstNode>();
|
||||
//! const Operand* opArray = inst->operands();
|
||||
//!
|
||||
//! for (uint32_t i = 0, opCount = inst->opCount(); i < opCount; i++) {
|
||||
//! const Operand& op = opArray[i];
|
||||
//! if (op.isReg()) {
|
||||
//! const x86::Reg& reg = op.as<x86::Reg>();
|
||||
//! if (reg.group() < BaseReg::kGroupVirt) {
|
||||
//! regMask[reg.group()] |= 1u << reg.id();
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! if (node == last)
|
||||
//! break;
|
||||
//! node = node->next();
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! static void setDirtyRegsOfFuncFrame(const x86::Builder& builder, FuncFrame& frame) {
|
||||
//! uint32_t regMask[BaseReg::kGroupVirt] {};
|
||||
//! collectDirtyRegs(builder.firstNode(), builder.lastNode(), regMask);
|
||||
//!
|
||||
//! // X86/X64 ABIs only require to save GP/XMM registers:
|
||||
//! frame.setDirtyRegs(x86::Reg::kGroupGp , regMask[x86::Reg::kGroupGp ]);
|
||||
//! frame.setDirtyRegs(x86::Reg::kGroupVec, regMask[x86::Reg::kGroupVec]);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Casting Between Various Emitters
|
||||
//!
|
||||
//! Even when \ref BaseAssembler and \ref BaseBuilder provide the same interface
|
||||
//! as defined by \ref BaseEmitter their platform dependent variants like \ref
|
||||
//! x86::Assembler and \ref x86::Builder cannot be interchanged or casted
|
||||
//! to each other by using a C++ `static_cast<>`. The main reason is the
|
||||
//! inheritance graph of these classes is different and cast-incompatible, as
|
||||
//! illustrated below:
|
||||
//!
|
||||
//! ```
|
||||
//! +--------------+ +=========================+
|
||||
//! +----------------------->| x86::Emitter |<--+--# x86::EmitterImplicitT<> #<--+
|
||||
//! | +--------------+ | +=========================+ |
|
||||
//! | (abstract) | (mixin) |
|
||||
//! | +--------------+ +~~~~~~~~~~~~~~+ | |
|
||||
//! +-->| BaseAssembler|---->|x86::Assembler|<--+ |
|
||||
//! | +--------------+ +~~~~~~~~~~~~~~+ | |
|
||||
//! | (abstract) (final) | |
|
||||
//! +===============+ | +--------------+ +~~~~~~~~~~~~~~+ | |
|
||||
//! # BaseEmitter #--+-->| BaseBuilder |--+->| x86::Builder |<--+ |
|
||||
//! +===============+ +--------------+ | +~~~~~~~~~~~~~~+ |
|
||||
//! (abstract) (abstract) | (final) |
|
||||
//! +---------------------+ |
|
||||
//! | |
|
||||
//! | +--------------+ +~~~~~~~~~~~~~~+ +=========================+ |
|
||||
//! +-->| BaseCompiler |---->| x86::Compiler|<-----# x86::EmitterExplicitT<> #---+
|
||||
//! +--------------+ +~~~~~~~~~~~~~~+ +=========================+
|
||||
//! (abstract) (final) (mixin)
|
||||
//! ```
|
||||
//!
|
||||
//! The graph basically shows that it's not possible to cast between \ref
|
||||
//! x86::Assembler and \ref x86::Builder. However, since both share the
|
||||
//! base interface (\ref BaseEmitter) it's possible to cast them to a class
|
||||
//! that cannot be instantiated, but defines the same interface - the class
|
||||
//! is called \ref x86::Emitter and was introduced to make it possible to
|
||||
//! write a function that can emit to both \ref x86::Assembler and \ref
|
||||
//! x86::Builder. Note that \ref x86::Emitter 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 architecture-specific emitter implements a member function called
|
||||
//! `as<arch::Emitter>()`, which casts the instance to the architecture
|
||||
//! specific emitter as illustrated below:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! static void emitSomething(x86::Emitter* e) {
|
||||
//! e->mov(x86::eax, x86::ebx);
|
||||
//! }
|
||||
//!
|
||||
//! static void assemble(CodeHolder& code, bool useAsm) {
|
||||
//! if (useAsm) {
|
||||
//! x86::Assembler assembler(&code);
|
||||
//! emitSomething(assembler.as<x86::Emitter>());
|
||||
//! }
|
||||
//! else {
|
||||
//! x86::Builder builder(&code);
|
||||
//! emitSomething(builder.as<x86::Emitter>());
|
||||
//!
|
||||
//! // NOTE: Builder requires `finalize()` to be called to serialize its
|
||||
//! // content to Assembler (it automatically creates one if not attached).
|
||||
//! builder.finalize();
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The example above shows how to create a function that can emit code to
|
||||
//! either \ref x86::Assembler or \ref x86::Builder through \ref x86::Emitter,
|
||||
//! which provides emitter-neutral functionality. \ref x86::Emitter, however,
|
||||
//! doesn't provide any emitter-specific functionality like `setCursor()`.
|
||||
//!
|
||||
//! ### Code Injection and Manipulation
|
||||
//!
|
||||
//! \ref BaseBuilder emitter stores its nodes in a double-linked list, which
|
||||
//! makes it easy to manipulate that list during the code generation or
|
||||
//! afterwards. Each node is always emitted next to the current cursor and the
|
||||
//! cursor is advanced to that newly emitted node. The cursor can be retrieved
|
||||
//! and changed by \ref BaseBuilder::cursor() and \ref BaseBuilder::setCursor(),
|
||||
//! respectively.
|
||||
//!
|
||||
//! The example below demonstrates how to remember a node and inject something
|
||||
//! next to it.
|
||||
//!
|
||||
//! ```
|
||||
//! static void example(x86::Builder& builder) {
|
||||
//! // Emit something, after it returns the cursor would point at the last
|
||||
//! // emitted node.
|
||||
//! builder.mov(x86::rax, x86::rdx); // [1]
|
||||
//!
|
||||
//! // We can retrieve the node.
|
||||
//! BaseNode* node = builder.cursor();
|
||||
//!
|
||||
//! // Change the instruction we just emitted, just for fun...
|
||||
//! if (node->isInst()) {
|
||||
//! InstNode* inst = node->as<InstNode>();
|
||||
//! // Changes the operands at index [1] to RCX.
|
||||
//! inst->setOp(1, x86::rcx);
|
||||
//! }
|
||||
//!
|
||||
//! // ------------------------- Generate Some Code -------------------------
|
||||
//! builder.add(x86::rax, x86::rdx); // [2]
|
||||
//! builder.shr(x86::rax, 3); // [3]
|
||||
//! // ----------------------------------------------------------------------
|
||||
//!
|
||||
//! // Now, we know where our node is, and we can simply change the cursor
|
||||
//! // and start emitting something after it. The setCursor() function
|
||||
//! // returns the previous cursor, and it's always a good practice to remember
|
||||
//! // it, because you never know if you are not already injecting the code
|
||||
//! // somewhere else...
|
||||
//! BaseNode* oldCursor = builder.setCursor(node);
|
||||
//!
|
||||
//! builder.mul(x86::rax, 8); // [4]
|
||||
//!
|
||||
//! // Restore the cursor
|
||||
//! builder.setCursor(oldCursor);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The function above would actually emit the following:
|
||||
//!
|
||||
//! ```
|
||||
//! mov rax, rcx ; [1] Patched at the beginning.
|
||||
//! mul rax, 8 ; [4] Injected.
|
||||
//! add rax, rdx ; [2] Followed [1] initially.
|
||||
//! shr rax, 3 ; [3] Follows [2].
|
||||
//! ```
|
||||
class ASMJIT_VIRTAPI Builder
|
||||
: public BaseBuilder,
|
||||
public EmitterImplicitT<Builder> {
|
||||
|
247
3rdparty/asmjit/src/asmjit/x86/x86callconv.cpp
vendored
247
3rdparty/asmjit/src/asmjit/x86/x86callconv.cpp
vendored
@ -33,13 +33,19 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
// [asmjit::x86::CallConvInternal - Init]
|
||||
// ============================================================================
|
||||
|
||||
static inline void CallConv_initX86Common(CallConv& cc) noexcept {
|
||||
cc.setNaturalStackAlignment(4);
|
||||
cc.setArchType(ArchInfo::kIdX86);
|
||||
cc.setPreservedRegs(Reg::kGroupGp, Support::bitMask(Gp::kIdBx, Gp::kIdSp, Gp::kIdBp, Gp::kIdSi, Gp::kIdDi));
|
||||
namespace CallConvInternal {
|
||||
|
||||
static inline bool shouldThreatAsCDeclIn64BitMode(uint32_t ccId) noexcept {
|
||||
return ccId == CallConv::kIdCDecl ||
|
||||
ccId == CallConv::kIdStdCall ||
|
||||
ccId == CallConv::kIdThisCall ||
|
||||
ccId == CallConv::kIdFastCall ||
|
||||
ccId == CallConv::kIdRegParm1 ||
|
||||
ccId == CallConv::kIdRegParm2 ||
|
||||
ccId == CallConv::kIdRegParm3;
|
||||
}
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error CallConvInternal::init(CallConv& cc, uint32_t ccId) noexcept {
|
||||
ASMJIT_FAVOR_SIZE Error init(CallConv& cc, uint32_t ccId, const Environment& environment) noexcept {
|
||||
constexpr uint32_t kGroupGp = Reg::kGroupGp;
|
||||
constexpr uint32_t kGroupVec = Reg::kGroupVec;
|
||||
constexpr uint32_t kGroupMm = Reg::kGroupMm;
|
||||
@ -54,110 +60,179 @@ ASMJIT_FAVOR_SIZE Error CallConvInternal::init(CallConv& cc, uint32_t ccId) noex
|
||||
constexpr uint32_t kZsi = Gp::kIdSi;
|
||||
constexpr uint32_t kZdi = Gp::kIdDi;
|
||||
|
||||
switch (ccId) {
|
||||
case CallConv::kIdX86StdCall:
|
||||
cc.setFlags(CallConv::kFlagCalleePopsStack);
|
||||
CallConv_initX86Common(cc);
|
||||
break;
|
||||
bool winABI = environment.isPlatformWindows() || environment.isAbiMSVC();
|
||||
|
||||
case CallConv::kIdX86MsThisCall:
|
||||
cc.setFlags(CallConv::kFlagCalleePopsStack);
|
||||
cc.setPassedOrder(kGroupGp, kZcx);
|
||||
CallConv_initX86Common(cc);
|
||||
break;
|
||||
cc.setArch(environment.arch());
|
||||
|
||||
case CallConv::kIdX86MsFastCall:
|
||||
case CallConv::kIdX86GccFastCall:
|
||||
cc.setFlags(CallConv::kFlagCalleePopsStack);
|
||||
cc.setPassedOrder(kGroupGp, kZcx, kZdx);
|
||||
CallConv_initX86Common(cc);
|
||||
break;
|
||||
if (environment.is32Bit()) {
|
||||
bool isStandardCallConv = true;
|
||||
|
||||
case CallConv::kIdX86GccRegParm1:
|
||||
cc.setPassedOrder(kGroupGp, kZax);
|
||||
CallConv_initX86Common(cc);
|
||||
break;
|
||||
cc.setPreservedRegs(Reg::kGroupGp, Support::bitMask(Gp::kIdBx, Gp::kIdSp, Gp::kIdBp, Gp::kIdSi, Gp::kIdDi));
|
||||
cc.setNaturalStackAlignment(4);
|
||||
|
||||
case CallConv::kIdX86GccRegParm2:
|
||||
cc.setPassedOrder(kGroupGp, kZax, kZdx);
|
||||
CallConv_initX86Common(cc);
|
||||
break;
|
||||
switch (ccId) {
|
||||
case CallConv::kIdCDecl:
|
||||
break;
|
||||
|
||||
case CallConv::kIdX86GccRegParm3:
|
||||
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx);
|
||||
CallConv_initX86Common(cc);
|
||||
break;
|
||||
case CallConv::kIdStdCall:
|
||||
cc.setFlags(CallConv::kFlagCalleePopsStack);
|
||||
break;
|
||||
|
||||
case CallConv::kIdX86CDecl:
|
||||
CallConv_initX86Common(cc);
|
||||
break;
|
||||
case CallConv::kIdFastCall:
|
||||
cc.setFlags(CallConv::kFlagCalleePopsStack);
|
||||
cc.setPassedOrder(kGroupGp, kZcx, kZdx);
|
||||
break;
|
||||
|
||||
case CallConv::kIdX86Win64:
|
||||
cc.setArchType(ArchInfo::kIdX64);
|
||||
cc.setStrategy(CallConv::kStrategyWin64);
|
||||
cc.setFlags(CallConv::kFlagPassFloatsByVec | CallConv::kFlagIndirectVecArgs);
|
||||
cc.setNaturalStackAlignment(16);
|
||||
cc.setSpillZoneSize(32);
|
||||
cc.setPassedOrder(kGroupGp, kZcx, kZdx, 8, 9);
|
||||
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3);
|
||||
cc.setPreservedRegs(kGroupGp, Support::bitMask(kZbx, kZsp, kZbp, kZsi, kZdi, 12, 13, 14, 15));
|
||||
cc.setPreservedRegs(kGroupVec, Support::bitMask(6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
|
||||
break;
|
||||
case CallConv::kIdVectorCall:
|
||||
cc.setFlags(CallConv::kFlagCalleePopsStack);
|
||||
cc.setPassedOrder(kGroupGp, kZcx, kZdx);
|
||||
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5);
|
||||
break;
|
||||
|
||||
case CallConv::kIdX86SysV64:
|
||||
cc.setArchType(ArchInfo::kIdX64);
|
||||
cc.setFlags(CallConv::kFlagPassFloatsByVec);
|
||||
cc.setNaturalStackAlignment(16);
|
||||
cc.setRedZoneSize(128);
|
||||
cc.setPassedOrder(kGroupGp, kZdi, kZsi, kZdx, kZcx, 8, 9);
|
||||
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPreservedRegs(kGroupGp, Support::bitMask(kZbx, kZsp, kZbp, 12, 13, 14, 15));
|
||||
break;
|
||||
case CallConv::kIdThisCall:
|
||||
// NOTE: Even MINGW (starting with GCC 4.7.0) now uses __thiscall on MS Windows,
|
||||
// so we won't bail to any other calling convention if __thiscall was specified.
|
||||
if (winABI) {
|
||||
cc.setFlags(CallConv::kFlagCalleePopsStack);
|
||||
cc.setPassedOrder(kGroupGp, kZcx);
|
||||
}
|
||||
else {
|
||||
ccId = CallConv::kIdCDecl;
|
||||
}
|
||||
break;
|
||||
|
||||
case CallConv::kIdX86LightCall2:
|
||||
case CallConv::kIdX86LightCall3:
|
||||
case CallConv::kIdX86LightCall4: {
|
||||
uint32_t n = (ccId - CallConv::kIdX86LightCall2) + 2;
|
||||
case CallConv::kIdRegParm1:
|
||||
cc.setPassedOrder(kGroupGp, kZax);
|
||||
break;
|
||||
|
||||
cc.setArchType(ArchInfo::kIdX86);
|
||||
cc.setFlags(CallConv::kFlagPassFloatsByVec);
|
||||
cc.setNaturalStackAlignment(16);
|
||||
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx, kZsi, kZdi);
|
||||
cc.setPassedOrder(kGroupMm, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPassedOrder(kGroupKReg, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
case CallConv::kIdRegParm2:
|
||||
cc.setPassedOrder(kGroupGp, kZax, kZdx);
|
||||
break;
|
||||
|
||||
cc.setPreservedRegs(kGroupGp , Support::lsbMask<uint32_t>(8));
|
||||
cc.setPreservedRegs(kGroupVec , Support::lsbMask<uint32_t>(8) & ~Support::lsbMask<uint32_t>(n));
|
||||
break;
|
||||
case CallConv::kIdRegParm3:
|
||||
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx);
|
||||
break;
|
||||
|
||||
case CallConv::kIdLightCall2:
|
||||
case CallConv::kIdLightCall3:
|
||||
case CallConv::kIdLightCall4: {
|
||||
uint32_t n = (ccId - CallConv::kIdLightCall2) + 2;
|
||||
|
||||
cc.setFlags(CallConv::kFlagPassFloatsByVec);
|
||||
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx, kZsi, kZdi);
|
||||
cc.setPassedOrder(kGroupMm, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPassedOrder(kGroupKReg, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPreservedRegs(kGroupGp, Support::lsbMask<uint32_t>(8));
|
||||
cc.setPreservedRegs(kGroupVec, Support::lsbMask<uint32_t>(8) & ~Support::lsbMask<uint32_t>(n));
|
||||
|
||||
cc.setNaturalStackAlignment(16);
|
||||
isStandardCallConv = false;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
}
|
||||
|
||||
case CallConv::kIdX64LightCall2:
|
||||
case CallConv::kIdX64LightCall3:
|
||||
case CallConv::kIdX64LightCall4: {
|
||||
uint32_t n = (ccId - CallConv::kIdX64LightCall2) + 2;
|
||||
if (isStandardCallConv) {
|
||||
// MMX arguments is something where compiler vendors disagree. For example
|
||||
// GCC and MSVC would pass first three via registers and the rest via stack,
|
||||
// however Clang passes all via stack. Returning MMX registers is even more
|
||||
// fun, where GCC uses MM0, but Clang uses EAX:EDX pair. I'm not sure it's
|
||||
// something we should be worried about as MMX is deprecated anyway.
|
||||
cc.setPassedOrder(kGroupMm, 0, 1, 2);
|
||||
|
||||
cc.setArchType(ArchInfo::kIdX64);
|
||||
cc.setFlags(CallConv::kFlagPassFloatsByVec);
|
||||
cc.setNaturalStackAlignment(16);
|
||||
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx, kZsi, kZdi);
|
||||
cc.setPassedOrder(kGroupMm, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPassedOrder(kGroupKReg, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
// Vector arguments (XMM|YMM|ZMM) are passed via registers. However, if the
|
||||
// function is variadic then they have to be passed via stack.
|
||||
cc.setPassedOrder(kGroupVec, 0, 1, 2);
|
||||
|
||||
cc.setPreservedRegs(kGroupGp , Support::lsbMask<uint32_t>(16));
|
||||
cc.setPreservedRegs(kGroupVec ,~Support::lsbMask<uint32_t>(n));
|
||||
break;
|
||||
// Functions with variable arguments always use stack for MM and vector
|
||||
// arguments.
|
||||
cc.addFlags(CallConv::kFlagPassVecByStackIfVA);
|
||||
}
|
||||
|
||||
default:
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
if (ccId == CallConv::kIdCDecl) {
|
||||
cc.addFlags(CallConv::kFlagVarArgCompatible);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Preprocess the calling convention into a common id as many conventions
|
||||
// are normally ignored even by C/C++ compilers and treated as `__cdecl`.
|
||||
if (shouldThreatAsCDeclIn64BitMode(ccId))
|
||||
ccId = winABI ? CallConv::kIdX64Windows : CallConv::kIdX64SystemV;
|
||||
|
||||
switch (ccId) {
|
||||
case CallConv::kIdX64SystemV: {
|
||||
cc.setFlags(CallConv::kFlagPassFloatsByVec |
|
||||
CallConv::kFlagPassMmxByXmm |
|
||||
CallConv::kFlagVarArgCompatible);
|
||||
cc.setNaturalStackAlignment(16);
|
||||
cc.setRedZoneSize(128);
|
||||
cc.setPassedOrder(kGroupGp, kZdi, kZsi, kZdx, kZcx, 8, 9);
|
||||
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPreservedRegs(kGroupGp, Support::bitMask(kZbx, kZsp, kZbp, 12, 13, 14, 15));
|
||||
break;
|
||||
}
|
||||
|
||||
case CallConv::kIdX64Windows: {
|
||||
cc.setStrategy(CallConv::kStrategyX64Windows);
|
||||
cc.setFlags(CallConv::kFlagPassFloatsByVec |
|
||||
CallConv::kFlagIndirectVecArgs |
|
||||
CallConv::kFlagPassMmxByGp |
|
||||
CallConv::kFlagVarArgCompatible);
|
||||
cc.setNaturalStackAlignment(16);
|
||||
// Maximum 4 arguments in registers, each adds 8 bytes to the spill zone.
|
||||
cc.setSpillZoneSize(4 * 8);
|
||||
cc.setPassedOrder(kGroupGp, kZcx, kZdx, 8, 9);
|
||||
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3);
|
||||
cc.setPreservedRegs(kGroupGp, Support::bitMask(kZbx, kZsp, kZbp, kZsi, kZdi, 12, 13, 14, 15));
|
||||
cc.setPreservedRegs(kGroupVec, Support::bitMask(6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
|
||||
break;
|
||||
}
|
||||
|
||||
case CallConv::kIdVectorCall: {
|
||||
cc.setStrategy(CallConv::kStrategyX64VectorCall);
|
||||
cc.setFlags(CallConv::kFlagPassFloatsByVec |
|
||||
CallConv::kFlagPassMmxByGp );
|
||||
cc.setNaturalStackAlignment(16);
|
||||
// Maximum 6 arguments in registers, each adds 8 bytes to the spill zone.
|
||||
cc.setSpillZoneSize(6 * 8);
|
||||
cc.setPassedOrder(kGroupGp, kZcx, kZdx, 8, 9);
|
||||
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5);
|
||||
cc.setPreservedRegs(kGroupGp, Support::bitMask(kZbx, kZsp, kZbp, kZsi, kZdi, 12, 13, 14, 15));
|
||||
cc.setPreservedRegs(kGroupVec, Support::bitMask(6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
|
||||
break;
|
||||
}
|
||||
|
||||
case CallConv::kIdLightCall2:
|
||||
case CallConv::kIdLightCall3:
|
||||
case CallConv::kIdLightCall4: {
|
||||
uint32_t n = (ccId - CallConv::kIdLightCall2) + 2;
|
||||
|
||||
cc.setFlags(CallConv::kFlagPassFloatsByVec);
|
||||
cc.setNaturalStackAlignment(16);
|
||||
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx, kZsi, kZdi);
|
||||
cc.setPassedOrder(kGroupMm, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
cc.setPassedOrder(kGroupKReg, 0, 1, 2, 3, 4, 5, 6, 7);
|
||||
|
||||
cc.setPreservedRegs(kGroupGp, Support::lsbMask<uint32_t>(16));
|
||||
cc.setPreservedRegs(kGroupVec, ~Support::lsbMask<uint32_t>(n));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return DebugUtils::errored(kErrorInvalidArgument);
|
||||
}
|
||||
}
|
||||
|
||||
cc.setId(ccId);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
} // {CallConvInternal}
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // ASMJIT_BUILD_X86
|
||||
|
@ -38,9 +38,11 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
|
||||
//! X86-specific function API (calling conventions and other utilities).
|
||||
namespace CallConvInternal {
|
||||
//! Initialize `CallConv` structure (X86 specific).
|
||||
Error init(CallConv& cc, uint32_t ccId) noexcept;
|
||||
}
|
||||
|
||||
//! Initialize `CallConv` structure (X86 specific).
|
||||
Error init(CallConv& cc, uint32_t ccId, const Environment& environment) noexcept;
|
||||
|
||||
} // {CallConvInternal}
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
12
3rdparty/asmjit/src/asmjit/x86/x86compiler.cpp
vendored
12
3rdparty/asmjit/src/asmjit/x86/x86compiler.cpp
vendored
@ -47,20 +47,24 @@ Compiler::~Compiler() noexcept {}
|
||||
Error Compiler::finalize() {
|
||||
ASMJIT_PROPAGATE(runPasses());
|
||||
Assembler a(_code);
|
||||
a.addEncodingOptions(encodingOptions());
|
||||
a.addValidationOptions(validationOptions());
|
||||
return serialize(&a);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::x86::Compiler - Events]
|
||||
// ============================================================================
|
||||
|
||||
Error Compiler::onAttach(CodeHolder* code) noexcept {
|
||||
uint32_t archId = code->archId();
|
||||
if (!ArchInfo::isX86Family(archId))
|
||||
uint32_t arch = code->arch();
|
||||
if (!Environment::isFamilyX86(arch))
|
||||
return DebugUtils::errored(kErrorInvalidArch);
|
||||
|
||||
ASMJIT_PROPAGATE(Base::onAttach(code));
|
||||
_gpRegInfo.setSignature(archId == ArchInfo::kIdX86 ? uint32_t(Gpd::kSignature) : uint32_t(Gpq::kSignature));
|
||||
|
||||
bool is32Bit = Environment::is32Bit(arch);
|
||||
_gpRegInfo.setSignature(is32Bit ? uint32_t(Gpd::kSignature)
|
||||
: uint32_t(Gpq::kSignature));
|
||||
|
||||
Error err = addPassT<X86RAPass>();
|
||||
if (ASMJIT_UNLIKELY(err)) {
|
||||
|
465
3rdparty/asmjit/src/asmjit/x86/x86compiler.h
vendored
465
3rdparty/asmjit/src/asmjit/x86/x86compiler.h
vendored
@ -41,7 +41,413 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
// [asmjit::x86::Compiler]
|
||||
// ============================================================================
|
||||
|
||||
//! Architecture-dependent asm-compiler (X86).
|
||||
//! X86/X64 compiler implementation.
|
||||
//!
|
||||
//! ### Compiler Basics
|
||||
//!
|
||||
//! The first \ref x86::Compiler example shows how to generate a function that
|
||||
//! simply returns an integer value. It's an analogy to the first Assembler example:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! // Signature of the generated function.
|
||||
//! typedef int (*Func)(void);
|
||||
//!
|
||||
//! int main() {
|
||||
//! JitRuntime rt; // Runtime specialized for JIT code execution.
|
||||
//! CodeHolder code; // Holds code and relocation information.
|
||||
//!
|
||||
//! code.init(rt.environment()); // Initialize code to match the JIT environment.
|
||||
//! x86::Compiler cc(&code); // Create and attach x86::Compiler to code.
|
||||
//!
|
||||
//! cc.addFunc(FuncSignatureT<int>());// Begin a function of `int fn(void)` signature.
|
||||
//!
|
||||
//! x86::Gp 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.
|
||||
//! // ----> x86::Compiler 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); // Explicitly remove the function from the runtime.
|
||||
//! return 0;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The \ref BaseCompiler::addFunc() and \ref BaseCompiler::endFunc() functions
|
||||
//! are used to define the function and its end. Both 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 simple memory copy function that uses
|
||||
//! `uint32_t` items:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! // Signature of the generated function.
|
||||
//! typedef void (*MemCpy32)(uint32_t* dst, const uint32_t* src, size_t count);
|
||||
//!
|
||||
//! int main() {
|
||||
//! JitRuntime rt; // Runtime specialized for JIT code execution.
|
||||
//! CodeHolder code; // Holds code and relocation information.
|
||||
//!
|
||||
//! code.init(rt.environment()); // Initialize code to match the JIT environment.
|
||||
//! x86::Compiler cc(&code); // Create and attach x86::Compiler to code.
|
||||
//!
|
||||
//! cc.addFunc( // Begin the function of the following signature:
|
||||
//! FuncSignatureT<void, // Return value - void (no return value).
|
||||
//! uint32_t*, // 1st argument - uint32_t* (machine reg-size).
|
||||
//! const uint32_t*, // 2nd argument - uint32_t* (machine reg-size).
|
||||
//! size_t>()); // 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.
|
||||
//!
|
||||
//! x86::Gp dst = cc.newIntPtr("dst");// Create `dst` register (destination pointer).
|
||||
//! x86::Gp src = cc.newIntPtr("src");// Create `src` register (source pointer).
|
||||
//! x86::Gp i = cc.newUIntPtr("i"); // Create `i` register (loop counter).
|
||||
//!
|
||||
//! cc.setArg(0, dst); // Assign `dst` argument.
|
||||
//! cc.setArg(1, src); // Assign `src` argument.
|
||||
//! cc.setArg(2, i); // Assign `i` argument.
|
||||
//!
|
||||
//! cc.test(i, i); // Early exit if length is zero.
|
||||
//! cc.jz(L_Exit);
|
||||
//!
|
||||
//! cc.bind(L_Loop); // Bind the beginning of the loop here.
|
||||
//!
|
||||
//! x86::Gp 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(i); // Loop until `i` 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.
|
||||
//! // ----> x86::Compiler is no longer needed from here and can be destroyed <----
|
||||
//!
|
||||
//! // Add the generated code to the runtime.
|
||||
//! MemCpy32 memcpy32;
|
||||
//! Error err = rt.add(&memcpy32, &code);
|
||||
//!
|
||||
//! // Handle a possible error returned by AsmJit.
|
||||
//! if (err)
|
||||
//! return 1;
|
||||
//! // ----> CodeHolder is no longer needed from here and can be destroyed <----
|
||||
//!
|
||||
//! // Test the generated code.
|
||||
//! uint32_t input[6] = { 1, 2, 3, 5, 8, 13 };
|
||||
//! uint32_t output[6];
|
||||
//! memcpy32(output, input, 6);
|
||||
//!
|
||||
//! for (uint32_t i = 0; i < 6; i++)
|
||||
//! printf("%d\n", output[i]);
|
||||
//!
|
||||
//! rt.release(memcpy32);
|
||||
//! return 0;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Recursive Functions
|
||||
//!
|
||||
//! It's possible to create more functions by using the same \ref x86::Compiler
|
||||
//! instance and make links between them. In such case it's important to keep
|
||||
//! the pointer to \ref FuncNode.
|
||||
//!
|
||||
//! The example below creates a simple Fibonacci function that calls itself recursively:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! // Signature of the generated function.
|
||||
//! typedef uint32_t (*Fibonacci)(uint32_t x);
|
||||
//!
|
||||
//! int main() {
|
||||
//! JitRuntime rt; // Runtime specialized for JIT code execution.
|
||||
//! CodeHolder code; // Holds code and relocation information.
|
||||
//!
|
||||
//! code.init(rt.environment()); // Initialize code to match the JIT environment.
|
||||
//! x86::Compiler cc(&code); // Create and attach x86::Compiler to code.
|
||||
//!
|
||||
//! FuncNode* func = cc.addFunc( // Begin of the Fibonacci function, addFunc()
|
||||
//! FuncSignatureT<int, int>()); // Returns a pointer to the FuncNode node.
|
||||
//!
|
||||
//! Label L_Exit = cc.newLabel() // Exit label.
|
||||
//! x86::Gp x = cc.newU32(); // Function x argument.
|
||||
//! x86::Gp 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.
|
||||
//!
|
||||
//! InvokeNode* invokeNode; // Function invocation:
|
||||
//! cc.invoke(&invokeNode, // - InvokeNode (output).
|
||||
//! func->label(), // - Function address or Label.
|
||||
//! FuncSignatureT<int, int>()); // - Function signature.
|
||||
//!
|
||||
//! invokeNode->setArg(0, x); // Assign x as the first argument.
|
||||
//! invokeNode->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.
|
||||
//! // ----> x86::Compiler 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 <----
|
||||
//!
|
||||
//! // Test the generated code.
|
||||
//! printf("Fib(%u) -> %u\n", 8, fib(8));
|
||||
//!
|
||||
//! rt.release(fib);
|
||||
//! return 0;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Stack Management
|
||||
//!
|
||||
//! Function's stack-frame is managed automatically, 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.
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//! #include <stdio.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! // Signature of the generated function.
|
||||
//! typedef int (*Func)(void);
|
||||
//!
|
||||
//! int main() {
|
||||
//! JitRuntime rt; // Runtime specialized for JIT code execution.
|
||||
//! CodeHolder code; // Holds code and relocation information.
|
||||
//!
|
||||
//! code.init(rt.environment()); // Initialize code to match the JIT environment.
|
||||
//! x86::Compiler cc(&code); // Create and attach x86::Compiler to code.
|
||||
//!
|
||||
//! cc.addFunc(FuncSignatureT<int>());// Create a function that returns int.
|
||||
//!
|
||||
//! x86::Gp p = cc.newIntPtr("p");
|
||||
//! x86::Gp i = cc.newIntPtr("i");
|
||||
//!
|
||||
//! // Allocate 256 bytes on the stack aligned to 4 bytes.
|
||||
//! x86::Mem stack = cc.newStack(256, 4);
|
||||
//!
|
||||
//! x86::Mem 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 is a 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`.
|
||||
//! x86::Gp sum = cc.newI32("sum");
|
||||
//! x86::Gp 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.
|
||||
//! // ----> x86::Compiler 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 <----
|
||||
//!
|
||||
//! printf("Func() -> %d\n", func()); // Test the generated code.
|
||||
//!
|
||||
//! rt.release(func);
|
||||
//! return 0;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Constant Pool
|
||||
//!
|
||||
//! Compiler provides two constant pools for a general purpose code generation:
|
||||
//!
|
||||
//! - Local constant pool - Part of \ref FuncNode, can be only used by a
|
||||
//! single function and added after the function epilog sequence (after
|
||||
//! `ret` instruction).
|
||||
//!
|
||||
//! - Global constant pool - Part of \ref BaseCompiler, flushed at the end
|
||||
//! of the generated code by \ref BaseEmitter::finalize().
|
||||
//!
|
||||
//! The example below illustrates how a built-in constant pool can be used:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! static void exampleUseOfConstPool(x86::Compiler& cc) {
|
||||
//! cc.addFunc(FuncSignatureT<int>());
|
||||
//!
|
||||
//! x86::Gp v0 = cc.newGpd("v0");
|
||||
//! x86::Gp v1 = cc.newGpd("v1");
|
||||
//!
|
||||
//! x86::Mem c0 = cc.newInt32Const(ConstPool::kScopeLocal, 200);
|
||||
//! x86::Mem c1 = cc.newInt32Const(ConstPool::kScopeLocal, 33);
|
||||
//!
|
||||
//! cc.mov(v0, c0);
|
||||
//! cc.mov(v1, c1);
|
||||
//! cc.add(v0, v1);
|
||||
//!
|
||||
//! cc.ret(v0);
|
||||
//! cc.endFunc();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Jump Tables
|
||||
//!
|
||||
//! x86::Compiler supports `jmp` instruction with reg/mem operand, which is a
|
||||
//! commonly used pattern to implement indirect jumps within a function, for
|
||||
//! example to implement `switch()` statement in a programming languages. By
|
||||
//! default AsmJit assumes that every basic block can be a possible jump
|
||||
//! target as it's unable to deduce targets from instruction's operands. This
|
||||
//! is a very pessimistic default that should be avoided if possible as it's
|
||||
//! costly and very unfriendly to liveness analysis and register allocation.
|
||||
//!
|
||||
//! Instead of relying on such pessimistic default behavior, let's use \ref
|
||||
//! JumpAnnotation to annotate a jump where all targets are known:
|
||||
//!
|
||||
//! ```
|
||||
//! #include <asmjit/x86.h>
|
||||
//!
|
||||
//! using namespace asmjit;
|
||||
//!
|
||||
//! static void exampleUseOfIndirectJump(x86::Compiler& cc) {
|
||||
//! cc.addFunc(FuncSignatureT<float, float, float, uint32_t>(CallConv::kIdHost));
|
||||
//!
|
||||
//! // Function arguments
|
||||
//! x86::Xmm a = cc.newXmmSs("a");
|
||||
//! x86::Xmm b = cc.newXmmSs("b");
|
||||
//! x86::Gp op = cc.newUInt32("op");
|
||||
//!
|
||||
//! x86::Gp target = cc.newIntPtr("target");
|
||||
//! x86::Gp offset = cc.newIntPtr("offset");
|
||||
//!
|
||||
//! Label L_Table = cc.newLabel();
|
||||
//! Label L_Add = cc.newLabel();
|
||||
//! Label L_Sub = cc.newLabel();
|
||||
//! Label L_Mul = cc.newLabel();
|
||||
//! Label L_Div = cc.newLabel();
|
||||
//! Label L_End = cc.newLabel();
|
||||
//!
|
||||
//! cc.setArg(0, a);
|
||||
//! cc.setArg(1, b);
|
||||
//! cc.setArg(2, op);
|
||||
//!
|
||||
//! // Jump annotation is a building block that allows to annotate all
|
||||
//! // possible targets where `jmp()` can jump. It then drives the CFG
|
||||
//! // contruction and liveness analysis, which impacts register allocation.
|
||||
//! JumpAnnotation* annotation = cc.newJumpAnnotation();
|
||||
//! annotation->addLabel(L_Add);
|
||||
//! annotation->addLabel(L_Sub);
|
||||
//! annotation->addLabel(L_Mul);
|
||||
//! annotation->addLabel(L_Div);
|
||||
//!
|
||||
//! // Most likely not the common indirect jump approach, but it
|
||||
//! // doesn't really matter how final address is calculated. The
|
||||
//! // most important path using JumpAnnotation with `jmp()`.
|
||||
//! cc.lea(offset, x86::ptr(L_Table));
|
||||
//! if (cc.is64Bit())
|
||||
//! cc.movsxd(target, x86::dword_ptr(offset, op.cloneAs(offset), 2));
|
||||
//! else
|
||||
//! cc.mov(target, x86::dword_ptr(offset, op.cloneAs(offset), 2));
|
||||
//! cc.add(target, offset);
|
||||
//! cc.jmp(target, annotation);
|
||||
//!
|
||||
//! // Acts like a switch() statement in C.
|
||||
//! cc.bind(L_Add);
|
||||
//! cc.addss(a, b);
|
||||
//! cc.jmp(L_End);
|
||||
//!
|
||||
//! cc.bind(L_Sub);
|
||||
//! cc.subss(a, b);
|
||||
//! cc.jmp(L_End);
|
||||
//!
|
||||
//! cc.bind(L_Mul);
|
||||
//! cc.mulss(a, b);
|
||||
//! cc.jmp(L_End);
|
||||
//!
|
||||
//! cc.bind(L_Div);
|
||||
//! cc.divss(a, b);
|
||||
//!
|
||||
//! cc.bind(L_End);
|
||||
//! cc.ret(a);
|
||||
//!
|
||||
//! cc.endFunc();
|
||||
//!
|
||||
//! // Relative int32_t offsets of `L_XXX - L_Table`.
|
||||
//! cc.bind(L_Table);
|
||||
//! cc.embedLabelDelta(L_Add, L_Table, 4);
|
||||
//! cc.embedLabelDelta(L_Sub, L_Table, 4);
|
||||
//! cc.embedLabelDelta(L_Mul, L_Table, 4);
|
||||
//! cc.embedLabelDelta(L_Div, L_Table, 4);
|
||||
//! }
|
||||
//! ```
|
||||
class ASMJIT_VIRTAPI Compiler
|
||||
: public BaseCompiler,
|
||||
public EmitterExplicitT<Compiler> {
|
||||
@ -62,18 +468,18 @@ public:
|
||||
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
# define ASMJIT_NEW_REG_FMT(OUT, PARAM, FORMAT, ARGS) \
|
||||
_newRegFmt(OUT, PARAM, FORMAT, ARGS)
|
||||
_newRegFmt(&OUT, PARAM, FORMAT, ARGS)
|
||||
#else
|
||||
# define ASMJIT_NEW_REG_FMT(OUT, PARAM, FORMAT, ARGS) \
|
||||
DebugUtils::unused(FORMAT); \
|
||||
DebugUtils::unused(std::forward<Args>(args)...); \
|
||||
_newReg(OUT, PARAM)
|
||||
_newReg(&OUT, PARAM)
|
||||
#endif
|
||||
|
||||
#define ASMJIT_NEW_REG_CUSTOM(FUNC, REG) \
|
||||
inline REG FUNC(uint32_t typeId) { \
|
||||
REG reg(Globals::NoInit); \
|
||||
_newReg(reg, typeId); \
|
||||
_newReg(®, typeId); \
|
||||
return reg; \
|
||||
} \
|
||||
\
|
||||
@ -87,7 +493,7 @@ public:
|
||||
#define ASMJIT_NEW_REG_TYPED(FUNC, REG, TYPE_ID) \
|
||||
inline REG FUNC() { \
|
||||
REG reg(Globals::NoInit); \
|
||||
_newReg(reg, TYPE_ID); \
|
||||
_newReg(®, TYPE_ID); \
|
||||
return reg; \
|
||||
} \
|
||||
\
|
||||
@ -170,7 +576,7 @@ public:
|
||||
//! Creates a new memory chunk allocated on the current function's stack.
|
||||
inline Mem newStack(uint32_t size, uint32_t alignment, const char* name = nullptr) {
|
||||
Mem m(Globals::NoInit);
|
||||
_newStack(m, size, alignment, name);
|
||||
_newStack(&m, size, alignment, name);
|
||||
return m;
|
||||
}
|
||||
|
||||
@ -182,7 +588,7 @@ public:
|
||||
//! Put data to a constant-pool and get a memory reference to it.
|
||||
inline Mem newConst(uint32_t scope, const void* data, size_t size) {
|
||||
Mem m(Globals::NoInit);
|
||||
_newConst(m, scope, data, size);
|
||||
_newConst(&m, scope, data, size);
|
||||
return m;
|
||||
}
|
||||
|
||||
@ -213,12 +619,16 @@ public:
|
||||
//! Put a DP-FP `val` to a constant-pool.
|
||||
inline Mem newDoubleConst(uint32_t scope, double val) noexcept { return newConst(scope, &val, 8); }
|
||||
|
||||
//! Put a MMX `val` to a constant-pool.
|
||||
#ifndef ASMJIT_NO_DEPRECATED
|
||||
ASMJIT_DEPRECATED("newMmConst() uses a deprecated Data64, use newConst() with your own data instead")
|
||||
inline Mem newMmConst(uint32_t scope, const Data64& val) noexcept { return newConst(scope, &val, 8); }
|
||||
//! Put a XMM `val` to a constant-pool.
|
||||
|
||||
ASMJIT_DEPRECATED("newXmmConst() uses a deprecated Data128, use newConst() with your own data instead")
|
||||
inline Mem newXmmConst(uint32_t scope, const Data128& val) noexcept { return newConst(scope, &val, 16); }
|
||||
//! Put a YMM `val` to a constant-pool.
|
||||
|
||||
ASMJIT_DEPRECATED("newYmmConst() uses a deprecated Data256, use newConst() with your own data instead")
|
||||
inline Mem newYmmConst(uint32_t scope, const Data256& val) noexcept { return newConst(scope, &val, 32); }
|
||||
#endif // !ASMJIT_NO_DEPRECATED
|
||||
|
||||
//! \}
|
||||
|
||||
@ -235,16 +645,37 @@ public:
|
||||
//! \name Function Call & Ret Intrinsics
|
||||
//! \{
|
||||
|
||||
//! Call a function.
|
||||
inline FuncCallNode* call(const Gp& target, const FuncSignature& sign) { return addCall(Inst::kIdCall, target, sign); }
|
||||
//! Invoke a function call without `target` type enforcement.
|
||||
inline Error invoke_(InvokeNode** out, const Operand_& target, const FuncSignature& signature) {
|
||||
return _addInvokeNode(out, Inst::kIdCall, target, signature);
|
||||
}
|
||||
|
||||
//! Invoke a function call of the given `target` and `signature` and store
|
||||
//! the added node to `out`.
|
||||
//!
|
||||
//! Creates a new \ref InvokeNode, initializes all the necessary members to
|
||||
//! match the given function `signature`, adds the node to the compiler, and
|
||||
//! stores its pointer to `out`. The operation is atomic, if anything fails
|
||||
//! nullptr is stored in `out` and error code is returned.
|
||||
inline Error invoke(InvokeNode** out, const Gp& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
|
||||
//! \overload
|
||||
inline FuncCallNode* call(const Mem& target, const FuncSignature& sign) { return addCall(Inst::kIdCall, target, sign); }
|
||||
inline Error invoke(InvokeNode** out, const Mem& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
|
||||
//! \overload
|
||||
inline FuncCallNode* call(const Label& target, const FuncSignature& sign) { return addCall(Inst::kIdCall, target, sign); }
|
||||
inline Error invoke(InvokeNode** out, const Label& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
|
||||
//! \overload
|
||||
inline FuncCallNode* call(const Imm& target, const FuncSignature& sign) { return addCall(Inst::kIdCall, target, sign); }
|
||||
inline Error invoke(InvokeNode** out, const Imm& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
|
||||
//! \overload
|
||||
inline FuncCallNode* call(uint64_t target, const FuncSignature& sign) { return addCall(Inst::kIdCall, Imm(int64_t(target)), sign); }
|
||||
inline Error invoke(InvokeNode** out, uint64_t target, const FuncSignature& signature) { return invoke_(out, Imm(int64_t(target)), signature); }
|
||||
|
||||
#ifndef _DOXYGEN
|
||||
template<typename Target>
|
||||
ASMJIT_DEPRECATED("Use invoke() instead of call()")
|
||||
inline InvokeNode* call(const Target& target, const FuncSignature& signature) {
|
||||
InvokeNode* invokeNode;
|
||||
invoke(&invokeNode, target, signature);
|
||||
return invokeNode;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! Return.
|
||||
inline FuncRetNode* ret() { return addRet(Operand(), Operand()); }
|
||||
@ -260,7 +691,9 @@ public:
|
||||
|
||||
using EmitterExplicitT<Compiler>::jmp;
|
||||
|
||||
//! Adds a jump to the given `target` with the provided jump `annotation`.
|
||||
inline Error jmp(const BaseReg& target, JumpAnnotation* annotation) { return emitAnnotatedJump(Inst::kIdJmp, target, annotation); }
|
||||
//! \overload
|
||||
inline Error jmp(const BaseMem& target, JumpAnnotation* annotation) { return emitAnnotatedJump(Inst::kIdJmp, target, annotation); }
|
||||
|
||||
//! \}
|
||||
|
4023
3rdparty/asmjit/src/asmjit/x86/x86emitter.h
vendored
4023
3rdparty/asmjit/src/asmjit/x86/x86emitter.h
vendored
File diff suppressed because it is too large
Load Diff
21
3rdparty/asmjit/src/asmjit/x86/x86features.cpp
vendored
21
3rdparty/asmjit/src/asmjit/x86/x86features.cpp
vendored
@ -106,22 +106,25 @@ static inline void simplifyCpuVendor(CpuInfo& cpu, uint32_t d0, uint32_t d1, uin
|
||||
}
|
||||
|
||||
static inline void simplifyCpuBrand(char* s) noexcept {
|
||||
// Used to always clear the current character to ensure that the result
|
||||
// doesn't contain garbage after the new zero terminator.
|
||||
char* d = s;
|
||||
|
||||
char c = s[0];
|
||||
char prev = 0;
|
||||
char curr = s[0];
|
||||
|
||||
// Used to always clear the current character to ensure that the result
|
||||
// doesn't contain garbage after a new null terminator is placed at the end.
|
||||
s[0] = '\0';
|
||||
|
||||
for (;;) {
|
||||
if (curr == 0)
|
||||
if (!c)
|
||||
break;
|
||||
|
||||
if (!(curr == ' ' && (prev == '@' || s[1] == ' ' || s[1] == '@')))
|
||||
*d++ = prev = curr;
|
||||
if (!(c == ' ' && (prev == '@' || s[1] == ' ' || s[1] == '@'))) {
|
||||
*d++ = c;
|
||||
prev = c;
|
||||
}
|
||||
|
||||
curr = *++s;
|
||||
c = *++s;
|
||||
s[0] = '\0';
|
||||
}
|
||||
|
||||
@ -136,7 +139,9 @@ ASMJIT_FAVOR_SIZE void detectCpu(CpuInfo& cpu) noexcept {
|
||||
Features& features = cpu._features.as<Features>();
|
||||
|
||||
cpu.reset();
|
||||
cpu._archInfo.init(ArchInfo::kIdHost);
|
||||
cpu._arch = Environment::kArchHost;
|
||||
cpu._subArch = Environment::kSubArchUnknown;
|
||||
cpu._reserved = 0;
|
||||
cpu._maxLogicalProcessors = 1;
|
||||
features.add(Features::kI486);
|
||||
|
||||
|
4
3rdparty/asmjit/src/asmjit/x86/x86features.h
vendored
4
3rdparty/asmjit/src/asmjit/x86/x86features.h
vendored
@ -40,6 +40,8 @@ class Features : public BaseFeatures {
|
||||
public:
|
||||
//! CPU feature ID.
|
||||
enum Id : uint32_t {
|
||||
// @EnumValuesBegin{"enum": "x86::Features::Id"}@
|
||||
|
||||
kNone = 0, //!< No feature (never set, used internally).
|
||||
|
||||
kMT, //!< CPU has multi-threading capabilities.
|
||||
@ -145,6 +147,8 @@ public:
|
||||
kXSAVEOPT, //!< CPU has XSAVEOPT.
|
||||
kXSAVES, //!< CPU has XSAVES.
|
||||
|
||||
// @EnumValuesEnd@
|
||||
|
||||
kCount //!< Count of X86 CPU features.
|
||||
};
|
||||
|
||||
|
@ -26,8 +26,9 @@
|
||||
|
||||
#include "../core/misc_p.h"
|
||||
#include "../core/support.h"
|
||||
#include "../x86/x86features.h"
|
||||
#include "../x86/x86formatter_p.h"
|
||||
#include "../x86/x86instdb_p.h"
|
||||
#include "../x86/x86logging_p.h"
|
||||
#include "../x86/x86operand.h"
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
@ -37,7 +38,7 @@
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::x86::LoggingInternal - Constants]
|
||||
// [asmjit::x86::FormatterInternal - Constants]
|
||||
// ============================================================================
|
||||
|
||||
struct RegFormatInfo {
|
||||
@ -196,56 +197,181 @@ static const char* x86GetAddressSizeString(uint32_t size) noexcept {
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::x86::LoggingInternal - Format Operand]
|
||||
// [asmjit::x86::FormatterInternal - Format Feature]
|
||||
// ============================================================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error LoggingInternal::formatOperand(
|
||||
Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept {
|
||||
// @EnumStringBegin{"enum": "x86::Features::Id", "output": "sFeature", "strip": "k"}@
|
||||
static const char sFeatureString[] =
|
||||
"None\0"
|
||||
"MT\0"
|
||||
"NX\0"
|
||||
"3DNOW\0"
|
||||
"3DNOW2\0"
|
||||
"ADX\0"
|
||||
"AESNI\0"
|
||||
"ALTMOVCR8\0"
|
||||
"AVX\0"
|
||||
"AVX2\0"
|
||||
"AVX512_4FMAPS\0"
|
||||
"AVX512_4VNNIW\0"
|
||||
"AVX512_BF16\0"
|
||||
"AVX512_BITALG\0"
|
||||
"AVX512_BW\0"
|
||||
"AVX512_CDI\0"
|
||||
"AVX512_DQ\0"
|
||||
"AVX512_ERI\0"
|
||||
"AVX512_F\0"
|
||||
"AVX512_IFMA\0"
|
||||
"AVX512_PFI\0"
|
||||
"AVX512_VBMI\0"
|
||||
"AVX512_VBMI2\0"
|
||||
"AVX512_VL\0"
|
||||
"AVX512_VNNI\0"
|
||||
"AVX512_VP2INTERSECT\0"
|
||||
"AVX512_VPOPCNTDQ\0"
|
||||
"BMI\0"
|
||||
"BMI2\0"
|
||||
"CLDEMOTE\0"
|
||||
"CLFLUSH\0"
|
||||
"CLFLUSHOPT\0"
|
||||
"CLWB\0"
|
||||
"CLZERO\0"
|
||||
"CMOV\0"
|
||||
"CMPXCHG16B\0"
|
||||
"CMPXCHG8B\0"
|
||||
"ENCLV\0"
|
||||
"ENQCMD\0"
|
||||
"ERMS\0"
|
||||
"F16C\0"
|
||||
"FMA\0"
|
||||
"FMA4\0"
|
||||
"FPU\0"
|
||||
"FSGSBASE\0"
|
||||
"FXSR\0"
|
||||
"FXSROPT\0"
|
||||
"GEODE\0"
|
||||
"GFNI\0"
|
||||
"HLE\0"
|
||||
"I486\0"
|
||||
"LAHFSAHF\0"
|
||||
"LWP\0"
|
||||
"LZCNT\0"
|
||||
"MMX\0"
|
||||
"MMX2\0"
|
||||
"MONITOR\0"
|
||||
"MONITORX\0"
|
||||
"MOVBE\0"
|
||||
"MOVDIR64B\0"
|
||||
"MOVDIRI\0"
|
||||
"MPX\0"
|
||||
"MSR\0"
|
||||
"MSSE\0"
|
||||
"OSXSAVE\0"
|
||||
"PCLMULQDQ\0"
|
||||
"PCOMMIT\0"
|
||||
"PCONFIG\0"
|
||||
"POPCNT\0"
|
||||
"PREFETCHW\0"
|
||||
"PREFETCHWT1\0"
|
||||
"RDPID\0"
|
||||
"RDRAND\0"
|
||||
"RDSEED\0"
|
||||
"RDTSC\0"
|
||||
"RDTSCP\0"
|
||||
"RTM\0"
|
||||
"SHA\0"
|
||||
"SKINIT\0"
|
||||
"SMAP\0"
|
||||
"SMEP\0"
|
||||
"SMX\0"
|
||||
"SSE\0"
|
||||
"SSE2\0"
|
||||
"SSE3\0"
|
||||
"SSE4_1\0"
|
||||
"SSE4_2\0"
|
||||
"SSE4A\0"
|
||||
"SSSE3\0"
|
||||
"SVM\0"
|
||||
"TBM\0"
|
||||
"TSX\0"
|
||||
"VAES\0"
|
||||
"VMX\0"
|
||||
"VPCLMULQDQ\0"
|
||||
"WAITPKG\0"
|
||||
"WBNOINVD\0"
|
||||
"XOP\0"
|
||||
"XSAVE\0"
|
||||
"XSAVEC\0"
|
||||
"XSAVEOPT\0"
|
||||
"XSAVES\0"
|
||||
"<Unknown>\0";
|
||||
|
||||
static const uint16_t sFeatureIndex[] = {
|
||||
0, 5, 8, 11, 17, 24, 28, 34, 44, 48, 53, 67, 81, 93, 107, 117, 128, 138,
|
||||
149, 158, 170, 181, 193, 206, 216, 228, 248, 265, 269, 274, 283, 291, 302,
|
||||
307, 314, 319, 330, 340, 346, 353, 358, 363, 367, 372, 376, 385, 390, 398,
|
||||
404, 409, 413, 418, 427, 431, 437, 441, 446, 454, 463, 469, 479, 487, 491,
|
||||
495, 500, 508, 518, 526, 534, 541, 551, 563, 569, 576, 583, 589, 596, 600,
|
||||
604, 611, 616, 621, 625, 629, 634, 639, 646, 653, 659, 665, 669, 673, 677,
|
||||
682, 686, 697, 705, 714, 718, 724, 731, 740, 747
|
||||
};
|
||||
// @EnumStringEnd@
|
||||
|
||||
return sb.append(sFeatureString + sFeatureIndex[Support::min<uint32_t>(featureId, x86::Features::kCount)]);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::x86::FormatterInternal - Format Operand]
|
||||
// ============================================================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t archId,
|
||||
uint32_t arch,
|
||||
const Operand_& op) noexcept {
|
||||
|
||||
if (op.isReg())
|
||||
return formatRegister(sb, flags, emitter, archId, op.as<BaseReg>().type(), op.as<BaseReg>().id());
|
||||
return formatRegister(sb, flags, emitter, arch, op.as<BaseReg>().type(), op.as<BaseReg>().id());
|
||||
|
||||
if (op.isMem()) {
|
||||
const Mem& m = op.as<Mem>();
|
||||
ASMJIT_PROPAGATE(sb.appendString(x86GetAddressSizeString(m.size())));
|
||||
ASMJIT_PROPAGATE(sb.append(x86GetAddressSizeString(m.size())));
|
||||
|
||||
// Segment override prefix.
|
||||
uint32_t seg = m.segmentId();
|
||||
if (seg != SReg::kIdNone && seg < SReg::kIdCount)
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("%s:", x86RegFormatInfo.nameStrings + 224 + seg * 4));
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("%s:", x86RegFormatInfo.nameStrings + 224 + size_t(seg) * 4));
|
||||
|
||||
ASMJIT_PROPAGATE(sb.appendChar('['));
|
||||
ASMJIT_PROPAGATE(sb.append('['));
|
||||
switch (m.addrType()) {
|
||||
case BaseMem::kAddrTypeAbs: ASMJIT_PROPAGATE(sb.appendString("abs ")); break;
|
||||
case BaseMem::kAddrTypeRel: ASMJIT_PROPAGATE(sb.appendString("rel ")); break;
|
||||
case BaseMem::kAddrTypeAbs: ASMJIT_PROPAGATE(sb.append("abs ")); break;
|
||||
case BaseMem::kAddrTypeRel: ASMJIT_PROPAGATE(sb.append("rel ")); break;
|
||||
}
|
||||
|
||||
char opSign = '\0';
|
||||
if (m.hasBase()) {
|
||||
opSign = '+';
|
||||
if (m.hasBaseLabel()) {
|
||||
ASMJIT_PROPAGATE(Logging::formatLabel(sb, flags, emitter, m.baseId()));
|
||||
ASMJIT_PROPAGATE(Formatter::formatLabel(sb, flags, emitter, m.baseId()));
|
||||
}
|
||||
else {
|
||||
uint32_t modifiedFlags = flags;
|
||||
if (m.isRegHome()) {
|
||||
ASMJIT_PROPAGATE(sb.appendString("&"));
|
||||
ASMJIT_PROPAGATE(sb.append("&"));
|
||||
modifiedFlags &= ~FormatOptions::kFlagRegCasts;
|
||||
}
|
||||
ASMJIT_PROPAGATE(formatRegister(sb, modifiedFlags, emitter, archId, m.baseType(), m.baseId()));
|
||||
ASMJIT_PROPAGATE(formatRegister(sb, modifiedFlags, emitter, arch, m.baseType(), m.baseId()));
|
||||
}
|
||||
}
|
||||
|
||||
if (m.hasIndex()) {
|
||||
if (opSign)
|
||||
ASMJIT_PROPAGATE(sb.appendChar(opSign));
|
||||
ASMJIT_PROPAGATE(sb.append(opSign));
|
||||
|
||||
opSign = '+';
|
||||
ASMJIT_PROPAGATE(formatRegister(sb, flags, emitter, archId, m.indexType(), m.indexId()));
|
||||
ASMJIT_PROPAGATE(formatRegister(sb, flags, emitter, arch, m.indexType(), m.indexId()));
|
||||
if (m.hasShift())
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("*%u", 1 << m.shift()));
|
||||
}
|
||||
@ -258,26 +384,26 @@ ASMJIT_FAVOR_SIZE Error LoggingInternal::formatOperand(
|
||||
}
|
||||
|
||||
if (opSign)
|
||||
ASMJIT_PROPAGATE(sb.appendChar(opSign));
|
||||
ASMJIT_PROPAGATE(sb.append(opSign));
|
||||
|
||||
uint32_t base = 10;
|
||||
if ((flags & FormatOptions::kFlagHexOffsets) != 0 && off > 9) {
|
||||
ASMJIT_PROPAGATE(sb.appendString("0x", 2));
|
||||
ASMJIT_PROPAGATE(sb.append("0x", 2));
|
||||
base = 16;
|
||||
}
|
||||
|
||||
ASMJIT_PROPAGATE(sb.appendUInt(off, base));
|
||||
}
|
||||
|
||||
return sb.appendChar(']');
|
||||
return sb.append(']');
|
||||
}
|
||||
|
||||
if (op.isImm()) {
|
||||
const Imm& i = op.as<Imm>();
|
||||
int64_t val = i.i64();
|
||||
int64_t val = i.value();
|
||||
|
||||
if ((flags & FormatOptions::kFlagHexImms) != 0 && uint64_t(val) > 9) {
|
||||
ASMJIT_PROPAGATE(sb.appendString("0x", 2));
|
||||
ASMJIT_PROPAGATE(sb.append("0x", 2));
|
||||
return sb.appendUInt(uint64_t(val), 16);
|
||||
}
|
||||
else {
|
||||
@ -286,14 +412,14 @@ ASMJIT_FAVOR_SIZE Error LoggingInternal::formatOperand(
|
||||
}
|
||||
|
||||
if (op.isLabel()) {
|
||||
return Logging::formatLabel(sb, flags, emitter, op.id());
|
||||
return Formatter::formatLabel(sb, flags, emitter, op.id());
|
||||
}
|
||||
|
||||
return sb.appendString("<None>");
|
||||
return sb.append("<None>");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::x86::LoggingInternal - Format Immediate (Extension)]
|
||||
// [asmjit::x86::FormatterInternal - Format Immediate (Extension)]
|
||||
// ============================================================================
|
||||
|
||||
static constexpr char kImmCharStart = '{';
|
||||
@ -312,22 +438,22 @@ struct ImmBits {
|
||||
char text[48 - 3];
|
||||
};
|
||||
|
||||
ASMJIT_FAVOR_SIZE static Error LoggingInternal_formatImmShuf(String& sb, uint32_t u8, uint32_t bits, uint32_t count) noexcept {
|
||||
ASMJIT_FAVOR_SIZE static Error FormatterInternal_formatImmShuf(String& sb, uint32_t u8, uint32_t bits, uint32_t count) noexcept {
|
||||
uint32_t mask = (1 << bits) - 1;
|
||||
|
||||
for (uint32_t i = 0; i < count; i++, u8 >>= bits) {
|
||||
uint32_t value = u8 & mask;
|
||||
ASMJIT_PROPAGATE(sb.appendChar(i == 0 ? kImmCharStart : kImmCharOr));
|
||||
ASMJIT_PROPAGATE(sb.append(i == 0 ? kImmCharStart : kImmCharOr));
|
||||
ASMJIT_PROPAGATE(sb.appendUInt(value));
|
||||
}
|
||||
|
||||
if (kImmCharEnd)
|
||||
ASMJIT_PROPAGATE(sb.appendChar(kImmCharEnd));
|
||||
ASMJIT_PROPAGATE(sb.append(kImmCharEnd));
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ASMJIT_FAVOR_SIZE static Error LoggingInternal_formatImmBits(String& sb, uint32_t u8, const ImmBits* bits, uint32_t count) noexcept {
|
||||
ASMJIT_FAVOR_SIZE static Error FormatterInternal_formatImmBits(String& sb, uint32_t u8, const ImmBits* bits, uint32_t count) noexcept {
|
||||
uint32_t n = 0;
|
||||
char buf[64];
|
||||
|
||||
@ -354,33 +480,33 @@ ASMJIT_FAVOR_SIZE static Error LoggingInternal_formatImmBits(String& sb, uint32_
|
||||
if (!str[0])
|
||||
continue;
|
||||
|
||||
ASMJIT_PROPAGATE(sb.appendChar(++n == 1 ? kImmCharStart : kImmCharOr));
|
||||
ASMJIT_PROPAGATE(sb.appendString(str));
|
||||
ASMJIT_PROPAGATE(sb.append(++n == 1 ? kImmCharStart : kImmCharOr));
|
||||
ASMJIT_PROPAGATE(sb.append(str));
|
||||
}
|
||||
|
||||
if (n && kImmCharEnd)
|
||||
ASMJIT_PROPAGATE(sb.appendChar(kImmCharEnd));
|
||||
ASMJIT_PROPAGATE(sb.append(kImmCharEnd));
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ASMJIT_FAVOR_SIZE static Error LoggingInternal_formatImmText(String& sb, uint32_t u8, uint32_t bits, uint32_t advance, const char* text, uint32_t count = 1) noexcept {
|
||||
ASMJIT_FAVOR_SIZE static Error FormatterInternal_formatImmText(String& sb, uint32_t u8, uint32_t bits, uint32_t advance, const char* text, uint32_t count = 1) noexcept {
|
||||
uint32_t mask = (1u << bits) - 1;
|
||||
uint32_t pos = 0;
|
||||
|
||||
for (uint32_t i = 0; i < count; i++, u8 >>= bits, pos += advance) {
|
||||
uint32_t value = (u8 & mask) + pos;
|
||||
ASMJIT_PROPAGATE(sb.appendChar(i == 0 ? kImmCharStart : kImmCharOr));
|
||||
ASMJIT_PROPAGATE(sb.appendString(Support::findPackedString(text, value)));
|
||||
ASMJIT_PROPAGATE(sb.append(i == 0 ? kImmCharStart : kImmCharOr));
|
||||
ASMJIT_PROPAGATE(sb.append(Support::findPackedString(text, value)));
|
||||
}
|
||||
|
||||
if (kImmCharEnd)
|
||||
ASMJIT_PROPAGATE(sb.appendChar(kImmCharEnd));
|
||||
ASMJIT_PROPAGATE(sb.append(kImmCharEnd));
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
ASMJIT_FAVOR_SIZE static Error LoggingInternal_explainConst(
|
||||
ASMJIT_FAVOR_SIZE static Error FormatterInternal_explainConst(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
uint32_t instId,
|
||||
@ -456,51 +582,51 @@ ASMJIT_FAVOR_SIZE static Error LoggingInternal_explainConst(
|
||||
{ 0x08u, 3, ImmBits::kModeLookup, "\0" "INEXACT\0" }
|
||||
};
|
||||
|
||||
uint32_t u8 = imm.u8();
|
||||
uint32_t u8 = imm.valueAs<uint8_t>();
|
||||
switch (instId) {
|
||||
case Inst::kIdVblendpd:
|
||||
case Inst::kIdBlendpd:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 1, vecSize / 8);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 1, vecSize / 8);
|
||||
|
||||
case Inst::kIdVblendps:
|
||||
case Inst::kIdBlendps:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 1, vecSize / 4);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 1, vecSize / 4);
|
||||
|
||||
case Inst::kIdVcmppd:
|
||||
case Inst::kIdVcmpps:
|
||||
case Inst::kIdVcmpsd:
|
||||
case Inst::kIdVcmpss:
|
||||
return LoggingInternal_formatImmText(sb, u8, 5, 0, vcmpx);
|
||||
return FormatterInternal_formatImmText(sb, u8, 5, 0, vcmpx);
|
||||
|
||||
case Inst::kIdCmppd:
|
||||
case Inst::kIdCmpps:
|
||||
case Inst::kIdCmpsd:
|
||||
case Inst::kIdCmpss:
|
||||
return LoggingInternal_formatImmText(sb, u8, 3, 0, vcmpx);
|
||||
return FormatterInternal_formatImmText(sb, u8, 3, 0, vcmpx);
|
||||
|
||||
case Inst::kIdVdbpsadbw:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 2, 4);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 2, 4);
|
||||
|
||||
case Inst::kIdVdppd:
|
||||
case Inst::kIdVdpps:
|
||||
case Inst::kIdDppd:
|
||||
case Inst::kIdDpps:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 1, 8);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 1, 8);
|
||||
|
||||
case Inst::kIdVmpsadbw:
|
||||
case Inst::kIdMpsadbw:
|
||||
return LoggingInternal_formatImmBits(sb, u8, vmpsadbw, Support::min<uint32_t>(vecSize / 8, 4));
|
||||
return FormatterInternal_formatImmBits(sb, u8, vmpsadbw, Support::min<uint32_t>(vecSize / 8, 4));
|
||||
|
||||
case Inst::kIdVpblendw:
|
||||
case Inst::kIdPblendw:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 1, 8);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 1, 8);
|
||||
|
||||
case Inst::kIdVpblendd:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 1, Support::min<uint32_t>(vecSize / 4, 8));
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 1, Support::min<uint32_t>(vecSize / 4, 8));
|
||||
|
||||
case Inst::kIdVpclmulqdq:
|
||||
case Inst::kIdPclmulqdq:
|
||||
return LoggingInternal_formatImmBits(sb, u8, vpclmulqdq, ASMJIT_ARRAY_SIZE(vpclmulqdq));
|
||||
return FormatterInternal_formatImmBits(sb, u8, vpclmulqdq, ASMJIT_ARRAY_SIZE(vpclmulqdq));
|
||||
|
||||
case Inst::kIdVroundpd:
|
||||
case Inst::kIdVroundps:
|
||||
@ -510,57 +636,57 @@ ASMJIT_FAVOR_SIZE static Error LoggingInternal_explainConst(
|
||||
case Inst::kIdRoundps:
|
||||
case Inst::kIdRoundsd:
|
||||
case Inst::kIdRoundss:
|
||||
return LoggingInternal_formatImmBits(sb, u8, vroundxx, ASMJIT_ARRAY_SIZE(vroundxx));
|
||||
return FormatterInternal_formatImmBits(sb, u8, vroundxx, ASMJIT_ARRAY_SIZE(vroundxx));
|
||||
|
||||
case Inst::kIdVshufpd:
|
||||
case Inst::kIdShufpd:
|
||||
return LoggingInternal_formatImmText(sb, u8, 1, 2, vshufpd, Support::min<uint32_t>(vecSize / 8, 8));
|
||||
return FormatterInternal_formatImmText(sb, u8, 1, 2, vshufpd, Support::min<uint32_t>(vecSize / 8, 8));
|
||||
|
||||
case Inst::kIdVshufps:
|
||||
case Inst::kIdShufps:
|
||||
return LoggingInternal_formatImmText(sb, u8, 2, 4, vshufps, 4);
|
||||
return FormatterInternal_formatImmText(sb, u8, 2, 4, vshufps, 4);
|
||||
|
||||
case Inst::kIdVcvtps2ph:
|
||||
return LoggingInternal_formatImmBits(sb, u8, vroundxx, 1);
|
||||
return FormatterInternal_formatImmBits(sb, u8, vroundxx, 1);
|
||||
|
||||
case Inst::kIdVperm2f128:
|
||||
case Inst::kIdVperm2i128:
|
||||
return LoggingInternal_formatImmBits(sb, u8, vperm2x128, ASMJIT_ARRAY_SIZE(vperm2x128));
|
||||
return FormatterInternal_formatImmBits(sb, u8, vperm2x128, ASMJIT_ARRAY_SIZE(vperm2x128));
|
||||
|
||||
case Inst::kIdVpermilpd:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 1, vecSize / 8);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 1, vecSize / 8);
|
||||
|
||||
case Inst::kIdVpermilps:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 2, 4);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 2, 4);
|
||||
|
||||
case Inst::kIdVpshufd:
|
||||
case Inst::kIdPshufd:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 2, 4);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 2, 4);
|
||||
|
||||
case Inst::kIdVpshufhw:
|
||||
case Inst::kIdVpshuflw:
|
||||
case Inst::kIdPshufhw:
|
||||
case Inst::kIdPshuflw:
|
||||
case Inst::kIdPshufw:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 2, 4);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 2, 4);
|
||||
|
||||
case Inst::kIdVfixupimmpd:
|
||||
case Inst::kIdVfixupimmps:
|
||||
case Inst::kIdVfixupimmsd:
|
||||
case Inst::kIdVfixupimmss:
|
||||
return LoggingInternal_formatImmBits(sb, u8, vfixupimmxx, ASMJIT_ARRAY_SIZE(vfixupimmxx));
|
||||
return FormatterInternal_formatImmBits(sb, u8, vfixupimmxx, ASMJIT_ARRAY_SIZE(vfixupimmxx));
|
||||
|
||||
case Inst::kIdVfpclasspd:
|
||||
case Inst::kIdVfpclassps:
|
||||
case Inst::kIdVfpclasssd:
|
||||
case Inst::kIdVfpclassss:
|
||||
return LoggingInternal_formatImmBits(sb, u8, vfpclassxx, ASMJIT_ARRAY_SIZE(vfpclassxx));
|
||||
return FormatterInternal_formatImmBits(sb, u8, vfpclassxx, ASMJIT_ARRAY_SIZE(vfpclassxx));
|
||||
|
||||
case Inst::kIdVgetmantpd:
|
||||
case Inst::kIdVgetmantps:
|
||||
case Inst::kIdVgetmantsd:
|
||||
case Inst::kIdVgetmantss:
|
||||
return LoggingInternal_formatImmBits(sb, u8, vgetmantxx, ASMJIT_ARRAY_SIZE(vgetmantxx));
|
||||
return FormatterInternal_formatImmBits(sb, u8, vgetmantxx, ASMJIT_ARRAY_SIZE(vgetmantxx));
|
||||
|
||||
case Inst::kIdVpcmpb:
|
||||
case Inst::kIdVpcmpd:
|
||||
@ -570,7 +696,7 @@ ASMJIT_FAVOR_SIZE static Error LoggingInternal_explainConst(
|
||||
case Inst::kIdVpcmpud:
|
||||
case Inst::kIdVpcmpuq:
|
||||
case Inst::kIdVpcmpuw:
|
||||
return LoggingInternal_formatImmText(sb, u8, 3, 0, vpcmpx);
|
||||
return FormatterInternal_formatImmText(sb, u8, 3, 0, vpcmpx);
|
||||
|
||||
case Inst::kIdVpcomb:
|
||||
case Inst::kIdVpcomd:
|
||||
@ -580,21 +706,21 @@ ASMJIT_FAVOR_SIZE static Error LoggingInternal_explainConst(
|
||||
case Inst::kIdVpcomud:
|
||||
case Inst::kIdVpcomuq:
|
||||
case Inst::kIdVpcomuw:
|
||||
return LoggingInternal_formatImmText(sb, u8, 3, 0, vpcomx);
|
||||
return FormatterInternal_formatImmText(sb, u8, 3, 0, vpcomx);
|
||||
|
||||
case Inst::kIdVpermq:
|
||||
case Inst::kIdVpermpd:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 2, 4);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 2, 4);
|
||||
|
||||
case Inst::kIdVpternlogd:
|
||||
case Inst::kIdVpternlogq:
|
||||
return LoggingInternal_formatImmShuf(sb, u8, 1, 8);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, 1, 8);
|
||||
|
||||
case Inst::kIdVrangepd:
|
||||
case Inst::kIdVrangeps:
|
||||
case Inst::kIdVrangesd:
|
||||
case Inst::kIdVrangess:
|
||||
return LoggingInternal_formatImmBits(sb, u8, vrangexx, ASMJIT_ARRAY_SIZE(vrangexx));
|
||||
return FormatterInternal_formatImmBits(sb, u8, vrangexx, ASMJIT_ARRAY_SIZE(vrangexx));
|
||||
|
||||
case Inst::kIdVreducepd:
|
||||
case Inst::kIdVreduceps:
|
||||
@ -604,7 +730,7 @@ ASMJIT_FAVOR_SIZE static Error LoggingInternal_explainConst(
|
||||
case Inst::kIdVrndscaleps:
|
||||
case Inst::kIdVrndscalesd:
|
||||
case Inst::kIdVrndscaless:
|
||||
return LoggingInternal_formatImmBits(sb, u8, vreducexx_vrndscalexx, ASMJIT_ARRAY_SIZE(vreducexx_vrndscalexx));
|
||||
return FormatterInternal_formatImmBits(sb, u8, vreducexx_vrndscalexx, ASMJIT_ARRAY_SIZE(vreducexx_vrndscalexx));
|
||||
|
||||
case Inst::kIdVshuff32x4:
|
||||
case Inst::kIdVshuff64x2:
|
||||
@ -612,7 +738,7 @@ ASMJIT_FAVOR_SIZE static Error LoggingInternal_explainConst(
|
||||
case Inst::kIdVshufi64x2: {
|
||||
uint32_t count = Support::max<uint32_t>(vecSize / 16, 2u);
|
||||
uint32_t bits = count <= 2 ? 1u : 2u;
|
||||
return LoggingInternal_formatImmShuf(sb, u8, bits, count);
|
||||
return FormatterInternal_formatImmShuf(sb, u8, bits, count);
|
||||
}
|
||||
|
||||
default:
|
||||
@ -621,11 +747,11 @@ ASMJIT_FAVOR_SIZE static Error LoggingInternal_explainConst(
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::x86::LoggingInternal - Format Register]
|
||||
// [asmjit::x86::FormatterInternal - Format Register]
|
||||
// ============================================================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error LoggingInternal::formatRegister(String& sb, uint32_t flags, const BaseEmitter* emitter, uint32_t archId, uint32_t rType, uint32_t rId) noexcept {
|
||||
DebugUtils::unused(archId);
|
||||
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegister(String& sb, uint32_t flags, const BaseEmitter* emitter, uint32_t arch, uint32_t rType, uint32_t rId) noexcept {
|
||||
DebugUtils::unused(arch);
|
||||
const RegFormatInfo& info = x86RegFormatInfo;
|
||||
|
||||
#ifndef ASMJIT_NO_COMPILER
|
||||
@ -638,7 +764,7 @@ ASMJIT_FAVOR_SIZE Error LoggingInternal::formatRegister(String& sb, uint32_t fla
|
||||
|
||||
const char* name = vReg->name();
|
||||
if (name && name[0] != '\0')
|
||||
ASMJIT_PROPAGATE(sb.appendString(name));
|
||||
ASMJIT_PROPAGATE(sb.append(name));
|
||||
else
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("%%%u", unsigned(Operand::virtIdToIndex(rId))));
|
||||
|
||||
@ -660,7 +786,7 @@ ASMJIT_FAVOR_SIZE Error LoggingInternal::formatRegister(String& sb, uint32_t fla
|
||||
const RegFormatInfo::NameEntry& nameEntry = info.nameEntries[rType];
|
||||
|
||||
if (rId < nameEntry.specialCount)
|
||||
return sb.appendString(info.nameStrings + nameEntry.specialIndex + rId * 4);
|
||||
return sb.append(info.nameStrings + nameEntry.specialIndex + rId * 4);
|
||||
|
||||
if (rId < nameEntry.count)
|
||||
return sb.appendFormat(info.nameStrings + nameEntry.formatIndex, unsigned(rId));
|
||||
@ -674,15 +800,15 @@ ASMJIT_FAVOR_SIZE Error LoggingInternal::formatRegister(String& sb, uint32_t fla
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::x86::LoggingInternal - Format Instruction]
|
||||
// [asmjit::x86::FormatterInternal - Format Instruction]
|
||||
// ============================================================================
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error LoggingInternal::formatInstruction(
|
||||
ASMJIT_FAVOR_SIZE Error FormatterInternal::formatInstruction(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t archId,
|
||||
const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept {
|
||||
uint32_t arch,
|
||||
const BaseInst& inst, const Operand_* operands, size_t opCount) noexcept {
|
||||
|
||||
uint32_t instId = inst.id();
|
||||
uint32_t options = inst.options();
|
||||
@ -690,21 +816,21 @@ ASMJIT_FAVOR_SIZE Error LoggingInternal::formatInstruction(
|
||||
// Format instruction options and instruction mnemonic.
|
||||
if (instId < Inst::_kIdCount) {
|
||||
// SHORT|LONG options.
|
||||
if (options & Inst::kOptionShortForm) ASMJIT_PROPAGATE(sb.appendString("short "));
|
||||
if (options & Inst::kOptionLongForm) ASMJIT_PROPAGATE(sb.appendString("long "));
|
||||
if (options & Inst::kOptionShortForm) ASMJIT_PROPAGATE(sb.append("short "));
|
||||
if (options & Inst::kOptionLongForm) ASMJIT_PROPAGATE(sb.append("long "));
|
||||
|
||||
// LOCK|XACQUIRE|XRELEASE options.
|
||||
if (options & Inst::kOptionXAcquire) ASMJIT_PROPAGATE(sb.appendString("xacquire "));
|
||||
if (options & Inst::kOptionXRelease) ASMJIT_PROPAGATE(sb.appendString("xrelease "));
|
||||
if (options & Inst::kOptionLock) ASMJIT_PROPAGATE(sb.appendString("lock "));
|
||||
if (options & Inst::kOptionXAcquire) ASMJIT_PROPAGATE(sb.append("xacquire "));
|
||||
if (options & Inst::kOptionXRelease) ASMJIT_PROPAGATE(sb.append("xrelease "));
|
||||
if (options & Inst::kOptionLock) ASMJIT_PROPAGATE(sb.append("lock "));
|
||||
|
||||
// REP|REPNE options.
|
||||
if (options & (Inst::kOptionRep | Inst::kOptionRepne)) {
|
||||
sb.appendString((options & Inst::kOptionRep) ? "rep " : "repnz ");
|
||||
sb.append((options & Inst::kOptionRep) ? "rep " : "repnz ");
|
||||
if (inst.hasExtraReg()) {
|
||||
ASMJIT_PROPAGATE(sb.appendString("{"));
|
||||
ASMJIT_PROPAGATE(formatOperand(sb, flags, emitter, archId, inst.extraReg().toReg<BaseReg>()));
|
||||
ASMJIT_PROPAGATE(sb.appendString("} "));
|
||||
ASMJIT_PROPAGATE(sb.append("{"));
|
||||
ASMJIT_PROPAGATE(formatOperand(sb, flags, emitter, arch, inst.extraReg().toReg<BaseReg>()));
|
||||
ASMJIT_PROPAGATE(sb.append("} "));
|
||||
}
|
||||
}
|
||||
|
||||
@ -715,23 +841,23 @@ ASMJIT_FAVOR_SIZE Error LoggingInternal::formatInstruction(
|
||||
Inst::kOptionOpCodeB |
|
||||
Inst::kOptionOpCodeW ;
|
||||
if (options & kRXBWMask) {
|
||||
sb.appendString("rex.");
|
||||
if (options & Inst::kOptionOpCodeR) sb.appendChar('r');
|
||||
if (options & Inst::kOptionOpCodeX) sb.appendChar('x');
|
||||
if (options & Inst::kOptionOpCodeB) sb.appendChar('b');
|
||||
if (options & Inst::kOptionOpCodeW) sb.appendChar('w');
|
||||
sb.appendChar(' ');
|
||||
sb.append("rex.");
|
||||
if (options & Inst::kOptionOpCodeR) sb.append('r');
|
||||
if (options & Inst::kOptionOpCodeX) sb.append('x');
|
||||
if (options & Inst::kOptionOpCodeB) sb.append('b');
|
||||
if (options & Inst::kOptionOpCodeW) sb.append('w');
|
||||
sb.append(' ');
|
||||
}
|
||||
else {
|
||||
ASMJIT_PROPAGATE(sb.appendString("rex "));
|
||||
ASMJIT_PROPAGATE(sb.append("rex "));
|
||||
}
|
||||
}
|
||||
|
||||
// VEX|EVEX options.
|
||||
if (options & Inst::kOptionVex3) ASMJIT_PROPAGATE(sb.appendString("vex3 "));
|
||||
if (options & Inst::kOptionEvex) ASMJIT_PROPAGATE(sb.appendString("evex "));
|
||||
if (options & Inst::kOptionVex3) ASMJIT_PROPAGATE(sb.append("vex3 "));
|
||||
if (options & Inst::kOptionEvex) ASMJIT_PROPAGATE(sb.append("evex "));
|
||||
|
||||
ASMJIT_PROPAGATE(InstAPI::instIdToString(archId, instId, sb));
|
||||
ASMJIT_PROPAGATE(InstAPI::instIdToString(arch, instId, sb));
|
||||
}
|
||||
else {
|
||||
ASMJIT_PROPAGATE(sb.appendFormat("[InstId=#%u]", unsigned(instId)));
|
||||
@ -741,29 +867,29 @@ ASMJIT_FAVOR_SIZE Error LoggingInternal::formatInstruction(
|
||||
const Operand_& op = operands[i];
|
||||
if (op.isNone()) break;
|
||||
|
||||
ASMJIT_PROPAGATE(sb.appendString(i == 0 ? " " : ", "));
|
||||
ASMJIT_PROPAGATE(formatOperand(sb, flags, emitter, archId, op));
|
||||
ASMJIT_PROPAGATE(sb.append(i == 0 ? " " : ", "));
|
||||
ASMJIT_PROPAGATE(formatOperand(sb, flags, emitter, arch, op));
|
||||
|
||||
if (op.isImm() && (flags & FormatOptions::kFlagExplainImms)) {
|
||||
uint32_t vecSize = 16;
|
||||
for (uint32_t j = 0; j < opCount; j++)
|
||||
if (operands[j].isReg())
|
||||
vecSize = Support::max<uint32_t>(vecSize, operands[j].size());
|
||||
ASMJIT_PROPAGATE(LoggingInternal_explainConst(sb, flags, instId, vecSize, op.as<Imm>()));
|
||||
ASMJIT_PROPAGATE(FormatterInternal_explainConst(sb, flags, instId, vecSize, op.as<Imm>()));
|
||||
}
|
||||
|
||||
// Support AVX-512 masking - {k}{z}.
|
||||
if (i == 0) {
|
||||
if (inst.extraReg().group() == Reg::kGroupKReg) {
|
||||
ASMJIT_PROPAGATE(sb.appendString(" {"));
|
||||
ASMJIT_PROPAGATE(formatRegister(sb, flags, emitter, archId, inst.extraReg().type(), inst.extraReg().id()));
|
||||
ASMJIT_PROPAGATE(sb.appendChar('}'));
|
||||
ASMJIT_PROPAGATE(sb.append(" {"));
|
||||
ASMJIT_PROPAGATE(formatRegister(sb, flags, emitter, arch, inst.extraReg().type(), inst.extraReg().id()));
|
||||
ASMJIT_PROPAGATE(sb.append('}'));
|
||||
|
||||
if (options & Inst::kOptionZMask)
|
||||
ASMJIT_PROPAGATE(sb.appendString("{z}"));
|
||||
ASMJIT_PROPAGATE(sb.append("{z}"));
|
||||
}
|
||||
else if (options & Inst::kOptionZMask) {
|
||||
ASMJIT_PROPAGATE(sb.appendString(" {z}"));
|
||||
ASMJIT_PROPAGATE(sb.append(" {z}"));
|
||||
}
|
||||
}
|
||||
|
@ -21,52 +21,60 @@
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#ifndef ASMJIT_X86_X86LOGGING_P_H_INCLUDED
|
||||
#define ASMJIT_X86_X86LOGGING_P_H_INCLUDED
|
||||
#ifndef ASMJIT_X86_X86FORMATTER_P_H_INCLUDED
|
||||
#define ASMJIT_X86_X86FORMATTER_P_H_INCLUDED
|
||||
|
||||
#include "../core/api-config.h"
|
||||
#ifndef ASMJIT_NO_LOGGING
|
||||
|
||||
#include "../core/logging.h"
|
||||
#include "../core/formatter.h"
|
||||
#include "../core/string.h"
|
||||
#include "../x86/x86globals.h"
|
||||
|
||||
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
|
||||
//! \cond INTERNAL
|
||||
//! \addtogroup asmjit_x86
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::x86::LoggingInternal]
|
||||
// [asmjit::x86::FormatterInternal]
|
||||
// ============================================================================
|
||||
|
||||
namespace LoggingInternal {
|
||||
Error formatRegister(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t archId,
|
||||
uint32_t regType,
|
||||
uint32_t regId) noexcept;
|
||||
namespace FormatterInternal {
|
||||
|
||||
Error formatOperand(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t archId,
|
||||
const Operand_& op) noexcept;
|
||||
Error formatFeature(
|
||||
String& sb,
|
||||
uint32_t featureId) noexcept;
|
||||
|
||||
Error formatInstruction(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t archId,
|
||||
const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept;
|
||||
};
|
||||
Error formatRegister(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t arch,
|
||||
uint32_t regType,
|
||||
uint32_t regId) noexcept;
|
||||
|
||||
Error formatOperand(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t arch,
|
||||
const Operand_& op) noexcept;
|
||||
|
||||
Error formatInstruction(
|
||||
String& sb,
|
||||
uint32_t flags,
|
||||
const BaseEmitter* emitter,
|
||||
uint32_t arch,
|
||||
const BaseInst& inst, const Operand_* operands, size_t opCount) noexcept;
|
||||
|
||||
} // {FormatterInternal}
|
||||
|
||||
//! \}
|
||||
//! \endcond
|
||||
|
||||
ASMJIT_END_SUB_NAMESPACE
|
||||
|
||||
#endif // !ASMJIT_NO_LOGGING
|
||||
#endif // ASMJIT_X86_X86LOGGING_P_H_INCLUDED
|
||||
#endif // ASMJIT_X86_X86FORMATTER_P_H_INCLUDED
|
170
3rdparty/asmjit/src/asmjit/x86/x86globals.h
vendored
170
3rdparty/asmjit/src/asmjit/x86/x86globals.h
vendored
@ -1652,14 +1652,14 @@ namespace Condition {
|
||||
kUnsignedGT = kA, //!< Unsigned `a > b`.
|
||||
kUnsignedGE = kAE, //!< Unsigned `a >= b`.
|
||||
|
||||
kZero = kZ,
|
||||
kNotZero = kNZ,
|
||||
kZero = kZ, //!< Zero flag.
|
||||
kNotZero = kNZ, //!< Non-zero flag.
|
||||
|
||||
kNegative = kS,
|
||||
kPositive = kNS,
|
||||
kNegative = kS, //!< Sign flag.
|
||||
kPositive = kNS, //!< No sign flag.
|
||||
|
||||
kParityEven = kP,
|
||||
kParityOdd = kPO
|
||||
kParityEven = kP, //!< Even parity flag.
|
||||
kParityOdd = kPO //!< Odd parity flag.
|
||||
};
|
||||
|
||||
static constexpr uint8_t reverseTable[kCount] = {
|
||||
@ -1696,54 +1696,92 @@ namespace Condition {
|
||||
// [asmjit::x86::FpuWord]
|
||||
// ============================================================================
|
||||
|
||||
//! FPU control and status word.
|
||||
//! FPU control and status words.
|
||||
namespace FpuWord {
|
||||
//! FPU status word.
|
||||
enum Status : uint32_t {
|
||||
//! Invalid operation.
|
||||
kStatusInvalid = 0x0001u,
|
||||
//! Denormalized operand.
|
||||
kStatusDenormalized = 0x0002u,
|
||||
//! Division by zero.
|
||||
kStatusDivByZero = 0x0004u,
|
||||
//! Overflown.
|
||||
kStatusOverflow = 0x0008u,
|
||||
//! Underflown.
|
||||
kStatusUnderflow = 0x0010u,
|
||||
//! Precision lost.
|
||||
kStatusPrecision = 0x0020u,
|
||||
//! Stack fault.
|
||||
kStatusStackFault = 0x0040u,
|
||||
//! Interrupt.
|
||||
kStatusInterrupt = 0x0080u,
|
||||
//! C0 flag.
|
||||
kStatusC0 = 0x0100u,
|
||||
//! C1 flag.
|
||||
kStatusC1 = 0x0200u,
|
||||
//! C2 flag.
|
||||
kStatusC2 = 0x0400u,
|
||||
//! Top of the stack.
|
||||
kStatusTop = 0x3800u,
|
||||
//! C3 flag.
|
||||
kStatusC3 = 0x4000u,
|
||||
//! FPU is busy.
|
||||
kStatusBusy = 0x8000u
|
||||
};
|
||||
|
||||
//! FPU control word.
|
||||
enum Control : uint32_t {
|
||||
// Bits 0-5.
|
||||
// [Bits 0-5]
|
||||
|
||||
//! Exception mask (0x3F).
|
||||
kControlEM_Mask = 0x003Fu,
|
||||
//! Invalid operation exception.
|
||||
kControlEM_Invalid = 0x0001u,
|
||||
//! Denormalized operand exception.
|
||||
kControlEM_Denormal = 0x0002u,
|
||||
//! Division by zero exception.
|
||||
kControlEM_DivByZero = 0x0004u,
|
||||
//! Overflow exception.
|
||||
kControlEM_Overflow = 0x0008u,
|
||||
//! Underflow exception.
|
||||
kControlEM_Underflow = 0x0010u,
|
||||
//! Inexact operation exception.
|
||||
kControlEM_Inexact = 0x0020u,
|
||||
|
||||
// Bits 8-9.
|
||||
// [Bits 8-9]
|
||||
|
||||
//! Precision control mask.
|
||||
kControlPC_Mask = 0x0300u,
|
||||
//! Single precision (24 bits).
|
||||
kControlPC_Float = 0x0000u,
|
||||
//! Reserved.
|
||||
kControlPC_Reserved = 0x0100u,
|
||||
//! Double precision (53 bits).
|
||||
kControlPC_Double = 0x0200u,
|
||||
//! Extended precision (64 bits).
|
||||
kControlPC_Extended = 0x0300u,
|
||||
|
||||
// Bits 10-11.
|
||||
// [Bits 10-11]
|
||||
|
||||
//! Rounding control mask.
|
||||
kControlRC_Mask = 0x0C00u,
|
||||
//! Round to nearest even.
|
||||
kControlRC_Nearest = 0x0000u,
|
||||
//! Round down (floor).
|
||||
kControlRC_Down = 0x0400u,
|
||||
//! Round up (ceil).
|
||||
kControlRC_Up = 0x0800u,
|
||||
//! Round towards zero (truncate).
|
||||
kControlRC_Truncate = 0x0C00u,
|
||||
|
||||
// Bit 12.
|
||||
// [Bit 12]
|
||||
|
||||
//! Infinity control.
|
||||
kControlIC_Mask = 0x1000u,
|
||||
//! Projective (not supported on X64).
|
||||
kControlIC_Projective = 0x0000u,
|
||||
//! Affine (default).
|
||||
kControlIC_Affine = 0x1000u
|
||||
};
|
||||
}
|
||||
@ -1760,26 +1798,39 @@ namespace Status {
|
||||
// [Architecture Neutral Flags - 0x000000FF]
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
kCF = 0x00000001u, //!< Carry flag.
|
||||
kOF = 0x00000002u, //!< Signed overflow flag.
|
||||
kSF = 0x00000004u, //!< Sign flag (negative/sign, if set).
|
||||
kZF = 0x00000008u, //!< Zero and/or equality flag (1 if zero/equal).
|
||||
//! Carry flag.
|
||||
kCF = 0x00000001u,
|
||||
//! Signed overflow flag.
|
||||
kOF = 0x00000002u,
|
||||
//! Sign flag (negative/sign, if set).
|
||||
kSF = 0x00000004u,
|
||||
//! Zero and/or equality flag (1 if zero/equal).
|
||||
kZF = 0x00000008u,
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// [Architecture Specific Flags - 0xFFFFFF00]
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
kAF = 0x00000100u, //!< Adjust flag.
|
||||
kPF = 0x00000200u, //!< Parity flag.
|
||||
kDF = 0x00000400u, //!< Direction flag.
|
||||
kIF = 0x00000800u, //!< Interrupt enable flag.
|
||||
//! Adjust flag.
|
||||
kAF = 0x00000100u,
|
||||
//! Parity flag.
|
||||
kPF = 0x00000200u,
|
||||
//! Direction flag.
|
||||
kDF = 0x00000400u,
|
||||
//! Interrupt enable flag.
|
||||
kIF = 0x00000800u,
|
||||
|
||||
kAC = 0x00001000u, //!< Alignment check.
|
||||
//! Alignment check.
|
||||
kAC = 0x00001000u,
|
||||
|
||||
kC0 = 0x00010000u, //!< FPU C0 status flag.
|
||||
kC1 = 0x00020000u, //!< FPU C1 status flag.
|
||||
kC2 = 0x00040000u, //!< FPU C2 status flag.
|
||||
kC3 = 0x00080000u //!< FPU C3 status flag.
|
||||
//! FPU C0 status flag.
|
||||
kC0 = 0x00010000u,
|
||||
//! FPU C1 status flag.
|
||||
kC1 = 0x00020000u,
|
||||
//! FPU C2 status flag.
|
||||
kC2 = 0x00040000u,
|
||||
//! FPU C3 status flag.
|
||||
kC3 = 0x00080000u
|
||||
};
|
||||
}
|
||||
|
||||
@ -1834,12 +1885,18 @@ namespace Predicate {
|
||||
|
||||
//! A predicate used by ROUND[PD|PS|SD|SS] instructions.
|
||||
enum Round : uint32_t {
|
||||
kRoundNearest = 0x00u, //!< Round to nearest (even).
|
||||
kRoundDown = 0x01u, //!< Round to down toward -INF (floor),
|
||||
kRoundUp = 0x02u, //!< Round to up toward +INF (ceil).
|
||||
kRoundTrunc = 0x03u, //!< Round toward zero (truncate).
|
||||
kRoundCurrent = 0x04u, //!< Round to the current rounding mode set (ignores other RC bits).
|
||||
kRoundInexact = 0x08u //!< Avoids inexact exception, if set.
|
||||
//! Round to nearest (even).
|
||||
kRoundNearest = 0x00u,
|
||||
//! Round to down toward -INF (floor),
|
||||
kRoundDown = 0x01u,
|
||||
//! Round to up toward +INF (ceil).
|
||||
kRoundUp = 0x02u,
|
||||
//! Round toward zero (truncate).
|
||||
kRoundTrunc = 0x03u,
|
||||
//! Round to the current rounding mode set (ignores other RC bits).
|
||||
kRoundCurrent = 0x04u,
|
||||
//! Avoids inexact exception, if set.
|
||||
kRoundInexact = 0x08u
|
||||
};
|
||||
|
||||
//! A predicate used by VCMP[PD|PS|SD|SS] instructions.
|
||||
@ -1998,25 +2055,46 @@ namespace Predicate {
|
||||
//! Bitwise ternary logic between 3 operands introduced by AVX-512.
|
||||
namespace TLog {
|
||||
//! A predicate that can be used to create a common predicate for VPTERNLOG[D|Q].
|
||||
//!
|
||||
//! There are 3 inputs to the instruction (\ref kA, \ref kB, \ref kC), and
|
||||
//! ternary logic can define any combination that would be performed on these
|
||||
//! 3 inputs to get the desired output - any combination of AND, OR, XOR, NOT.
|
||||
enum Operator : uint32_t {
|
||||
k0 = 0x00u, //!< 0 value.
|
||||
k1 = 0xFFu, //!< 1 value.
|
||||
kA = 0xF0u, //!< A value.
|
||||
kB = 0xCCu, //!< B value.
|
||||
kC = 0xAAu, //!< C value.
|
||||
kNotA = kA ^ k1, //!< `!A` expression.
|
||||
kNotB = kB ^ k1, //!< `!B` expression.
|
||||
kNotC = kC ^ k1, //!< `!C` expression.
|
||||
//! 0 value.
|
||||
k0 = 0x00u,
|
||||
//! 1 value.
|
||||
k1 = 0xFFu,
|
||||
//! A value.
|
||||
kA = 0xF0u,
|
||||
//! B value.
|
||||
kB = 0xCCu,
|
||||
//! C value.
|
||||
kC = 0xAAu,
|
||||
|
||||
kAB = kA & kB, //!< `A & B` expression.
|
||||
kAC = kA & kC, //!< `A & C` expression.
|
||||
kBC = kB & kC, //!< `B & C` expression.
|
||||
kNotAB = kAB ^ k1, //!< `!(A & B)` expression.
|
||||
kNotAC = kAC ^ k1, //!< `!(A & C)` expression.
|
||||
kNotBC = kBC ^ k1, //!< `!(B & C)` expression.
|
||||
//! `!A` expression.
|
||||
kNotA = kA ^ k1,
|
||||
//! `!B` expression.
|
||||
kNotB = kB ^ k1,
|
||||
//! `!C` expression.
|
||||
kNotC = kC ^ k1,
|
||||
|
||||
kABC = kAB & kC, //!< `A & B & C` expression.
|
||||
kNotABC = kABC ^ k1 //!< `!(A & B & C)` expression.
|
||||
//! `A & B` expression.
|
||||
kAB = kA & kB,
|
||||
//! `A & C` expression.
|
||||
kAC = kA & kC,
|
||||
//! `B & C` expression.
|
||||
kBC = kB & kC,
|
||||
//! `!(A & B)` expression.
|
||||
kNotAB = kAB ^ k1,
|
||||
//! `!(A & C)` expression.
|
||||
kNotAC = kAC ^ k1,
|
||||
//! `!(B & C)` expression.
|
||||
kNotBC = kBC ^ k1,
|
||||
|
||||
//! `A & B & C` expression.
|
||||
kABC = kAB & kC,
|
||||
//! `!(A & B & C)` expression.
|
||||
kNotABC = kABC ^ k1
|
||||
};
|
||||
|
||||
//! Creates an immediate that can be used by VPTERNLOG[D|Q] instructions.
|
||||
|
324
3rdparty/asmjit/src/asmjit/x86/x86instapi.cpp
vendored
324
3rdparty/asmjit/src/asmjit/x86/x86instapi.cpp
vendored
@ -58,18 +58,18 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
// ============================================================================
|
||||
|
||||
#ifndef ASMJIT_NO_TEXT
|
||||
Error InstInternal::instIdToString(uint32_t archId, uint32_t instId, String& output) noexcept {
|
||||
DebugUtils::unused(archId);
|
||||
Error InstInternal::instIdToString(uint32_t arch, uint32_t instId, String& output) noexcept {
|
||||
DebugUtils::unused(arch);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!Inst::isDefinedId(instId)))
|
||||
return DebugUtils::errored(kErrorInvalidInstruction);
|
||||
|
||||
const InstDB::InstInfo& info = InstDB::infoById(instId);
|
||||
return output.appendString(InstDB::_nameData + info._nameDataIndex);
|
||||
return output.append(InstDB::_nameData + info._nameDataIndex);
|
||||
}
|
||||
|
||||
uint32_t InstInternal::stringToInstId(uint32_t archId, const char* s, size_t len) noexcept {
|
||||
DebugUtils::unused(archId);
|
||||
uint32_t InstInternal::stringToInstId(uint32_t arch, const char* s, size_t len) noexcept {
|
||||
DebugUtils::unused(arch);
|
||||
|
||||
if (ASMJIT_UNLIKELY(!s))
|
||||
return Inst::kIdNone;
|
||||
@ -235,18 +235,18 @@ static ASMJIT_INLINE bool x86CheckOSig(const InstDB::OpSignature& op, const Inst
|
||||
return true;
|
||||
}
|
||||
|
||||
ASMJIT_FAVOR_SIZE Error InstInternal::validate(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept {
|
||||
// Only called when `archId` matches X86 family.
|
||||
ASMJIT_ASSERT(ArchInfo::isX86Family(archId));
|
||||
ASMJIT_FAVOR_SIZE Error InstInternal::validate(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, uint32_t validationFlags) noexcept {
|
||||
// Only called when `arch` matches X86 family.
|
||||
ASMJIT_ASSERT(Environment::isFamilyX86(arch));
|
||||
|
||||
const X86ValidationData* vd;
|
||||
if (archId == ArchInfo::kIdX86)
|
||||
if (arch == Environment::kArchX86)
|
||||
vd = &_x86ValidationData;
|
||||
else
|
||||
vd = &_x64ValidationData;
|
||||
|
||||
uint32_t i;
|
||||
uint32_t mode = InstDB::modeFromArchId(archId);
|
||||
uint32_t mode = InstDB::modeFromArch(arch);
|
||||
|
||||
// Get the instruction data.
|
||||
uint32_t instId = inst.id();
|
||||
@ -328,7 +328,6 @@ ASMJIT_FAVOR_SIZE Error InstInternal::validate(uint32_t archId, const BaseInst&
|
||||
// that the register is virtual and its index will be assigned later
|
||||
// by the register allocator. We must pass unless asked to disallow
|
||||
// virtual registers.
|
||||
// TODO: We need an option to refuse virtual regs here.
|
||||
uint32_t regId = op.id();
|
||||
if (regId < Operand::kVirtIdMin) {
|
||||
if (ASMJIT_UNLIKELY(regId >= 32))
|
||||
@ -341,6 +340,8 @@ ASMJIT_FAVOR_SIZE Error InstInternal::validate(uint32_t archId, const BaseInst&
|
||||
combinedRegMask |= regMask;
|
||||
}
|
||||
else {
|
||||
if (!(validationFlags & InstAPI::kValidationFlagVirtRegs))
|
||||
return DebugUtils::errored(kErrorIllegalVirtReg);
|
||||
regMask = 0xFFFFFFFFu;
|
||||
}
|
||||
break;
|
||||
@ -393,6 +394,9 @@ ASMJIT_FAVOR_SIZE Error InstInternal::validate(uint32_t archId, const BaseInst&
|
||||
// memory operand. Basically only usable for string instructions and other
|
||||
// instructions where memory operand is implicit and has 'seg:[reg]' form.
|
||||
if (baseId < Operand::kVirtIdMin) {
|
||||
if (ASMJIT_UNLIKELY(baseId >= 32))
|
||||
return DebugUtils::errored(kErrorInvalidPhysId);
|
||||
|
||||
// Physical base id.
|
||||
regMask = Support::bitMask(baseId);
|
||||
combinedRegMask |= regMask;
|
||||
@ -400,6 +404,8 @@ ASMJIT_FAVOR_SIZE Error InstInternal::validate(uint32_t archId, const BaseInst&
|
||||
else {
|
||||
// Virtual base id - fill the whole mask for implicit mem validation.
|
||||
// The register is not assigned yet, so we cannot predict the phys id.
|
||||
if (!(validationFlags & InstAPI::kValidationFlagVirtRegs))
|
||||
return DebugUtils::errored(kErrorIllegalVirtReg);
|
||||
regMask = 0xFFFFFFFFu;
|
||||
}
|
||||
|
||||
@ -461,8 +467,16 @@ ASMJIT_FAVOR_SIZE Error InstInternal::validate(uint32_t archId, const BaseInst&
|
||||
return DebugUtils::errored(kErrorInvalidAddress);
|
||||
|
||||
uint32_t indexId = m.indexId();
|
||||
if (indexId < Operand::kVirtIdMin)
|
||||
if (indexId < Operand::kVirtIdMin) {
|
||||
if (ASMJIT_UNLIKELY(indexId >= 32))
|
||||
return DebugUtils::errored(kErrorInvalidPhysId);
|
||||
|
||||
combinedRegMask |= Support::bitMask(indexId);
|
||||
}
|
||||
else {
|
||||
if (!(validationFlags & InstAPI::kValidationFlagVirtRegs))
|
||||
return DebugUtils::errored(kErrorIllegalVirtReg);
|
||||
}
|
||||
|
||||
// Only used for implicit memory operands having 'seg:[reg]' form, so clear it.
|
||||
regMask = 0;
|
||||
@ -490,7 +504,7 @@ ASMJIT_FAVOR_SIZE Error InstInternal::validate(uint32_t archId, const BaseInst&
|
||||
}
|
||||
|
||||
case Operand::kOpImm: {
|
||||
uint64_t immValue = op.as<Imm>().u64();
|
||||
uint64_t immValue = op.as<Imm>().valueAs<uint64_t>();
|
||||
uint32_t immFlags = 0;
|
||||
|
||||
if (int64_t(immValue) >= 0) {
|
||||
@ -574,7 +588,9 @@ ASMJIT_FAVOR_SIZE Error InstInternal::validate(uint32_t archId, const BaseInst&
|
||||
}
|
||||
else {
|
||||
// Illegal use of a high 8-bit register with REX prefix.
|
||||
if (ASMJIT_UNLIKELY((combinedOpFlags & InstDB::kOpGpbHi) != 0 && (combinedRegMask & 0xFFFFFF00u) != 0))
|
||||
bool hasREX = inst.hasOption(Inst::kOptionRex) ||
|
||||
((combinedRegMask & 0xFFFFFF00u) != 0);
|
||||
if (ASMJIT_UNLIKELY(hasREX && (combinedOpFlags & InstDB::kOpGpbHi) != 0))
|
||||
return DebugUtils::errored(kErrorInvalidUseOfGpbHi);
|
||||
}
|
||||
|
||||
@ -761,12 +777,6 @@ static const uint64_t rwRegGroupByteMask[Reg::kGroupCount] = {
|
||||
0x00000000000000FFu // RIP.
|
||||
};
|
||||
|
||||
// TODO: Make universal.
|
||||
static ASMJIT_INLINE uint32_t gpRegSizeByArchId(uint32_t archId) noexcept {
|
||||
static const uint8_t table[] = { 0, 4, 8, 4, 8 };
|
||||
return table[archId];
|
||||
}
|
||||
|
||||
static ASMJIT_INLINE void rwZeroExtendGp(OpRWInfo& opRwInfo, const Gp& reg, uint32_t nativeGpSize) noexcept {
|
||||
ASMJIT_ASSERT(BaseReg::isGp(reg.as<Operand>()));
|
||||
if (reg.size() + 4 == nativeGpSize) {
|
||||
@ -793,11 +803,11 @@ static ASMJIT_INLINE void rwZeroExtendNonVec(OpRWInfo& opRwInfo, const Reg& reg)
|
||||
}
|
||||
}
|
||||
|
||||
Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, InstRWInfo& out) noexcept {
|
||||
Error InstInternal::queryRWInfo(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, InstRWInfo* out) noexcept {
|
||||
using namespace Status;
|
||||
|
||||
// Only called when `archId` matches X86 family.
|
||||
ASMJIT_ASSERT(ArchInfo::isX86Family(archId));
|
||||
// Only called when `arch` matches X86 family.
|
||||
ASMJIT_ASSERT(Environment::isFamilyX86(arch));
|
||||
|
||||
// Get the instruction data.
|
||||
uint32_t instId = inst.id();
|
||||
@ -817,14 +827,14 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
const InstDB::RWInfo& instRwInfo = InstDB::rwInfo[InstDB::rwInfoIndex[instId * 2u + uint32_t(opCount != 2)]];
|
||||
const InstDB::RWInfoRm& instRmInfo = InstDB::rwInfoRm[instRwInfo.rmInfo];
|
||||
|
||||
out._instFlags = 0;
|
||||
out._opCount = uint8_t(opCount);
|
||||
out._rmFeature = instRmInfo.rmFeature;
|
||||
out._extraReg.reset();
|
||||
out._readFlags = rwFlags.readFlags;
|
||||
out._writeFlags = rwFlags.writeFlags;
|
||||
out->_instFlags = 0;
|
||||
out->_opCount = uint8_t(opCount);
|
||||
out->_rmFeature = instRmInfo.rmFeature;
|
||||
out->_extraReg.reset();
|
||||
out->_readFlags = rwFlags.readFlags;
|
||||
out->_writeFlags = rwFlags.writeFlags;
|
||||
|
||||
uint32_t nativeGpSize = gpRegSizeByArchId(archId);
|
||||
uint32_t nativeGpSize = Environment::registerSizeFromArch(arch);
|
||||
|
||||
constexpr uint32_t R = OpRWInfo::kRead;
|
||||
constexpr uint32_t W = OpRWInfo::kWrite;
|
||||
@ -839,7 +849,7 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
uint32_t rmMaxSize = 0;
|
||||
|
||||
for (i = 0; i < opCount; i++) {
|
||||
OpRWInfo& op = out._operands[i];
|
||||
OpRWInfo& op = out->_operands[i];
|
||||
const Operand_& srcOp = operands[i];
|
||||
const InstDB::RWInfoOp& rwOpData = InstDB::rwInfoOp[instRwInfo.opInfoIndex[i]];
|
||||
|
||||
@ -893,7 +903,7 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
do {
|
||||
i = it.next();
|
||||
|
||||
OpRWInfo& op = out._operands[i];
|
||||
OpRWInfo& op = out->_operands[i];
|
||||
op.addOpFlags(RegM);
|
||||
|
||||
switch (instRmInfo.category) {
|
||||
@ -932,38 +942,38 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
const Reg& o1 = operands[1].as<Reg>();
|
||||
|
||||
if (o0.isGp() && o1.isGp()) {
|
||||
out._operands[0].reset(W | RegM, operands[0].size());
|
||||
out._operands[1].reset(R | RegM, operands[1].size());
|
||||
out->_operands[0].reset(W | RegM, operands[0].size());
|
||||
out->_operands[1].reset(R | RegM, operands[1].size());
|
||||
|
||||
rwZeroExtendGp(out._operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (o0.isGp() && o1.isSReg()) {
|
||||
out._operands[0].reset(W | RegM, nativeGpSize);
|
||||
out._operands[0].setRmSize(2);
|
||||
out._operands[1].reset(R, 2);
|
||||
out->_operands[0].reset(W | RegM, nativeGpSize);
|
||||
out->_operands[0].setRmSize(2);
|
||||
out->_operands[1].reset(R, 2);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (o0.isSReg() && o1.isGp()) {
|
||||
out._operands[0].reset(W, 2);
|
||||
out._operands[1].reset(R | RegM, 2);
|
||||
out._operands[1].setRmSize(2);
|
||||
out->_operands[0].reset(W, 2);
|
||||
out->_operands[1].reset(R | RegM, 2);
|
||||
out->_operands[1].setRmSize(2);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (o0.isGp() && (o1.isCReg() || o1.isDReg())) {
|
||||
out._operands[0].reset(W, nativeGpSize);
|
||||
out._operands[1].reset(R, nativeGpSize);
|
||||
out._writeFlags = kOF | kSF | kZF | kAF | kPF | kCF;
|
||||
out->_operands[0].reset(W, nativeGpSize);
|
||||
out->_operands[1].reset(R, nativeGpSize);
|
||||
out->_writeFlags = kOF | kSF | kZF | kAF | kPF | kCF;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if ((o0.isCReg() || o0.isDReg()) && o1.isGp()) {
|
||||
out._operands[0].reset(W, nativeGpSize);
|
||||
out._operands[1].reset(R, nativeGpSize);
|
||||
out._writeFlags = kOF | kSF | kZF | kAF | kPF | kCF;
|
||||
out->_operands[0].reset(W, nativeGpSize);
|
||||
out->_operands[1].reset(R, nativeGpSize);
|
||||
out->_writeFlags = kOF | kSF | kZF | kAF | kPF | kCF;
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
@ -974,18 +984,18 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
|
||||
if (o0.isGp()) {
|
||||
if (!o1.isOffset64Bit())
|
||||
out._operands[0].reset(W, o0.size());
|
||||
out->_operands[0].reset(W, o0.size());
|
||||
else
|
||||
out._operands[0].reset(W | RegPhys, o0.size(), Gp::kIdAx);
|
||||
out->_operands[0].reset(W | RegPhys, o0.size(), Gp::kIdAx);
|
||||
|
||||
out._operands[1].reset(R | MibRead, o0.size());
|
||||
rwZeroExtendGp(out._operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
out->_operands[1].reset(R | MibRead, o0.size());
|
||||
rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (o0.isSReg()) {
|
||||
out._operands[0].reset(W, 2);
|
||||
out._operands[1].reset(R, 2);
|
||||
out->_operands[0].reset(W, 2);
|
||||
out->_operands[1].reset(R, 2);
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
@ -995,34 +1005,34 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
const Reg& o1 = operands[1].as<Reg>();
|
||||
|
||||
if (o1.isGp()) {
|
||||
out._operands[0].reset(W | MibRead, o1.size());
|
||||
out->_operands[0].reset(W | MibRead, o1.size());
|
||||
if (!o0.isOffset64Bit())
|
||||
out._operands[1].reset(R, o1.size());
|
||||
out->_operands[1].reset(R, o1.size());
|
||||
else
|
||||
out._operands[1].reset(R | RegPhys, o1.size(), Gp::kIdAx);
|
||||
out->_operands[1].reset(R | RegPhys, o1.size(), Gp::kIdAx);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (o1.isSReg()) {
|
||||
out._operands[0].reset(W | MibRead, 2);
|
||||
out._operands[1].reset(R, 2);
|
||||
out->_operands[0].reset(W | MibRead, 2);
|
||||
out->_operands[1].reset(R, 2);
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
|
||||
if (Reg::isGp(operands[0]) && operands[1].isImm()) {
|
||||
const Reg& o0 = operands[0].as<Reg>();
|
||||
out._operands[0].reset(W | RegM, o0.size());
|
||||
out._operands[1].reset();
|
||||
out->_operands[0].reset(W | RegM, o0.size());
|
||||
out->_operands[1].reset();
|
||||
|
||||
rwZeroExtendGp(out._operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (operands[0].isMem() && operands[1].isImm()) {
|
||||
const Reg& o0 = operands[0].as<Reg>();
|
||||
out._operands[0].reset(W | MibRead, o0.size());
|
||||
out._operands[1].reset();
|
||||
out->_operands[0].reset(W | MibRead, o0.size());
|
||||
out->_operands[1].reset();
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
@ -1040,51 +1050,51 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
|
||||
if (opCount == 2) {
|
||||
if (operands[0].isReg() && operands[1].isImm()) {
|
||||
out._operands[0].reset(X, operands[0].size());
|
||||
out._operands[1].reset();
|
||||
out->_operands[0].reset(X, operands[0].size());
|
||||
out->_operands[1].reset();
|
||||
|
||||
rwZeroExtendGp(out._operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (Reg::isGpw(operands[0]) && operands[1].size() == 1) {
|
||||
// imul ax, r8/m8 <- AX = AL * r8/m8
|
||||
out._operands[0].reset(X | RegPhys, 2, Gp::kIdAx);
|
||||
out._operands[0].setReadByteMask(Support::lsbMask<uint64_t>(1));
|
||||
out._operands[1].reset(R | RegM, 1);
|
||||
out->_operands[0].reset(X | RegPhys, 2, Gp::kIdAx);
|
||||
out->_operands[0].setReadByteMask(Support::lsbMask<uint64_t>(1));
|
||||
out->_operands[1].reset(R | RegM, 1);
|
||||
}
|
||||
else {
|
||||
// imul r?, r?/m?
|
||||
out._operands[0].reset(X, operands[0].size());
|
||||
out._operands[1].reset(R | RegM, operands[0].size());
|
||||
rwZeroExtendGp(out._operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
out->_operands[0].reset(X, operands[0].size());
|
||||
out->_operands[1].reset(R | RegM, operands[0].size());
|
||||
rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
}
|
||||
|
||||
if (operands[1].isMem())
|
||||
out._operands[1].addOpFlags(MibRead);
|
||||
out->_operands[1].addOpFlags(MibRead);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (opCount == 3) {
|
||||
if (operands[2].isImm()) {
|
||||
out._operands[0].reset(W, operands[0].size());
|
||||
out._operands[1].reset(R | RegM, operands[1].size());
|
||||
out._operands[2].reset();
|
||||
out->_operands[0].reset(W, operands[0].size());
|
||||
out->_operands[1].reset(R | RegM, operands[1].size());
|
||||
out->_operands[2].reset();
|
||||
|
||||
rwZeroExtendGp(out._operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
if (operands[1].isMem())
|
||||
out._operands[1].addOpFlags(MibRead);
|
||||
out->_operands[1].addOpFlags(MibRead);
|
||||
return kErrorOk;
|
||||
}
|
||||
else {
|
||||
out._operands[0].reset(W | RegPhys, operands[0].size(), Gp::kIdDx);
|
||||
out._operands[1].reset(X | RegPhys, operands[1].size(), Gp::kIdAx);
|
||||
out._operands[2].reset(R | RegM, operands[2].size());
|
||||
out->_operands[0].reset(W | RegPhys, operands[0].size(), Gp::kIdDx);
|
||||
out->_operands[1].reset(X | RegPhys, operands[1].size(), Gp::kIdAx);
|
||||
out->_operands[2].reset(R | RegM, operands[2].size());
|
||||
|
||||
rwZeroExtendGp(out._operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
rwZeroExtendGp(out._operands[1], operands[1].as<Gp>(), nativeGpSize);
|
||||
rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
rwZeroExtendGp(out->_operands[1], operands[1].as<Gp>(), nativeGpSize);
|
||||
if (operands[2].isMem())
|
||||
out._operands[2].addOpFlags(MibRead);
|
||||
out->_operands[2].addOpFlags(MibRead);
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
@ -1097,16 +1107,16 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
// 2 or 3 operands that are use `kCategoryGeneric`.
|
||||
if (opCount == 2) {
|
||||
if (BaseReg::isVec(operands[0]) && operands[1].isMem()) {
|
||||
out._operands[0].reset(W, 8);
|
||||
out._operands[0].setWriteByteMask(Support::lsbMask<uint64_t>(8) << 8);
|
||||
out._operands[1].reset(R | MibRead, 8);
|
||||
out->_operands[0].reset(W, 8);
|
||||
out->_operands[0].setWriteByteMask(Support::lsbMask<uint64_t>(8) << 8);
|
||||
out->_operands[1].reset(R | MibRead, 8);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (operands[0].isMem() && BaseReg::isVec(operands[1])) {
|
||||
out._operands[0].reset(W | MibRead, 8);
|
||||
out._operands[1].reset(R, 8);
|
||||
out._operands[1].setReadByteMask(Support::lsbMask<uint64_t>(8) << 8);
|
||||
out->_operands[0].reset(W | MibRead, 8);
|
||||
out->_operands[1].reset(R, 8);
|
||||
out->_operands[1].setReadByteMask(Support::lsbMask<uint64_t>(8) << 8);
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
@ -1117,18 +1127,18 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
// Special case for 'vmaskmovpd|vmaskmovps|vpmaskmovd|vpmaskmovq' instructions.
|
||||
if (opCount == 3) {
|
||||
if (BaseReg::isVec(operands[0]) && BaseReg::isVec(operands[1]) && operands[2].isMem()) {
|
||||
out._operands[0].reset(W, operands[0].size());
|
||||
out._operands[1].reset(R, operands[1].size());
|
||||
out._operands[2].reset(R | MibRead, operands[1].size());
|
||||
out->_operands[0].reset(W, operands[0].size());
|
||||
out->_operands[1].reset(R, operands[1].size());
|
||||
out->_operands[2].reset(R | MibRead, operands[1].size());
|
||||
|
||||
rwZeroExtendAvxVec(out._operands[0], operands[0].as<Vec>());
|
||||
rwZeroExtendAvxVec(out->_operands[0], operands[0].as<Vec>());
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (operands[0].isMem() && BaseReg::isVec(operands[1]) && BaseReg::isVec(operands[2])) {
|
||||
out._operands[0].reset(X | MibRead, operands[1].size());
|
||||
out._operands[1].reset(R, operands[1].size());
|
||||
out._operands[2].reset(R, operands[2].size());
|
||||
out->_operands[0].reset(X | MibRead, operands[1].size());
|
||||
out->_operands[1].reset(R, operands[1].size());
|
||||
out->_operands[2].reset(R, operands[2].size());
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
@ -1145,11 +1155,11 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
uint32_t o0Size = operands[0].size();
|
||||
uint32_t o1Size = o0Size == 16 ? 8 : o0Size;
|
||||
|
||||
out._operands[0].reset(W, o0Size);
|
||||
out._operands[1].reset(R | RegM, o1Size);
|
||||
out._operands[1]._readByteMask &= 0x00FF00FF00FF00FFu;
|
||||
out->_operands[0].reset(W, o0Size);
|
||||
out->_operands[1].reset(R | RegM, o1Size);
|
||||
out->_operands[1]._readByteMask &= 0x00FF00FF00FF00FFu;
|
||||
|
||||
rwZeroExtendAvxVec(out._operands[0], operands[0].as<Vec>());
|
||||
rwZeroExtendAvxVec(out->_operands[0], operands[0].as<Vec>());
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
@ -1157,10 +1167,10 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
uint32_t o0Size = operands[0].size();
|
||||
uint32_t o1Size = o0Size == 16 ? 8 : o0Size;
|
||||
|
||||
out._operands[0].reset(W, o0Size);
|
||||
out._operands[1].reset(R | MibRead, o1Size);
|
||||
out->_operands[0].reset(W, o0Size);
|
||||
out->_operands[1].reset(R | MibRead, o1Size);
|
||||
|
||||
rwZeroExtendAvxVec(out._operands[0], operands[0].as<Vec>());
|
||||
rwZeroExtendAvxVec(out->_operands[0], operands[0].as<Vec>());
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
@ -1172,9 +1182,9 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
// Special case for 'vmovmskpd|vmovmskps' instructions.
|
||||
if (opCount == 2) {
|
||||
if (BaseReg::isGp(operands[0]) && BaseReg::isVec(operands[1])) {
|
||||
out._operands[0].reset(W, 1);
|
||||
out._operands[0].setExtendByteMask(Support::lsbMask<uint32_t>(nativeGpSize - 1) << 1);
|
||||
out._operands[1].reset(R, operands[1].size());
|
||||
out->_operands[0].reset(W, 1);
|
||||
out->_operands[0].setExtendByteMask(Support::lsbMask<uint32_t>(nativeGpSize - 1) << 1);
|
||||
out->_operands[1].reset(R, operands[1].size());
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
@ -1208,32 +1218,32 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
if (opCount >= 3) {
|
||||
if (opCount > 3)
|
||||
return DebugUtils::errored(kErrorInvalidInstruction);
|
||||
out._operands[2].reset();
|
||||
out->_operands[2].reset();
|
||||
}
|
||||
|
||||
if (operands[0].isReg() && operands[1].isReg()) {
|
||||
uint32_t size1 = operands[1].size();
|
||||
uint32_t size0 = size1 >> shift;
|
||||
|
||||
out._operands[0].reset(W, size0);
|
||||
out._operands[1].reset(R, size1);
|
||||
out->_operands[0].reset(W, size0);
|
||||
out->_operands[1].reset(R, size1);
|
||||
|
||||
if (instRmInfo.rmOpsMask & 0x1) {
|
||||
out._operands[0].addOpFlags(RegM);
|
||||
out._operands[0].setRmSize(size0);
|
||||
out->_operands[0].addOpFlags(RegM);
|
||||
out->_operands[0].setRmSize(size0);
|
||||
}
|
||||
|
||||
if (instRmInfo.rmOpsMask & 0x2) {
|
||||
out._operands[1].addOpFlags(RegM);
|
||||
out._operands[1].setRmSize(size1);
|
||||
out->_operands[1].addOpFlags(RegM);
|
||||
out->_operands[1].setRmSize(size1);
|
||||
}
|
||||
|
||||
// Handle 'pmovmskb|vpmovmskb'.
|
||||
if (BaseReg::isGp(operands[0]))
|
||||
rwZeroExtendGp(out._operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
rwZeroExtendGp(out->_operands[0], operands[0].as<Gp>(), nativeGpSize);
|
||||
|
||||
if (BaseReg::isVec(operands[0]))
|
||||
rwZeroExtendAvxVec(out._operands[0], operands[0].as<Vec>());
|
||||
rwZeroExtendAvxVec(out->_operands[0], operands[0].as<Vec>());
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
@ -1242,8 +1252,8 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
uint32_t size1 = operands[1].size() ? operands[1].size() : uint32_t(16);
|
||||
uint32_t size0 = size1 >> shift;
|
||||
|
||||
out._operands[0].reset(W, size0);
|
||||
out._operands[1].reset(R | MibRead, size1);
|
||||
out->_operands[0].reset(W, size0);
|
||||
out->_operands[1].reset(R | MibRead, size1);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
@ -1251,8 +1261,8 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
uint32_t size1 = operands[1].size();
|
||||
uint32_t size0 = size1 >> shift;
|
||||
|
||||
out._operands[0].reset(W | MibRead, size0);
|
||||
out._operands[1].reset(R, size1);
|
||||
out->_operands[0].reset(W | MibRead, size0);
|
||||
out->_operands[1].reset(R, size1);
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
@ -1285,30 +1295,30 @@ Error InstInternal::queryRWInfo(uint32_t archId, const BaseInst& inst, const Ope
|
||||
if (opCount >= 3) {
|
||||
if (opCount > 3)
|
||||
return DebugUtils::errored(kErrorInvalidInstruction);
|
||||
out._operands[2].reset();
|
||||
out->_operands[2].reset();
|
||||
}
|
||||
|
||||
uint32_t size0 = operands[0].size();
|
||||
uint32_t size1 = size0 >> shift;
|
||||
|
||||
out._operands[0].reset(W, size0);
|
||||
out._operands[1].reset(R, size1);
|
||||
out->_operands[0].reset(W, size0);
|
||||
out->_operands[1].reset(R, size1);
|
||||
|
||||
if (operands[0].isReg() && operands[1].isReg()) {
|
||||
if (instRmInfo.rmOpsMask & 0x1) {
|
||||
out._operands[0].addOpFlags(RegM);
|
||||
out._operands[0].setRmSize(size0);
|
||||
out->_operands[0].addOpFlags(RegM);
|
||||
out->_operands[0].setRmSize(size0);
|
||||
}
|
||||
|
||||
if (instRmInfo.rmOpsMask & 0x2) {
|
||||
out._operands[1].addOpFlags(RegM);
|
||||
out._operands[1].setRmSize(size1);
|
||||
out->_operands[1].addOpFlags(RegM);
|
||||
out->_operands[1].setRmSize(size1);
|
||||
}
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
if (operands[0].isReg() && operands[1].isMem()) {
|
||||
out._operands[1].addOpFlags(MibRead);
|
||||
out->_operands[1].addOpFlags(MibRead);
|
||||
return kErrorOk;
|
||||
}
|
||||
}
|
||||
@ -1334,7 +1344,7 @@ struct RegAnalysis {
|
||||
}
|
||||
};
|
||||
|
||||
static RegAnalysis InstInternal_regAnalysis(const Operand_* operands, uint32_t opCount) noexcept {
|
||||
static RegAnalysis InstInternal_regAnalysis(const Operand_* operands, size_t opCount) noexcept {
|
||||
uint32_t mask = 0;
|
||||
uint32_t highVecUsed = 0;
|
||||
|
||||
@ -1359,10 +1369,10 @@ static RegAnalysis InstInternal_regAnalysis(const Operand_* operands, uint32_t o
|
||||
return RegAnalysis { mask, highVecUsed };
|
||||
}
|
||||
|
||||
Error InstInternal::queryFeatures(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, BaseFeatures& out) noexcept {
|
||||
// Only called when `archId` matches X86 family.
|
||||
DebugUtils::unused(archId);
|
||||
ASMJIT_ASSERT(ArchInfo::isX86Family(archId));
|
||||
Error InstInternal::queryFeatures(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, BaseFeatures* out) noexcept {
|
||||
// Only called when `arch` matches X86 family.
|
||||
DebugUtils::unused(arch);
|
||||
ASMJIT_ASSERT(Environment::isFamilyX86(arch));
|
||||
|
||||
// Get the instruction data.
|
||||
uint32_t instId = inst.id();
|
||||
@ -1378,12 +1388,12 @@ Error InstInternal::queryFeatures(uint32_t archId, const BaseInst& inst, const O
|
||||
const uint8_t* fEnd = tableB.featuresEnd();
|
||||
|
||||
// Copy all features to `out`.
|
||||
out.reset();
|
||||
out->reset();
|
||||
do {
|
||||
uint32_t feature = fData[0];
|
||||
if (!feature)
|
||||
break;
|
||||
out.add(feature);
|
||||
out->add(feature);
|
||||
} while (++fData != fEnd);
|
||||
|
||||
// Since AsmJit aggregates instructions that share the same name we have to
|
||||
@ -1392,19 +1402,19 @@ Error InstInternal::queryFeatures(uint32_t archId, const BaseInst& inst, const O
|
||||
RegAnalysis regAnalysis = InstInternal_regAnalysis(operands, opCount);
|
||||
|
||||
// Handle MMX vs SSE overlap.
|
||||
if (out.has(Features::kMMX) || out.has(Features::kMMX2)) {
|
||||
if (out->has(Features::kMMX) || out->has(Features::kMMX2)) {
|
||||
// Only instructions defined by SSE and SSE2 overlap. Instructions
|
||||
// introduced by newer instruction sets like SSE3+ don't state MMX as
|
||||
// they require SSE3+.
|
||||
if (out.has(Features::kSSE) || out.has(Features::kSSE2)) {
|
||||
if (out->has(Features::kSSE) || out->has(Features::kSSE2)) {
|
||||
if (!regAnalysis.hasRegType(Reg::kTypeXmm)) {
|
||||
// The instruction doesn't use XMM register(s), thus it's MMX/MMX2 only.
|
||||
out.remove(Features::kSSE);
|
||||
out.remove(Features::kSSE2);
|
||||
out->remove(Features::kSSE);
|
||||
out->remove(Features::kSSE2);
|
||||
}
|
||||
else {
|
||||
out.remove(Features::kMMX);
|
||||
out.remove(Features::kMMX2);
|
||||
out->remove(Features::kMMX);
|
||||
out->remove(Features::kMMX2);
|
||||
}
|
||||
|
||||
// Special case: PEXTRW instruction is MMX/SSE2 instruction. However,
|
||||
@ -1414,34 +1424,34 @@ Error InstInternal::queryFeatures(uint32_t archId, const BaseInst& inst, const O
|
||||
// can extract directly to memory. This instruction is, of course, not
|
||||
// compatible with MMX/SSE2 and would #UD if SSE4.1 is not supported.
|
||||
if (instId == Inst::kIdPextrw) {
|
||||
ASMJIT_ASSERT(out.has(Features::kSSE2));
|
||||
ASMJIT_ASSERT(out.has(Features::kSSE4_1));
|
||||
ASMJIT_ASSERT(out->has(Features::kSSE2));
|
||||
ASMJIT_ASSERT(out->has(Features::kSSE4_1));
|
||||
|
||||
if (opCount >= 1 && operands[0].isMem())
|
||||
out.remove(Features::kSSE2);
|
||||
out->remove(Features::kSSE2);
|
||||
else
|
||||
out.remove(Features::kSSE4_1);
|
||||
out->remove(Features::kSSE4_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle PCLMULQDQ vs VPCLMULQDQ.
|
||||
if (out.has(Features::kVPCLMULQDQ)) {
|
||||
if (out->has(Features::kVPCLMULQDQ)) {
|
||||
if (regAnalysis.hasRegType(Reg::kTypeZmm) || Support::bitTest(options, Inst::kOptionEvex)) {
|
||||
// AVX512_F & VPCLMULQDQ.
|
||||
out.remove(Features::kAVX, Features::kPCLMULQDQ);
|
||||
out->remove(Features::kAVX, Features::kPCLMULQDQ);
|
||||
}
|
||||
else if (regAnalysis.hasRegType(Reg::kTypeYmm)) {
|
||||
out.remove(Features::kAVX512_F, Features::kAVX512_VL);
|
||||
out->remove(Features::kAVX512_F, Features::kAVX512_VL);
|
||||
}
|
||||
else {
|
||||
// AVX & PCLMULQDQ.
|
||||
out.remove(Features::kAVX512_F, Features::kAVX512_VL, Features::kVPCLMULQDQ);
|
||||
out->remove(Features::kAVX512_F, Features::kAVX512_VL, Features::kVPCLMULQDQ);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle AVX vs AVX2 overlap.
|
||||
if (out.has(Features::kAVX) && out.has(Features::kAVX2)) {
|
||||
if (out->has(Features::kAVX) && out->has(Features::kAVX2)) {
|
||||
bool isAVX2 = true;
|
||||
// Special case: VBROADCASTSS and VBROADCASTSD were introduced in AVX, but
|
||||
// only version that uses memory as a source operand. AVX2 then added support
|
||||
@ -1459,15 +1469,15 @@ Error InstInternal::queryFeatures(uint32_t archId, const BaseInst& inst, const O
|
||||
}
|
||||
|
||||
if (isAVX2)
|
||||
out.remove(Features::kAVX);
|
||||
out->remove(Features::kAVX);
|
||||
else
|
||||
out.remove(Features::kAVX2);
|
||||
out->remove(Features::kAVX2);
|
||||
}
|
||||
|
||||
// Handle AVX|AVX2|FMA|F16C vs AVX512 overlap.
|
||||
if (out.has(Features::kAVX) || out.has(Features::kAVX2) || out.has(Features::kFMA) || out.has(Features::kF16C)) {
|
||||
if (out->has(Features::kAVX) || out->has(Features::kAVX2) || out->has(Features::kFMA) || out->has(Features::kF16C)) {
|
||||
// Only AVX512-F|BW|DQ allow to encode AVX/AVX2/FMA/F16C instructions
|
||||
if (out.has(Features::kAVX512_F) || out.has(Features::kAVX512_BW) || out.has(Features::kAVX512_DQ)) {
|
||||
if (out->has(Features::kAVX512_F) || out->has(Features::kAVX512_BW) || out->has(Features::kAVX512_DQ)) {
|
||||
uint32_t hasEvex = options & (Inst::kOptionEvex | Inst::_kOptionAvx512Mask);
|
||||
uint32_t hasKMask = inst.extraReg().type() == Reg::kTypeKReg;
|
||||
uint32_t hasKOrZmm = regAnalysis.regTypeMask & Support::bitMask(Reg::kTypeZmm, Reg::kTypeKReg);
|
||||
@ -1500,15 +1510,15 @@ Error InstInternal::queryFeatures(uint32_t archId, const BaseInst& inst, const O
|
||||
}
|
||||
|
||||
if (!(hasEvex | mustUseEvex | hasKMask | hasKOrZmm | regAnalysis.highVecUsed))
|
||||
out.remove(Features::kAVX512_F, Features::kAVX512_BW, Features::kAVX512_DQ, Features::kAVX512_VL);
|
||||
out->remove(Features::kAVX512_F, Features::kAVX512_BW, Features::kAVX512_DQ, Features::kAVX512_VL);
|
||||
else
|
||||
out.remove(Features::kAVX, Features::kAVX2, Features::kFMA, Features::kF16C);
|
||||
out->remove(Features::kAVX, Features::kAVX2, Features::kFMA, Features::kF16C);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear AVX512_VL if ZMM register is used.
|
||||
if (regAnalysis.hasRegType(Reg::kTypeZmm))
|
||||
out.remove(Features::kAVX512_VL);
|
||||
out->remove(Features::kAVX512_VL);
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
|
10
3rdparty/asmjit/src/asmjit/x86/x86instapi_p.h
vendored
10
3rdparty/asmjit/src/asmjit/x86/x86instapi_p.h
vendored
@ -36,17 +36,17 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
|
||||
namespace InstInternal {
|
||||
|
||||
#ifndef ASMJIT_NO_TEXT
|
||||
Error instIdToString(uint32_t archId, uint32_t instId, String& output) noexcept;
|
||||
uint32_t stringToInstId(uint32_t archId, const char* s, size_t len) noexcept;
|
||||
Error instIdToString(uint32_t arch, uint32_t instId, String& output) noexcept;
|
||||
uint32_t stringToInstId(uint32_t arch, const char* s, size_t len) noexcept;
|
||||
#endif // !ASMJIT_NO_TEXT
|
||||
|
||||
#ifndef ASMJIT_NO_VALIDATION
|
||||
Error validate(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount) noexcept;
|
||||
Error validate(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, uint32_t validationFlags) noexcept;
|
||||
#endif // !ASMJIT_NO_VALIDATION
|
||||
|
||||
#ifndef ASMJIT_NO_INTROSPECTION
|
||||
Error queryRWInfo(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, InstRWInfo& out) noexcept;
|
||||
Error queryFeatures(uint32_t archId, const BaseInst& inst, const Operand_* operands, uint32_t opCount, BaseFeatures& out) noexcept;
|
||||
Error queryRWInfo(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, InstRWInfo* out) noexcept;
|
||||
Error queryFeatures(uint32_t arch, const BaseInst& inst, const Operand_* operands, size_t opCount, BaseFeatures* out) noexcept;
|
||||
#endif // !ASMJIT_NO_INTROSPECTION
|
||||
|
||||
} // {InstInternal}
|
||||
|
1015
3rdparty/asmjit/src/asmjit/x86/x86instdb.cpp
vendored
1015
3rdparty/asmjit/src/asmjit/x86/x86instdb.cpp
vendored
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user