mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-16 20:17:05 +03:00
AsmJit cleanup and refactoring
This commit is contained in:
110
.travis.yml
110
.travis.yml
@@ -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
|
||||
cd ${BUILD_TYPE}
|
||||
else
|
||||
cmake --build .
|
||||
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"
|
||||
echo ""
|
||||
eval "$RUN_CMD ./asmjit_test_opcode > /dev/null"
|
||||
echo ""
|
||||
eval "$RUN_CMD ./asmjit_test_x86_asm"
|
||||
echo ""
|
||||
eval "$RUN_CMD ./asmjit_test_x86_sections"
|
||||
if [ -f ./asmjit_test_x86_instinfo ]; then
|
||||
echo ""
|
||||
eval "$RUN_CMD ./asmjit_test_x86_instinfo"
|
||||
fi
|
||||
if [ -f ./asmjit_test_x86_cc ]; then
|
||||
echo ""
|
||||
eval "$RUN_CMD ./asmjit_test_x86_cc"
|
||||
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
|
||||
cd ..
|
||||
fi
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
2005
src/asmjit/core.h
2005
src/asmjit/core.h
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)
|
||||
|
||||
@@ -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) * 10000000 + (MINOR) * 100000 + (PATCH))
|
||||
|
||||
// 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_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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}
|
||||
|
||||
//! \}
|
||||
|
||||
|
||||
@@ -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,80 @@ 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));
|
||||
|
||||
const uint8_t* start = writer.cursor();
|
||||
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 +249,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 +290,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 +301,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 +310,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 +328,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 +352,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 +383,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 +391,6 @@ Error BaseAssembler::onDetach(CodeHolder* code) noexcept {
|
||||
_bufferData = nullptr;
|
||||
_bufferEnd = nullptr;
|
||||
_bufferPtr = nullptr;
|
||||
|
||||
_op4.reset();
|
||||
_op5.reset();
|
||||
|
||||
return Base::onDetach(code);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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)")
|
||||
ASMJIT_API 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;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
src/asmjit/core/codebuffer.h
Normal file
126
src/asmjit/core/codebuffer.h
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
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -270,20 +254,6 @@ Error CodeHolder::detach(BaseEmitter* emitter) noexcept {
|
||||
return err;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [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]
|
||||
// ============================================================================
|
||||
@@ -291,8 +261,7 @@ void CodeHolder::clearEmitterOptions(uint32_t options) noexcept {
|
||||
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
|
||||
@@ -440,7 +409,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 +427,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 +439,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 +512,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 +534,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 +553,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 +564,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 +583,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 +595,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 +880,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 +891,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 +953,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 +976,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 +1037,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 +1045,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 +1057,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 +1065,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 +1081,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 +1089,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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
//! Attach an error handler to this `CodeHolder`.
|
||||
inline void setErrorHandler(ErrorHandler* handler) noexcept { _errorHandler = handler; }
|
||||
//! Resets the global error handler to none.
|
||||
//! 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
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,50 @@ 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).
|
||||
//! - Emitter options have been changed (\ref CodeHolder::addEmitterOptions()
|
||||
//! or \ref CodeHolder::clearEmitterOptions() have 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
src/asmjit/core/emitterutils.cpp
Normal file
150
src/asmjit/core/emitterutils.cpp
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
src/asmjit/core/emitterutils_p.h
Normal file
109
src/asmjit/core/emitterutils_p.h
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
src/asmjit/core/environment.cpp
Normal file
64
src/asmjit/core/environment.cpp
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
src/asmjit/core/environment.h
Normal file
591
src/asmjit/core/environment.h
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
src/asmjit/core/errorhandler.cpp
Normal file
37
src/asmjit/core/errorhandler.cpp
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
src/asmjit/core/errorhandler.h
Normal file
267
src/asmjit/core/errorhandler.h
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
|
||||
|
||||
@@ -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
src/asmjit/core/formatter.cpp
Normal file
469
src/asmjit/core/formatter.cpp
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
src/asmjit/core/formatter.h
Normal file
252
src/asmjit/core/formatter.h
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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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[] = "";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -35,7 +35,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
class CodeHolder;
|
||||
|
||||
//! \addtogroup asmjit_jit
|
||||
//! \addtogroup asmjit_virtual_memory
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
|
||||
124
src/asmjit/core/logger.cpp
Normal file
124
src/asmjit/core/logger.cpp
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
|
||||
@@ -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
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
src/asmjit/core/osutils_p.h
Normal file
94
src/asmjit/core/osutils_p.h
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -31,8 +31,7 @@ ASMJIT_BEGIN_NAMESPACE
|
||||
// ============================================================================
|
||||
|
||||
Target::Target() noexcept
|
||||
: _targetType(kTargetNone),
|
||||
_codeInfo() {}
|
||||
: _environment() {}
|
||||
Target::~Target() noexcept {}
|
||||
|
||||
ASMJIT_END_NAMESPACE
|
||||
|
||||
@@ -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
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
//! \}
|
||||
|
||||
@@ -317,9 +317,9 @@ static ASMJIT_INLINE bool VirtMem_isHardened() noexcept {
|
||||
// version 10.14+ (Mojave) and IOS.
|
||||
static ASMJIT_INLINE bool VirtMem_hasMapJitSupport() noexcept {
|
||||
#if TARGET_OS_OSX
|
||||
static volatile uint32_t globalVersion;
|
||||
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)) {
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
ASMJIT_BEGIN_NAMESPACE
|
||||
|
||||
//! \addtogroup asmjit_jit
|
||||
//! \addtogroup asmjit_virtual_memory
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) {}
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
@@ -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
src/asmjit/x86/x86archdata.cpp
Normal file
137
src/asmjit/x86/x86archdata.cpp
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
src/asmjit/x86/x86archdata_p.h
Normal file
51
src/asmjit/x86/x86archdata_p.h
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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -55,12 +55,15 @@ Error Compiler::finalize() {
|
||||
// ============================================================================
|
||||
|
||||
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)) {
|
||||
|
||||
@@ -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); }
|
||||
|
||||
//! \}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -46,9 +46,9 @@ enum Mode : uint32_t {
|
||||
kModeAny = 0x03u //!< Both X86 and X64 modes supported.
|
||||
};
|
||||
|
||||
static constexpr uint32_t modeFromArchId(uint32_t archId) noexcept {
|
||||
return archId == ArchInfo::kIdX86 ? kModeX86 :
|
||||
archId == ArchInfo::kIdX64 ? kModeX64 : kModeNone;
|
||||
static constexpr uint32_t modeFromArch(uint32_t arch) noexcept {
|
||||
return arch == Environment::kArchX86 ? kModeX86 :
|
||||
arch == Environment::kArchX64 ? kModeX64 : kModeNone;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -142,12 +142,6 @@ enum MemFlags : uint32_t {
|
||||
enum Flags : uint32_t {
|
||||
kFlagNone = 0x00000000u, //!< No flags.
|
||||
|
||||
// TODO: Deprecated
|
||||
// ----------------
|
||||
|
||||
kFlagVolatile = 0x00000040u,
|
||||
kFlagPrivileged = 0x00000080u, //!< This is a privileged operation that cannot run in user mode.
|
||||
|
||||
// Instruction Family
|
||||
// ------------------
|
||||
//
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user