Preparation for AArch64 support

This commit is contained in:
kobalicek
2020-09-12 16:25:35 +02:00
parent 9cb2b298e1
commit cd44f41d9b
81 changed files with 10121 additions and 4441 deletions

View File

@@ -45,10 +45,6 @@ if (NOT DEFINED ASMJIT_STATIC)
set(ASMJIT_STATIC ${ASMJIT_EMBED})
endif()
if (NOT DEFINED ASMJIT_BUILD_ARM)
set(ASMJIT_BUILD_ARM FALSE)
endif()
if (NOT DEFINED ASMJIT_BUILD_X86)
set(ASMJIT_BUILD_X86 FALSE)
endif()
@@ -72,7 +68,6 @@ set(ASMJIT_EMBED ${ASMJIT_EMBED} CACHE BOOL "Embed 'asmjit' lib
set(ASMJIT_STATIC ${ASMJIT_STATIC} CACHE BOOL "Build 'asmjit' library as static")
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 (ARM/Trumb and AArch64")
# =============================================================================
# [AsmJit - Project]
@@ -140,6 +135,7 @@ function(asmjit_add_target target target_type)
add_library(${target} ${target_type} ${X_SOURCES})
endif()
set_target_properties(${target} PROPERTIES DEFINE_SYMBOL "")
target_link_libraries(${target} PRIVATE ${X_LIBRARIES})
# target_link_options was added in cmake v3.13, don't use it for now...
@@ -255,8 +251,6 @@ endif()
foreach(build_option ASMJIT_STATIC
ASMJIT_BUILD_X86
#ASMJIT_BUILD_ARM
ASMJIT_BUILD_A64
ASMJIT_NO_DEPRECATED
ASMJIT_NO_JIT
ASMJIT_NO_LOGGING
@@ -291,25 +285,28 @@ set(ASMJIT_SRC_LIST
asmjit/core.h
asmjit/core/api-build_p.h
asmjit/core/api-config.h
asmjit/core/arch.cpp
asmjit/core/arch.h
asmjit/core/archtraits.cpp
asmjit/core/archtraits.h
asmjit/core/archcommons.h
asmjit/core/assembler.cpp
asmjit/core/assembler.h
asmjit/core/builder.cpp
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
asmjit/core/codewriter.cpp
asmjit/core/codewriter_p.h
asmjit/core/compiler.cpp
asmjit/core/compiler.h
asmjit/core/compilerdefs.h
asmjit/core/constpool.cpp
asmjit/core/constpool.h
asmjit/core/cpuinfo.cpp
asmjit/core/cpuinfo.h
asmjit/core/datatypes.h
asmjit/core/emithelper.cpp
asmjit/core/emithelper_p.h
asmjit/core/emitter.cpp
asmjit/core/emitter.h
asmjit/core/emitterutils.cpp
@@ -323,6 +320,8 @@ set(ASMJIT_SRC_LIST
asmjit/core/formatter.h
asmjit/core/func.cpp
asmjit/core/func.h
asmjit/core/funcargscontext.cpp
asmjit/core/funcargscontext_p.h
asmjit/core/globals.cpp
asmjit/core/globals.h
asmjit/core/inst.cpp
@@ -372,24 +371,23 @@ set(ASMJIT_SRC_LIST
asmjit/core/zonevector.h
asmjit/x86.h
asmjit/x86/x86archdata.cpp
asmjit/x86/x86archdata_p.h
asmjit/x86/x86archtraits_p.h
asmjit/x86/x86assembler.cpp
asmjit/x86/x86assembler.h
asmjit/x86/x86builder.cpp
asmjit/x86/x86builder.h
asmjit/x86/x86callconv.cpp
asmjit/x86/x86callconv_p.h
asmjit/x86/x86compiler.cpp
asmjit/x86/x86compiler.h
asmjit/x86/x86emithelper.cpp
asmjit/x86/x86emithelper_p.h
asmjit/x86/x86emitter.h
asmjit/x86/x86features.cpp
asmjit/x86/x86features.h
asmjit/x86/x86formatter.cpp
asmjit/x86/x86formatter_p.h
asmjit/x86/x86func.cpp
asmjit/x86/x86func_p.h
asmjit/x86/x86globals.h
asmjit/x86/x86internal.cpp
asmjit/x86/x86internal_p.h
asmjit/x86/x86instdb.cpp
asmjit/x86/x86instdb.h
asmjit/x86/x86instdb_p.h
@@ -518,15 +516,17 @@ if (NOT ASMJIT_EMBED)
endif()
if (NOT (ASMJIT_NO_BUILDER OR ASMJIT_NO_COMPILER))
# Vectorcall tests and XMM tests require at least SSE2 (required in 32-bit mode).
# Vectorcall tests and XMM tests require at least SSE2 in 32-bit mode (in 64-bit mode it's implicit).
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
asmjit_add_target(asmjit_test_compiler TEST
SOURCES test/asmjit_test_compiler.cpp
test/asmjit_test_compiler_x86.cpp
test/asmjit_test_compiler.h
LIBRARIES asmjit::asmjit
CFLAGS ${ASMJIT_PRIVATE_CFLAGS} ${sse2_flags}
CFLAGS_DBG ${ASMJIT_PRIVATE_CFLAGS_DBG}

View File

@@ -2007,10 +2007,9 @@ namespace asmjit {
#include "./core/globals.h"
#include "./core/arch.h"
#include "./core/archtraits.h"
#include "./core/assembler.h"
#include "./core/builder.h"
#include "./core/callconv.h"
#include "./core/codeholder.h"
#include "./core/compiler.h"
#include "./core/constpool.h"

View File

@@ -68,9 +68,6 @@ namespace asmjit {
//! 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
@@ -102,7 +99,6 @@ namespace asmjit {
#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
@@ -116,6 +112,13 @@ namespace asmjit {
} // {asmjit}
#endif // _DOXYGEN
// Enable all features at IDE level, so it's properly highlighted and indexed.
#ifdef __INTELLISENSE__
#ifndef ASMJIT_BUILD_X86
#define ASMJIT_BUILD_X86
#endif
#endif
// ============================================================================
// [asmjit::Dependencies]
// ============================================================================
@@ -253,10 +256,6 @@ namespace asmjit {
#if ASMJIT_ARCH_X86 && !defined(ASMJIT_BUILD_X86)
#define ASMJIT_BUILD_X86
#endif
#if ASMJIT_ARCH_ARM && !defined(ASMJIT_BUILD_ARM)
// #define ASMJIT_BUILD_ARM
#endif
#endif
// Define 'ASMJIT_BUILD_HOST' if we know that host architecture will be built.
@@ -264,10 +263,6 @@ namespace asmjit {
#define ASMJIT_BUILD_HOST
#endif
#if !defined(ASMJIT_BUILD_HOST) && ASMJIT_ARCH_ARM && defined(ASMJIT_BUILD_ARM)
#define ASMJIT_BUILD_HOST
#endif
// ============================================================================
// [asmjit::Build - Globals - C++ Compiler and Features Detection]
// ============================================================================

View File

@@ -1,59 +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"
#include "../core/arch.h"
#ifdef ASMJIT_BUILD_X86
#include "../x86/x86archdata_p.h"
#endif
#ifdef ASMJIT_BUILD_ARM
#include "../arm/armarchdata_p.h"
#endif
ASMJIT_BEGIN_NAMESPACE
// ============================================================================
// [asmjit::ArchUtils]
// ============================================================================
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();
#ifdef ASMJIT_BUILD_X86
if (Environment::isFamilyX86(arch))
return x86::ArchInternal::typeIdToRegInfo(arch, typeId, typeIdOut, regInfoOut);
#endif
#ifdef ASMJIT_BUILD_ARM
if (Environment::isFamilyARM(arch))
return arm::ArchInternal::typeIdToRegInfo(arch, typeId, typeIdOut, regInfoOut);
#endif
return DebugUtils::errored(kErrorInvalidArch);
}
ASMJIT_END_NAMESPACE

View File

@@ -1,64 +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.
#ifndef ASMJIT_CORE_ARCH_H_INCLUDED
#define ASMJIT_CORE_ARCH_H_INCLUDED
#include "../core/environment.h"
#include "../core/operand.h"
ASMJIT_BEGIN_NAMESPACE
//! \addtogroup asmjit_core
//! \{
// ============================================================================
// [asmjit::ArchRegs]
// ============================================================================
//! Information about registers of a CPU architecture.
struct ArchRegs {
//! Register information and signatures indexed by `BaseReg::RegType`.
RegInfo regInfo[BaseReg::kTypeMax + 1];
//! Count (maximum) of registers per `BaseReg::RegType`.
uint8_t regCount[BaseReg::kTypeMax + 1];
//! Converts RegType to TypeId, see `Type::Id`.
uint8_t regTypeToTypeId[BaseReg::kTypeMax + 1];
};
// ============================================================================
// [asmjit::ArchUtils]
// ============================================================================
//! Architecture utilities.
namespace ArchUtils {
ASMJIT_API Error typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfo) noexcept;
} // {ArchUtils}
//! \}
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_ARCH_H_INCLUDED

View File

@@ -0,0 +1,164 @@
// 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_ARCHCOMMONS_H_INCLUDED
#define ASMJIT_CORE_ARCHCOMMONS_H_INCLUDED
// This file provides architecture-specific classes that are required in the
// core library. For example Imm operand allows to be created from arm::Shift
// in a const-expr way, so the arm::Shift must be provided. So this header
// file provides everything architecture-specific that is used by the Core API.
#include "../core/globals.h"
// ============================================================================
// [asmjit::arm]
// ============================================================================
ASMJIT_BEGIN_SUB_NAMESPACE(arm)
//! \addtogroup asmjit_arm
//! \{
//! Represents ARM immediate shift operation type and value.
class Shift {
public:
//! Operation predicate (ARM) describes either SHIFT or EXTEND operation.
//!
//! \note The constants are AsmJit specific. The first 5 values describe real
//! constants on ARM32 and AArch64 hardware, however, the addition constants
//! that describe extend modes are specific to AsmJit and would be translated
//! to the AArch64 specific constants by the assembler.
enum Op : uint32_t {
//! Shift left logical operation (default).
//!
//! Available to all ARM architectures.
kOpLSL = 0x00u,
//! Shift right logical operation.
//!
//! Available to all ARM architectures.
kOpLSR = 0x01u,
//! Shift right arithmetic operation.
//!
//! Available to all ARM architectures.
kOpASR = 0x02u,
//! Rotate right operation.
//!
//! \note Not available in AArch64 mode.
kOpROR = 0x03u,
//! Rotate right with carry operation (encoded as `kShiftROR` with zero).
//!
//! \note Not available in AArch64 mode.
kOpRRX = 0x04u,
//! Shift left by filling low order bits with ones.
kOpMSL = 0x05u,
//! UXTN extend register operation (AArch64 only).
kOpUXTB = 0x06u,
//! UXTH extend register operation (AArch64 only).
kOpUXTH = 0x07u,
//! UXTW extend register operation (AArch64 only).
kOpUXTW = 0x08u,
//! UXTX extend register operation (AArch64 only).
kOpUXTX = 0x09u,
//! SXTB extend register operation (AArch64 only).
kOpSXTB = 0x0Au,
//! SXTH extend register operation (AArch64 only).
kOpSXTH = 0x0Bu,
//! SXTW extend register operation (AArch64 only).
kOpSXTW = 0x0Cu,
//! SXTX extend register operation (AArch64 only).
kOpSXTX = 0x0Du
// NOTE: 0xE and 0xF are used by memory operand to specify POST|PRE offset mode.
};
//! Shift operation.
uint32_t _op;
//! Shift Value.
uint32_t _value;
//! Default constructed Shift is not initialized.
inline Shift() noexcept = default;
//! Copy constructor (default)
constexpr Shift(const Shift& other) noexcept = default;
//! Constructs Shift from operation `op` and shift `value`.
constexpr Shift(uint32_t op, uint32_t value) noexcept
: _op(op),
_value(value) {}
//! Returns the shift operation.
constexpr uint32_t op() const noexcept { return _op; }
//! Returns the shift smount.
constexpr uint32_t value() const noexcept { return _value; }
//! Sets shift operation to `op`.
inline void setOp(uint32_t op) noexcept { _op = op; }
//! Sets shift amount to `value`.
inline void setValue(uint32_t value) noexcept { _value = value; }
};
//! Constructs a `LSL #value` shift (logical shift left).
static constexpr Shift lsl(uint32_t value) noexcept { return Shift(Shift::kOpLSL, value); }
//! Constructs a `LSR #value` shift (logical shift right).
static constexpr Shift lsr(uint32_t value) noexcept { return Shift(Shift::kOpLSR, value); }
//! Constructs a `ASR #value` shift (arithmetic shift right).
static constexpr Shift asr(uint32_t value) noexcept { return Shift(Shift::kOpASR, value); }
//! Constructs a `ROR #value` shift (rotate right).
static constexpr Shift ror(uint32_t value) noexcept { return Shift(Shift::kOpROR, value); }
//! Constructs a `RRX` shift (rotate with carry by 1).
static constexpr Shift rrx() noexcept { return Shift(Shift::kOpRRX, 0); }
//! Constructs a `MSL #value` shift (logical shift left filling ones).
static constexpr Shift msl(uint32_t value) noexcept { return Shift(Shift::kOpMSL, value); }
//! Constructs a `UXTB #value` extend and shift (unsigned byte extend).
static constexpr Shift uxtb(uint32_t value) noexcept { return Shift(Shift::kOpUXTB, value); }
//! Constructs a `UXTH #value` extend and shift (unsigned hword extend).
static constexpr Shift uxth(uint32_t value) noexcept { return Shift(Shift::kOpUXTH, value); }
//! Constructs a `UXTW #value` extend and shift (unsigned word extend).
static constexpr Shift uxtw(uint32_t value) noexcept { return Shift(Shift::kOpUXTW, value); }
//! Constructs a `UXTX #value` extend and shift (unsigned dword extend).
static constexpr Shift uxtx(uint32_t value) noexcept { return Shift(Shift::kOpUXTX, value); }
//! Constructs a `SXTB #value` extend and shift (signed byte extend).
static constexpr Shift sxtb(uint32_t value) noexcept { return Shift(Shift::kOpSXTB, value); }
//! Constructs a `SXTH #value` extend and shift (signed hword extend).
static constexpr Shift sxth(uint32_t value) noexcept { return Shift(Shift::kOpSXTH, value); }
//! Constructs a `SXTW #value` extend and shift (signed word extend).
static constexpr Shift sxtw(uint32_t value) noexcept { return Shift(Shift::kOpSXTW, value); }
//! Constructs a `SXTX #value` extend and shift (signed dword extend).
static constexpr Shift sxtx(uint32_t value) noexcept { return Shift(Shift::kOpSXTX, value); }
//! \}
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_CORE_ARCHCOMMONS_H_INCLUDED

View File

@@ -0,0 +1,155 @@
// 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/archtraits.h"
#include "../core/misc_p.h"
#ifdef ASMJIT_BUILD_X86
#include "../x86/x86archtraits_p.h"
#endif
#ifdef ASMJIT_BUILD_ARM
#include "../arm/armarchtraits_p.h"
#endif
ASMJIT_BEGIN_NAMESPACE
// ============================================================================
// [asmjit::ArchTraits]
// ============================================================================
static const constexpr ArchTraits noArchTraits = {
0xFF, // SP.
0xFF, // FP.
0xFF, // LR.
0xFF, // PC.
{ 0, 0, 0 }, // Reserved.
0, // HW stack alignment.
0, // Min stack offset.
0, // Max stack offset.
{ 0, 0, 0, 0}, // ISA features [Gp, Vec, Other0, Other1].
{ { 0 } }, // RegTypeToSignature.
{ 0 }, // RegTypeToTypeId.
{ 0 } // TypeIdToRegType.
};
ASMJIT_VARAPI const ArchTraits _archTraits[Environment::kArchCount] = {
// No architecture.
noArchTraits,
// X86/X86 architectures.
#ifdef ASMJIT_BUILD_X86
x86::x86ArchTraits,
x86::x64ArchTraits,
#else
noArchTraits,
noArchTraits,
#endif
// RISCV32/RISCV64 architectures.
noArchTraits,
noArchTraits,
// ARM architecture
noArchTraits,
// AArch64 architecture.
#ifdef ASMJIT_BUILD_ARM
arm::a64ArchTraits,
#else
noArchTraits,
#endif
// ARM/Thumb architecture.
noArchTraits,
// Reserved.
noArchTraits,
// MIPS32/MIPS64
noArchTraits,
noArchTraits
};
// ============================================================================
// [asmjit::ArchUtils]
// ============================================================================
ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfoOut) noexcept {
const ArchTraits& archTraits = ArchTraits::byArch(arch);
// Passed RegType instead of TypeId?
if (typeId <= BaseReg::kTypeMax)
typeId = archTraits.regTypeToTypeId(typeId);
if (ASMJIT_UNLIKELY(!Type::isValid(typeId)))
return DebugUtils::errored(kErrorInvalidTypeId);
// First normalize architecture dependent types.
if (Type::isAbstract(typeId)) {
bool is32Bit = Environment::is32Bit(arch);
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;
if (typeId >= Type::_kIdBaseStart && typeId < Type::_kIdVec32Start) {
regType = archTraits._typeIdToRegType[typeId - Type::_kIdBaseStart];
if (!regType) {
if (typeId == Type::kIdI64 || typeId == Type::kIdU64)
return DebugUtils::errored(kErrorInvalidUseOfGpq);
else
return DebugUtils::errored(kErrorInvalidTypeId);
}
}
else {
if (size <= 8 && archTraits._regInfo[BaseReg::kTypeVec64].isValid())
regType = BaseReg::kTypeVec64;
else if (size <= 16 && archTraits._regInfo[BaseReg::kTypeVec128].isValid())
regType = BaseReg::kTypeVec128;
else if (size == 32 && archTraits._regInfo[BaseReg::kTypeVec256].isValid())
regType = BaseReg::kTypeVec256;
else if (archTraits._regInfo[BaseReg::kTypeVec512].isValid())
regType = BaseReg::kTypeVec512;
else
return DebugUtils::errored(kErrorInvalidTypeId);
}
*typeIdOut = typeId;
regInfoOut->reset(archTraits.regTypeToSignature(regType));
return kErrorOk;
}
ASMJIT_END_NAMESPACE

View File

@@ -0,0 +1,174 @@
// 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_ARCHTRAITS_H_INCLUDED
#define ASMJIT_CORE_ARCHTRAITS_H_INCLUDED
#include "../core/environment.h"
#include "../core/operand.h"
#include "../core/type.h"
ASMJIT_BEGIN_NAMESPACE
//! \addtogroup asmjit_core
//! \{
// ============================================================================
// [asmjit::ArchTraits]
// ============================================================================
//! Architecture traits used by Function API and Compiler's register allocator.
struct ArchTraits {
//! ISA features for each register group.
enum IsaFeatures : uint32_t {
//! ISA features a register swap by using a single instruction.
kIsaFeatureSwap = 0x01u,
//! ISA features a push/pop like instruction for this register group.
kIsaFeaturePushPop = 0x02u,
};
//! Stack pointer register id.
uint8_t _spRegId;
//! Frame pointer register id.
uint8_t _fpRegId;
//! Link register id.
uint8_t _linkRegId;
//! Instruction pointer (or program counter) register id, if accessible.
uint8_t _ipRegId;
// Reserved.
uint8_t _reserved[3];
//! Hardware stack alignment requirement.
uint8_t _hwStackAlignment;
//! Minimum addressable offset on stack guaranteed for all instructions.
uint32_t _minStackOffset;
//! Maximum addressable offset on stack depending on specific instruction.
uint32_t _maxStackOffset;
//! Flags for each virtual register group (always covers GP and Vec groups).
uint8_t _isaFlags[BaseReg::kGroupVirt];
//! Maps register type into a signature, that provides group, size and can
//! be used to construct register operands.
RegInfo _regInfo[BaseReg::kTypeMax + 1];
//! Maps a register to type-id, see \ref Type::Id.
uint8_t _regTypeToTypeId[BaseReg::kTypeMax + 1];
//! Maps base TypeId values (from TypeId::_kIdBaseStart) to register types, see \ref Type::Id.
uint8_t _typeIdToRegType[32];
//! Resets all members to zeros.
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
//! \name Accessors
//! \{
//! Returns stack pointer register id.
inline constexpr uint32_t spRegId() const noexcept { return _spRegId; }
//! Returns stack frame register id.
inline constexpr uint32_t fpRegId() const noexcept { return _fpRegId; }
//! Returns link register id, if the architecture provides it.
inline constexpr uint32_t linkRegId() const noexcept { return _linkRegId; }
//! Returns instruction pointer register id, if the architecture provides it.
inline constexpr uint32_t ipRegId() const noexcept { return _ipRegId; }
//! Returns a hardware stack alignment requirement.
//!
//! \note This is a hardware constraint. Architectures that don't constrain
//! it would return the lowest alignment (1), however, some architectures may
//! constrain the alignment, for example AArch64 requires 16-byte alignment.
inline constexpr uint32_t hwStackAlignment() const noexcept { return _hwStackAlignment; }
//! Tests whether the architecture provides link register, which is used across
//! function calls. If the link register is not provided then a function call
//! pushes the return address on stack (X86/X64).
inline constexpr bool hasLinkReg() const noexcept { return _linkRegId != BaseReg::kIdBad; }
//! Returns minimum addressable offset on stack guaranteed for all instructions.
inline constexpr uint32_t minStackOffset() const noexcept { return _minStackOffset; }
//! Returns maximum addressable offset on stack depending on specific instruction.
inline constexpr uint32_t maxStackOffset() const noexcept { return _maxStackOffset; }
//! Returns ISA flags of the given register `group`.
inline constexpr uint32_t isaFlags(uint32_t group) const noexcept { return _isaFlags[group]; }
//! Tests whether the given register `group` has the given `flag` set.
inline constexpr bool hasIsaFlag(uint32_t group, uint32_t flag) const noexcept { return (_isaFlags[group] & flag) != 0; }
//! Tests whether the ISA provides register swap instruction for the given register `group`.
inline constexpr bool hasSwap(uint32_t group) const noexcept { return hasIsaFlag(group, kIsaFeatureSwap); }
//! Tests whether the ISA provides push/pop instructions for the given register `group`.
inline constexpr bool hasPushPop(uint32_t group) const noexcept { return hasIsaFlag(group, kIsaFeaturePushPop); }
inline uint32_t hasRegType(uint32_t rType) const noexcept {
return rType <= BaseReg::kTypeMax && _regInfo[rType].signature() != 0;
}
inline uint32_t regTypeToSignature(uint32_t rType) const noexcept {
ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
return _regInfo[rType].signature();
}
inline uint32_t regTypeToGroup(uint32_t rType) const noexcept {
ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
return _regInfo[rType].group();
}
inline uint32_t regTypeToSize(uint32_t rType) const noexcept {
ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
return _regInfo[rType].size();
}
inline uint32_t regTypeToTypeId(uint32_t rType) const noexcept {
ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
return _regTypeToTypeId[rType];
}
//! \}
//! \name Statics
//! \{
//! Returns a const reference to `ArchTraits` for the given architecture `arch`.
static inline const ArchTraits& byArch(uint32_t arch) noexcept;
//! \}
};
ASMJIT_VARAPI const ArchTraits _archTraits[Environment::kArchCount];
inline const ArchTraits& ArchTraits::byArch(uint32_t arch) noexcept { return _archTraits[arch & ~Environment::kArchBigEndianMask]; }
// ============================================================================
// [asmjit::ArchUtils]
// ============================================================================
//! Architecture utilities.
namespace ArchUtils {
ASMJIT_API Error typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfo) noexcept;
} // {ArchUtils}
//! \}
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_ARCHTRAITS_H_INCLUDED

View File

@@ -23,7 +23,7 @@
#include "../core/api-build_p.h"
#include "../core/assembler.h"
#include "../core/codebufferwriter_p.h"
#include "../core/codewriter_p.h"
#include "../core/constpool.h"
#include "../core/emitterutils_p.h"
#include "../core/formatter.h"
@@ -37,11 +37,8 @@ ASMJIT_BEGIN_NAMESPACE
// ============================================================================
BaseAssembler::BaseAssembler() noexcept
: BaseEmitter(kTypeAssembler),
_section(nullptr),
_bufferData(nullptr),
_bufferEnd(nullptr),
_bufferPtr(nullptr) {}
: BaseEmitter(kTypeAssembler) {}
BaseAssembler::~BaseAssembler() noexcept {}
// ============================================================================
@@ -161,7 +158,7 @@ Error BaseAssembler::embed(const void* data, size_t dataSize) {
if (dataSize == 0)
return kErrorOk;
CodeBufferWriter writer(this);
CodeWriter writer(this);
ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize));
writer.emitData(data, dataSize);
@@ -194,7 +191,7 @@ Error BaseAssembler::embedDataArray(uint32_t typeId, const void* data, size_t it
if (ASMJIT_UNLIKELY(of))
return reportError(DebugUtils::errored(kErrorOutOfMemory));
CodeBufferWriter writer(this);
CodeWriter writer(this);
ASMJIT_PROPAGATE(writer.ensureSpace(this, totalSize));
#ifndef ASMJIT_NO_LOGGING
@@ -225,7 +222,7 @@ Error BaseAssembler::embedConstPool(const Label& label, const ConstPool& pool) {
ASMJIT_PROPAGATE(bind(label));
size_t size = pool.size();
CodeBufferWriter writer(this);
CodeWriter writer(this);
ASMJIT_PROPAGATE(writer.ensureSpace(this, size));
pool.fill(writer.cursor());
@@ -258,7 +255,7 @@ Error BaseAssembler::embedLabel(const Label& label, size_t dataSize) {
if (ASMJIT_UNLIKELY(!Support::isPowerOf2(dataSize) || dataSize > 8))
return reportError(DebugUtils::errored(kErrorInvalidOperandSize));
CodeBufferWriter writer(this);
CodeWriter writer(this);
ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize));
#ifndef ASMJIT_NO_LOGGING
@@ -271,21 +268,26 @@ Error BaseAssembler::embedLabel(const Label& label, size_t dataSize) {
}
#endif
Error err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, uint32_t(dataSize));
Error err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs);
if (ASMJIT_UNLIKELY(err))
return reportError(err);
re->_sourceSectionId = _section->id();
re->_sourceOffset = offset();
re->_format.resetToDataValue(uint32_t(dataSize));
if (le->isBound()) {
re->_targetSectionId = le->section()->id();
re->_payload = le->offset();
}
else {
LabelLink* link = _code->newLabelLink(le, _section->id(), offset(), 0);
OffsetFormat of;
of.resetToDataValue(uint32_t(dataSize));
LabelLink* link = _code->newLabelLink(le, _section->id(), offset(), 0, of);
if (ASMJIT_UNLIKELY(!link))
return reportError(DebugUtils::errored(kErrorOutOfMemory));
link->relocId = re->id();
}
@@ -312,7 +314,7 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, size
if (ASMJIT_UNLIKELY(!Support::isPowerOf2(dataSize) || dataSize > 8))
return reportError(DebugUtils::errored(kErrorInvalidOperandSize));
CodeBufferWriter writer(this);
CodeWriter writer(this);
ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize));
#ifndef ASMJIT_NO_LOGGING
@@ -334,7 +336,7 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, size
}
else {
RelocEntry* re;
Error err = _code->newRelocEntry(&re, RelocEntry::kTypeExpression, uint32_t(dataSize));
Error err = _code->newRelocEntry(&re, RelocEntry::kTypeExpression);
if (ASMJIT_UNLIKELY(err))
return reportError(err);
@@ -347,6 +349,7 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, size
exp->setValueAsLabel(0, labelEntry);
exp->setValueAsLabel(1, baseEntry);
re->_format.resetToDataValue(dataSize);
re->_sourceSectionId = _section->id();
re->_sourceOffset = offset();
re->_payload = (uint64_t)(uintptr_t)exp;

View File

@@ -54,13 +54,13 @@ public:
typedef BaseEmitter Base;
//! Current section where the assembling happens.
Section* _section;
Section* _section = nullptr;
//! Start of the CodeBuffer of the current section.
uint8_t* _bufferData;
uint8_t* _bufferData = nullptr;
//! End (first invalid byte) of the current section.
uint8_t* _bufferEnd;
uint8_t* _bufferEnd = nullptr;
//! Pointer in the CodeBuffer of the current section.
uint8_t* _bufferPtr;
uint8_t* _bufferPtr = nullptr;
//! \name Construction & Destruction
//! \{

View File

@@ -69,13 +69,7 @@ BaseBuilder::BaseBuilder() noexcept
_codeZone(32768 - Zone::kBlockOverhead),
_dataZone(16384 - Zone::kBlockOverhead),
_passZone(65536 - Zone::kBlockOverhead),
_allocator(&_codeZone),
_passes(),
_labelNodes(),
_cursor(nullptr),
_firstNode(nullptr),
_lastNode(nullptr),
_nodeFlags(0) {}
_allocator(&_codeZone) {}
BaseBuilder::~BaseBuilder() noexcept {
BaseBuilder_deletePasses(this);
@@ -918,8 +912,7 @@ Error BaseBuilder::onDetach(CodeHolder* code) noexcept {
// ============================================================================
Pass::Pass(const char* name) noexcept
: _cb(nullptr),
_name(name) {}
: _name(name) {}
Pass::~Pass() noexcept {}
ASMJIT_END_NAMESPACE

View File

@@ -97,23 +97,23 @@ public:
ZoneAllocator _allocator;
//! Array of `Pass` objects.
ZoneVector<Pass*> _passes;
ZoneVector<Pass*> _passes {};
//! Maps section indexes to `LabelNode` nodes.
ZoneVector<SectionNode*> _sectionNodes;
ZoneVector<SectionNode*> _sectionNodes {};
//! Maps label indexes to `LabelNode` nodes.
ZoneVector<LabelNode*> _labelNodes;
ZoneVector<LabelNode*> _labelNodes {};
//! Current node (cursor).
BaseNode* _cursor;
BaseNode* _cursor = nullptr;
//! First node of the current section.
BaseNode* _firstNode;
BaseNode* _firstNode = nullptr;
//! Last node of the current section.
BaseNode* _lastNode;
BaseNode* _lastNode = nullptr;
//! Flags assigned to each new node.
uint32_t _nodeFlags;
uint32_t _nodeFlags = 0;
//! The sections links are dirty (used internally).
bool _dirtySectionLinks;
bool _dirtySectionLinks = false;
//! \name Construction & Destruction
//! \{
@@ -1393,9 +1393,9 @@ public:
ASMJIT_NONCOPYABLE(Pass)
//! BaseBuilder this pass is assigned to.
BaseBuilder* _cb;
BaseBuilder* _cb = nullptr;
//! Name of the pass.
const char* _name;
const char* _name = nullptr;
//! \name Construction & Destruction
//! \{

View File

@@ -1,59 +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"
#include "../core/arch.h"
#include "../core/func.h"
#include "../core/type.h"
#ifdef ASMJIT_BUILD_X86
#include "../x86/x86callconv_p.h"
#endif
#ifdef ASMJIT_BUILD_ARM
#include "../arm/armcallconv_p.h"
#endif
ASMJIT_BEGIN_NAMESPACE
// ============================================================================
// [asmjit::CallConv - Init / Reset]
// ============================================================================
ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId, const Environment& environment) noexcept {
reset();
#ifdef ASMJIT_BUILD_X86
if (environment.isFamilyX86())
return x86::CallConvInternal::init(*this, ccId, environment);
#endif
#ifdef ASMJIT_BUILD_ARM
if (environment.isFamilyARM())
return arm::CallConvInternal::init(*this, ccIdv, environment);
#endif
return DebugUtils::errored(kErrorInvalidArgument);
}
ASMJIT_END_NAMESPACE

View File

@@ -1,374 +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.
#ifndef ASMJIT_CORE_CALLCONV_H_INCLUDED
#define ASMJIT_CORE_CALLCONV_H_INCLUDED
#include "../core/arch.h"
#include "../core/operand.h"
#include "../core/support.h"
ASMJIT_BEGIN_NAMESPACE
//! \addtogroup asmjit_function
//! \{
// ============================================================================
// [asmjit::CallConv]
// ============================================================================
//! Function calling convention.
//!
//! Function calling convention is a scheme that defines how function parameters
//! are passed and how function returns its result. AsmJit defines a variety of
//! architecture and OS specific calling conventions and also provides a compile
//! time detection to make the code-generation easier.
struct CallConv {
//! Calling convention id, see `Id`.
uint8_t _id;
//! Architecture identifier, see \ref Environment::Arch.
uint8_t _arch;
//! Register assignment strategy.
uint8_t _strategy;
//! Flags.
uint8_t _flags;
//! Red zone size (AMD64 == 128 bytes).
uint8_t _redZoneSize;
//! Spill zone size (WIN64 == 32 bytes).
uint8_t _spillZoneSize;
//! Natural stack alignment as defined by OS/ABI.
uint8_t _naturalStackAlignment;
uint8_t _reserved[1];
//! Mask of all passed registers, per group.
uint32_t _passedRegs[BaseReg::kGroupVirt];
//! Mask of all preserved registers, per group.
uint32_t _preservedRegs[BaseReg::kGroupVirt];
//! Internal limits of AsmJit's CallConv.
enum Limits : uint32_t {
kMaxRegArgsPerGroup = 16
};
//! Passed registers' order.
union RegOrder {
//! Passed registers, ordered.
uint8_t id[kMaxRegArgsPerGroup];
uint32_t packed[(kMaxRegArgsPerGroup + 3) / 4];
};
//! 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 Calling Conventions]
// ------------------------------------------------------------------------
//! 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 = 16,
kIdLightCall3 = 17,
kIdLightCall4 = 18,
// ------------------------------------------------------------------------
// [ABI-Specific Calling Conventions]
// ------------------------------------------------------------------------
kIdX64SystemV = 32,
kIdX64Windows = 33,
// ------------------------------------------------------------------------
// [Host]
// ------------------------------------------------------------------------
kIdHost =
#if ASMJIT_ARCH_ARM == 32 && defined(__SOFTFP__)
kIdSoftFloat
#elif ASMJIT_ARCH_ARM == 32 && !defined(__SOFTFP__)
kIdHardFloat
#else
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.
//!
//! This is AsmJit specific. It basically describes how AsmJit should convert
//! the function arguments defined by `FuncSignature` into register IDs and
//! stack offsets. The default strategy `kStrategyDefault` assigns registers
//! and then stack whereas `kStrategyWin64` strategy does register shadowing
//! as defined by WIN64 calling convention - it applies to 64-bit calling
//! conventions only.
enum Strategy : uint32_t {
//! 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 {
//! 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
//! \{
//! 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));
}
//! \}
//! \name Accessors
//! \{
//! Returns the calling convention id, see `Id`.
inline uint32_t id() const noexcept { return _id; }
//! Sets the calling convention id, see `Id`.
inline void setId(uint32_t id) noexcept { _id = uint8_t(id); }
//! Returns the calling function architecture id.
inline uint32_t arch() const noexcept { return _arch; }
//! Sets the calling function architecture id.
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; }
//! Sets the strategy used to assign registers to arguments, see `Strategy`.
inline void setStrategy(uint32_t strategy) noexcept { _strategy = uint8_t(strategy); }
//! Tests whether the calling convention has the given `flag` set.
inline bool hasFlag(uint32_t flag) const noexcept { return (uint32_t(_flags) & flag) != 0; }
//! Returns the calling convention flags, see `Flags`.
inline uint32_t flags() const noexcept { return _flags; }
//! Adds the calling convention flags, see `Flags`.
inline void setFlags(uint32_t flag) noexcept { _flags = uint8_t(flag); };
//! Adds the calling convention flags, see `Flags`.
inline void addFlags(uint32_t flags) noexcept { _flags = uint8_t(_flags | flags); };
//! Tests whether this calling convention specifies 'RedZone'.
inline bool hasRedZone() const noexcept { return _redZoneSize != 0; }
//! Tests whether this calling convention specifies 'SpillZone'.
inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; }
//! Returns size of 'RedZone'.
inline uint32_t redZoneSize() const noexcept { return _redZoneSize; }
//! Returns size of 'SpillZone'.
inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; }
//! Sets size of 'RedZone'.
inline void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = uint8_t(size); }
//! Sets size of 'SpillZone'.
inline void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = uint8_t(size); }
//! Returns a natural stack alignment.
inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; }
//! Sets a natural stack alignment.
//!
//! This function can be used to override the default stack alignment in case
//! that you know that it's alignment is different. For example it allows to
//! implement custom calling conventions that guarantee higher stack alignment.
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];
}
inline void _setPassedPacked(uint32_t group, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
_passedOrder[group].packed[0] = p0;
_passedOrder[group].packed[1] = p1;
_passedOrder[group].packed[2] = p2;
_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);
_setPassedPacked(group, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
_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);
// NOTE: This should always be called with all arguments known at compile time,
// so even if it looks scary it should be translated into few instructions.
_setPassedPacked(group, Support::bytepack32_4x8(a0, a1, a2, a3),
Support::bytepack32_4x8(a4, a5, a6, a7),
0xFFFFFFFFu,
0xFFFFFFFFu);
_passedRegs[group] = (a0 != 0xFF ? 1u << a0 : 0u) |
(a1 != 0xFF ? 1u << a1 : 0u) |
(a2 != 0xFF ? 1u << a2 : 0u) |
(a3 != 0xFF ? 1u << a3 : 0u) |
(a4 != 0xFF ? 1u << a4 : 0u) |
(a5 != 0xFF ? 1u << a5 : 0u) |
(a6 != 0xFF ? 1u << a6 : 0u) |
(a7 != 0xFF ? 1u << a7 : 0u) ;
}
//! 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;
}
//! \}
};
//! \}
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_CALLCONV_H_INCLUDED

View File

@@ -23,6 +23,7 @@
#include "../core/api-build_p.h"
#include "../core/assembler.h"
#include "../core/codewriter_p.h"
#include "../core/logger.h"
#include "../core/support.h"
@@ -507,20 +508,7 @@ static uint32_t CodeHolder_hashNameAndGetSize(const char* name, size_t& nameSize
return hashCode;
}
static bool CodeHolder_writeDisplacement(void* dst, int64_t displacement, uint32_t displacementSize) {
if (displacementSize == 4 && Support::isInt32(displacement)) {
Support::writeI32uLE(dst, int32_t(displacement));
return true;
}
else if (displacementSize == 1 && Support::isInt8(displacement)) {
Support::writeI8(dst, int8_t(displacement));
return true;
}
return false;
}
LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept {
LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel, const OffsetFormat& format) noexcept {
LabelLink* link = _allocator.allocT<LabelLink>();
if (ASMJIT_UNLIKELY(!link)) return nullptr;
@@ -531,6 +519,7 @@ LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t o
link->relocId = Globals::kInvalidId;
link->offset = offset;
link->rel = rel;
link->format = format;
_unresolvedLinkCount++;
return link;
@@ -577,9 +566,9 @@ Error CodeHolder::newNamedLabelEntry(LabelEntry** entryOut, const char* name, si
break;
case Label::kTypeGlobal:
case Label::kTypeExternal:
if (ASMJIT_UNLIKELY(parentId != Globals::kInvalidId))
return DebugUtils::errored(kErrorNonLocalLabelCannotHaveParent);
break;
default:
@@ -656,18 +645,16 @@ ASMJIT_API Error CodeHolder::resolveUnresolvedLinks() noexcept {
ASMJIT_ASSERT(linkOffset < buf.size());
// Calculate the offset relative to the start of the virtual base.
uint64_t fromOffset = Support::addOverflow<uint64_t>(fromSection->offset(), linkOffset, &of);
Support::FastUInt8 localOF = of;
uint64_t fromOffset = Support::addOverflow<uint64_t>(fromSection->offset(), linkOffset, &localOF);
int64_t displacement = int64_t(toOffset - fromOffset + uint64_t(int64_t(link->rel)));
if (!of) {
if (!localOF) {
ASMJIT_ASSERT(size_t(linkOffset) < buf.size());
// Size of the value we are going to patch. Only BYTE/DWORD is allowed.
uint32_t displacementSize = buf._data[linkOffset];
ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= displacementSize);
ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= link->format.valueSize());
// Overwrite a real displacement in the CodeBuffer.
if (CodeHolder_writeDisplacement(buf._data + linkOffset, displacement, displacementSize)) {
if (CodeWriterUtils::writeOffset(buf._data + linkOffset, displacement, link->format)) {
link.resolveAndNext(this);
continue;
}
@@ -730,11 +717,10 @@ ASMJIT_API Error CodeHolder::bindLabel(const Label& label, uint32_t toSectionId,
int64_t displacement = int64_t(toOffset - uint64_t(linkOffset) + uint64_t(int64_t(link->rel)));
// Size of the value we are going to patch. Only BYTE/DWORD is allowed.
uint32_t displacementSize = buf._data[linkOffset];
ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= displacementSize);
ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= link->format.regionSize());
// Overwrite a real displacement in the CodeBuffer.
if (!CodeHolder_writeDisplacement(buf._data + linkOffset, displacement, displacementSize)) {
if (!CodeWriterUtils::writeOffset(buf._data + linkOffset, displacement, link->format)) {
err = DebugUtils::errored(kErrorInvalidDisplacement);
link.next();
continue;
@@ -751,7 +737,7 @@ ASMJIT_API Error CodeHolder::bindLabel(const Label& label, uint32_t toSectionId,
// [asmjit::BaseEmitter - Relocations]
// ============================================================================
Error CodeHolder::newRelocEntry(RelocEntry** dst, uint32_t relocType, uint32_t valueSize) noexcept {
Error CodeHolder::newRelocEntry(RelocEntry** dst, uint32_t relocType) noexcept {
ASMJIT_PROPAGATE(_relocations.willGrow(&_allocator));
uint32_t relocId = _relocations.size();
@@ -764,7 +750,6 @@ Error CodeHolder::newRelocEntry(RelocEntry** dst, uint32_t relocType, uint32_t v
re->_id = relocId;
re->_relocType = uint8_t(relocType);
re->_valueSize = uint8_t(valueSize);
re->_sourceSectionId = Globals::kInvalidId;
re->_targetSectionId = Globals::kInvalidId;
_relocations.appendUnsafe(re);
@@ -946,13 +931,13 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept {
uint64_t sourceOffset = re->sourceOffset();
// Make sure that the `RelocEntry` doesn't go out of bounds.
size_t regionSize = re->leadingSize() + re->valueSize() + re->trailingSize();
size_t regionSize = re->format().regionSize();
if (ASMJIT_UNLIKELY(re->sourceOffset() >= sourceSection->bufferSize() ||
sourceSection->bufferSize() - size_t(re->sourceOffset()) < regionSize))
return DebugUtils::errored(kErrorInvalidRelocEntry);
uint8_t* buffer = sourceSection->data();
size_t valueOffset = size_t(re->sourceOffset()) + re->leadingSize();
size_t valueOffset = size_t(re->sourceOffset()) + re->format().valueOffset();
switch (re->relocType()) {
case RelocEntry::kTypeExpression: {
@@ -984,7 +969,7 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept {
}
case RelocEntry::kTypeX64AddressEntry: {
if (re->valueSize() != 4 || re->leadingSize() < 2)
if (re->format().valueSize() != 4 || valueOffset < 2)
return DebugUtils::errored(kErrorInvalidRelocEntry);
// First try whether a relative 32-bit displacement would work.
@@ -1038,7 +1023,7 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept {
return DebugUtils::errored(kErrorInvalidRelocEntry);
}
switch (re->valueSize()) {
switch (re->format().valueSize()) {
case 1:
Support::writeU8(buffer + valueOffset, uint32_t(value & 0xFFu));
break;

View File

@@ -24,7 +24,7 @@
#ifndef ASMJIT_CORE_CODEHOLDER_H_INCLUDED
#define ASMJIT_CORE_CODEHOLDER_H_INCLUDED
#include "../core/arch.h"
#include "../core/archtraits.h"
#include "../core/codebuffer.h"
#include "../core/datatypes.h"
#include "../core/errorhandler.h"
@@ -68,6 +68,83 @@ enum AlignMode : uint32_t {
kAlignCount = 3
};
// ============================================================================
// [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;
}
//! 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 = 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;
}
};
// ============================================================================
// [asmjit::Section]
// ============================================================================
@@ -165,6 +242,203 @@ public:
//! \}
};
// ============================================================================
// [asmjit::OffsetFormat]
// ============================================================================
//! Provides information about formatting offsets, absolute addresses, or their
//! parts. Offset format is used by both \ref RelocEntry and \ref LabelLink.
//!
//! The illustration above describes the relation of region size and offset size.
//! Region size is the size of the whole unit whereas offset size is the size of
//! the unit that will be patched.
//!
//! ```
//! +-> Code buffer | The subject of the relocation (region) |
//! | | (Word-Offset) (Word-Size) |
//! |xxxxxxxxxxxxxxx|................|*PATCHED*|................|xxxxxxxxxxxx->
//! | |
//! [Word Offset points here]----+ +--- [WordOffset + WordSize]
//! ```
//!
//! Once the offset word has been located it can be patched like this:
//!
//! ```
//! |ImmDiscardLSB (discard LSB bits).
//! |..
//! [0000000000000iiiiiiiiiiiiiiiiiDD] - Offset value (32-bit)
//! [000000000000000iiiiiiiiiiiiiiiii] - Offset value after discard LSB.
//! [00000000000iiiiiiiiiiiiiiiii0000] - Offset value shifted by ImmBitShift.
//! [xxxxxxxxxxxiiiiiiiiiiiiiiiiixxxx] - Patched word (32-bit)
//! |...............|
//! (ImmBitCount) +- ImmBitShift
//! ```
struct OffsetFormat {
//! Type of the displacement.
uint8_t _type;
//! Encoding flags.
uint8_t _flags;
//! Size of the region (in bytes) containing the offset value, if the offset
//! value is part of an instruction, otherwise it would be the same as
//! `_valueSize`.
uint8_t _regionSize;
//! Size of the offset value, in bytes (1, 2, 4, or 8).
uint8_t _valueSize;
//! Offset of the offset value, in bytes, relative to the start of the region
//! or data. Value offset would be zero if both region size and value size are
//! equal.
uint8_t _valueOffset;
//! Size of the displacement immediate value in bits.
uint8_t _immBitCount;
//! Shift of the displacement immediate value in bits in the target word.
uint8_t _immBitShift;
//! Number of least significant bits to discard before writing the immediate
//! to the destination. All discarded bits must be zero otherwise the value
//! is invalid.
uint8_t _immDiscardLsb;
//! Type of the displacement.
enum Type : uint8_t {
//! A value having `_immBitCount` bits and shifted by `_immBitShift`.
//!
//! This displacement type is sufficient for both X86/X64 and many other
//! architectures that store displacement as continuous bits within a machine
//! word.
kTypeCommon = 0,
//! AARCH64 ADR format of `[.|immlo:2|.....|immhi:19|.....]`.
kTypeAArch64_ADR,
//! AARCH64 ADRP format of `[.|immlo:2|.....|immhi:19|.....]` (4kB pages).
kTypeAArch64_ADRP,
//! Count of displacement types.
kTypeCount
};
//! Returns the type of the displacement.
inline uint32_t type() const noexcept { return _type; }
//! Returns flags.
inline uint32_t flags() const noexcept { return _flags; }
//! Returns the size of the region/instruction where the displacement is encoded.
inline uint32_t regionSize() const noexcept { return _regionSize; }
//! Returns the the offset of the word relative to the start of the region
//! where the displacement is.
inline uint32_t valueOffset() const noexcept { return _valueOffset; }
//! Returns the size of the data-type (word) that contains the displacement, in bytes.
inline uint32_t valueSize() const noexcept { return _valueSize; }
//! Returns the count of bits of the displacement value in the data it's stored in.
inline uint32_t immBitCount() const noexcept { return _immBitCount; }
//! Returns the bit-shift of the displacement value in the data it's stored in.
inline uint32_t immBitShift() const noexcept { return _immBitShift; }
//! Returns the number of least significant bits of the displacement value,
//! that must be zero and that are not part of the encoded data.
inline uint32_t immDiscardLsb() const noexcept { return _immDiscardLsb; }
//! Resets this offset format to a simple data value of `dataSize` bytes.
//!
//! The region will be the same size as data and immediate bits would correspond
//! to `dataSize * 8`. There will be no immediate bit shift or discarded bits.
inline void resetToDataValue(size_t dataSize) noexcept {
ASMJIT_ASSERT(dataSize <= 8u);
_type = uint8_t(kTypeCommon);
_flags = uint8_t(0);
_regionSize = uint8_t(dataSize);
_valueSize = uint8_t(dataSize);
_valueOffset = uint8_t(0);
_immBitCount = uint8_t(dataSize * 8u);
_immBitShift = uint8_t(0);
_immDiscardLsb = uint8_t(0);
}
inline void resetToImmValue(uint32_t type, size_t valueSize, uint32_t immBitShift, uint32_t immBitCount, uint32_t immDiscardLsb) noexcept {
ASMJIT_ASSERT(valueSize <= 8u);
ASMJIT_ASSERT(immBitShift < valueSize * 8u);
ASMJIT_ASSERT(immBitCount <= 64u);
ASMJIT_ASSERT(immDiscardLsb <= 64u);
_type = uint8_t(type);
_flags = uint8_t(0);
_regionSize = uint8_t(valueSize);
_valueSize = uint8_t(valueSize);
_valueOffset = uint8_t(0);
_immBitCount = uint8_t(immBitCount);
_immBitShift = uint8_t(immBitShift);
_immDiscardLsb = uint8_t(immDiscardLsb);
}
inline void setRegion(size_t regionSize, size_t valueOffset) noexcept {
_regionSize = uint8_t(regionSize);
_valueOffset = uint8_t(valueOffset);
}
inline void setLeadingAndTrailingSize(size_t leadingSize, size_t trailingSize) noexcept {
_regionSize = uint8_t(leadingSize + trailingSize + _valueSize);
_valueOffset = uint8_t(leadingSize);
}
};
// ============================================================================
// [asmjit::RelocEntry]
// ============================================================================
//! Relocation entry.
struct RelocEntry {
//! Relocation id.
uint32_t _id;
//! Type of the relocation.
uint32_t _relocType;
//! Format of the relocated value.
OffsetFormat _format;
//! Source section id.
uint32_t _sourceSectionId;
//! Target section id.
uint32_t _targetSectionId;
//! Source offset (relative to start of the section).
uint64_t _sourceOffset;
//! Payload (target offset, target address, expression, etc).
uint64_t _payload;
//! Relocation type.
enum RelocType : uint32_t {
//! None/deleted (no relocation).
kTypeNone = 0,
//! Expression evaluation, `_payload` is pointer to `Expression`.
kTypeExpression = 1,
//! Relocate absolute to absolute.
kTypeAbsToAbs = 2,
//! Relocate relative to absolute.
kTypeRelToAbs = 3,
//! Relocate absolute to relative.
kTypeAbsToRel = 4,
//! Relocate absolute to relative or use trampoline.
kTypeX64AddressEntry = 5
};
//! \name Accessors
//! \{
inline uint32_t id() const noexcept { return _id; }
inline uint32_t relocType() const noexcept { return _relocType; }
inline const OffsetFormat& format() const noexcept { return _format; }
inline uint32_t sourceSectionId() const noexcept { return _sourceSectionId; }
inline uint32_t targetSectionId() const noexcept { return _targetSectionId; }
inline uint64_t sourceOffset() const noexcept { return _sourceOffset; }
inline uint64_t payload() const noexcept { return _payload; }
Expression* payloadAsExpression() const noexcept {
return reinterpret_cast<Expression*>(uintptr_t(_payload));
}
//! \}
};
// ============================================================================
// [asmjit::LabelLink]
// ============================================================================
@@ -181,83 +455,8 @@ struct LabelLink {
size_t offset;
//! Inlined rel8/rel32.
intptr_t rel;
};
// ============================================================================
// [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;
}
//! 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 = 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;
}
//! Offset format information.
OffsetFormat format;
};
// ============================================================================
@@ -369,82 +568,6 @@ public:
//! \}
};
// ============================================================================
// [asmjit::RelocEntry]
// ============================================================================
//! Relocation entry.
//!
//! We describe relocation data in the following way:
//!
//! ```
//! +- Start of the buffer +- End of the data
//! | |*PATCHED*| | or instruction
//! |xxxxxxxxxxxxxxxxxxxxxx|LeadSize|ValueSize|TrailSize|xxxxxxxxxxxxxxxxxxxx->
//! |
//! +- Source offset
//! ```
struct RelocEntry {
//! Relocation id.
uint32_t _id;
//! Type of the relocation.
uint8_t _relocType;
//! Size of the relocation data/value (1, 2, 4 or 8 bytes).
uint8_t _valueSize;
//! Number of bytes after `_sourceOffset` to reach the value to be patched.
uint8_t _leadingSize;
//! Number of bytes after `_sourceOffset + _valueSize` to reach end of the
//! instruction.
uint8_t _trailingSize;
//! Source section id.
uint32_t _sourceSectionId;
//! Target section id.
uint32_t _targetSectionId;
//! Source offset (relative to start of the section).
uint64_t _sourceOffset;
//! Payload (target offset, target address, expression, etc).
uint64_t _payload;
//! Relocation type.
enum RelocType : uint32_t {
//! None/deleted (no relocation).
kTypeNone = 0,
//! Expression evaluation, `_payload` is pointer to `Expression`.
kTypeExpression = 1,
//! Relocate absolute to absolute.
kTypeAbsToAbs = 2,
//! Relocate relative to absolute.
kTypeRelToAbs = 3,
//! Relocate absolute to relative.
kTypeAbsToRel = 4,
//! Relocate absolute to relative or use trampoline.
kTypeX64AddressEntry = 5
};
//! \name Accessors
//! \{
inline uint32_t id() const noexcept { return _id; }
inline uint32_t relocType() const noexcept { return _relocType; }
inline uint32_t valueSize() const noexcept { return _valueSize; }
inline uint32_t leadingSize() const noexcept { return _leadingSize; }
inline uint32_t trailingSize() const noexcept { return _trailingSize; }
inline uint32_t sourceSectionId() const noexcept { return _sourceSectionId; }
inline uint32_t targetSectionId() const noexcept { return _targetSectionId; }
inline uint64_t sourceOffset() const noexcept { return _sourceOffset; }
inline uint64_t payload() const noexcept { return _payload; }
Expression* payloadAsExpression() const noexcept {
return reinterpret_cast<Expression*>(uintptr_t(_payload));
}
//! \}
};
// ============================================================================
// [asmjit::AddressTableEntry]
// ============================================================================
@@ -843,7 +966,7 @@ public:
//! Creates a new label-link used to store information about yet unbound labels.
//!
//! Returns `null` if the allocation failed.
ASMJIT_API LabelLink* newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept;
ASMJIT_API LabelLink* newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel, const OffsetFormat& format) noexcept;
//! Resolves cross-section links (`LabelLink`) associated with each label that
//! was used as a destination in code of a different section. It's only useful
@@ -869,10 +992,10 @@ public:
//! Returns a RelocEntry of the given `id`.
inline RelocEntry* relocEntry(uint32_t id) const noexcept { return _relocations[id]; }
//! Creates a new relocation entry of type `relocType` and size `valueSize`.
//! Creates a new relocation entry of type `relocType`.
//!
//! Additional fields can be set after the relocation entry was created.
ASMJIT_API Error newRelocEntry(RelocEntry** dst, uint32_t relocType, uint32_t valueSize) noexcept;
ASMJIT_API Error newRelocEntry(RelocEntry** dst, uint32_t relocType) noexcept;
//! \}

View File

@@ -0,0 +1,151 @@
// 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/codeholder.h"
#include "../core/codewriter_p.h"
ASMJIT_BEGIN_NAMESPACE
bool CodeWriterUtils::encodeOffset32(uint32_t* dst, int64_t offset64, const OffsetFormat& format) noexcept {
uint32_t bitCount = format.immBitCount();
uint32_t bitShift = format.immBitShift();
uint32_t discardLsb = format.immDiscardLsb();
if (!bitCount || bitCount > format.valueSize() * 8u)
return false;
if (discardLsb) {
ASMJIT_ASSERT(discardLsb <= 32);
if ((offset64 & Support::lsbMask<uint32_t>(discardLsb)) != 0)
return false;
offset64 >>= discardLsb;
}
if (!Support::isInt32(offset64))
return false;
int32_t offset32 = int32_t(offset64);
if (!Support::isEncodableOffset32(offset32, bitCount))
return false;
switch (format.type()) {
case OffsetFormat::kTypeCommon: {
*dst = (uint32_t(offset32) & Support::lsbMask<uint32_t>(bitCount)) << bitShift;
return true;
}
case OffsetFormat::kTypeAArch64_ADR:
case OffsetFormat::kTypeAArch64_ADRP: {
// Sanity checks.
if (format.valueSize() != 4 || bitCount != 21 || bitShift != 5)
return false;
uint32_t immLo = uint32_t(offset32) & 0x3u;
uint32_t immHi = uint32_t(offset32 >> 2) & Support::lsbMask<uint32_t>(19);
*dst = (immLo << 29) | (immHi << 5);
return true;
}
default:
return false;
}
}
bool CodeWriterUtils::encodeOffset64(uint64_t* dst, int64_t offset64, const OffsetFormat& format) noexcept {
uint32_t bitCount = format.immBitCount();
uint32_t discardLsb = format.immDiscardLsb();
if (!bitCount || bitCount > format.valueSize() * 8u)
return false;
if (discardLsb) {
ASMJIT_ASSERT(discardLsb <= 32);
if ((offset64 & Support::lsbMask<uint32_t>(discardLsb)) != 0)
return false;
offset64 >>= discardLsb;
}
if (!Support::isEncodableOffset64(offset64, bitCount))
return false;
switch (format.type()) {
case OffsetFormat::kTypeCommon: {
*dst = (uint64_t(offset64) & Support::lsbMask<uint64_t>(bitCount)) << format.immBitShift();
return true;
}
default:
return false;
}
}
bool CodeWriterUtils::writeOffset(void* dst, int64_t offset64, const OffsetFormat& format) noexcept {
// Offset the destination by ValueOffset so the `dst` points to the
// patched word instead of the beginning of the patched region.
dst = static_cast<char*>(dst) + format.valueOffset();
switch (format.valueSize()) {
case 1: {
uint32_t mask;
if (!encodeOffset32(&mask, offset64, format))
return false;
Support::writeU8(dst, Support::readU8(dst) | mask);
return true;
}
case 2: {
uint32_t mask;
if (!encodeOffset32(&mask, offset64, format))
return false;
Support::writeU16uLE(dst, Support::readU16uLE(dst) | mask);
return true;
}
case 4: {
uint32_t mask;
if (!encodeOffset32(&mask, offset64, format))
return false;
Support::writeU32uLE(dst, Support::readU32uLE(dst) | mask);
return true;
}
case 8: {
uint64_t mask;
if (!encodeOffset64(&mask, offset64, format))
return false;
Support::writeU64uLE(dst, Support::readU64uLE(dst) | mask);
return true;
}
default:
return false;
}
}
ASMJIT_END_NAMESPACE

View File

@@ -35,15 +35,21 @@ ASMJIT_BEGIN_NAMESPACE
//! \{
// ============================================================================
// [asmjit::CodeBufferWriter]
// [Forward Declarations]
// ============================================================================
//! Helper that is used to write into a `CodeBuffer` held by `BaseAssembler`.
class CodeBufferWriter {
struct OffsetFormat;
// ============================================================================
// [asmjit::CodeWriter]
// ============================================================================
//! Helper that is used to write into a \ref CodeBuffer held by \ref BaseAssembler.
class CodeWriter {
public:
uint8_t* _cursor;
ASMJIT_INLINE explicit CodeBufferWriter(BaseAssembler* a) noexcept
ASMJIT_INLINE explicit CodeWriter(BaseAssembler* a) noexcept
: _cursor(a->_bufferPtr) {}
ASMJIT_INLINE Error ensureSpace(BaseAssembler* a, size_t n) noexcept {
@@ -181,6 +187,19 @@ public:
}
};
// ============================================================================
// [asmjit::CodeWriterUtils]
// ============================================================================
namespace CodeWriterUtils {
bool encodeOffset32(uint32_t* dst, int64_t offset64, const OffsetFormat& format) noexcept;
bool encodeOffset64(uint64_t* dst, int64_t offset64, const OffsetFormat& format) noexcept;
bool writeOffset(void* dst, int64_t offset64, const OffsetFormat& format) noexcept;
} // {CodeWriterUtils}
//! \}
//! \endcond

View File

@@ -282,11 +282,10 @@ Error BaseCompiler::newVirtReg(VirtReg** out, uint32_t typeId, uint32_t signatur
}
Error BaseCompiler::_newReg(BaseReg* out, uint32_t typeId, const char* name) {
RegInfo regInfo;
out->reset();
RegInfo regInfo;
Error err = ArchUtils::typeIdToRegInfo(arch(), typeId, &typeId, &regInfo);
if (ASMJIT_UNLIKELY(err))
return reportError(err);
@@ -569,6 +568,10 @@ JumpAnnotation* BaseCompiler::newJumpAnnotation() {
Error BaseCompiler::onAttach(CodeHolder* code) noexcept {
ASMJIT_PROPAGATE(Base::onAttach(code));
const ArchTraits& archTraits = ArchTraits::byArch(code->arch());
uint32_t nativeRegType = Environment::is32Bit(code->arch()) ? BaseReg::kTypeGp32 : BaseReg::kTypeGp64;
_gpRegInfo.setSignature(archTraits.regTypeToSignature(nativeRegType));
Error err = addPassT<GlobalConstPoolPass>();
if (ASMJIT_UNLIKELY(err)) {
onDetach(code);

View File

@@ -30,6 +30,7 @@
#include "../core/assembler.h"
#include "../core/builder.h"
#include "../core/constpool.h"
#include "../core/compilerdefs.h"
#include "../core/func.h"
#include "../core/inst.h"
#include "../core/operand.h"
@@ -43,11 +44,7 @@ ASMJIT_BEGIN_NAMESPACE
// [Forward Declarations]
// ============================================================================
struct RATiedReg;
class RAWorkReg;
class JumpAnnotation;
class JumpNode;
class FuncNode;
class FuncRetNode;
@@ -56,131 +53,6 @@ class InvokeNode;
//! \addtogroup asmjit_compiler
//! \{
// ============================================================================
// [asmjit::VirtReg]
// ============================================================================
//! Virtual register data, managed by \ref BaseCompiler.
class VirtReg {
public:
ASMJIT_NONCOPYABLE(VirtReg)
//! Virtual register id.
uint32_t _id;
//! Virtual register info (signature).
RegInfo _info;
//! Virtual register size (can be smaller than `regInfo._size`).
uint32_t _virtSize;
//! Virtual register alignment (for spilling).
uint8_t _alignment;
//! Type-id.
uint8_t _typeId;
//! Virtual register weight for alloc/spill decisions.
uint8_t _weight;
//! True if this is a fixed register, never reallocated.
uint8_t _isFixed : 1;
//! True if the virtual register is only used as a stack (never accessed as register).
uint8_t _isStack : 1;
uint8_t _reserved : 6;
//! Virtual register name (user provided or automatically generated).
ZoneString<16> _name;
// -------------------------------------------------------------------------
// The following members are used exclusively by RAPass. They are initialized
// when the VirtReg is created to NULL pointers and then changed during RAPass
// execution. RAPass sets them back to NULL before it returns.
// -------------------------------------------------------------------------
//! Reference to `RAWorkReg`, used during register allocation.
RAWorkReg* _workReg;
//! \name Construction & Destruction
//! \{
inline VirtReg(uint32_t id, uint32_t signature, uint32_t virtSize, uint32_t alignment, uint32_t typeId) noexcept
: _id(id),
_virtSize(virtSize),
_alignment(uint8_t(alignment)),
_typeId(uint8_t(typeId)),
_weight(1),
_isFixed(false),
_isStack(false),
_reserved(0),
_name(),
_workReg(nullptr) { _info._signature = signature; }
//! \}
//! \name Accessors
//! \{
//! Returns the virtual register id.
inline uint32_t id() const noexcept { return _id; }
//! Returns the virtual register name.
inline const char* name() const noexcept { return _name.data(); }
//! Returns the size of the virtual register name.
inline uint32_t nameSize() const noexcept { return _name.size(); }
//! Returns a register information that wraps the register signature.
inline const RegInfo& info() const noexcept { return _info; }
//! Returns a virtual register type (maps to the physical register type as well).
inline uint32_t type() const noexcept { return _info.type(); }
//! Returns a virtual register group (maps to the physical register group as well).
inline uint32_t group() const noexcept { return _info.group(); }
//! Returns a real size of the register this virtual register maps to.
//!
//! For example if this is a 128-bit SIMD register used for a scalar single
//! precision floating point value then its virtSize would be 4, however, the
//! `regSize` would still say 16 (128-bits), because it's the smallest size
//! of that register type.
inline uint32_t regSize() const noexcept { return _info.size(); }
//! Returns a register signature of this virtual register.
inline uint32_t signature() const noexcept { return _info.signature(); }
//! Returns the virtual register size.
//!
//! The virtual register size describes how many bytes the virtual register
//! needs to store its content. It can be smaller than the physical register
//! size, see `regSize()`.
inline uint32_t virtSize() const noexcept { return _virtSize; }
//! Returns the virtual register alignment.
inline uint32_t alignment() const noexcept { return _alignment; }
//! Returns the virtual register type id, see `Type::Id`.
inline uint32_t typeId() const noexcept { return _typeId; }
//! Returns the virtual register weight - the register allocator can use it
//! as explicit hint for alloc/spill decisions.
inline uint32_t weight() const noexcept { return _weight; }
//! Sets the virtual register weight (0 to 255) - the register allocator can
//! use it as explicit hint for alloc/spill decisions and initial bin-packing.
inline void setWeight(uint32_t weight) noexcept { _weight = uint8_t(weight); }
//! Returns whether the virtual register is always allocated to a fixed
//! physical register (and never reallocated).
//!
//! \note This is only used for special purposes and it's mostly internal.
inline bool isFixed() const noexcept { return bool(_isFixed); }
//! Returns whether the virtual register is indeed a stack that only uses
//! the virtual register id for making it accessible.
//!
//! \note It's an error if a stack is accessed as a register.
inline bool isStack() const noexcept { return bool(_isStack); }
inline bool hasWorkReg() const noexcept { return _workReg != nullptr; }
inline RAWorkReg* workReg() const noexcept { return _workReg; }
inline void setWorkReg(RAWorkReg* workReg) noexcept { _workReg = workReg; }
inline void resetWorkReg() noexcept { _workReg = nullptr; }
//! \}
};
// ============================================================================
// [asmjit::BaseCompiler]
// ============================================================================

View File

@@ -0,0 +1,170 @@
// 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_COMPILERDEFS_H_INCLUDED
#define ASMJIT_CORE_COMPILERDEFS_H_INCLUDED
#include "../core/api-config.h"
#include "../core/operand.h"
#include "../core/zonestring.h"
ASMJIT_BEGIN_NAMESPACE
// ============================================================================
// [Forward Declarations]
// ============================================================================
class RAWorkReg;
//! \addtogroup asmjit_compiler
//! \{
// ============================================================================
// [asmjit::VirtReg]
// ============================================================================
//! Virtual register data, managed by \ref BaseCompiler.
class VirtReg {
public:
ASMJIT_NONCOPYABLE(VirtReg)
//! Virtual register id.
uint32_t _id = 0;
//! Virtual register info (signature).
RegInfo _info = {};
//! Virtual register size (can be smaller than `regInfo._size`).
uint32_t _virtSize = 0;
//! Virtual register alignment (for spilling).
uint8_t _alignment = 0;
//! Type-id.
uint8_t _typeId = 0;
//! Virtual register weight for alloc/spill decisions.
uint8_t _weight = 1;
//! True if this is a fixed register, never reallocated.
uint8_t _isFixed : 1;
//! True if the virtual register is only used as a stack (never accessed as register).
uint8_t _isStack : 1;
uint8_t _reserved : 6;
//! Virtual register name (user provided or automatically generated).
ZoneString<16> _name {};
// -------------------------------------------------------------------------
// The following members are used exclusively by RAPass. They are initialized
// when the VirtReg is created to NULL pointers and then changed during RAPass
// execution. RAPass sets them back to NULL before it returns.
// -------------------------------------------------------------------------
//! Reference to `RAWorkReg`, used during register allocation.
RAWorkReg* _workReg = nullptr;
//! \name Construction & Destruction
//! \{
inline VirtReg(uint32_t id, uint32_t signature, uint32_t virtSize, uint32_t alignment, uint32_t typeId) noexcept
: _id(id),
_info { signature },
_virtSize(virtSize),
_alignment(uint8_t(alignment)),
_typeId(uint8_t(typeId)),
_isFixed(false),
_isStack(false),
_reserved(0) {}
//! \}
//! \name Accessors
//! \{
//! Returns the virtual register id.
inline uint32_t id() const noexcept { return _id; }
//! Returns the virtual register name.
inline const char* name() const noexcept { return _name.data(); }
//! Returns the size of the virtual register name.
inline uint32_t nameSize() const noexcept { return _name.size(); }
//! Returns a register information that wraps the register signature.
inline const RegInfo& info() const noexcept { return _info; }
//! Returns a virtual register type (maps to the physical register type as well).
inline uint32_t type() const noexcept { return _info.type(); }
//! Returns a virtual register group (maps to the physical register group as well).
inline uint32_t group() const noexcept { return _info.group(); }
//! Returns a real size of the register this virtual register maps to.
//!
//! For example if this is a 128-bit SIMD register used for a scalar single
//! precision floating point value then its virtSize would be 4, however, the
//! `regSize` would still say 16 (128-bits), because it's the smallest size
//! of that register type.
inline uint32_t regSize() const noexcept { return _info.size(); }
//! Returns a register signature of this virtual register.
inline uint32_t signature() const noexcept { return _info.signature(); }
//! Returns the virtual register size.
//!
//! The virtual register size describes how many bytes the virtual register
//! needs to store its content. It can be smaller than the physical register
//! size, see `regSize()`.
inline uint32_t virtSize() const noexcept { return _virtSize; }
//! Returns the virtual register alignment.
inline uint32_t alignment() const noexcept { return _alignment; }
//! Returns the virtual register type id, see `Type::Id`.
inline uint32_t typeId() const noexcept { return _typeId; }
//! Returns the virtual register weight - the register allocator can use it
//! as explicit hint for alloc/spill decisions.
inline uint32_t weight() const noexcept { return _weight; }
//! Sets the virtual register weight (0 to 255) - the register allocator can
//! use it as explicit hint for alloc/spill decisions and initial bin-packing.
inline void setWeight(uint32_t weight) noexcept { _weight = uint8_t(weight); }
//! Returns whether the virtual register is always allocated to a fixed
//! physical register (and never reallocated).
//!
//! \note This is only used for special purposes and it's mostly internal.
inline bool isFixed() const noexcept { return bool(_isFixed); }
//! Returns whether the virtual register is indeed a stack that only uses
//! the virtual register id for making it accessible.
//!
//! \note It's an error if a stack is accessed as a register.
inline bool isStack() const noexcept { return bool(_isStack); }
inline bool hasWorkReg() const noexcept { return _workReg != nullptr; }
inline RAWorkReg* workReg() const noexcept { return _workReg; }
inline void setWorkReg(RAWorkReg* workReg) noexcept { _workReg = workReg; }
inline void resetWorkReg() noexcept { _workReg = nullptr; }
//! \}
};
//! \}
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_COMPILERDEFS_H_INCLUDED

View File

@@ -24,7 +24,7 @@
#ifndef ASMJIT_CORE_CPUINFO_H_INCLUDED
#define ASMJIT_CORE_CPUINFO_H_INCLUDED
#include "../core/arch.h"
#include "../core/archtraits.h"
#include "../core/features.h"
#include "../core/globals.h"
#include "../core/string.h"

View File

@@ -0,0 +1,351 @@
// 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/archtraits.h"
#include "../core/emithelper_p.h"
#include "../core/formatter.h"
#include "../core/funcargscontext_p.h"
#include "../core/radefs_p.h"
// Can be used for debugging...
// #define ASMJIT_DUMP_ARGS_ASSIGNMENT
ASMJIT_BEGIN_NAMESPACE
// ============================================================================
// [asmjit::BaseEmitHelper - Formatting]
// ============================================================================
#ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
static void dumpFuncValue(String& sb, uint32_t arch, const FuncValue& value) noexcept {
Formatter::formatTypeId(sb, value.typeId());
sb.append('@');
if (value.isIndirect())
sb.append('[');
if (value.isReg())
Formatter::formatRegister(sb, 0, nullptr, arch, value.regType(), value.regId());
else if (value.isStack())
sb.appendFormat("[%d]", value.stackOffset());
else
sb.append("<none>");
if (value.isIndirect())
sb.append(']');
}
static void dumpAssignment(String& sb, const FuncArgsContext& ctx) noexcept {
typedef FuncArgsContext::Var Var;
uint32_t arch = ctx.arch();
uint32_t varCount = ctx.varCount();
for (uint32_t i = 0; i < varCount; i++) {
const Var& var = ctx.var(i);
const FuncValue& dst = var.out;
const FuncValue& cur = var.cur;
sb.appendFormat("Var%u: ", i);
dumpFuncValue(sb, arch, dst);
sb.append(" <- ");
dumpFuncValue(sb, arch, cur);
if (var.isDone())
sb.append(" {Done}");
sb.append('\n');
}
}
#endif
// ============================================================================
// [asmjit::BaseEmitHelper - EmitArgsAssignment]
// ============================================================================
ASMJIT_FAVOR_SIZE Error BaseEmitHelper::emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args) {
typedef FuncArgsContext::Var Var;
typedef FuncArgsContext::WorkData WorkData;
enum WorkFlags : uint32_t {
kWorkNone = 0x00,
kWorkDidSome = 0x01,
kWorkPending = 0x02,
kWorkPostponed = 0x04
};
uint32_t arch = frame.arch();
const ArchTraits& archTraits = ArchTraits::byArch(arch);
RAConstraints constraints;
FuncArgsContext ctx;
ASMJIT_PROPAGATE(constraints.init(arch));
ASMJIT_PROPAGATE(ctx.initWorkData(frame, args, &constraints));
#ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
{
String sb;
dumpAssignment(sb, ctx);
printf("%s\n", sb.data());
}
#endif
uint32_t varCount = ctx._varCount;
WorkData* workData = ctx._workData;
uint32_t saVarId = ctx._saVarId;
BaseReg sp = BaseReg::fromSignatureAndId(_emitter->_gpRegInfo.signature(), archTraits.spRegId());
BaseReg sa = sp;
if (frame.hasDynamicAlignment()) {
if (frame.hasPreservedFP())
sa.setId(archTraits.fpRegId());
else
sa.setId(saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId());
}
// --------------------------------------------------------------------------
// Register to stack and stack to stack moves must be first as now we have
// the biggest chance of having as many as possible unassigned registers.
// --------------------------------------------------------------------------
if (ctx._stackDstMask) {
// Base address of all arguments passed by stack.
BaseMem baseArgPtr(sa, int32_t(frame.saOffset(sa.id())));
BaseMem baseStackPtr(sp, 0);
for (uint32_t varId = 0; varId < varCount; varId++) {
Var& var = ctx._vars[varId];
if (!var.out.isStack())
continue;
FuncValue& cur = var.cur;
FuncValue& out = var.out;
ASMJIT_ASSERT(cur.isReg() || cur.isStack());
BaseReg reg;
BaseMem dstStackPtr = baseStackPtr.cloneAdjusted(out.stackOffset());
BaseMem srcStackPtr = baseArgPtr.cloneAdjusted(cur.stackOffset());
if (cur.isIndirect()) {
if (cur.isStack()) {
// TODO: Indirect stack.
return DebugUtils::errored(kErrorInvalidAssignment);
}
else {
srcStackPtr.setBaseId(cur.regId());
}
}
if (cur.isReg() && !cur.isIndirect()) {
WorkData& wd = workData[archTraits.regTypeToGroup(cur.regType())];
uint32_t rId = cur.regId();
reg.setSignatureAndId(archTraits.regTypeToSignature(cur.regType()), rId);
wd.unassign(varId, rId);
}
else {
// Stack to reg move - tricky since we move stack to stack we can decide which
// register to use. In general we follow the rule that IntToInt moves will use
// GP regs with possibility to signature or zero extend, and all other moves will
// either use GP or VEC regs depending on the size of the move.
RegInfo rInfo = getSuitableRegForMemToMemMove(arch, out.typeId(), cur.typeId());
if (ASMJIT_UNLIKELY(!rInfo.isValid()))
return DebugUtils::errored(kErrorInvalidState);
WorkData& wd = workData[rInfo.group()];
uint32_t availableRegs = wd.availableRegs();
if (ASMJIT_UNLIKELY(!availableRegs))
return DebugUtils::errored(kErrorInvalidState);
uint32_t rId = Support::ctz(availableRegs);
reg.setSignatureAndId(rInfo.signature(), rId);
ASMJIT_PROPAGATE(emitArgMove(reg, out.typeId(), srcStackPtr, cur.typeId()));
}
if (cur.isIndirect() && cur.isReg())
workData[BaseReg::kGroupGp].unassign(varId, cur.regId());
// Register to stack move.
ASMJIT_PROPAGATE(emitRegMove(dstStackPtr, reg, cur.typeId()));
var.markDone();
}
}
// --------------------------------------------------------------------------
// Shuffle all registers that are currently assigned accordingly to target
// assignment.
// --------------------------------------------------------------------------
uint32_t workFlags = kWorkNone;
for (;;) {
for (uint32_t varId = 0; varId < varCount; varId++) {
Var& var = ctx._vars[varId];
if (var.isDone() || !var.cur.isReg())
continue;
FuncValue& cur = var.cur;
FuncValue& out = var.out;
uint32_t curGroup = archTraits.regTypeToGroup(cur.regType());
uint32_t outGroup = archTraits.regTypeToGroup(out.regType());
uint32_t curId = cur.regId();
uint32_t outId = out.regId();
if (curGroup != outGroup) {
// TODO: Conversion is not supported.
return DebugUtils::errored(kErrorInvalidAssignment);
}
else {
WorkData& wd = workData[outGroup];
if (!wd.isAssigned(outId)) {
EmitMove:
ASMJIT_PROPAGATE(
emitArgMove(
BaseReg::fromSignatureAndId(archTraits.regTypeToSignature(out.regType()), outId), out.typeId(),
BaseReg::fromSignatureAndId(archTraits.regTypeToSignature(cur.regType()), curId), cur.typeId()));
wd.reassign(varId, outId, curId);
cur.initReg(out.regType(), outId, out.typeId());
if (outId == out.regId())
var.markDone();
workFlags |= kWorkDidSome | kWorkPending;
}
else {
uint32_t altId = wd._physToVarId[outId];
Var& altVar = ctx._vars[altId];
if (!altVar.out.isInitialized() || (altVar.out.isReg() && altVar.out.regId() == curId)) {
// Only few architectures provide swap operations, and only for few register groups.
if (archTraits.hasSwap(curGroup)) {
uint32_t highestType = Support::max(cur.regType(), altVar.cur.regType());
if (Support::isBetween<uint32_t>(highestType, BaseReg::kTypeGp8Lo, BaseReg::kTypeGp16))
highestType = BaseReg::kTypeGp32;
uint32_t signature = archTraits.regTypeToSignature(highestType);
ASMJIT_PROPAGATE(
emitRegSwap(BaseReg::fromSignatureAndId(signature, outId),
BaseReg::fromSignatureAndId(signature, curId)));
wd.swap(varId, curId, altId, outId);
cur.setRegId(outId);
var.markDone();
altVar.cur.setRegId(curId);
if (altVar.out.isInitialized())
altVar.markDone();
workFlags |= kWorkDidSome;
}
else {
// If there is a scratch register it can be used to perform the swap.
uint32_t availableRegs = wd.availableRegs();
if (availableRegs) {
uint32_t inOutRegs = wd.dstRegs();
if (availableRegs & ~inOutRegs)
availableRegs &= ~inOutRegs;
outId = Support::ctz(availableRegs);
goto EmitMove;
}
else {
workFlags |= kWorkPending;
}
}
}
else {
workFlags |= kWorkPending;
}
}
}
}
if (!(workFlags & kWorkPending))
break;
// If we did nothing twice it means that something is really broken.
if ((workFlags & (kWorkDidSome | kWorkPostponed)) == kWorkPostponed)
return DebugUtils::errored(kErrorInvalidState);
workFlags = (workFlags & kWorkDidSome) ? kWorkNone : kWorkPostponed;
}
// --------------------------------------------------------------------------
// Load arguments passed by stack into registers. This is pretty simple and
// it never requires multiple iterations like the previous phase.
// --------------------------------------------------------------------------
if (ctx._hasStackSrc) {
uint32_t iterCount = 1;
if (frame.hasDynamicAlignment() && !frame.hasPreservedFP())
sa.setId(saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId());
// Base address of all arguments passed by stack.
BaseMem baseArgPtr(sa, int32_t(frame.saOffset(sa.id())));
for (uint32_t iter = 0; iter < iterCount; iter++) {
for (uint32_t varId = 0; varId < varCount; varId++) {
Var& var = ctx._vars[varId];
if (var.isDone())
continue;
if (var.cur.isStack()) {
ASMJIT_ASSERT(var.out.isReg());
uint32_t outId = var.out.regId();
uint32_t outType = var.out.regType();
uint32_t group = archTraits.regTypeToGroup(outType);
WorkData& wd = ctx._workData[group];
if (outId == sa.id() && group == BaseReg::kGroupGp) {
// This register will be processed last as we still need `saRegId`.
if (iterCount == 1) {
iterCount++;
continue;
}
wd.unassign(wd._physToVarId[outId], outId);
}
BaseReg dstReg = BaseReg::fromSignatureAndId(archTraits.regTypeToSignature(outType), outId);
BaseMem srcMem = baseArgPtr.cloneAdjusted(var.cur.stackOffset());
ASMJIT_PROPAGATE(emitArgMove(
dstReg, var.out.typeId(),
srcMem, var.cur.typeId()));
wd.assign(varId, outId);
var.cur.initReg(outType, outId, var.cur.typeId(), FuncValue::kFlagIsDone);
}
}
}
}
return kErrorOk;
}
ASMJIT_END_NAMESPACE

View File

@@ -0,0 +1,83 @@
// 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_EMITHELPER_P_H_INCLUDED
#define ASMJIT_CORE_EMITHELPER_P_H_INCLUDED
#include "../core/emitter.h"
#include "../core/operand.h"
#include "../core/type.h"
ASMJIT_BEGIN_NAMESPACE
//! \cond INTERNAL
//! \addtogroup asmjit_core
//! \{
// ============================================================================
// [asmjit::BaseEmitHelper]
// ============================================================================
//! Helper class that provides utilities for each supported architecture.
class BaseEmitHelper {
public:
BaseEmitter* _emitter;
inline explicit BaseEmitHelper(BaseEmitter* emitter = nullptr) noexcept
: _emitter(emitter) {}
inline BaseEmitter* emitter() const noexcept { return _emitter; }
inline void setEmitter(BaseEmitter* emitter) noexcept { _emitter = emitter; }
//! Emits a pure move operation between two registers or the same type or
//! between a register and its home slot. This function does not handle
//! register conversion.
virtual Error emitRegMove(
const Operand_& dst_,
const Operand_& src_, uint32_t typeId, const char* comment = nullptr) = 0;
//! Emits swap between two registers.
virtual Error emitRegSwap(
const BaseReg& a,
const BaseReg& b, const char* comment = nullptr) = 0;
//! Emits move from a function argument (either register or stack) to a register.
//!
//! This function can handle the necessary conversion from one argument to
//! another, and from one register type to another, if it's possible. Any
//! attempt of conversion that requires third register of a different group
//! (for example conversion from K to MMX on X86/X64) will fail.
virtual Error emitArgMove(
const BaseReg& dst_, uint32_t dstTypeId,
const Operand_& src_, uint32_t srcTypeId, const char* comment = nullptr) = 0;
Error emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args);
};
//! \}
//! \endcond
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_EMITHELPER_P_H_INCLUDED

View File

@@ -28,13 +28,13 @@
#include "../core/support.h"
#ifdef ASMJIT_BUILD_X86
#include "../x86/x86internal_p.h"
#include "../x86/x86emithelper_p.h"
#include "../x86/x86instdb_p.h"
#endif // ASMJIT_BUILD_X86
#ifdef ASMJIT_BUILD_ARM
#include "../arm/arminternal_p.h"
#include "../arm/arminstdb.h"
#include "../arm/a64emithelper_p.h"
#include "../arm/a64instdb.h"
#endif // ASMJIT_BUILD_ARM
ASMJIT_BEGIN_NAMESPACE
@@ -44,21 +44,7 @@ ASMJIT_BEGIN_NAMESPACE
// ============================================================================
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),
_extraReg(),
_inlineComment(nullptr) {}
: _emitterType(uint8_t(emitterType)) {}
BaseEmitter::~BaseEmitter() noexcept {
if (_code) {
@@ -257,13 +243,17 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitProlog(const FuncFrame& frame) {
return DebugUtils::errored(kErrorNotInitialized);
#ifdef ASMJIT_BUILD_X86
if (environment().isFamilyX86())
return x86::X86Internal::emitProlog(as<x86::Emitter>(), frame);
if (environment().isFamilyX86()) {
x86::EmitHelper emitHelper(this, frame.isAvxEnabled());
return emitHelper.emitProlog(frame);
}
#endif
#ifdef ASMJIT_BUILD_ARM
if (environment().isFamilyARM())
return arm::ArmInternal::emitProlog(as<arm::Emitter>(), frame);
if (environment().isArchAArch64()) {
a64::EmitHelper emitHelper(this);
return emitHelper.emitProlog(frame);
}
#endif
return DebugUtils::errored(kErrorInvalidArch);
@@ -274,13 +264,17 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitEpilog(const FuncFrame& frame) {
return DebugUtils::errored(kErrorNotInitialized);
#ifdef ASMJIT_BUILD_X86
if (environment().isFamilyX86())
return x86::X86Internal::emitEpilog(as<x86::Emitter>(), frame);
if (environment().isFamilyX86()) {
x86::EmitHelper emitHelper(this, frame.isAvxEnabled());
return emitHelper.emitEpilog(frame);
}
#endif
#ifdef ASMJIT_BUILD_ARM
if (environment().isFamilyARM())
return arm::ArmInternal::emitEpilog(as<arm::Emitter>(), frame);
if (environment().isArchAArch64()) {
a64::EmitHelper emitHelper(this);
return emitHelper.emitEpilog(frame);
}
#endif
return DebugUtils::errored(kErrorInvalidArch);
@@ -291,13 +285,17 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitArgsAssignment(const FuncFrame& frame,
return DebugUtils::errored(kErrorNotInitialized);
#ifdef ASMJIT_BUILD_X86
if (environment().isFamilyX86())
return x86::X86Internal::emitArgsAssignment(as<x86::Emitter>(), frame, args);
if (environment().isFamilyX86()) {
x86::EmitHelper emitHelper(this, frame.isAvxEnabled());
return emitHelper.emitArgsAssignment(frame, args);
}
#endif
#ifdef ASMJIT_BUILD_ARM
if (environment().isFamilyARM())
return arm::ArmInternal::emitArgsAssignment(as<arm::Emitter>(), frame, args);
if (environment().isArchAArch64()) {
a64::EmitHelper emitHelper(this);
return emitHelper.emitArgsAssignment(frame, args);
}
#endif
return DebugUtils::errored(kErrorInvalidArch);
@@ -349,6 +347,10 @@ Error BaseEmitter::onAttach(CodeHolder* code) noexcept {
_code = code;
_environment = code->environment();
const ArchTraits& archTraits = ArchTraits::byArch(code->arch());
uint32_t nativeRegType = Environment::is32Bit(code->arch()) ? BaseReg::kTypeGp32 : BaseReg::kTypeGp64;
_gpRegInfo.setSignature(archTraits._regInfo[nativeRegType].signature());
onSettingsUpdated();
return kErrorOk;
}

View File

@@ -24,7 +24,7 @@
#ifndef ASMJIT_CORE_EMITTER_H_INCLUDED
#define ASMJIT_CORE_EMITTER_H_INCLUDED
#include "../core/arch.h"
#include "../core/archtraits.h"
#include "../core/codeholder.h"
#include "../core/inst.h"
#include "../core/operand.h"
@@ -54,43 +54,43 @@ public:
ASMJIT_BASE_CLASS(BaseEmitter)
//! See \ref EmitterType.
uint8_t _emitterType;
uint8_t _emitterType = 0;
//! See \ref BaseEmitter::EmitterFlags.
uint8_t _emitterFlags;
uint8_t _emitterFlags = 0;
//! 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;
uint8_t _validationFlags = 0;
//! Validation options, see \ref ValidationOptions.
uint8_t _validationOptions;
uint8_t _validationOptions = 0;
//! Encoding options, see \ref EncodingOptions.
uint32_t _encodingOptions;
uint32_t _encodingOptions = 0;
//! Forced instruction options, combined with \ref _instOptions by \ref emit().
uint32_t _forcedInstOptions;
uint32_t _forcedInstOptions = BaseInst::kOptionReserved;
//! Internal private data used freely by any emitter.
uint32_t _privateData;
uint32_t _privateData = 0;
//! CodeHolder the emitter is attached to.
CodeHolder* _code;
CodeHolder* _code = nullptr;
//! Attached \ref Logger.
Logger* _logger;
Logger* _logger = nullptr;
//! Attached \ref ErrorHandler.
ErrorHandler* _errorHandler;
ErrorHandler* _errorHandler = nullptr;
//! Describes the target environment, matches \ref CodeHolder::environment().
Environment _environment;
Environment _environment {};
//! Native GP register signature and signature related information.
RegInfo _gpRegInfo;
RegInfo _gpRegInfo {};
//! Next instruction options (affects the next instruction).
uint32_t _instOptions;
uint32_t _instOptions = 0;
//! Extra register (op-mask {k} on AVX-512) (affects the next instruction).
RegOnly _extraReg;
RegOnly _extraReg {};
//! Inline comment of the next instruction (affects the next instruction).
const char* _inlineComment;
const char* _inlineComment = nullptr;
//! Emitter type.
enum EmitterType : uint32_t {
@@ -494,6 +494,11 @@ public:
//! Creates a new named label.
virtual Label newNamedLabel(const char* name, size_t nameSize = SIZE_MAX, uint32_t type = Label::kTypeGlobal, uint32_t parentId = Globals::kInvalidId) = 0;
//! Creates a new external label.
inline Label newExternalLabel(const char* name, size_t nameSize = SIZE_MAX) {
return newNamedLabel(name, nameSize, Label::kTypeExternal);
}
//! Returns `Label` by `name`.
//!
//! Returns invalid Label in case that the name is invalid or label was not found.

View File

@@ -101,16 +101,16 @@ public:
// 8 is not used, even numbers are 64-bit architectures.
//! 32-bit MIPS architecture in (little endian).
kArchMIPS_LE = 9,
kArchMIPS32_LE = 9,
//! 32-bit MIPS architecture in (big endian).
kArchMIPS_BE = kArchMIPS_LE | kArchBigEndianMask,
kArchMIPS32_BE = kArchMIPS32_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
kArchCount = 11
};
//! Sub-architecture.
@@ -246,8 +246,8 @@ public:
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 == 32 && ASMJIT_ARCH_LE ? kArchMIPS32_LE :
ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_BE ? kArchMIPS32_BE :
ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_LE ? kArchMIPS64_LE :
ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_BE ? kArchMIPS64_BE :
@@ -410,6 +410,16 @@ public:
_reserved = 0;
}
inline bool isArchX86() const noexcept { return _arch == kArchX86; }
inline bool isArchX64() const noexcept { return _arch == kArchX64; }
inline bool isArchRISCV32() const noexcept { return _arch == kArchRISCV32; }
inline bool isArchRISCV64() const noexcept { return _arch == kArchRISCV64; }
inline bool isArchARM() const noexcept { return (_arch & ~kArchBigEndianMask) == kArchARM; }
inline bool isArchThumb() const noexcept { return (_arch & ~kArchBigEndianMask) == kArchThumb; }
inline bool isArchAArch64() const noexcept { return (_arch & ~kArchBigEndianMask) == kArchAArch64; }
inline bool isArchMIPS32() const noexcept { return (_arch & ~kArchBigEndianMask) == kArchMIPS32_LE; }
inline bool isArchMIPS64() const noexcept { return (_arch & ~kArchBigEndianMask) == kArchMIPS64_LE; }
//! Tests whether the architecture is 32-bit.
inline bool is32Bit() const noexcept { return is32Bit(_arch); }
//! Tests whether the architecture is 64-bit.
@@ -422,11 +432,11 @@ public:
//! Tests whether this architecture is of X86 family.
inline bool isFamilyX86() const noexcept { return isFamilyX86(_arch); }
//! Tests whether this architecture is of ARM family.
//! Tests whether this architecture family is RISC-V (both 32-bit and 64-bit).
inline bool isFamilyRISCV() const noexcept { return isFamilyRISCV(_arch); }
//! Tests whether this architecture is of ARM family.
//! Tests whether this architecture family is ARM, Thumb, or AArch64.
inline bool isFamilyARM() const noexcept { return isFamilyARM(_arch); }
//! Tests whether this architecture is of ARM family.
//! Tests whether this architecture family is MISP or MIPS64.
inline bool isFamilyMIPS() const noexcept { return isFamilyMIPS(_arch); }
//! Tests whether the environment platform is Windows.
@@ -486,6 +496,11 @@ public:
//! \name Static Utilities
//! \{
static inline bool isValidArch(uint32_t arch) noexcept {
return (arch & ~kArchBigEndianMask) != 0 &&
(arch & ~kArchBigEndianMask) < kArchCount;
}
//! Tests whether the given architecture `arch` is 32-bit.
static inline bool is32Bit(uint32_t arch) noexcept {
return (arch & kArch32BitMask) == kArch32BitMask;
@@ -506,6 +521,12 @@ public:
return (arch & kArchBigEndianMask) == kArchBigEndianMask;
}
//! Tests whether the given architecture is AArch64.
static inline bool isArchAArch64(uint32_t arch) noexcept {
arch &= ~kArchBigEndianMask;
return arch == kArchAArch64;
}
//! Tests whether the given architecture family is X86 or X64.
static inline bool isFamilyX86(uint32_t arch) noexcept {
return arch == kArchX86 ||
@@ -529,7 +550,7 @@ public:
//! Tests whether the given architecture family is MISP or MIPS64.
static inline bool isFamilyMIPS(uint32_t arch) noexcept {
arch &= ~kArchBigEndianMask;
return arch == kArchMIPS_LE ||
return arch == kArchMIPS32_LE ||
arch == kArchMIPS64_LE;
}

View File

@@ -22,22 +22,42 @@
// 3. This notice may not be removed or altered from any source distribution.
#include "../core/api-build_p.h"
#include "../core/arch.h"
#include "../core/archtraits.h"
#include "../core/func.h"
#include "../core/operand.h"
#include "../core/type.h"
#include "../core/funcargscontext_p.h"
#ifdef ASMJIT_BUILD_X86
#include "../x86/x86internal_p.h"
#include "../x86/x86operand.h"
#include "../x86/x86func_p.h"
#endif
#ifdef ASMJIT_BUILD_ARM
#include "../arm/arminternal_p.h"
#include "../arm/armoperand.h"
#include "../arm/armfunc_p.h"
#endif
ASMJIT_BEGIN_NAMESPACE
// ============================================================================
// [asmjit::CallConv - Init / Reset]
// ============================================================================
ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId, const Environment& environment) noexcept {
reset();
#ifdef ASMJIT_BUILD_X86
if (environment.isFamilyX86())
return x86::FuncInternal::initCallConv(*this, ccId, environment);
#endif
#ifdef ASMJIT_BUILD_ARM
if (environment.isFamilyARM())
return arm::FuncInternal::initCallConv(*this, ccId, environment);
#endif
return DebugUtils::errored(kErrorInvalidArgument);
}
// ============================================================================
// [asmjit::FuncDetail - Init / Reset]
// ============================================================================
@@ -69,12 +89,12 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const E
#ifdef ASMJIT_BUILD_X86
if (environment.isFamilyX86())
return x86::X86Internal::initFuncDetail(*this, signature, registerSize);
return x86::FuncInternal::initFuncDetail(*this, signature, registerSize);
#endif
#ifdef ASMJIT_BUILD_ARM
if (environment.isFamilyARM())
return arm::ArmInternal::initFuncDetail(*this, signature, registerSize);
return arm::FuncInternal::initFuncDetail(*this, signature, registerSize);
#endif
// We should never bubble here as if `cc.init()` succeeded then there has to
@@ -83,35 +103,186 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const E
}
// ============================================================================
// [asmjit::FuncFrame - Init / Reset / Finalize]
// [asmjit::FuncFrame - Init / Finalize]
// ============================================================================
ASMJIT_FAVOR_SIZE Error FuncFrame::init(const FuncDetail& func) noexcept {
#ifdef ASMJIT_BUILD_X86
if (Environment::isFamilyX86(func.callConv().arch()))
return x86::X86Internal::initFuncFrame(*this, func);
#endif
uint32_t arch = func.callConv().arch();
if (!Environment::isValidArch(arch))
return DebugUtils::errored(kErrorInvalidArch);
#ifdef ASMJIT_BUILD_ARM
if (Environment::isFamilyARM(func.callConv().arch()))
return arm::ArmInternal::initFuncFrame(*this, func);
#endif
const ArchTraits& archTraits = ArchTraits::byArch(arch);
return DebugUtils::errored(kErrorInvalidArgument);
// Initializing FuncFrame means making a copy of some properties of `func`.
// Properties like `_localStackSize` will be set by the user before the frame
// is finalized.
reset();
_arch = uint8_t(arch);
_spRegId = uint8_t(archTraits.spRegId());
_saRegId = uint8_t(BaseReg::kIdBad);
uint32_t naturalStackAlignment = func.callConv().naturalStackAlignment();
uint32_t minDynamicAlignment = Support::max<uint32_t>(naturalStackAlignment, 16);
if (minDynamicAlignment == naturalStackAlignment)
minDynamicAlignment <<= 1;
_naturalStackAlignment = uint8_t(naturalStackAlignment);
_minDynamicAlignment = uint8_t(minDynamicAlignment);
_redZoneSize = uint8_t(func.redZoneSize());
_spillZoneSize = uint8_t(func.spillZoneSize());
_finalStackAlignment = uint8_t(_naturalStackAlignment);
if (func.hasFlag(CallConv::kFlagCalleePopsStack)) {
_calleeStackCleanup = uint16_t(func.argStackSize());
}
// Initial masks of dirty and preserved registers.
for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) {
_dirtyRegs[group] = func.usedRegs(group);
_preservedRegs[group] = func.preservedRegs(group);
}
// Exclude stack pointer - this register is never included in saved GP regs.
_preservedRegs[BaseReg::kGroupGp] &= ~Support::bitMask(archTraits.spRegId());
// The size and alignment of save/restore area of registers for each significant register group.
memcpy(_saveRestoreRegSize, func.callConv()._saveRestoreRegSize, sizeof(_saveRestoreRegSize));
memcpy(_saveRestoreAlignment, func.callConv()._saveRestoreAlignment, sizeof(_saveRestoreAlignment));
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error FuncFrame::finalize() noexcept {
#ifdef ASMJIT_BUILD_X86
if (Environment::isFamilyX86(arch()))
return x86::X86Internal::finalizeFuncFrame(*this);
#endif
if (!Environment::isValidArch(arch()))
return DebugUtils::errored(kErrorInvalidArch);
#ifdef ASMJIT_BUILD_ARM
if (Environment::isFamilyARM(arch()))
return arm::ArmInternal::finalizeFuncFrame(*this);
#endif
const ArchTraits& archTraits = ArchTraits::byArch(arch());
return DebugUtils::errored(kErrorInvalidArgument);
uint32_t registerSize = _saveRestoreRegSize[BaseReg::kGroupGp];
uint32_t vectorSize = _saveRestoreRegSize[BaseReg::kGroupVec];
uint32_t returnAddressSize = archTraits.hasLinkReg() ? 0u : registerSize;
// The final stack alignment must be updated accordingly to call and local stack alignments.
uint32_t stackAlignment = _finalStackAlignment;
ASMJIT_ASSERT(stackAlignment == Support::max(_naturalStackAlignment,
_callStackAlignment,
_localStackAlignment));
bool hasFP = hasPreservedFP();
bool hasDA = hasDynamicAlignment();
uint32_t kSp = archTraits.spRegId();
uint32_t kFp = archTraits.fpRegId();
uint32_t kLr = archTraits.linkRegId();
// Make frame pointer dirty if the function uses it.
if (hasFP) {
_dirtyRegs[BaseReg::kGroupGp] |= Support::bitMask(kFp);
// Currently required by ARM, if this works differently across architectures
// we would have to generalize most likely in CallConv.
if (kLr != BaseReg::kIdBad)
_dirtyRegs[BaseReg::kGroupGp] |= Support::bitMask(kLr);
}
// These two are identical if the function doesn't align its stack dynamically.
uint32_t saRegId = _saRegId;
if (saRegId == BaseReg::kIdBad)
saRegId = kSp;
// Fix stack arguments base-register from SP to FP in case it was not picked
// before and the function performs dynamic stack alignment.
if (hasDA && saRegId == kSp)
saRegId = kFp;
// Mark as dirty any register but SP if used as SA pointer.
if (saRegId != kSp)
_dirtyRegs[BaseReg::kGroupGp] |= Support::bitMask(saRegId);
_spRegId = uint8_t(kSp);
_saRegId = uint8_t(saRegId);
// Setup stack size used to save preserved registers.
uint32_t saveRestoreSizes[2] {};
for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++)
saveRestoreSizes[size_t(!archTraits.hasPushPop(group))]
+= Support::alignUp(Support::popcnt(savedRegs(group)) * saveRestoreRegSize(group), saveRestoreAlignment(group));
_pushPopSaveSize = uint16_t(saveRestoreSizes[0]);
_extraRegSaveSize = uint16_t(saveRestoreSizes[1]);
uint32_t v = 0; // The beginning of the stack frame relative to SP after prolog.
v += callStackSize(); // Count 'callStackSize' <- This is used to call functions.
v = Support::alignUp(v, stackAlignment); // Align to function's stack alignment.
_localStackOffset = v; // Store 'localStackOffset' <- Function's local stack starts here.
v += localStackSize(); // Count 'localStackSize' <- Function's local stack ends here.
// If the function's stack must be aligned, calculate the alignment necessary
// to store vector registers, and set `FuncFrame::kAttrAlignedVecSR` to inform
// PEI that it can use instructions that perform aligned stores/loads.
if (stackAlignment >= vectorSize && _extraRegSaveSize) {
addAttributes(FuncFrame::kAttrAlignedVecSR);
v = Support::alignUp(v, vectorSize); // Align 'extraRegSaveOffset'.
}
_extraRegSaveOffset = v; // Store 'extraRegSaveOffset' <- Non-GP save/restore starts here.
v += _extraRegSaveSize; // Count 'extraRegSaveSize' <- Non-GP save/restore ends here.
// Calculate if dynamic alignment (DA) slot (stored as offset relative to SP) is required and its offset.
if (hasDA && !hasFP) {
_daOffset = v; // Store 'daOffset' <- DA pointer would be stored here.
v += registerSize; // Count 'daOffset'.
}
else {
_daOffset = FuncFrame::kTagInvalidOffset;
}
// Link Register
// -------------
//
// The stack is aligned after the function call as the return address is
// stored in a link register. Some architectures may require to always
// have aligned stack after PUSH/POP operation, which is represented by
// ArchTraits::stackAlignmentConstraint().
//
// No Link Register (X86/X64)
// --------------------------
//
// The return address should be stored after GP save/restore regs. It has
// the same size as `registerSize` (basically the native register/pointer
// size). We don't adjust it now as `v` now contains the exact size that the
// function requires to adjust (call frame + stack frame, vec stack size).
// The stack (if we consider this size) is misaligned now, as it's always
// aligned before the function call - when `call()` is executed it pushes
// the current EIP|RIP onto the stack, and misaligns it by 12 or 8 bytes
// (depending on the architecture). So count number of bytes needed to align
// it up to the function's CallFrame (the beginning).
if (v || hasFuncCalls() || !returnAddressSize)
v += Support::alignUpDiff(v + pushPopSaveSize() + returnAddressSize, stackAlignment);
_pushPopSaveOffset = v; // Store 'pushPopSaveOffset' <- Function's push/pop save/restore starts here.
_stackAdjustment = v; // Store 'stackAdjustment' <- SA used by 'add SP, SA' and 'sub SP, SA'.
v += _pushPopSaveSize; // Count 'pushPopSaveSize' <- Function's push/pop save/restore ends here.
_finalStackSize = v; // Store 'finalStackSize' <- Final stack used by the function.
if (!archTraits.hasLinkReg())
v += registerSize; // Count 'ReturnAddress' <- As CALL pushes onto stack.
// If the function performs dynamic stack alignment then the stack-adjustment must be aligned.
if (hasDA)
_stackAdjustment = Support::alignUp(_stackAdjustment, stackAlignment);
// Calculate where the function arguments start relative to SP.
_saOffsetFromSP = hasDA ? FuncFrame::kTagInvalidOffset : v;
// Calculate where the function arguments start relative to FP or user-provided register.
_saOffsetFromSA = hasFP ? returnAddressSize + registerSize // Return address + frame pointer.
: returnAddressSize + _pushPopSaveSize; // Return address + all push/pop regs.
return kErrorOk;
}
// ============================================================================
@@ -125,17 +296,15 @@ ASMJIT_FAVOR_SIZE Error FuncArgsAssignment::updateFuncFrame(FuncFrame& frame) co
if (!func)
return DebugUtils::errored(kErrorInvalidState);
#ifdef ASMJIT_BUILD_X86
if (Environment::isFamilyX86(arch))
return x86::X86Internal::argsToFuncFrame(*this, frame);
#endif
RAConstraints constraints;
ASMJIT_PROPAGATE(constraints.init(arch));
#ifdef ASMJIT_BUILD_ARM
if (Environment::isFamilyARM(arch))
return arm::ArmInternal::argsToFuncFrame(*this, frame);
#endif
return DebugUtils::errored(kErrorInvalidArch);
FuncArgsContext ctx;
ASMJIT_PROPAGATE(ctx.initWorkData(frame, *this, &constraints));
ASMJIT_PROPAGATE(ctx.markDstRegsDirty(frame));
ASMJIT_PROPAGATE(ctx.markScratchRegs(frame));
ASMJIT_PROPAGATE(ctx.markStackArgsReg(frame));
return kErrorOk;
}
ASMJIT_END_NAMESPACE

View File

@@ -24,8 +24,7 @@
#ifndef ASMJIT_CORE_FUNC_H_INCLUDED
#define ASMJIT_CORE_FUNC_H_INCLUDED
#include "../core/arch.h"
#include "../core/callconv.h"
#include "../core/archtraits.h"
#include "../core/environment.h"
#include "../core/operand.h"
#include "../core/type.h"
@@ -36,6 +35,365 @@ ASMJIT_BEGIN_NAMESPACE
//! \addtogroup asmjit_function
//! \{
// ============================================================================
// [asmjit::CallConv]
// ============================================================================
//! Function calling convention.
//!
//! Function calling convention is a scheme that defines how function parameters
//! are passed and how function returns its result. AsmJit defines a variety of
//! architecture and OS specific calling conventions and also provides a compile
//! time detection to make the code-generation easier.
struct CallConv {
//! Calling convention id, see \ref Id.
uint8_t _id;
//! Architecture identifier, see \ref Environment::Arch.
uint8_t _arch;
//! Register assignment strategy, see \ref Strategy.
uint8_t _strategy;
//! Red zone size (AMD64 == 128 bytes).
uint8_t _redZoneSize;
//! Spill zone size (WIN-X64 == 32 bytes).
uint8_t _spillZoneSize;
//! Natural stack alignment as defined by OS/ABI.
uint8_t _naturalStackAlignment;
//! Flags.
uint16_t _flags;
//! Size to save/restore per register group.
uint8_t _saveRestoreRegSize[BaseReg::kGroupVirt];
//! Alignment of save/restore groups.
uint8_t _saveRestoreAlignment[BaseReg::kGroupVirt];
//! Mask of all passed registers, per group.
uint32_t _passedRegs[BaseReg::kGroupVirt];
//! Mask of all preserved registers, per group.
uint32_t _preservedRegs[BaseReg::kGroupVirt];
//! Internal limits of AsmJit's CallConv.
enum Limits : uint32_t {
//! Maximum number of register arguments per register group.
//!
//! \note This is not really AsmJit's limitatation, it's just the number
//! that makes sense considering all common calling conventions. Usually
//! even conventions that use registers to pass function arguments are
//! limited to 8 and less arguments passed via registers per group.
kMaxRegArgsPerGroup = 16
};
//! Passed registers' order.
union RegOrder {
//! Passed registers, ordered.
uint8_t id[kMaxRegArgsPerGroup];
//! Packed IDs in `uint32_t` array.
uint32_t packed[(kMaxRegArgsPerGroup + 3) / 4];
};
//! 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 Calling Conventions]
// ------------------------------------------------------------------------
//! Standard function call or explicit `__cdecl` where it can be specified.
//!
//! This is a universal calling 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 (X86).
//!
//! \note This calling convention is only supported on 32-bit X86. If used
//! on environment that doesn't support this calling convention it will be
//! replaced by \ref kIdCDecl.
kIdStdCall = 2,
//! `__fastcall` on targets that support this calling convention (X86).
//!
//! \note This calling convention is only supported on 32-bit X86. If used
//! on environment that doesn't support this calling convention it will be
//! replaced by \ref kIdCDecl.
kIdFastCall = 3,
//! `__vectorcall` on targets that support this calling convention (X86/X64).
//!
//! \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 it will be replaced by \ref kIdCDecl.
kIdVectorCall = 4,
//! `__thiscall` on targets that support this calling convention (X86).
//!
//! \note This calling convention is only supported on 32-bit X86 Windows
//! platform. If used on environment that doesn't support this calling
//! convention it will be replaced by \ref kIdCDecl.
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 = 16,
kIdLightCall3 = 17,
kIdLightCall4 = 18,
// ------------------------------------------------------------------------
// [ABI-Specific Calling Conventions]
// ------------------------------------------------------------------------
//! X64 System-V calling convention.
kIdX64SystemV = 32,
//! X64 Windows calling convention.
kIdX64Windows = 33,
// ------------------------------------------------------------------------
// [Host]
// ------------------------------------------------------------------------
//! Host calling convention detected at compile-time.
kIdHost =
#if ASMJIT_ARCH_ARM == 32 && defined(__SOFTFP__)
kIdSoftFloat
#elif ASMJIT_ARCH_ARM == 32 && !defined(__SOFTFP__)
kIdHardFloat
#else
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.
//!
//! This is AsmJit specific. It basically describes how AsmJit should convert
//! the function arguments defined by `FuncSignature` into register IDs and
//! stack offsets. The default strategy `kStrategyDefault` assigns registers
//! and then stack whereas `kStrategyWin64` strategy does register shadowing
//! as defined by WIN64 calling convention - it applies to 64-bit calling
//! conventions only.
enum Strategy : uint32_t {
//! 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 {
//! Callee is responsible for cleaning up the stack.
kFlagCalleePopsStack = 0x0001u,
//! Pass vector arguments indirectly (as a pointer).
kFlagIndirectVecArgs = 0x0002u,
//! Pass F32 and F64 arguments via VEC128 register.
kFlagPassFloatsByVec = 0x0004u,
//! Pass MMX and vector arguments via stack if the function has variable arguments.
kFlagPassVecByStackIfVA = 0x0008u,
//! MMX registers are passed and returned via GP registers.
kFlagPassMmxByGp = 0x0010u,
//! MMX registers are passed and returned via XMM registers.
kFlagPassMmxByXmm = 0x0020u,
//! Calling convention can be used with variable arguments.
kFlagVarArgCompatible = 0x0080u
};
//! \name Construction & Destruction
//! \{
//! 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));
}
//! \}
//! \name Accessors
//! \{
//! Returns the calling convention id, see `Id`.
inline uint32_t id() const noexcept { return _id; }
//! Sets the calling convention id, see `Id`.
inline void setId(uint32_t id) noexcept { _id = uint8_t(id); }
//! Returns the calling function architecture id.
inline uint32_t arch() const noexcept { return _arch; }
//! Sets the calling function architecture id.
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; }
//! Sets the strategy used to assign registers to arguments, see `Strategy`.
inline void setStrategy(uint32_t strategy) noexcept { _strategy = uint8_t(strategy); }
//! Tests whether the calling convention has the given `flag` set.
inline bool hasFlag(uint32_t flag) const noexcept { return (uint32_t(_flags) & flag) != 0; }
//! Returns the calling convention flags, see `Flags`.
inline uint32_t flags() const noexcept { return _flags; }
//! Adds the calling convention flags, see `Flags`.
inline void setFlags(uint32_t flag) noexcept { _flags = uint16_t(flag); };
//! Adds the calling convention flags, see `Flags`.
inline void addFlags(uint32_t flags) noexcept { _flags = uint16_t(_flags | flags); };
//! Tests whether this calling convention specifies 'RedZone'.
inline bool hasRedZone() const noexcept { return _redZoneSize != 0; }
//! Tests whether this calling convention specifies 'SpillZone'.
inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; }
//! Returns size of 'RedZone'.
inline uint32_t redZoneSize() const noexcept { return _redZoneSize; }
//! Returns size of 'SpillZone'.
inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; }
//! Sets size of 'RedZone'.
inline void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = uint8_t(size); }
//! Sets size of 'SpillZone'.
inline void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = uint8_t(size); }
//! Returns a natural stack alignment.
inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; }
//! Sets a natural stack alignment.
//!
//! This function can be used to override the default stack alignment in case
//! that you know that it's alignment is different. For example it allows to
//! implement custom calling conventions that guarantee higher stack alignment.
inline void setNaturalStackAlignment(uint32_t value) noexcept { _naturalStackAlignment = uint8_t(value); }
//! Returns the size of a register (or its part) to be saved and restored of the given `group`.
inline uint32_t saveRestoreRegSize(uint32_t group) const noexcept { return _saveRestoreRegSize[group]; }
//! Sets the size of a vector register (or its part) to be saved and restored.
inline void setSaveRestoreRegSize(uint32_t group, uint32_t size) noexcept { _saveRestoreRegSize[group] = uint8_t(size); }
//! Returns the alignment of a save-restore area of the given `group`.
inline uint32_t saveRestoreAlignment(uint32_t group) const noexcept { return _saveRestoreAlignment[group]; }
//! Sets the alignment of a save-restore area of the given `group`.
inline void setSaveRestoreAlignment(uint32_t group, uint32_t alignment) noexcept { _saveRestoreAlignment[group] = uint8_t(alignment); }
//! 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];
}
inline void _setPassedPacked(uint32_t group, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
_passedOrder[group].packed[0] = p0;
_passedOrder[group].packed[1] = p1;
_passedOrder[group].packed[2] = p2;
_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);
_setPassedPacked(group, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
_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);
// NOTE: This should always be called with all arguments known at compile time,
// so even if it looks scary it should be translated into few instructions.
_setPassedPacked(group, Support::bytepack32_4x8(a0, a1, a2, a3),
Support::bytepack32_4x8(a4, a5, a6, a7),
0xFFFFFFFFu,
0xFFFFFFFFu);
_passedRegs[group] = (a0 != 0xFF ? 1u << a0 : 0u) |
(a1 != 0xFF ? 1u << a1 : 0u) |
(a2 != 0xFF ? 1u << a2 : 0u) |
(a3 != 0xFF ? 1u << a3 : 0u) |
(a4 != 0xFF ? 1u << a4 : 0u) |
(a5 != 0xFF ? 1u << a5 : 0u) |
(a6 != 0xFF ? 1u << a6 : 0u) |
(a7 != 0xFF ? 1u << a7 : 0u) ;
}
//! 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;
}
//! \}
};
// ============================================================================
// [asmjit::FuncSignature]
// ============================================================================
@@ -629,15 +987,19 @@ public:
uint32_t _dirtyRegs[BaseReg::kGroupVirt];
//! Registers that must be preserved (copied from CallConv).
uint32_t _preservedRegs[BaseReg::kGroupVirt];
//! Size to save/restore per register group.
uint8_t _saveRestoreRegSize[BaseReg::kGroupVirt];
//! Alignment of save/restore area per register group.
uint8_t _saveRestoreAlignment[BaseReg::kGroupVirt];
//! Final stack size required to save GP regs.
uint16_t _gpSaveSize;
//! Final Stack size required to save other than GP regs.
uint16_t _nonGpSaveSize;
//! Final offset where saved GP regs are stored.
uint32_t _gpSaveOffset;
//! Final offset where saved other than GP regs are stored.
uint32_t _nonGpSaveOffset;
//! Stack size required to save registers with push/pop.
uint16_t _pushPopSaveSize;
//! Stack size required to save extra registers that cannot use push/pop.
uint16_t _extraRegSaveSize;
//! Offset where registers saved/restored via push/pop are stored
uint32_t _pushPopSaveOffset;
//! Offset where extra ragisters that cannot use push/pop are stored.
uint32_t _extraRegSaveOffset;
//! \name Construction & Destruction
//! \{
@@ -881,20 +1243,35 @@ public:
return _preservedRegs[group];
}
inline uint32_t saveRestoreRegSize(uint32_t group) const noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
return _saveRestoreRegSize[group];
}
inline uint32_t saveRestoreAlignment(uint32_t group) const noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
return _saveRestoreAlignment[group];
}
inline bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; }
inline uint32_t saRegId() const noexcept { return _saRegId; }
inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); }
inline void resetSARegId() { setSARegId(BaseReg::kIdBad); }
//! Returns stack size required to save GP registers.
inline uint32_t gpSaveSize() const noexcept { return _gpSaveSize; }
//! Returns stack size required to save other than GP registers (MM, XMM|YMM|ZMM, K, VFP, etc...).
inline uint32_t nonGpSaveSize() const noexcept { return _nonGpSaveSize; }
//! Returns stack size required to save/restore registers via push/pop.
inline uint32_t pushPopSaveSize() const noexcept { return _pushPopSaveSize; }
//! Returns an offset to the stack where registers are saved via push/pop.
inline uint32_t pushPopSaveOffset() const noexcept { return _pushPopSaveOffset; }
//! Returns an offset to the stack where general purpose registers are saved.
inline uint32_t gpSaveOffset() const noexcept { return _gpSaveOffset; }
//! Returns an offset to the stack where other than GP registers are saved.
inline uint32_t nonGpSaveOffset() const noexcept { return _nonGpSaveOffset; }
//! Returns stack size required to save/restore extra registers that don't
//! use push/pop/
//!
//! \note On X86 this covers all registers except GP registers, on other
//! architectures it can be always zero (for example AArch64 saves all
//! registers via push/pop like instructions, so this would be zero).
inline uint32_t extraRegSaveSize() const noexcept { return _extraRegSaveSize; }
//! Returns an offset to the stack where extra registers are saved.
inline uint32_t extraRegSaveOffset() const noexcept { return _extraRegSaveOffset; }
//! Tests whether the functions contains stack adjustment.
inline bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; }

View File

@@ -0,0 +1,315 @@
// 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/funcargscontext_p.h"
ASMJIT_BEGIN_NAMESPACE
//! \cond INTERNAL
//! \addtogroup asmjit_core
//! \{
FuncArgsContext::FuncArgsContext() noexcept {
for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++)
_workData[group].reset();
}
ASMJIT_FAVOR_SIZE Error FuncArgsContext::initWorkData(const FuncFrame& frame, const FuncArgsAssignment& args, const RAConstraints* constraints) noexcept {
// The code has to be updated if this changes.
ASMJIT_ASSERT(BaseReg::kGroupVirt == 4);
uint32_t i;
uint32_t arch = frame.arch();
const FuncDetail& func = *args.funcDetail();
_archTraits = &ArchTraits::byArch(arch);
_constraints = constraints;
_arch = uint8_t(arch);
// Initialize `_archRegs`.
for (i = 0; i < BaseReg::kGroupVirt; i++)
_workData[i]._archRegs = _constraints->availableRegs(i);
if (frame.hasPreservedFP())
_workData[BaseReg::kGroupGp]._archRegs &= ~Support::bitMask(archTraits().fpRegId());
// Extract information from all function arguments/assignments and build Var[] array.
uint32_t varId = 0;
for (uint32_t argIndex = 0; argIndex < Globals::kMaxFuncArgs; argIndex++) {
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
const FuncValue& dst_ = args.arg(argIndex, valueIndex);
if (!dst_.isAssigned())
continue;
const FuncValue& src_ = func.arg(argIndex, valueIndex);
if (ASMJIT_UNLIKELY(!src_.isAssigned()))
return DebugUtils::errored(kErrorInvalidState);
Var& var = _vars[varId];
var.init(src_, dst_);
FuncValue& src = var.cur;
FuncValue& dst = var.out;
uint32_t dstGroup = 0xFFFFFFFFu;
uint32_t dstId = BaseReg::kIdBad;
WorkData* dstWd = nullptr;
// Not supported.
if (src.isIndirect())
return DebugUtils::errored(kErrorInvalidAssignment);
if (dst.isReg()) {
uint32_t dstType = dst.regType();
if (ASMJIT_UNLIKELY(!archTraits().hasRegType(dstType)))
return DebugUtils::errored(kErrorInvalidRegType);
// Copy TypeId from source if the destination doesn't have it. The RA
// used by BaseCompiler would never leave TypeId undefined, but users
// of FuncAPI can just assign phys regs without specifying the type.
if (!dst.hasTypeId())
dst.setTypeId(archTraits().regTypeToTypeId(dst.regType()));
dstGroup = archTraits().regTypeToGroup(dstType);
if (ASMJIT_UNLIKELY(dstGroup >= BaseReg::kGroupVirt))
return DebugUtils::errored(kErrorInvalidRegGroup);
dstWd = &_workData[dstGroup];
dstId = dst.regId();
if (ASMJIT_UNLIKELY(dstId >= 32 || !Support::bitTest(dstWd->archRegs(), dstId)))
return DebugUtils::errored(kErrorInvalidPhysId);
if (ASMJIT_UNLIKELY(Support::bitTest(dstWd->dstRegs(), dstId)))
return DebugUtils::errored(kErrorOverlappedRegs);
dstWd->_dstRegs |= Support::bitMask(dstId);
dstWd->_dstShuf |= Support::bitMask(dstId);
dstWd->_usedRegs |= Support::bitMask(dstId);
}
else {
if (!dst.hasTypeId())
dst.setTypeId(src.typeId());
RegInfo regInfo = getSuitableRegForMemToMemMove(arch, dst.typeId(), src.typeId());
if (ASMJIT_UNLIKELY(!regInfo.isValid()))
return DebugUtils::errored(kErrorInvalidState);
_stackDstMask = uint8_t(_stackDstMask | Support::bitMask(regInfo.group()));
}
if (src.isReg()) {
uint32_t srcId = src.regId();
uint32_t srcGroup = archTraits().regTypeToGroup(src.regType());
if (dstGroup == srcGroup) {
dstWd->assign(varId, srcId);
// The best case, register is allocated where it is expected to be.
if (dstId == srcId)
var.markDone();
}
else {
if (ASMJIT_UNLIKELY(srcGroup >= BaseReg::kGroupVirt))
return DebugUtils::errored(kErrorInvalidState);
WorkData& srcData = _workData[srcGroup];
srcData.assign(varId, srcId);
}
}
else {
if (dstWd)
dstWd->_numStackArgs++;
_hasStackSrc = true;
}
varId++;
}
}
// Initialize WorkData::workRegs.
for (i = 0; i < BaseReg::kGroupVirt; i++) {
_workData[i]._workRegs = (_workData[i].archRegs() & (frame.dirtyRegs(i) | ~frame.preservedRegs(i))) | _workData[i].dstRegs() | _workData[i].assignedRegs();
}
// Create a variable that represents `SARegId` if necessary.
bool saRegRequired = _hasStackSrc && frame.hasDynamicAlignment() && !frame.hasPreservedFP();
WorkData& gpRegs = _workData[BaseReg::kGroupGp];
uint32_t saCurRegId = frame.saRegId();
uint32_t saOutRegId = args.saRegId();
if (saCurRegId != BaseReg::kIdBad) {
// Check if the provided `SARegId` doesn't collide with input registers.
if (ASMJIT_UNLIKELY(gpRegs.isAssigned(saCurRegId)))
return DebugUtils::errored(kErrorOverlappedRegs);
}
if (saOutRegId != BaseReg::kIdBad) {
// Check if the provided `SARegId` doesn't collide with argument assignments.
if (ASMJIT_UNLIKELY(Support::bitTest(gpRegs.dstRegs(), saOutRegId)))
return DebugUtils::errored(kErrorOverlappedRegs);
saRegRequired = true;
}
if (saRegRequired) {
uint32_t ptrTypeId = Environment::is32Bit(arch) ? Type::kIdU32 : Type::kIdU64;
uint32_t ptrRegType = Environment::is32Bit(arch) ? BaseReg::kTypeGp32 : BaseReg::kTypeGp64;
_saVarId = uint8_t(varId);
_hasPreservedFP = frame.hasPreservedFP();
Var& var = _vars[varId];
var.reset();
if (saCurRegId == BaseReg::kIdBad) {
if (saOutRegId != BaseReg::kIdBad && !gpRegs.isAssigned(saOutRegId)) {
saCurRegId = saOutRegId;
}
else {
uint32_t availableRegs = gpRegs.availableRegs();
if (!availableRegs)
availableRegs = gpRegs.archRegs() & ~gpRegs.workRegs();
if (ASMJIT_UNLIKELY(!availableRegs))
return DebugUtils::errored(kErrorNoMorePhysRegs);
saCurRegId = Support::ctz(availableRegs);
}
}
var.cur.initReg(ptrRegType, saCurRegId, ptrTypeId);
gpRegs.assign(varId, saCurRegId);
gpRegs._workRegs |= Support::bitMask(saCurRegId);
if (saOutRegId != BaseReg::kIdBad) {
var.out.initReg(ptrRegType, saOutRegId, ptrTypeId);
gpRegs._dstRegs |= Support::bitMask(saOutRegId);
gpRegs._workRegs |= Support::bitMask(saOutRegId);
}
else {
var.markDone();
}
varId++;
}
_varCount = varId;
// Detect register swaps.
for (varId = 0; varId < _varCount; varId++) {
Var& var = _vars[varId];
if (var.cur.isReg() && var.out.isReg()) {
uint32_t srcId = var.cur.regId();
uint32_t dstId = var.out.regId();
uint32_t group = archTraits().regTypeToGroup(var.cur.regType());
if (group != archTraits().regTypeToGroup(var.out.regType()))
continue;
WorkData& wd = _workData[group];
if (wd.isAssigned(dstId)) {
Var& other = _vars[wd._physToVarId[dstId]];
if (archTraits().regTypeToGroup(other.out.regType()) == group && other.out.regId() == srcId) {
wd._numSwaps++;
_regSwapsMask = uint8_t(_regSwapsMask | Support::bitMask(group));
}
}
}
}
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error FuncArgsContext::markDstRegsDirty(FuncFrame& frame) noexcept {
for (uint32_t i = 0; i < BaseReg::kGroupVirt; i++) {
WorkData& wd = _workData[i];
uint32_t regs = wd.usedRegs() | wd._dstShuf;
wd._workRegs |= regs;
frame.addDirtyRegs(i, regs);
}
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error FuncArgsContext::markScratchRegs(FuncFrame& frame) noexcept {
uint32_t groupMask = 0;
// Handle stack to stack moves.
groupMask |= _stackDstMask;
// Handle register swaps.
groupMask |= _regSwapsMask & ~Support::bitMask(BaseReg::kGroupGp);
if (!groupMask)
return kErrorOk;
// Selects one dirty register per affected group that can be used as a scratch register.
for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) {
if (Support::bitTest(groupMask, group)) {
WorkData& wd = _workData[group];
// Initially, pick some clobbered or dirty register.
uint32_t workRegs = wd.workRegs();
uint32_t regs = workRegs & ~(wd.usedRegs() | wd._dstShuf);
// If that didn't work out pick some register which is not in 'used'.
if (!regs)
regs = workRegs & ~wd.usedRegs();
// If that didn't work out pick any other register that is allocable.
// This last resort case will, however, result in marking one more
// register dirty.
if (!regs)
regs = wd.archRegs() & ~workRegs;
// If that didn't work out we will have to use XORs instead of MOVs.
if (!regs)
continue;
uint32_t regMask = Support::blsi(regs);
wd._workRegs |= regMask;
frame.addDirtyRegs(group, regMask);
}
}
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error FuncArgsContext::markStackArgsReg(FuncFrame& frame) noexcept {
if (_saVarId != kVarIdNone) {
const Var& var = _vars[_saVarId];
frame.setSARegId(var.cur.regId());
}
else if (frame.hasPreservedFP()) {
frame.setSARegId(archTraits().fpRegId());
}
return kErrorOk;
}
//! \}
//! \endcond
ASMJIT_END_NAMESPACE

View File

@@ -0,0 +1,224 @@
// 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_FUNCARGSCONTEXT_P_H_INCLUDED
#define ASMJIT_CORE_FUNCARGSCONTEXT_P_H_INCLUDED
#include "../core/archtraits.h"
#include "../core/environment.h"
#include "../core/func.h"
#include "../core/operand.h"
#include "../core/radefs_p.h"
#include "../core/support.h"
ASMJIT_BEGIN_NAMESPACE
//! \cond INTERNAL
//! \addtogroup asmjit_core
//! \{
// ============================================================================
// [TODO: Place somewhere else]
// ============================================================================
static inline RegInfo getSuitableRegForMemToMemMove(uint32_t arch, uint32_t dstTypeId, uint32_t srcTypeId) noexcept {
const ArchTraits& archTraits = ArchTraits::byArch(arch);
uint32_t dstSize = Type::sizeOf(dstTypeId);
uint32_t srcSize = Type::sizeOf(srcTypeId);
uint32_t maxSize = Support::max<uint32_t>(dstSize, srcSize);
uint32_t regSize = Environment::registerSizeFromArch(arch);
uint32_t signature = 0;
if (maxSize <= regSize || (Type::isInt(dstTypeId) && Type::isInt(srcTypeId)))
signature = maxSize <= 4 ? archTraits.regTypeToSignature(BaseReg::kTypeGp32)
: archTraits.regTypeToSignature(BaseReg::kTypeGp64);
else if (maxSize <= 8 && archTraits.hasRegType(BaseReg::kTypeVec64))
signature = archTraits.regTypeToSignature(BaseReg::kTypeVec64);
else if (maxSize <= 16 && archTraits.hasRegType(BaseReg::kTypeVec128))
signature = archTraits.regTypeToSignature(BaseReg::kTypeVec128);
else if (maxSize <= 32 && archTraits.hasRegType(BaseReg::kTypeVec256))
signature = archTraits.regTypeToSignature(BaseReg::kTypeVec256);
else if (maxSize <= 64 && archTraits.hasRegType(BaseReg::kTypeVec512))
signature = archTraits.regTypeToSignature(BaseReg::kTypeVec512);
return RegInfo { signature };
}
// ============================================================================
// [asmjit::FuncArgsContext]
// ============================================================================
class FuncArgsContext {
public:
enum VarId : uint32_t {
kVarIdNone = 0xFF
};
//! Contains information about a single argument or SA register that may need shuffling.
struct Var {
FuncValue cur;
FuncValue out;
inline void init(const FuncValue& cur_, const FuncValue& out_) noexcept {
cur = cur_;
out = out_;
}
//! Reset the value to its unassigned state.
inline void reset() noexcept {
cur.reset();
out.reset();
}
inline bool isDone() const noexcept { return cur.isDone(); }
inline void markDone() noexcept { cur.addFlags(FuncValue::kFlagIsDone); }
};
struct WorkData {
//! All allocable registers provided by the architecture.
uint32_t _archRegs;
//! All registers that can be used by the shuffler.
uint32_t _workRegs;
//! Registers used by the shuffler (all).
uint32_t _usedRegs;
//! Assigned registers.
uint32_t _assignedRegs;
//! Destination registers assigned to arguments or SA.
uint32_t _dstRegs;
//! Destination registers that require shuffling.
uint32_t _dstShuf;
//! Number of register swaps.
uint8_t _numSwaps;
//! Number of stack loads.
uint8_t _numStackArgs;
//! Reserved (only used as padding).
uint8_t _reserved[6];
//! Physical ID to variable ID mapping.
uint8_t _physToVarId[32];
inline void reset() noexcept {
_archRegs = 0;
_workRegs = 0;
_usedRegs = 0;
_assignedRegs = 0;
_dstRegs = 0;
_dstShuf = 0;
_numSwaps = 0;
_numStackArgs = 0;
memset(_reserved, 0, sizeof(_reserved));
memset(_physToVarId, kVarIdNone, 32);
}
inline bool isAssigned(uint32_t regId) const noexcept {
ASMJIT_ASSERT(regId < 32);
return Support::bitTest(_assignedRegs, regId);
}
inline void assign(uint32_t varId, uint32_t regId) noexcept {
ASMJIT_ASSERT(!isAssigned(regId));
ASMJIT_ASSERT(_physToVarId[regId] == kVarIdNone);
_physToVarId[regId] = uint8_t(varId);
_assignedRegs ^= Support::bitMask(regId);
}
inline void reassign(uint32_t varId, uint32_t newId, uint32_t oldId) noexcept {
ASMJIT_ASSERT( isAssigned(oldId));
ASMJIT_ASSERT(!isAssigned(newId));
ASMJIT_ASSERT(_physToVarId[oldId] == varId);
ASMJIT_ASSERT(_physToVarId[newId] == kVarIdNone);
_physToVarId[oldId] = uint8_t(kVarIdNone);
_physToVarId[newId] = uint8_t(varId);
_assignedRegs ^= Support::bitMask(newId) ^ Support::bitMask(oldId);
}
inline void swap(uint32_t aVarId, uint32_t aRegId, uint32_t bVarId, uint32_t bRegId) noexcept {
ASMJIT_ASSERT(isAssigned(aRegId));
ASMJIT_ASSERT(isAssigned(bRegId));
ASMJIT_ASSERT(_physToVarId[aRegId] == aVarId);
ASMJIT_ASSERT(_physToVarId[bRegId] == bVarId);
_physToVarId[aRegId] = uint8_t(bVarId);
_physToVarId[bRegId] = uint8_t(aVarId);
}
inline void unassign(uint32_t varId, uint32_t regId) noexcept {
ASMJIT_ASSERT(isAssigned(regId));
ASMJIT_ASSERT(_physToVarId[regId] == varId);
DebugUtils::unused(varId);
_physToVarId[regId] = uint8_t(kVarIdNone);
_assignedRegs ^= Support::bitMask(regId);
}
inline uint32_t archRegs() const noexcept { return _archRegs; }
inline uint32_t workRegs() const noexcept { return _workRegs; }
inline uint32_t usedRegs() const noexcept { return _usedRegs; }
inline uint32_t assignedRegs() const noexcept { return _assignedRegs; }
inline uint32_t dstRegs() const noexcept { return _dstRegs; }
inline uint32_t availableRegs() const noexcept { return _workRegs & ~_assignedRegs; }
};
//! Architecture traits.
const ArchTraits* _archTraits = nullptr;
const RAConstraints* _constraints = nullptr;
//! Architecture identifier.
uint8_t _arch = 0;
//! Has arguments passed via stack (SRC).
bool _hasStackSrc = false;
//! Has preserved frame-pointer (FP).
bool _hasPreservedFP = false;
//! Has arguments assigned to stack (DST).
uint8_t _stackDstMask = 0;
//! Register swap groups (bit-mask).
uint8_t _regSwapsMask = 0;
uint8_t _saVarId = kVarIdNone;
uint32_t _varCount = 0;
WorkData _workData[BaseReg::kGroupVirt];
Var _vars[Globals::kMaxFuncArgs * Globals::kMaxValuePack + 1];
FuncArgsContext() noexcept;
inline const ArchTraits& archTraits() const noexcept { return *_archTraits; }
inline uint32_t arch() const noexcept { return _arch; }
inline uint32_t varCount() const noexcept { return _varCount; }
inline size_t indexOf(const Var* var) const noexcept { return (size_t)(var - _vars); }
inline Var& var(size_t varId) noexcept { return _vars[varId]; }
inline const Var& var(size_t varId) const noexcept { return _vars[varId]; }
Error initWorkData(const FuncFrame& frame, const FuncArgsAssignment& args, const RAConstraints* constraints) noexcept;
Error markScratchRegs(FuncFrame& frame) noexcept;
Error markDstRegsDirty(FuncFrame& frame) noexcept;
Error markStackArgsReg(FuncFrame& frame) noexcept;
};
//! \}
//! \endcond
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_FUNCARGSCONTEXT_P_H_INCLUDED

View File

@@ -67,6 +67,7 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
"InvalidRegGroup\0"
"InvalidPhysId\0"
"InvalidVirtId\0"
"InvalidElementIndex\0"
"InvalidPrefixCombination\0"
"InvalidLockPrefix\0"
"InvalidXAcquirePrefix\0"
@@ -108,9 +109,9 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
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
518, 538, 563, 581, 603, 625, 642, 659, 675, 691, 707, 724, 739, 754, 774,
794, 814, 847, 867, 882, 899, 918, 939, 959, 973, 994, 1008, 1026, 1042,
1058, 1077, 1092, 1108, 1123, 1138, 1168, 1192, 1211
};
// @EnumStringEnd@

View File

@@ -296,27 +296,29 @@ enum ErrorCode : uint32_t {
kErrorInvalidPhysId,
//! Invalid virtual register id.
kErrorInvalidVirtId,
//! Invalid prefix combination.
//! Invalid element index (ARM).
kErrorInvalidElementIndex,
//! Invalid prefix combination (X86|X64).
kErrorInvalidPrefixCombination,
//! Invalid LOCK prefix.
//! Invalid LOCK prefix (X86|X64).
kErrorInvalidLockPrefix,
//! Invalid XACQUIRE prefix.
//! Invalid XACQUIRE prefix (X86|X64).
kErrorInvalidXAcquirePrefix,
//! Invalid XRELEASE prefix.
//! Invalid XRELEASE prefix (X86|X64).
kErrorInvalidXReleasePrefix,
//! Invalid REP prefix.
//! Invalid REP prefix (X86|X64).
kErrorInvalidRepPrefix,
//! Invalid REX prefix.
//! Invalid REX prefix (X86|X64).
kErrorInvalidRexPrefix,
//! Invalid {...} register.
//! Invalid {...} register (X86|X64).
kErrorInvalidExtraReg,
//! Invalid {k} use (not supported by the instruction).
//! Invalid {k} use (not supported by the instruction) (X86|X64).
kErrorInvalidKMaskUse,
//! Invalid {k}{z} use (not supported by the instruction).
//! Invalid {k}{z} use (not supported by the instruction) (X86|X64).
kErrorInvalidKZeroUse,
//! Invalid broadcast - Currently only related to invalid use of AVX-512 {1tox}.
//! Invalid broadcast - Currently only related to invalid use of AVX-512 {1tox} (X86|X64).
kErrorInvalidBroadcast,
//! Invalid 'embedded-rounding' {er} or 'suppress-all-exceptions' {sae} (AVX-512).
//! Invalid 'embedded-rounding' {er} or 'suppress-all-exceptions' {sae} (AVX-512) (X86|X64).
kErrorInvalidEROrSAE,
//! Invalid address used (not encodable).
kErrorInvalidAddress,

View File

@@ -24,7 +24,7 @@
#include "../core/api-build_p.h"
#ifdef ASMJIT_BUILD_X86
#include "../core/arch.h"
#include "../core/archtraits.h"
#include "../core/inst.h"
#ifdef ASMJIT_BUILD_X86
@@ -32,7 +32,7 @@
#endif
#ifdef ASMJIT_BUILD_ARM
#include "../arm/arminstapi_p.h"
#include "../arm/a64instapi_p.h"
#endif
ASMJIT_BEGIN_NAMESPACE
@@ -49,8 +49,8 @@ Error InstAPI::instIdToString(uint32_t arch, uint32_t instId, String& output) no
#endif
#ifdef ASMJIT_BUILD_ARM
if (Environment::isFamilyARM(arch))
return arm::InstInternal::instIdToString(arch, instId, output);
if (Environment::isArchAArch64(arch))
return a64::InstInternal::instIdToString(arch, instId, output);
#endif
return DebugUtils::errored(kErrorInvalidArch);
@@ -63,8 +63,8 @@ uint32_t InstAPI::stringToInstId(uint32_t arch, const char* s, size_t len) noexc
#endif
#ifdef ASMJIT_BUILD_ARM
if (Environment::isFamilyARM(arch))
return arm::InstInternal::stringToInstId(arch, s, len);
if (Environment::isArchAArch64(arch))
return a64::InstInternal::stringToInstId(arch, s, len);
#endif
return 0;
@@ -83,8 +83,8 @@ Error InstAPI::validate(uint32_t arch, const BaseInst& inst, const Operand_* ope
#endif
#ifdef ASMJIT_BUILD_ARM
if (Environment::isFamilyARM(arch))
return arm::InstInternal::validate(arch, inst, operands, opCount, validationFlags);
if (Environment::isArchAArch64(arch))
return a64::InstInternal::validate(arch, inst, operands, opCount, validationFlags);
#endif
return DebugUtils::errored(kErrorInvalidArch);
@@ -106,8 +106,8 @@ Error InstAPI::queryRWInfo(uint32_t arch, const BaseInst& inst, const Operand_*
#endif
#ifdef ASMJIT_BUILD_ARM
if (Environment::isFamilyARM(arch))
return arm::InstInternal::queryRWInfo(arch, inst, operands, opCount, out);
if (Environment::isArchAArch64(arch))
return a64::InstInternal::queryRWInfo(arch, inst, operands, opCount, out);
#endif
return DebugUtils::errored(kErrorInvalidArch);
@@ -126,8 +126,8 @@ Error InstAPI::queryFeatures(uint32_t arch, const BaseInst& inst, const Operand_
#endif
#ifdef ASMJIT_BUILD_ARM
if (Environment::isFamilyARM(arch))
return arm::InstInternal::queryFeatures(arch, inst, operands, opCount, out);
if (Environment::isArchAArch64(arch))
return a64::InstInternal::queryFeatures(arch, inst, operands, opCount, out);
#endif
return DebugUtils::errored(kErrorInvalidArch);

View File

@@ -24,7 +24,7 @@
#include "../core/api-build_p.h"
#ifndef ASMJIT_NO_JIT
#include "../core/arch.h"
#include "../core/archtraits.h"
#include "../core/jitallocator.h"
#include "../core/osutils_p.h"
#include "../core/support.h"

View File

@@ -35,11 +35,19 @@ ASMJIT_BEGIN_NAMESPACE
// Only useful on non-x86 architectures.
static inline void JitRuntime_flushInstructionCache(const void* p, size_t size) noexcept {
#if defined(_WIN32) && !ASMJIT_ARCH_X86
#if ASMJIT_ARCH_X86
DebugUtils::unused(p, size);
#else
# if defined(_WIN32)
// Windows has a built-in support in `kernel32.dll`.
::FlushInstructionCache(::GetCurrentProcess(), p, size);
#else
# elif defined(__GNUC__)
void* start = const_cast<void*>(p);
void* end = static_cast<uint8_t*>(start) + size;
__builtin___clear_cache(start, end);
# else
DebugUtils::unused(p, size);
# endif
#endif
}

View File

@@ -32,9 +32,11 @@ ASMJIT_BEGIN_NAMESPACE
//! \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))
#define ASMJIT_LOOKUP_TABLE_4(T, I) T((I)), T((I+1)), T((I+2)), T((I+3))
#define ASMJIT_LOOKUP_TABLE_8(T, I) ASMJIT_LOOKUP_TABLE_4(T, I), ASMJIT_LOOKUP_TABLE_4(T, I + 4)
#define ASMJIT_LOOKUP_TABLE_16(T, I) ASMJIT_LOOKUP_TABLE_8(T, I), ASMJIT_LOOKUP_TABLE_8(T, I + 8)
#define ASMJIT_LOOKUP_TABLE_32(T, I) ASMJIT_LOOKUP_TABLE_16(T, I), ASMJIT_LOOKUP_TABLE_16(T, I + 16)
#define ASMJIT_LOOKUP_TABLE_40(T, I) ASMJIT_LOOKUP_TABLE_16(T, I), ASMJIT_LOOKUP_TABLE_16(T, I + 16), ASMJIT_LOOKUP_TABLE_8(T, I + 32)
#define ASMJIT_LOOKUP_TABLE_64(T, I) ASMJIT_LOOKUP_TABLE_32(T, I), ASMJIT_LOOKUP_TABLE_32(T, I + 32)
#define ASMJIT_LOOKUP_TABLE_128(T, I) ASMJIT_LOOKUP_TABLE_64(T, I), ASMJIT_LOOKUP_TABLE_64(T, I + 64)
#define ASMJIT_LOOKUP_TABLE_256(T, I) ASMJIT_LOOKUP_TABLE_128(T, I), ASMJIT_LOOKUP_TABLE_128(T, I + 128)

View File

@@ -68,7 +68,7 @@ UNIT(operand) {
uint32_t rSig = Operand::kOpReg | (1 << Operand::kSignatureRegTypeShift ) |
(2 << Operand::kSignatureRegGroupShift) |
(8 << Operand::kSignatureSizeShift ) ;
BaseReg r1(rSig, 5);
BaseReg r1 = BaseReg::fromSignatureAndId(rSig, 5);
EXPECT(r1.isValid() == true);
EXPECT(r1.isReg() == true);
@@ -126,10 +126,17 @@ UNIT(operand) {
INFO("Checking basic functionality of Imm");
Imm immValue(-42);
EXPECT(immValue.type() == Imm::kTypeInteger);
EXPECT(Imm(-1).value() == -1);
EXPECT(imm(-1).value() == -1);
EXPECT(immValue.value() == -42);
EXPECT(imm(0xFFFFFFFF).value() == int64_t(0xFFFFFFFF));
Imm immDouble(0.4);
EXPECT(immDouble.type() == Imm::kTypeDouble);
EXPECT(immDouble.valueAs<double>() == 0.4);
EXPECT(immDouble == imm(0.4));
}
#endif

View File

@@ -24,6 +24,7 @@
#ifndef ASMJIT_CORE_OPERAND_H_INCLUDED
#define ASMJIT_CORE_OPERAND_H_INCLUDED
#include "../core/archcommons.h"
#include "../core/support.h"
ASMJIT_BEGIN_NAMESPACE
@@ -47,7 +48,7 @@ struct RegTraits<REG_TYPE> { \
static constexpr uint32_t kSize = SIZE; \
\
static constexpr uint32_t kSignature = \
(Operand::kOpReg << Operand::kSignatureOpShift ) | \
(Operand::kOpReg << Operand::kSignatureOpTypeShift ) | \
(kType << Operand::kSignatureRegTypeShift ) | \
(kGroup << Operand::kSignatureRegGroupShift) | \
(kSize << Operand::kSignatureSizeShift ) ; \
@@ -60,7 +61,7 @@ struct RegTraits<REG_TYPE> { \
public: \
/*! Default constructor that only setups basics. */ \
constexpr REG() noexcept \
: BASE(kSignature, kIdBad) {} \
: BASE(SignatureAndId(kSignature, kIdBad)) {} \
\
/*! Makes a copy of the `other` register operand. */ \
constexpr REG(const REG& other) noexcept \
@@ -71,8 +72,8 @@ public: \
: BASE(other, rId) {} \
\
/*! Creates a register based on `signature` and `rId`. */ \
constexpr REG(uint32_t signature, uint32_t rId) noexcept \
: BASE(signature, rId) {} \
constexpr explicit REG(const SignatureAndId& sid) noexcept \
: BASE(sid) {} \
\
/*! Creates a completely uninitialized REG register operand (garbage). */ \
inline explicit REG(Globals::NoInit_) noexcept \
@@ -80,7 +81,12 @@ public: \
\
/*! Creates a new register from register type and id. */ \
static inline REG fromTypeAndId(uint32_t rType, uint32_t rId) noexcept { \
return REG(signatureOf(rType), rId); \
return REG(SignatureAndId(signatureOf(rType), rId)); \
} \
\
/*! Creates a new register from register signature and id. */ \
static inline REG fromSignatureAndId(uint32_t rSgn, uint32_t rId) noexcept {\
return REG(SignatureAndId(rSgn, rId)); \
} \
\
/*! Clones the register operand. */ \
@@ -101,7 +107,7 @@ public: \
\
/*! Creates a register operand having its id set to `rId`. */ \
constexpr explicit REG(uint32_t rId) noexcept \
: BASE(kSignature, rId) {}
: BASE(SignatureAndId(kSignature, rId)) {}
//! \addtogroup asmjit_assembler
//! \{
@@ -176,8 +182,8 @@ struct Operand_ {
enum SignatureBits : uint32_t {
// Operand type (3 least significant bits).
// |........|........|........|.....XXX|
kSignatureOpShift = 0,
kSignatureOpMask = 0x07u << kSignatureOpShift,
kSignatureOpTypeShift = 0,
kSignatureOpTypeMask = 0x07u << kSignatureOpTypeShift,
// Register type (5 bits).
// |........|........|........|XXXXX...|
@@ -204,16 +210,21 @@ struct Operand_ {
kSignatureMemBaseIndexShift = 3,
kSignatureMemBaseIndexMask = 0x3FFu << kSignatureMemBaseIndexShift,
// Memory address type (2 bits).
// |........|........|.XX.....|........|
kSignatureMemAddrTypeShift = 13,
kSignatureMemAddrTypeMask = 0x03u << kSignatureMemAddrTypeShift,
// This memory operand represents a home-slot or stack (BaseCompiler).
// |........|........|X.......|........|
kSignatureMemRegHomeShift = 15,
// This memory operand represents a home-slot or stack (Compiler) (1 bit).
// |........|........|..X.....|........|
kSignatureMemRegHomeShift = 13,
kSignatureMemRegHomeFlag = 0x01u << kSignatureMemRegHomeShift,
// Immediate type (1 bit).
// |........|........|........|....X...|
kSignatureImmTypeShift = 4,
kSignatureImmTypeMask = 0x01u << kSignatureImmTypeShift,
// Predicate used by either registers or immediate values (4 bits).
// |........|XXXX....|........|........|
kSignaturePredicateShift = 20,
kSignaturePredicateMask = 0x0Fu << kSignaturePredicateShift,
// Operand size (8 most significant bits).
// |XXXXXXXX|........|........|........|
kSignatureSizeShift = 24,
@@ -320,9 +331,9 @@ struct Operand_ {
//! \name Accessors
//! \{
//! Tests whether the operand matches the given signature `sign`.
//! Tests whether the operand's signature matches the given signature `sign`.
constexpr bool hasSignature(uint32_t signature) const noexcept { return _signature == signature; }
//! Tests whether the operand matches the signature of the `other` operand.
//! Tests whether the operand's signature matches the signature of the `other` operand.
constexpr bool hasSignature(const Operand_& other) const noexcept { return _signature == other.signature(); }
//! Returns operand signature as unsigned 32-bit integer.
@@ -343,6 +354,11 @@ struct Operand_ {
return (_signature & mask) != 0;
}
template<uint32_t mask>
constexpr bool _hasSignaturePart(uint32_t signature) const noexcept {
return (_signature & mask) == signature;
}
template<uint32_t mask>
constexpr uint32_t _getSignaturePart() const noexcept {
return (_signature >> Support::constCtz(mask)) & (mask >> Support::constCtz(mask));
@@ -356,7 +372,7 @@ struct Operand_ {
//! \endcond
//! Returns the type of the operand, see `OpType`.
constexpr uint32_t opType() const noexcept { return _getSignaturePart<kSignatureOpMask>(); }
constexpr uint32_t opType() const noexcept { return _getSignaturePart<kSignatureOpTypeMask>(); }
//! Tests whether the operand is none (`kOpNone`).
constexpr bool isNone() const noexcept { return _signature == 0; }
//! Tests whether the operand is a register (`kOpReg`).
@@ -422,8 +438,8 @@ struct Operand_ {
//! Tests whether the operand is a register matching `rType`.
constexpr bool isReg(uint32_t rType) const noexcept {
return (_signature & (kSignatureOpMask | kSignatureRegTypeMask)) ==
((kOpReg << kSignatureOpShift) | (rType << kSignatureRegTypeShift));
return (_signature & (kSignatureOpTypeMask | kSignatureRegTypeMask)) ==
((kOpReg << kSignatureOpTypeShift) | (rType << kSignatureRegTypeShift));
}
//! Tests whether the operand is register and of `rType` and `rId`.
@@ -527,8 +543,10 @@ public:
kTypeLocal = 1,
//! Global label (never has parentId).
kTypeGlobal = 2,
//! External label (references an external symbol).
kTypeExternal = 3,
//! Number of label types.
kTypeCount = 3
kTypeCount = 4
};
//! \name Construction & Destruction
@@ -598,8 +616,8 @@ struct BaseRegTraits {
//! No size by default.
static constexpr uint32_t kSize = 0;
//! Empty signature by default.
static constexpr uint32_t kSignature = Operand::kOpReg;
//! Empty signature by default (not even having operand type set to register).
static constexpr uint32_t kSignature = 0;
};
//! \endcond
@@ -622,7 +640,7 @@ struct RegInfo {
constexpr bool isValid() const noexcept { return _signature != 0; }
constexpr uint32_t signature() const noexcept { return _signature; }
constexpr uint32_t opType() const noexcept { return _getSignaturePart<Operand::kSignatureOpMask>(); }
constexpr uint32_t opType() const noexcept { return _getSignaturePart<Operand::kSignatureOpTypeMask>(); }
constexpr uint32_t group() const noexcept { return _getSignaturePart<Operand::kSignatureRegGroupMask>(); }
constexpr uint32_t type() const noexcept { return _getSignaturePart<Operand::kSignatureRegTypeMask>(); }
constexpr uint32_t size() const noexcept { return _getSignaturePart<Operand::kSignatureSizeMask>(); }
@@ -633,6 +651,12 @@ struct RegInfo {
//! Physical or virtual register operand.
class BaseReg : public Operand {
public:
static constexpr uint32_t kBaseSignature =
kSignatureOpTypeMask |
kSignatureRegTypeMask |
kSignatureRegGroupMask |
kSignatureSizeMask ;
//! Architecture neutral register types.
//!
//! These must be reused by any platform that contains that types. All GP
@@ -654,26 +678,30 @@ public:
kTypeGp32 = 5,
//! 64-bit general purpose register (X86|ARM).
kTypeGp64 = 6,
//! 8-bit view of a vector register (ARM).
kTypeVec8 = 7,
//! 16-bit view of a vector register (ARM).
kTypeVec16 = 8,
//! 32-bit view of a vector register (ARM).
kTypeVec32 = 7,
kTypeVec32 = 9,
//! 64-bit view of a vector register (ARM).
kTypeVec64 = 8,
kTypeVec64 = 10,
//! 128-bit view of a vector register (X86|ARM).
kTypeVec128 = 9,
kTypeVec128 = 11,
//! 256-bit view of a vector register (X86).
kTypeVec256 = 10,
kTypeVec256 = 12,
//! 512-bit view of a vector register (X86).
kTypeVec512 = 11,
kTypeVec512 = 13,
//! 1024-bit view of a vector register (future).
kTypeVec1024 = 12,
kTypeVec1024 = 14,
//! Other0 register, should match `kOther0` group.
kTypeOther0 = 13,
kTypeOther0 = 15,
//! Other1 register, should match `kOther1` group.
kTypeOther1 = 14,
kTypeOther1 = 16,
//! Universal id of IP/PC register (if separate).
kTypeIP = 15,
//! Start of platform dependent register types (must be honored).
kTypeCustom = 16,
kTypeIP = 17,
//! Start of platform dependent register types.
kTypeCustom = 18,
//! Maximum possible register type value.
kTypeMax = 31
};
@@ -688,9 +716,9 @@ public:
kGroupOther0 = 2,
//! Group that is architecture dependent.
kGroupOther1 = 3,
//! Count of register groups used by virtual registers.
//! Count of register groups used by physical and virtual registers.
kGroupVirt = 4,
//! Count of register groups used by physical registers.
//! Count of register groups used by physical registers only.
kGroupCount = 16
};
@@ -699,6 +727,22 @@ public:
kIdBad = 0xFFu
};
//! A helper used by constructors.
struct SignatureAndId {
uint32_t _signature;
uint32_t _id;
inline SignatureAndId() noexcept = default;
constexpr SignatureAndId(const SignatureAndId& other) noexcept = default;
constexpr explicit SignatureAndId(uint32_t signature, uint32_t id) noexcept
: _signature(signature),
_id(id) {}
constexpr uint32_t signature() const noexcept { return _signature; }
constexpr uint32_t id() const noexcept { return _id; }
};
static constexpr uint32_t kSignature = kOpReg;
//! \name Construction & Destruction
@@ -717,12 +761,17 @@ public:
: Operand(Globals::Init, other._signature, rId, 0, 0) {}
//! Creates a register initialized to `signature` and `rId`.
constexpr BaseReg(uint32_t signature, uint32_t rId) noexcept
: Operand(Globals::Init, signature, rId, 0, 0) {}
constexpr explicit BaseReg(const SignatureAndId& sid) noexcept
: Operand(Globals::Init, sid._signature, sid._id, 0, 0) {}
inline explicit BaseReg(Globals::NoInit_) noexcept
: Operand(Globals::NoInit) {}
/*! Creates a new register from register signature `rSgn` and id. */
static inline BaseReg fromSignatureAndId(uint32_t rSgn, uint32_t rId) noexcept {
return BaseReg(SignatureAndId(rSgn, rId));
}
//! \}
//! \name Overloaded Operators
@@ -735,6 +784,21 @@ public:
//! \name Accessors
//! \{
//! Returns base signature of the register associated with each register type.
//!
//! Base signature only contains the operand type, register type, register
//! group, and register size. It doesn't contain element type, predicate, or
//! other architecture-specific data. Base signature is a signature that is
//! provided by architecture-specific `RegTraits`, like \ref x86::RegTraits.
constexpr uint32_t baseSignature() const noexcept {
return _signature & (kBaseSignature);
}
//! Tests whether the operand's base signature matches the given signature `sign`.
constexpr bool hasBaseSignature(uint32_t signature) const noexcept { return baseSignature() == signature; }
//! Tests whether the operand's base signature matches the base signature of the `other` operand.
constexpr bool hasBaseSignature(const BaseReg& other) const noexcept { return baseSignature() == other.baseSignature(); }
//! Tests whether this register is the same as `other`.
//!
//! This is just an optimization. Registers by default only use the first
@@ -778,6 +842,21 @@ public:
//! Returns the register group.
constexpr uint32_t group() const noexcept { return _getSignaturePart<kSignatureRegGroupMask>(); }
//! Returns operation predicate of the register (ARM/AArch64).
//!
//! The meaning depends on architecture, for example on ARM hardware this
//! describes \ref arm::Predicate::ShiftOp of the register.
constexpr uint32_t predicate() const noexcept { return _getSignaturePart<kSignaturePredicateMask>(); }
//! Sets operation predicate of the register to `predicate` (ARM/AArch64).
//!
//! The meaning depends on architecture, for example on ARM hardware this
//! describes \ref arm::Predicate::ShiftOp of the register.
inline void setPredicate(uint32_t predicate) noexcept { _setSignaturePart<kSignaturePredicateMask>(predicate); }
//! Resets shift operation type of the register to the default value (ARM/AArch64).
inline void resetPredicate() noexcept { _setSignaturePart<kSignaturePredicateMask>(0); }
//! Clones the register operand.
constexpr BaseReg clone() const noexcept { return BaseReg(*this); }
@@ -791,7 +870,7 @@ public:
//!
//! \note Improper use of `cloneAs()` can lead to hard-to-debug errors.
template<typename RegT>
constexpr RegT cloneAs(const RegT& other) const noexcept { return RegT(other.signature(), id()); }
constexpr RegT cloneAs(const RegT& other) const noexcept { return RegT(SignatureAndId(other.signature(), id())); }
//! Sets the register id to `rId`.
inline void setId(uint32_t rId) noexcept { _baseId = rId; }
@@ -814,17 +893,17 @@ public:
//! 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 ) |
const uint32_t kSgn = (kOpReg << kSignatureOpTypeShift ) |
(kGroupGp << kSignatureRegGroupShift) ;
return (op.signature() & (kSignatureOpMask | kSignatureRegGroupMask)) == kSgn;
return (op.signature() & (kSignatureOpTypeMask | kSignatureRegGroupMask)) == kSgn;
}
//! 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 ) |
const uint32_t kSgn = (kOpReg << kSignatureOpTypeShift ) |
(kGroupVec << kSignatureRegGroupShift) ;
return (op.signature() & (kSignatureOpMask | kSignatureRegGroupMask)) == kSgn;
return (op.signature() & (kSignatureOpTypeMask | kSignatureRegGroupMask)) == kSgn;
}
//! Tests whether the `op` is a general purpose register of the given `rId`.
@@ -913,7 +992,7 @@ struct RegOnly {
//! Converts this ExtraReg to a real `RegT` operand.
template<typename RegT>
constexpr RegT toReg() const noexcept { return RegT(_signature, _id); }
constexpr RegT toReg() const noexcept { return RegT(BaseReg::SignatureAndId(_signature, _id)); }
//! \}
};
@@ -927,46 +1006,28 @@ struct RegOnly {
//! \note It's tricky to pack all possible cases that define a memory operand
//! into just 16 bytes. The `BaseMem` splits data into the following parts:
//!
//! BASE - Base register or label - requires 36 bits total. 4 bits are used to
//! encode the type of the BASE operand (label vs. register type) and
//! the remaining 32 bits define the BASE id, which can be a physical or
//! virtual register index. If BASE type is zero, which is never used as
//! a register-type and label doesn't use it as well then BASE field
//! contains a high DWORD of a possible 64-bit absolute address, which is
//! possible on X64.
//! - BASE - Base register or label - requires 36 bits total. 4 bits are used
//! to encode the type of the BASE operand (label vs. register type) and the
//! remaining 32 bits define the BASE id, which can be a physical or virtual
//! register index. If BASE type is zero, which is never used as a register
//! type and label doesn't use it as well then BASE field contains a high
//! DWORD of a possible 64-bit absolute address, which is possible on X64.
//!
//! INDEX - Index register (or theoretically Label, which doesn't make sense).
//! Encoding is similar to BASE - it also requires 36 bits and splits
//! the encoding to INDEX type (4 bits defining the register type) and
//! id (32-bits).
//! - INDEX - Index register (or theoretically Label, which doesn't make sense).
//! Encoding is similar to BASE - it also requires 36 bits and splits the
//! encoding to INDEX type (4 bits defining the register type) and id (32-bits).
//!
//! OFFSET - A relative offset of the address. Basically if BASE is specified
//! the relative displacement adjusts BASE and an optional INDEX. if
//! BASE is not specified then the OFFSET should be considered as ABSOLUTE
//! address (at least on X86). In that case its low 32 bits are stored in
//! DISPLACEMENT field and the remaining high 32 bits are stored in BASE.
//! - OFFSET - A relative offset of the address. Basically if BASE is specified
//! the relative displacement adjusts BASE and an optional INDEX. if BASE is
//! not specified then the OFFSET should be considered as ABSOLUTE address (at
//! least on X86). In that case its low 32 bits are stored in DISPLACEMENT
//! field and the remaining high 32 bits are stored in BASE.
//!
//! OTHER - There is rest 8 bits that can be used for whatever purpose. The
//! x86::Mem operand uses these bits to store segment override prefix and
//! index shift (scale).
//! - OTHER - There is rest 8 bits that can be used for whatever purpose. For
//! example \ref x86::Mem operand uses these bits to store segment override
//! prefix and index shift (or 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,
//! Absolute address type.
kAddrTypeAbs = 1,
//! Relative address type.
kAddrTypeRel = 2
};
// Shortcuts.
enum SignatureMem : uint32_t {
kSignatureMemAbs = kAddrTypeAbs << kSignatureMemAddrTypeShift,
kSignatureMemRel = kAddrTypeRel << kSignatureMemAddrTypeShift
};
//! \cond INTERNAL
//! Used internally to construct `BaseMem` operand from decomposed data.
struct Decomposed {
@@ -991,6 +1052,18 @@ public:
constexpr BaseMem(const BaseMem& other) noexcept
: Operand(other) {}
//! Creates a `BaseMem` operand from `baseReg` and `offset`.
//!
//! \note This is an architecture independent constructor that can be used to
//! create an architecture independent memory operand to be used in portable
//! code that can handle multiple architectures.
constexpr explicit BaseMem(const BaseReg& baseReg, int32_t offset = 0) noexcept
: Operand(Globals::Init,
kOpMem | (baseReg.type() << kSignatureMemBaseTypeShift),
baseReg.id(),
0,
uint32_t(offset)) {}
//! \cond INTERNAL
//! Creates a `BaseMem` operand from 4 integers as used by `Operand_` struct.
@@ -1036,24 +1109,12 @@ 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); }
//! Creates a new copy of this memory operand adjusted by `off`.
inline BaseMem cloneAdjusted(int64_t off) const noexcept {
BaseMem result(*this);
result.addOffset(off);
return result;
}
//! Tests whether this memory operand is a register home (only used by \ref asmjit_compiler)
constexpr bool isRegHome() const noexcept { return _hasSignaturePart<kSignatureMemRegHomeFlag>(); }
@@ -1221,20 +1282,48 @@ public:
//! with any type, not just the default 64-bit int.
class Imm : public Operand {
public:
//! Type of the immediate.
enum Type : uint32_t {
//! Immediate is integer.
kTypeInteger = 0,
//! Immediate is a floating point stored as double-precision.
kTypeDouble = 1
};
//! \name Construction & Destruction
//! \{
//! Creates a new immediate value (initial value is 0).
constexpr Imm() noexcept
inline constexpr Imm() noexcept
: Operand(Globals::Init, kOpImm, 0, 0, 0) {}
//! Creates a new immediate value from `other`.
constexpr Imm(const Imm& other) noexcept
inline constexpr Imm(const Imm& other) noexcept
: Operand(other) {}
//! Creates a new signed immediate value, assigning the value to `val`.
constexpr explicit Imm(int64_t val) noexcept
: Operand(Globals::Init, kOpImm, 0, Support::unpackU32At0(val), Support::unpackU32At1(val)) {}
//! Creates a new immediate value from ARM/AArch64 specific `shift`.
inline constexpr Imm(const arm::Shift& shift) noexcept
: Operand(Globals::Init, kOpImm | (shift.op() << kSignaturePredicateShift),
0,
Support::unpackU32At0(shift.value()),
Support::unpackU32At1(shift.value())) {}
//! Creates a new signed immediate value, assigning the value to `val` and
//! an architecture-specific predicate to `predicate`.
//!
//! \note Predicate is currently only used by ARM architectures.
template<typename T>
inline constexpr Imm(const T& val, const uint32_t predicate = 0) noexcept
: Operand(Globals::Init, kOpImm | (predicate << kSignaturePredicateShift),
0,
Support::unpackU32At0(int64_t(val)),
Support::unpackU32At1(int64_t(val))) {}
inline Imm(const float& val, const uint32_t predicate = 0) noexcept
: Operand(Globals::Init, kOpImm | (predicate << kSignaturePredicateShift), 0, 0, 0) { setValue(val); }
inline Imm(const double& val, const uint32_t predicate = 0) noexcept
: Operand(Globals::Init, kOpImm | (predicate << kSignaturePredicateShift), 0, 0, 0) { setValue(val); }
inline explicit Imm(Globals::NoInit_) noexcept
: Operand(Globals::NoInit) {}
@@ -1252,23 +1341,50 @@ public:
//! \name Accessors
//! \{
//! Returns immediate type, see \ref Type.
constexpr uint32_t type() const noexcept { return _getSignaturePart<kSignatureImmTypeMask>(); }
//! Sets the immediate type to `type`, see \ref Type.
inline void setType(uint32_t type) noexcept { _setSignaturePart<kSignatureImmTypeMask>(type); }
//! Resets immediate type to `kTypeInteger`.
inline void resetType() noexcept { setType(kTypeInteger); }
//! Returns operation predicate of the immediate.
//!
//! The meaning depends on architecture, for example on ARM hardware this
//! describes \ref arm::Predicate::ShiftOp of the immediate.
constexpr uint32_t predicate() const noexcept { return _getSignaturePart<kSignaturePredicateMask>(); }
//! Sets operation predicate of the immediate to `predicate`.
//!
//! The meaning depends on architecture, for example on ARM hardware this
//! describes \ref arm::Predicate::ShiftOp of the immediate.
inline void setPredicate(uint32_t predicate) noexcept { _setSignaturePart<kSignaturePredicateMask>(predicate); }
//! Resets the shift operation type of the immediate to the default value (no operation).
inline void resetPredicate() noexcept { _setSignaturePart<kSignaturePredicateMask>(0); }
//! 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 this immediate value is integer of any size.
constexpr uint32_t isInteger() const noexcept { return type() == kTypeInteger; }
//! Tests whether this immediate value is a double precision floating point value.
constexpr uint32_t isDouble() const noexcept { return type() == kTypeDouble; }
//! Tests whether the immediate can be casted to 8-bit signed integer.
constexpr bool isInt8() const noexcept { return Support::isInt8(value()); }
constexpr bool isInt8() const noexcept { return type() == kTypeInteger && Support::isInt8(value()); }
//! Tests whether the immediate can be casted to 8-bit unsigned integer.
constexpr bool isUInt8() const noexcept { return Support::isUInt8(value()); }
constexpr bool isUInt8() const noexcept { return type() == kTypeInteger && Support::isUInt8(value()); }
//! Tests whether the immediate can be casted to 16-bit signed integer.
constexpr bool isInt16() const noexcept { return Support::isInt16(value()); }
constexpr bool isInt16() const noexcept { return type() == kTypeInteger && Support::isInt16(value()); }
//! Tests whether the immediate can be casted to 16-bit unsigned integer.
constexpr bool isUInt16() const noexcept { return Support::isUInt16(value()); }
constexpr bool isUInt16() const noexcept { return type() == kTypeInteger && Support::isUInt16(value()); }
//! Tests whether the immediate can be casted to 32-bit signed integer.
constexpr bool isInt32() const noexcept { return Support::isInt32(value()); }
constexpr bool isInt32() const noexcept { return type() == kTypeInteger && Support::isInt32(value()); }
//! Tests whether the immediate can be casted to 32-bit unsigned integer.
constexpr bool isUInt32() const noexcept { return _data[kDataImmValueHi] == 0; }
constexpr bool isUInt32() const noexcept { return type() == kTypeInteger && _data[kDataImmValueHi] == 0; }
//! Returns the immediate value casted to `T`.
//!
@@ -1276,9 +1392,7 @@ public:
//! simply the representation of `T` considering the original value's lowest
//! bits.
template<typename T>
constexpr T valueAs() const noexcept {
return T(uint64_t(value()) & Support::allOnes<typename std::make_unsigned<T>::type>());
}
inline T valueAs() const noexcept { return Support::immediateToT<T>(value()); }
//! Returns low 32-bit signed integer.
constexpr int32_t int32Lo() const noexcept { return int32_t(_data[kDataImmValueLo]); }
@@ -1292,13 +1406,13 @@ public:
//! 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);
_setValueInternal(Support::immediateFromT(val), std::is_floating_point<T>::value ? kTypeDouble : kTypeInteger);
}
inline void setDouble(double d) noexcept {
setValue(Support::bitCast<uint64_t>(d));
inline void _setValueInternal(int64_t val, uint32_t type) noexcept {
setType(type);
_data[kDataImmValueHi] = uint32_t(uint64_t(val) >> 32);
_data[kDataImmValueLo] = uint32_t(uint64_t(val) & 0xFFFFFFFFu);
}
//! \}
@@ -1369,9 +1483,7 @@ public:
//! Using `imm(x)` is much nicer than using `Imm(x)` as this is a template
//! which can accept any integer including pointers and function pointers.
template<typename T>
static constexpr Imm imm(T val) noexcept {
return Imm(std::is_signed<T>::value ? int64_t(val) : int64_t(uint64_t(val)));
}
static constexpr Imm imm(const T& val) noexcept { return Imm(val); }
//! \}

View File

@@ -54,6 +54,17 @@ public:
};
struct Layout {
//! Index of architecture registers per group.
RARegIndex physIndex;
//! Count of architecture registers per group.
RARegCount physCount;
//! Count of physical registers of all groups.
uint32_t physTotal;
//! Count of work registers.
uint32_t workCount;
//! WorkRegs data (vector).
const RAWorkRegs* workRegs;
inline void reset() noexcept {
physIndex.reset();
physCount.reset();
@@ -61,54 +72,52 @@ public:
workCount = 0;
workRegs = nullptr;
}
RARegIndex physIndex; //!< Index of architecture registers per group.
RARegCount physCount; //!< Count of architecture registers per group.
uint32_t physTotal; //!< Count of physical registers of all groups.
uint32_t workCount; //!< Count of work registers.
const RAWorkRegs* workRegs; //!< WorkRegs data (vector).
};
struct PhysToWorkMap {
static inline size_t sizeOf(uint32_t count) noexcept {
return sizeof(PhysToWorkMap) - sizeof(uint32_t) + size_t(count) * sizeof(uint32_t);
//! Assigned registers (each bit represents one physical reg).
RARegMask assigned;
//! Dirty registers (spill slot out of sync or no spill slot).
RARegMask dirty;
//! PhysReg to WorkReg mapping.
uint32_t workIds[1 /* ... */];
static inline size_t sizeOf(size_t count) noexcept {
return sizeof(PhysToWorkMap) - sizeof(uint32_t) + count * sizeof(uint32_t);
}
inline void reset(uint32_t count) noexcept {
inline void reset(size_t count) noexcept {
assigned.reset();
dirty.reset();
for (uint32_t i = 0; i < count; i++)
for (size_t i = 0; i < count; i++)
workIds[i] = kWorkNone;
}
inline void copyFrom(const PhysToWorkMap* other, uint32_t count) noexcept {
inline void copyFrom(const PhysToWorkMap* other, size_t count) noexcept {
size_t size = sizeOf(count);
memcpy(this, other, size);
}
RARegMask assigned; //!< Assigned registers (each bit represents one physical reg).
RARegMask dirty; //!< Dirty registers (spill slot out of sync or no spill slot).
uint32_t workIds[1 /* ... */]; //!< PhysReg to WorkReg mapping.
};
struct WorkToPhysMap {
static inline size_t sizeOf(uint32_t count) noexcept {
//! WorkReg to PhysReg mapping
uint8_t physIds[1 /* ... */];
static inline size_t sizeOf(size_t count) noexcept {
return size_t(count) * sizeof(uint8_t);
}
inline void reset(uint32_t count) noexcept {
for (uint32_t i = 0; i < count; i++)
inline void reset(size_t count) noexcept {
for (size_t i = 0; i < count; i++)
physIds[i] = kPhysNone;
}
inline void copyFrom(const WorkToPhysMap* other, uint32_t count) noexcept {
inline void copyFrom(const WorkToPhysMap* other, size_t count) noexcept {
size_t size = sizeOf(count);
if (ASMJIT_LIKELY(size))
memcpy(this, other, size);
}
uint8_t physIds[1 /* ... */]; //!< WorkReg to PhysReg mapping
};
//! Physical registers layout.

View File

@@ -37,29 +37,28 @@ ASMJIT_BEGIN_NAMESPACE
//! \{
// ============================================================================
// [asmjit::RACFGBuilder]
// [asmjit::RACFGBuilderT]
// ============================================================================
template<typename This>
class RACFGBuilder {
class RACFGBuilderT {
public:
RAPass* _pass;
BaseCompiler* _cc;
RABlock* _curBlock;
RABlock* _retBlock;
FuncNode* _funcNode;
RARegsStats _blockRegStats;
uint32_t _exitLabelId;
ZoneVector<uint32_t> _sharedAssignmentsMap;
BaseRAPass* _pass = nullptr;
BaseCompiler* _cc = nullptr;
RABlock* _curBlock = nullptr;
RABlock* _retBlock = nullptr;
FuncNode* _funcNode = nullptr;
RARegsStats _blockRegStats {};
uint32_t _exitLabelId = Globals::kInvalidId;
ZoneVector<uint32_t> _sharedAssignmentsMap {};
// Only used by logging, it's fine to be here to prevent more #ifdefs...
bool _hasCode;
RABlock* _lastLoggedBlock;
bool _hasCode = false;
RABlock* _lastLoggedBlock = nullptr;
#ifndef ASMJIT_NO_LOGGING
Logger* _logger;
uint32_t _logFlags;
Logger* _logger = nullptr;
uint32_t _logFlags = FormatOptions::kFlagPositions;
StringTmp<512> _sb;
#endif
@@ -72,20 +71,11 @@ public:
// position that is [at that time] unassigned.
static constexpr uint32_t kNodePositionDidOnBefore = 0xFFFFFFFFu;
inline RACFGBuilder(RAPass* pass) noexcept
inline RACFGBuilderT(BaseRAPass* pass) noexcept
: _pass(pass),
_cc(pass->cc()),
_curBlock(nullptr),
_retBlock(nullptr),
_funcNode(nullptr),
_blockRegStats{},
_exitLabelId(Globals::kInvalidId),
_hasCode(false),
_lastLoggedBlock(nullptr) {
_cc(pass->cc()) {
#ifndef ASMJIT_NO_LOGGING
_logger = _pass->debugLogger();
_logFlags = FormatOptions::kFlagPositions;
if (_logger)
_logFlags |= _logger->flags();
#endif

View File

@@ -25,11 +25,12 @@
#define ASMJIT_CORE_RADEFS_P_H_INCLUDED
#include "../core/api-config.h"
#ifndef ASMJIT_NO_COMPILER
#include "../core/compiler.h"
#include "../core/archtraits.h"
#include "../core/compilerdefs.h"
#include "../core/logger.h"
#include "../core/operand.h"
#include "../core/support.h"
#include "../core/type.h"
#include "../core/zone.h"
#include "../core/zonevector.h"
@@ -64,13 +65,52 @@ ASMJIT_BEGIN_NAMESPACE
// [Forward Declarations]
// ============================================================================
class RAPass;
class BaseRAPass;
class RABlock;
class BaseNode;
struct RAStackSlot;
typedef ZoneVector<RABlock*> RABlocks;
typedef ZoneVector<RAWorkReg*> RAWorkRegs;
// ============================================================================
// [asmjit::RAConstraints]
// ============================================================================
class RAConstraints {
public:
uint32_t _availableRegs[BaseReg::kGroupVirt] {};
inline RAConstraints() noexcept {}
ASMJIT_NOINLINE Error init(uint32_t arch) noexcept {
switch (arch) {
case Environment::kArchX86:
case Environment::kArchX64: {
uint32_t registerCount = arch == Environment::kArchX86 ? 8 : 16;
_availableRegs[BaseReg::kGroupGp] = Support::lsbMask<uint32_t>(registerCount) & ~Support::bitMask(4u);
_availableRegs[BaseReg::kGroupVec] = Support::lsbMask<uint32_t>(registerCount);
_availableRegs[BaseReg::kGroupOther0] = Support::lsbMask<uint32_t>(8);
_availableRegs[BaseReg::kGroupOther1] = Support::lsbMask<uint32_t>(8);
return kErrorOk;
}
case Environment::kArchAArch64: {
_availableRegs[BaseReg::kGroupGp] = 0xFFFFFFFFu & ~Support::bitMask(18, 31u);
_availableRegs[BaseReg::kGroupVec] = 0xFFFFFFFFu;
_availableRegs[BaseReg::kGroupOther0] = 0;
_availableRegs[BaseReg::kGroupOther1] = 0;
return kErrorOk;
}
default:
return DebugUtils::errored(kErrorInvalidArch);
}
}
inline uint32_t availableRegs(uint32_t group) const noexcept { return _availableRegs[group]; }
};
// ============================================================================
// [asmjit::RAStrategy]
// ============================================================================
@@ -93,45 +133,6 @@ struct RAStrategy {
inline bool isComplex() const noexcept { return _type >= kStrategyComplex; }
};
// ============================================================================
// [asmjit::RAArchTraits]
// ============================================================================
//! Traits.
struct RAArchTraits {
enum Flags : uint32_t {
//! Registers can be swapped by a single instruction.
kHasSwap = 0x01u
};
uint8_t _flags[BaseReg::kGroupVirt];
//! \name Construction & Destruction
//! \{
inline RAArchTraits() noexcept { reset(); }
inline void reset() noexcept { memset(_flags, 0, sizeof(_flags)); }
//! \}
//! \name Accessors
//! \{
inline bool hasFlag(uint32_t group, uint32_t flag) const noexcept { return (_flags[group] & flag) != 0; }
inline bool hasSwap(uint32_t group) const noexcept { return hasFlag(group, kHasSwap); }
inline uint8_t& operator[](uint32_t group) noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
return _flags[group];
}
inline const uint8_t& operator[](uint32_t group) const noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
return _flags[group];
}
//! \}
};
// ============================================================================
// [asmjit::RARegCount]
@@ -317,8 +318,9 @@ struct RARegMask {
//! before the register allocator tries to do its job. For example to use fast
//! register allocation inside a block or loop it cannot have clobbered and/or
//! fixed registers, etc...
struct RARegsStats {
uint32_t _packed;
class RARegsStats {
public:
uint32_t _packed = 0;
enum Index : uint32_t {
kIndexUsed = 0,
@@ -355,12 +357,12 @@ struct RARegsStats {
//! Count of live registers, per group.
class RALiveCount {
public:
uint32_t n[BaseReg::kGroupVirt];
uint32_t n[BaseReg::kGroupVirt] {};
//! \name Construction & Destruction
//! \{
inline RALiveCount() noexcept { reset(); }
inline RALiveCount() noexcept = default;
inline RALiveCount(const RALiveCount& other) noexcept = default;
inline void init(const RALiveCount& other) noexcept {
@@ -674,19 +676,9 @@ public:
//! Statistics about a register liveness.
class RALiveStats {
public:
uint32_t _width;
float _freq;
float _priority;
//! \name Construction & Destruction
//! \{
inline RALiveStats()
: _width(0),
_freq(0.0f),
_priority(0.0f) {}
//! \}
uint32_t _width = 0;
float _freq = 0.0f;
float _priority = 0.0f;
//! \name Accessors
//! \{
@@ -928,46 +920,46 @@ public:
ASMJIT_NONCOPYABLE(RAWorkReg)
//! RAPass specific ID used during analysis and allocation.
uint32_t _workId;
//! Copy of ID used by `VirtReg`.
uint32_t _virtId;
uint32_t _workId = 0;
//! Copy of ID used by \ref VirtReg.
uint32_t _virtId = 0;
//! Permanent association with `VirtReg`.
VirtReg* _virtReg;
//! Temporary association with `RATiedReg`.
RATiedReg* _tiedReg;
//! Permanent association with \ref VirtReg.
VirtReg* _virtReg = nullptr;
//! Temporary association with \ref RATiedReg.
RATiedReg* _tiedReg = nullptr;
//! Stack slot associated with the register.
RAStackSlot* _stackSlot;
RAStackSlot* _stackSlot = nullptr;
//! Copy of a signature used by `VirtReg`.
RegInfo _info;
//! Copy of a signature used by \ref VirtReg.
RegInfo _info {};
//! RAPass specific flags used during analysis and allocation.
uint32_t _flags;
uint32_t _flags = 0;
//! IDs of all physical registers this WorkReg has been allocated to.
uint32_t _allocatedMask;
uint32_t _allocatedMask = 0;
//! IDs of all physical registers that are clobbered during the lifetime of
//! this WorkReg.
//!
//! This mask should be updated by `RAPass::buildLiveness()`, because it's
//! global and should be updated after unreachable code has been removed.
uint32_t _clobberSurvivalMask;
uint32_t _clobberSurvivalMask = 0;
//! A byte-mask where each bit represents one valid byte of the register.
uint64_t _regByteMask;
uint64_t _regByteMask = 0;
//! Argument index (or `kNoArgIndex` if none).
uint8_t _argIndex;
uint8_t _argIndex = kNoArgIndex;
//! Argument value index in the pack (0 by default).
uint8_t _argValueIndex;
uint8_t _argValueIndex = 0;
//! Global home register ID (if any, assigned by RA).
uint8_t _homeRegId;
uint8_t _homeRegId = BaseReg::kIdBad;
//! Global hint register ID (provided by RA or user).
uint8_t _hintRegId;
uint8_t _hintRegId = BaseReg::kIdBad;
//! Live spans of the `VirtReg`.
LiveRegSpans _liveSpans;
LiveRegSpans _liveSpans {};
//! Live statistics.
RALiveStats _liveStats;
RALiveStats _liveStats {};
//! All nodes that read/write this VirtReg/WorkReg.
ZoneVector<BaseNode*> _refs;
@@ -1000,20 +992,7 @@ public:
: _workId(workId),
_virtId(vReg->id()),
_virtReg(vReg),
_tiedReg(nullptr),
_stackSlot(nullptr),
_info(vReg->info()),
_flags(0),
_allocatedMask(0),
_clobberSurvivalMask(0),
_regByteMask(0),
_argIndex(kNoArgIndex),
_argValueIndex(0),
_homeRegId(BaseReg::kIdBad),
_hintRegId(BaseReg::kIdBad),
_liveSpans(),
_liveStats(),
_refs() {}
_info(vReg->info()) {}
//! \}
@@ -1095,5 +1074,4 @@ public:
ASMJIT_END_NAMESPACE
#endif // !ASMJIT_NO_COMPILER
#endif // ASMJIT_CORE_RADEFS_P_H_INCLUDED

View File

@@ -243,7 +243,7 @@ Error RALocalAllocator::switchToAssignment(
// Reset as we will do some changes to the current assignment.
runId = -1;
if (_archTraits.hasSwap(group)) {
if (_archTraits->hasSwap(group)) {
ASMJIT_PROPAGATE(onSwapReg(group, curWorkId, physId, dstWorkId, altPhysId));
}
else {
@@ -653,7 +653,7 @@ Error RALocalAllocator::allocInst(InstNode* node) noexcept {
// just a single instruction. However, swap is only available on few
// architectures and it's definitely not available for each register
// group. Calling `onSwapReg()` before checking these would be fatal.
if (_archTraits.hasSwap(group) && thisPhysId != RAAssignment::kPhysNone) {
if (_archTraits->hasSwap(group) && thisPhysId != RAAssignment::kPhysNone) {
ASMJIT_PROPAGATE(onSwapReg(group, thisWorkId, thisPhysId, targetWorkId, targetPhysId));
thisTiedReg->markUseDone();
@@ -767,7 +767,7 @@ Error RALocalAllocator::allocInst(InstNode* node) noexcept {
uint32_t dstId = it.next();
if (dstId == srcId)
continue;
_pass->onEmitMove(workId, dstId, srcId);
_pass->emitMove(workId, dstId, srcId);
}
}
@@ -916,7 +916,7 @@ Error RALocalAllocator::allocBranch(InstNode* node, RABlock* target, RABlock* co
node->clearInstOptions(BaseInst::kOptionShortForm);
// Finalize the switch assignment sequence.
ASMJIT_PROPAGATE(_pass->onEmitJump(savedTarget));
ASMJIT_PROPAGATE(_pass->emitJump(savedTarget));
_cc->_setCursor(injectionPoint);
_cc->bind(trampoline);
}
@@ -932,6 +932,7 @@ Error RALocalAllocator::allocBranch(InstNode* node, RABlock* target, RABlock* co
}
Error RALocalAllocator::allocJumpTable(InstNode* node, const RABlocks& targets, RABlock* cont) noexcept {
// TODO: Do we really need to use `cont`?
DebugUtils::unused(cont);
if (targets.empty())

View File

@@ -50,13 +50,13 @@ public:
typedef RAAssignment::PhysToWorkMap PhysToWorkMap;
typedef RAAssignment::WorkToPhysMap WorkToPhysMap;
//! Link to `RAPass`.
RAPass* _pass;
//! Link to `BaseRAPass`.
BaseRAPass* _pass;
//! Link to `BaseCompiler`.
BaseCompiler* _cc;
//! Architecture traits.
RAArchTraits _archTraits;
const ArchTraits* _archTraits;
//! Registers available to the allocator.
RARegMask _availableRegs;
//! Registers clobbered by the allocator.
@@ -82,7 +82,7 @@ public:
//! \name Construction & Destruction
//! \{
inline RALocalAllocator(RAPass* pass) noexcept
inline RALocalAllocator(BaseRAPass* pass) noexcept
: _pass(pass),
_cc(pass->cc()),
_archTraits(pass->_archTraits),
@@ -219,7 +219,7 @@ public:
inline Error onMoveReg(uint32_t group, uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept {
if (dstPhysId == srcPhysId) return kErrorOk;
_curAssignment.reassign(group, workId, dstPhysId, srcPhysId);
return _pass->onEmitMove(workId, dstPhysId, srcPhysId);
return _pass->emitMove(workId, dstPhysId, srcPhysId);
}
//! Emits a swap between two physical registers and fixes their assignment.
@@ -227,14 +227,14 @@ public:
//! \note Target must support this operation otherwise this would ASSERT.
inline Error onSwapReg(uint32_t group, uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept {
_curAssignment.swap(group, aWorkId, aPhysId, bWorkId, bPhysId);
return _pass->onEmitSwap(aWorkId, aPhysId, bWorkId, bPhysId);
return _pass->emitSwap(aWorkId, aPhysId, bWorkId, bPhysId);
}
//! Emits a load from [VirtReg/WorkReg]'s spill slot to a physical register
//! and makes it assigned and clean.
inline Error onLoadReg(uint32_t group, uint32_t workId, uint32_t physId) noexcept {
_curAssignment.assign(group, workId, physId, RAAssignment::kClean);
return _pass->onEmitLoad(workId, physId);
return _pass->emitLoad(workId, physId);
}
//! Emits a save a physical register to a [VirtReg/WorkReg]'s spill slot,
@@ -244,7 +244,7 @@ public:
ASMJIT_ASSERT(_curAssignment.physToWorkId(group, physId) == workId);
_curAssignment.makeClean(group, workId, physId);
return _pass->onEmitSave(workId, physId);
return _pass->emitSave(workId, physId);
}
//! Assigns a register, the content of it is undefined at this point.

View File

@@ -70,50 +70,17 @@ Error RABlock::prependSuccessor(RABlock* successor) noexcept {
}
// ============================================================================
// [asmjit::RAPass - Construction / Destruction]
// [asmjit::BaseRAPass - Construction / Destruction]
// ============================================================================
RAPass::RAPass() noexcept
: FuncPass("RAPass"),
_allocator(),
_logger(nullptr),
_debugLogger(nullptr),
_loggerFlags(0),
_func(nullptr),
_stop(nullptr),
_extraBlock(nullptr),
_blocks(),
_exits(),
_pov(),
_instructionCount(0),
_createdBlockCount(0),
_sharedAssignments(),
_lastTimestamp(0),
_archRegsInfo(nullptr),
_archTraits(),
_physRegIndex(),
_physRegCount(),
_physRegTotal(0),
_scratchRegIndexes{},
_availableRegs(),
_availableRegCount(),
_clobberedRegs(),
_globalMaxLiveCount(),
_globalLiveSpans {},
_temporaryMem(),
_sp(),
_fp(),
_stackAllocator(),
_argsAssignment(),
_numStackArgsToStackSlots(0),
_maxWorkRegNameSize(0) {}
RAPass::~RAPass() noexcept {}
BaseRAPass::BaseRAPass() noexcept : FuncPass("BaseRAPass") {}
BaseRAPass::~BaseRAPass() noexcept {}
// ============================================================================
// [asmjit::RAPass - RunOnFunction]
// [asmjit::BaseRAPass - RunOnFunction]
// ============================================================================
static void RAPass_reset(RAPass* self, FuncDetail* funcDetail) noexcept {
static void RAPass_reset(BaseRAPass* self, FuncDetail* funcDetail) noexcept {
ZoneAllocator* allocator = self->allocator();
self->_blocks.reset();
@@ -126,8 +93,7 @@ static void RAPass_reset(RAPass* self, FuncDetail* funcDetail) noexcept {
self->_sharedAssignments.reset();
self->_lastTimestamp = 0;
self->_archRegsInfo = nullptr;
self->_archTraits.reset();
self->_archTraits = nullptr;
self->_physRegIndex.reset();
self->_physRegCount.reset();
self->_physRegTotal = 0;
@@ -154,7 +120,7 @@ static void RAPass_reset(RAPass* self, FuncDetail* funcDetail) noexcept {
self->_maxWorkRegNameSize = 0;
}
static void RAPass_resetVirtRegData(RAPass* self) noexcept {
static void RAPass_resetVirtRegData(BaseRAPass* self) noexcept {
// Zero everything so it cannot be used by accident.
for (RAWorkReg* wReg : self->_workRegs) {
VirtReg* vReg = wReg->virtReg();
@@ -162,7 +128,7 @@ static void RAPass_resetVirtRegData(RAPass* self) noexcept {
}
}
Error RAPass::runOnFunction(Zone* zone, Logger* logger, FuncNode* func) {
Error BaseRAPass::runOnFunction(Zone* zone, Logger* logger, FuncNode* func) {
_allocator.reset(zone);
#ifndef ASMJIT_NO_LOGGING
@@ -223,7 +189,7 @@ Error RAPass::runOnFunction(Zone* zone, Logger* logger, FuncNode* func) {
return err;
}
Error RAPass::onPerformAllSteps() noexcept {
Error BaseRAPass::onPerformAllSteps() noexcept {
ASMJIT_PROPAGATE(buildCFG());
ASMJIT_PROPAGATE(buildViews());
ASMJIT_PROPAGATE(removeUnreachableBlocks());
@@ -249,10 +215,10 @@ Error RAPass::onPerformAllSteps() noexcept {
}
// ============================================================================
// [asmjit::RAPass - CFG - Basic Block Management]
// [asmjit::BaseRAPass - CFG - Basic Block Management]
// ============================================================================
RABlock* RAPass::newBlock(BaseNode* initialNode) noexcept {
RABlock* BaseRAPass::newBlock(BaseNode* initialNode) noexcept {
RABlock* block = zone()->newT<RABlock>(this);
if (ASMJIT_UNLIKELY(!block))
return nullptr;
@@ -264,7 +230,7 @@ RABlock* RAPass::newBlock(BaseNode* initialNode) noexcept {
return block;
}
RABlock* RAPass::newBlockOrExistingAt(LabelNode* cbLabel, BaseNode** stoppedAt) noexcept {
RABlock* BaseRAPass::newBlockOrExistingAt(LabelNode* cbLabel, BaseNode** stoppedAt) noexcept {
if (cbLabel->hasPassData())
return cbLabel->passData<RABlock>();
@@ -351,7 +317,7 @@ RABlock* RAPass::newBlockOrExistingAt(LabelNode* cbLabel, BaseNode** stoppedAt)
return block;
}
Error RAPass::addBlock(RABlock* block) noexcept {
Error BaseRAPass::addBlock(RABlock* block) noexcept {
ASMJIT_PROPAGATE(_blocks.willGrow(allocator()));
block->_blockId = blockCount();
@@ -360,10 +326,10 @@ Error RAPass::addBlock(RABlock* block) noexcept {
}
// ============================================================================
// [asmjit::RAPass - CFG - Build]
// [asmjit::BaseRAPass - CFG - Build]
// ============================================================================
Error RAPass::initSharedAssignments(const ZoneVector<uint32_t>& sharedAssignmentsMap) noexcept {
Error BaseRAPass::initSharedAssignments(const ZoneVector<uint32_t>& sharedAssignmentsMap) noexcept {
if (sharedAssignmentsMap.empty())
return kErrorOk;
@@ -403,7 +369,7 @@ Error RAPass::initSharedAssignments(const ZoneVector<uint32_t>& sharedAssignment
}
// ============================================================================
// [asmjit::RAPass - CFG - Views Order]
// [asmjit::BaseRAPass - CFG - Views Order]
// ============================================================================
class RABlockVisitItem {
@@ -425,7 +391,7 @@ public:
uint32_t _index;
};
Error RAPass::buildViews() noexcept {
Error BaseRAPass::buildViews() noexcept {
#ifndef ASMJIT_NO_LOGGING
Logger* logger = debugLogger();
ASMJIT_RA_LOG_FORMAT("[RAPass::BuildViews]\n");
@@ -497,7 +463,7 @@ Error RAPass::buildViews() noexcept {
}
// ============================================================================
// [asmjit::RAPass - CFG - Dominators]
// [asmjit::BaseRAPass - CFG - Dominators]
// ============================================================================
static ASMJIT_INLINE RABlock* intersectBlocks(RABlock* b1, RABlock* b2) noexcept {
@@ -509,7 +475,7 @@ static ASMJIT_INLINE RABlock* intersectBlocks(RABlock* b1, RABlock* b2) noexcept
}
// Based on "A Simple, Fast Dominance Algorithm".
Error RAPass::buildDominators() noexcept {
Error BaseRAPass::buildDominators() noexcept {
#ifndef ASMJIT_NO_LOGGING
Logger* logger = debugLogger();
ASMJIT_RA_LOG_FORMAT("[RAPass::BuildDominators]\n");
@@ -556,7 +522,7 @@ Error RAPass::buildDominators() noexcept {
return kErrorOk;
}
bool RAPass::_strictlyDominates(const RABlock* a, const RABlock* b) const noexcept {
bool BaseRAPass::_strictlyDominates(const RABlock* a, const RABlock* b) const noexcept {
ASMJIT_ASSERT(a != nullptr); // There must be at least one block if this function is
ASMJIT_ASSERT(b != nullptr); // called, as both `a` and `b` must be valid blocks.
ASMJIT_ASSERT(a != b); // Checked by `dominates()` and `strictlyDominates()`.
@@ -573,7 +539,7 @@ bool RAPass::_strictlyDominates(const RABlock* a, const RABlock* b) const noexce
return iDom != entryBlock;
}
const RABlock* RAPass::_nearestCommonDominator(const RABlock* a, const RABlock* b) const noexcept {
const RABlock* BaseRAPass::_nearestCommonDominator(const RABlock* a, const RABlock* b) const noexcept {
ASMJIT_ASSERT(a != nullptr); // There must be at least one block if this function is
ASMJIT_ASSERT(b != nullptr); // called, as both `a` and `b` must be valid blocks.
ASMJIT_ASSERT(a != b); // Checked by `dominates()` and `properlyDominates()`.
@@ -611,10 +577,10 @@ const RABlock* RAPass::_nearestCommonDominator(const RABlock* a, const RABlock*
}
// ============================================================================
// [asmjit::RAPass - CFG - Utilities]
// [asmjit::BaseRAPass - CFG - Utilities]
// ============================================================================
Error RAPass::removeUnreachableBlocks() noexcept {
Error BaseRAPass::removeUnreachableBlocks() noexcept {
uint32_t numAllBlocks = blockCount();
uint32_t numReachableBlocks = reachableBlockCount();
@@ -661,13 +627,13 @@ Error RAPass::removeUnreachableBlocks() noexcept {
return kErrorOk;
}
BaseNode* RAPass::findSuccessorStartingAt(BaseNode* node) noexcept {
BaseNode* BaseRAPass::findSuccessorStartingAt(BaseNode* node) noexcept {
while (node && (node->isInformative() || node->hasNoEffect()))
node = node->next();
return node;
}
bool RAPass::isNextTo(BaseNode* node, BaseNode* target) noexcept {
bool BaseRAPass::isNextTo(BaseNode* node, BaseNode* target) noexcept {
for (;;) {
node = node->next();
if (node == target)
@@ -682,10 +648,10 @@ bool RAPass::isNextTo(BaseNode* node, BaseNode* target) noexcept {
}
// ============================================================================
// [asmjit::RAPass - ?]
// [asmjit::BaseRAPass - ?]
// ============================================================================
Error RAPass::_asWorkReg(VirtReg* vReg, RAWorkReg** out) noexcept {
Error BaseRAPass::_asWorkReg(VirtReg* vReg, RAWorkReg** out) noexcept {
// Checked by `asWorkReg()` - must be true.
ASMJIT_ASSERT(vReg->_workReg == nullptr);
@@ -715,7 +681,7 @@ Error RAPass::_asWorkReg(VirtReg* vReg, RAWorkReg** out) noexcept {
return kErrorOk;
}
RAAssignment::WorkToPhysMap* RAPass::newWorkToPhysMap() noexcept {
RAAssignment::WorkToPhysMap* BaseRAPass::newWorkToPhysMap() noexcept {
uint32_t count = workRegCount();
size_t size = WorkToPhysMap::sizeOf(count);
@@ -734,7 +700,7 @@ RAAssignment::WorkToPhysMap* RAPass::newWorkToPhysMap() noexcept {
return map;
}
RAAssignment::PhysToWorkMap* RAPass::newPhysToWorkMap() noexcept {
RAAssignment::PhysToWorkMap* BaseRAPass::newPhysToWorkMap() noexcept {
uint32_t count = physRegTotal();
size_t size = PhysToWorkMap::sizeOf(count);
@@ -747,7 +713,7 @@ RAAssignment::PhysToWorkMap* RAPass::newPhysToWorkMap() noexcept {
}
// ============================================================================
// [asmjit::RAPass - Registers - Liveness Analysis and Statistics]
// [asmjit::BaseRAPass - Registers - Liveness Analysis and Statistics]
// ============================================================================
namespace LiveOps {
@@ -823,7 +789,7 @@ namespace LiveOps {
}
}
ASMJIT_FAVOR_SPEED Error RAPass::buildLiveness() noexcept {
ASMJIT_FAVOR_SPEED Error BaseRAPass::buildLiveness() noexcept {
#ifndef ASMJIT_NO_LOGGING
Logger* logger = debugLogger();
StringTmp<512> sb;
@@ -1106,7 +1072,7 @@ ASMJIT_FAVOR_SPEED Error RAPass::buildLiveness() noexcept {
return kErrorOk;
}
Error RAPass::assignArgIndexToWorkRegs() noexcept {
Error BaseRAPass::assignArgIndexToWorkRegs() noexcept {
ZoneBitVector& liveIn = entryBlock()->liveIn();
uint32_t argCount = func()->argCount();
@@ -1130,7 +1096,7 @@ Error RAPass::assignArgIndexToWorkRegs() noexcept {
workReg->setArgIndex(argIndex, valueIndex);
const FuncValue& arg = func()->detail().arg(argIndex, valueIndex);
if (arg.isReg() && _archRegsInfo->regInfo[arg.regType()].group() == workReg->group()) {
if (arg.isReg() && _archTraits->regTypeToGroup(arg.regType()) == workReg->group()) {
workReg->setHintRegId(arg.regId());
}
}
@@ -1138,8 +1104,9 @@ Error RAPass::assignArgIndexToWorkRegs() noexcept {
return kErrorOk;
}
// ============================================================================
// [asmjit::RAPass - Allocation - Global]
// [asmjit::BaseRAPass - Allocation - Global]
// ============================================================================
#ifndef ASMJIT_NO_LOGGING
@@ -1156,7 +1123,7 @@ static void RAPass_dumpSpans(String& sb, uint32_t index, const LiveRegSpans& liv
}
#endif
Error RAPass::runGlobalAllocator() noexcept {
Error BaseRAPass::runGlobalAllocator() noexcept {
ASMJIT_PROPAGATE(initGlobalLiveSpans());
for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) {
@@ -1166,16 +1133,19 @@ Error RAPass::runGlobalAllocator() noexcept {
return kErrorOk;
}
ASMJIT_FAVOR_SPEED Error RAPass::initGlobalLiveSpans() noexcept {
ASMJIT_FAVOR_SPEED Error BaseRAPass::initGlobalLiveSpans() noexcept {
for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) {
size_t physCount = _physRegCount[group];
LiveRegSpans* liveSpans = allocator()->allocT<LiveRegSpans>(physCount * sizeof(LiveRegSpans));
LiveRegSpans* liveSpans = nullptr;
if (physCount) {
liveSpans = allocator()->allocT<LiveRegSpans>(physCount * sizeof(LiveRegSpans));
if (ASMJIT_UNLIKELY(!liveSpans))
return DebugUtils::errored(kErrorOutOfMemory);
for (size_t physId = 0; physId < physCount; physId++)
new(&liveSpans[physId]) LiveRegSpans();
}
_globalLiveSpans[group] = liveSpans;
}
@@ -1183,7 +1153,7 @@ ASMJIT_FAVOR_SPEED Error RAPass::initGlobalLiveSpans() noexcept {
return kErrorOk;
}
ASMJIT_FAVOR_SPEED Error RAPass::binPack(uint32_t group) noexcept {
ASMJIT_FAVOR_SPEED Error BaseRAPass::binPack(uint32_t group) noexcept {
if (workRegCount(group) == 0)
return kErrorOk;
@@ -1323,10 +1293,10 @@ ASMJIT_FAVOR_SPEED Error RAPass::binPack(uint32_t group) noexcept {
}
// ============================================================================
// [asmjit::RAPass - Allocation - Local]
// [asmjit::BaseRAPass - Allocation - Local]
// ============================================================================
Error RAPass::runLocalAllocator() noexcept {
Error BaseRAPass::runLocalAllocator() noexcept {
RALocalAllocator lra(this);
ASMJIT_PROPAGATE(lra.init());
@@ -1396,7 +1366,7 @@ Error RAPass::runLocalAllocator() noexcept {
ASMJIT_PROPAGATE(lra.allocInst(inst));
if (inst->type() == BaseNode::kNodeInvoke)
ASMJIT_PROPAGATE(onEmitPreCall(inst->as<InvokeNode>()));
ASMJIT_PROPAGATE(emitPreCall(inst->as<InvokeNode>()));
else
ASMJIT_PROPAGATE(lra.spillAfterAllocation(inst));
}
@@ -1458,7 +1428,7 @@ Error RAPass::runLocalAllocator() noexcept {
return kErrorOk;
}
Error RAPass::setBlockEntryAssignment(RABlock* block, const RABlock* fromBlock, const RAAssignment& fromAssignment) noexcept {
Error BaseRAPass::setBlockEntryAssignment(RABlock* block, const RABlock* fromBlock, const RAAssignment& fromAssignment) noexcept {
if (block->hasSharedAssignmentId()) {
uint32_t sharedAssignmentId = block->sharedAssignmentId();
@@ -1514,7 +1484,7 @@ Error RAPass::setBlockEntryAssignment(RABlock* block, const RABlock* fromBlock,
return blockEntryAssigned(as);
}
Error RAPass::setSharedAssignment(uint32_t sharedAssignmentId, const RAAssignment& fromAssignment) noexcept {
Error BaseRAPass::setSharedAssignment(uint32_t sharedAssignmentId, const RAAssignment& fromAssignment) noexcept {
ASMJIT_ASSERT(_sharedAssignments[sharedAssignmentId].empty());
PhysToWorkMap* physToWorkMap = clonePhysToWorkMap(fromAssignment.physToWorkMap());
@@ -1582,7 +1552,7 @@ Error RAPass::setSharedAssignment(uint32_t sharedAssignmentId, const RAAssignmen
return blockEntryAssigned(as);
}
Error RAPass::blockEntryAssigned(const RAAssignment& as) noexcept {
Error BaseRAPass::blockEntryAssigned(const RAAssignment& as) noexcept {
// Complex allocation strategy requires to record register assignments upon
// block entry (or per shared state).
for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) {
@@ -1603,10 +1573,10 @@ Error RAPass::blockEntryAssigned(const RAAssignment& as) noexcept {
}
// ============================================================================
// [asmjit::RAPass - Allocation - Utilities]
// [asmjit::BaseRAPass - Allocation - Utilities]
// ============================================================================
Error RAPass::useTemporaryMem(BaseMem& out, uint32_t size, uint32_t alignment) noexcept {
Error BaseRAPass::useTemporaryMem(BaseMem& out, uint32_t size, uint32_t alignment) noexcept {
ASMJIT_ASSERT(alignment <= 64);
if (_temporaryMem.isNone()) {
@@ -1627,10 +1597,10 @@ Error RAPass::useTemporaryMem(BaseMem& out, uint32_t size, uint32_t alignment) n
}
// ============================================================================
// [asmjit::RAPass - Allocation - Prolog / Epilog]
// [asmjit::BaseRAPass - Allocation - Prolog / Epilog]
// ============================================================================
Error RAPass::updateStackFrame() noexcept {
Error BaseRAPass::updateStackFrame() noexcept {
// Update some StackFrame information that we updated during allocation. The
// only information we don't have at the moment is final local stack size,
// which is calculated last.
@@ -1668,7 +1638,7 @@ Error RAPass::updateStackFrame() noexcept {
return kErrorOk;
}
Error RAPass::_markStackArgsToKeep() noexcept {
Error BaseRAPass::_markStackArgsToKeep() noexcept {
FuncFrame& frame = func()->frame();
bool hasSAReg = frame.hasPreservedFP() || !frame.hasDynamicAlignment();
@@ -1707,7 +1677,7 @@ Error RAPass::_markStackArgsToKeep() noexcept {
return kErrorOk;
}
Error RAPass::_updateStackArgs() noexcept {
Error BaseRAPass::_updateStackArgs() noexcept {
FuncFrame& frame = func()->frame();
RAWorkRegs& workRegs = _workRegs;
uint32_t numWorkRegs = workRegCount();
@@ -1741,12 +1711,12 @@ Error RAPass::_updateStackArgs() noexcept {
return kErrorOk;
}
Error RAPass::insertPrologEpilog() noexcept {
Error BaseRAPass::insertPrologEpilog() noexcept {
FuncFrame& frame = _func->frame();
cc()->_setCursor(func());
ASMJIT_PROPAGATE(cc()->emitProlog(frame));
ASMJIT_PROPAGATE(cc()->emitArgsAssignment(frame, _argsAssignment));
ASMJIT_PROPAGATE(_iEmitHelper->emitArgsAssignment(frame, _argsAssignment));
cc()->_setCursor(func()->exitNode());
ASMJIT_PROPAGATE(cc()->emitEpilog(frame));
@@ -1755,10 +1725,10 @@ Error RAPass::insertPrologEpilog() noexcept {
}
// ============================================================================
// [asmjit::RAPass - Rewriter]
// [asmjit::BaseRAPass - Rewriter]
// ============================================================================
Error RAPass::rewrite() noexcept {
Error BaseRAPass::rewrite() noexcept {
#ifndef ASMJIT_NO_LOGGING
Logger* logger = debugLogger();
ASMJIT_RA_LOG_FORMAT("[RAPass::Rewrite]\n");
@@ -1767,7 +1737,7 @@ Error RAPass::rewrite() noexcept {
return _rewrite(_func, _stop);
}
ASMJIT_FAVOR_SPEED Error RAPass::_rewrite(BaseNode* first, BaseNode* stop) noexcept {
ASMJIT_FAVOR_SPEED Error BaseRAPass::_rewrite(BaseNode* first, BaseNode* stop) noexcept {
uint32_t virtCount = cc()->_vRegArray.size();
BaseNode* node = first;
@@ -1814,7 +1784,7 @@ ASMJIT_FAVOR_SPEED Error RAPass::_rewrite(BaseNode* first, BaseNode* stop) noexc
RABlock* block = raInst->block();
if (!isNextTo(node, _func->exitNode())) {
cc()->_setCursor(node->prev());
ASMJIT_PROPAGATE(onEmitJump(_func->exitNode()->label()));
ASMJIT_PROPAGATE(emitJump(_func->exitNode()->label()));
}
BaseNode* prev = node->prev();
@@ -1856,11 +1826,11 @@ ASMJIT_FAVOR_SPEED Error RAPass::_rewrite(BaseNode* first, BaseNode* stop) noexc
}
// ============================================================================
// [asmjit::RAPass - Logging]
// [asmjit::BaseRAPass - Logging]
// ============================================================================
#ifndef ASMJIT_NO_LOGGING
static void RAPass_dumpRAInst(RAPass* pass, String& sb, const RAInst* raInst) noexcept {
static void RAPass_dumpRAInst(BaseRAPass* pass, String& sb, const RAInst* raInst) noexcept {
const RATiedReg* tiedRegs = raInst->tiedRegs();
uint32_t tiedCount = raInst->tiedCount();
@@ -1895,7 +1865,7 @@ static void RAPass_dumpRAInst(RAPass* pass, String& sb, const RAInst* raInst) no
}
}
ASMJIT_FAVOR_SIZE Error RAPass::annotateCode() noexcept {
ASMJIT_FAVOR_SIZE Error BaseRAPass::annotateCode() noexcept {
uint32_t loggerFlags = _loggerFlags;
StringTmp<1024> sb;
@@ -1930,7 +1900,7 @@ ASMJIT_FAVOR_SIZE Error RAPass::annotateCode() noexcept {
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error RAPass::_dumpBlockIds(String& sb, const RABlocks& blocks) noexcept {
ASMJIT_FAVOR_SIZE Error BaseRAPass::_dumpBlockIds(String& sb, const RABlocks& blocks) noexcept {
for (uint32_t i = 0, size = blocks.size(); i < size; i++) {
const RABlock* block = blocks[i];
if (i != 0)
@@ -1941,7 +1911,7 @@ ASMJIT_FAVOR_SIZE Error RAPass::_dumpBlockIds(String& sb, const RABlocks& blocks
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error RAPass::_dumpBlockLiveness(String& sb, const RABlock* block) noexcept {
ASMJIT_FAVOR_SIZE Error BaseRAPass::_dumpBlockLiveness(String& sb, const RABlock* block) noexcept {
for (uint32_t liveType = 0; liveType < RABlock::kLiveCount; liveType++) {
const char* bitsName = liveType == RABlock::kLiveIn ? "IN " :
liveType == RABlock::kLiveOut ? "OUT " :
@@ -1973,7 +1943,7 @@ ASMJIT_FAVOR_SIZE Error RAPass::_dumpBlockLiveness(String& sb, const RABlock* bl
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error RAPass::_dumpLiveSpans(String& sb) noexcept {
ASMJIT_FAVOR_SIZE Error BaseRAPass::_dumpLiveSpans(String& sb) noexcept {
uint32_t numWorkRegs = _workRegs.size();
uint32_t maxSize = _maxWorkRegNameSize;

View File

@@ -27,6 +27,8 @@
#include "../core/api-config.h"
#ifndef ASMJIT_NO_COMPILER
#include "../core/compiler.h"
#include "../core/emithelper_p.h"
#include "../core/raassignment_p.h"
#include "../core/radefs_p.h"
#include "../core/rastack_p.h"
@@ -80,42 +82,42 @@ public:
};
//! Register allocator pass.
RAPass* _ra;
BaseRAPass* _ra;
//! Block id (indexed from zero).
uint32_t _blockId;
uint32_t _blockId = kUnassignedId;
//! Block flags, see `Flags`.
uint32_t _flags;
uint32_t _flags = 0;
//! First `BaseNode` of this block (inclusive).
BaseNode* _first;
BaseNode* _first = nullptr;
//! Last `BaseNode` of this block (inclusive).
BaseNode* _last;
BaseNode* _last = nullptr;
//! Initial position of this block (inclusive).
uint32_t _firstPosition;
uint32_t _firstPosition = 0;
//! End position of this block (exclusive).
uint32_t _endPosition;
uint32_t _endPosition = 0;
//! Weight of this block (default 0, each loop adds one).
uint32_t _weight;
uint32_t _weight = 0;
//! Post-order view order, used during POV construction.
uint32_t _povOrder;
uint32_t _povOrder = 0;
//! Basic statistics about registers.
RARegsStats _regsStats;
RARegsStats _regsStats = RARegsStats();
//! Maximum live-count per register group.
RALiveCount _maxLiveCount;
RALiveCount _maxLiveCount = RALiveCount();
//! Timestamp (used by block visitors).
mutable uint64_t _timestamp;
mutable uint64_t _timestamp = 0;
//! Immediate dominator of this block.
RABlock* _idom;
RABlock* _idom = nullptr;
//! Block predecessors.
RABlocks _predecessors;
RABlocks _predecessors {};
//! Block successors.
RABlocks _successors;
RABlocks _successors {};
enum LiveType : uint32_t {
kLiveIn = 0,
@@ -126,52 +128,33 @@ public:
};
//! Liveness in/out/use/kill.
ZoneBitVector _liveBits[kLiveCount];
ZoneBitVector _liveBits[kLiveCount] {};
//! Shared assignment it or `Globals::kInvalidId` if this block doesn't
//! have shared assignment. See `RASharedAssignment` for more details.
uint32_t _sharedAssignmentId;
uint32_t _sharedAssignmentId = Globals::kInvalidId;
//! Scratch registers that cannot be allocated upon block entry.
uint32_t _entryScratchGpRegs;
uint32_t _entryScratchGpRegs = 0;
//! Scratch registers used at exit, by a terminator instruction.
uint32_t _exitScratchGpRegs;
uint32_t _exitScratchGpRegs = 0;
//! Register assignment (PhysToWork) on entry.
PhysToWorkMap* _entryPhysToWorkMap;
PhysToWorkMap* _entryPhysToWorkMap = nullptr;
//! Register assignment (WorkToPhys) on entry.
WorkToPhysMap* _entryWorkToPhysMap;
WorkToPhysMap* _entryWorkToPhysMap = nullptr;
//! \name Construction & Destruction
//! \{
inline RABlock(RAPass* ra) noexcept
: _ra(ra),
_blockId(kUnassignedId),
_flags(0),
_first(nullptr),
_last(nullptr),
_firstPosition(0),
_endPosition(0),
_weight(0),
_povOrder(kUnassignedId),
_regsStats(),
_maxLiveCount(),
_timestamp(0),
_idom(nullptr),
_predecessors(),
_successors(),
_sharedAssignmentId(Globals::kInvalidId),
_entryScratchGpRegs(0),
_exitScratchGpRegs(0),
_entryPhysToWorkMap(nullptr),
_entryWorkToPhysMap(nullptr) {}
inline RABlock(BaseRAPass* ra) noexcept
: _ra(ra) {}
//! \}
//! \name Accessors
//! \{
inline RAPass* pass() const noexcept { return _ra; }
inline BaseRAPass* pass() const noexcept { return _ra; }
inline ZoneAllocator* allocator() const noexcept;
inline uint32_t blockId() const noexcept { return _blockId; }
@@ -620,21 +603,16 @@ public:
//! ISA limits (like jecx/loop instructions on x86) or because the registers
//! are used by jump/branch instruction that uses registers to perform an
//! indirect jump.
uint32_t _entryScratchGpRegs;
uint32_t _entryScratchGpRegs = 0;
//! Union of all live-in registers.
ZoneBitVector _liveIn;
ZoneBitVector _liveIn {};
//! Register assignment (PhysToWork).
PhysToWorkMap* _physToWorkMap;
PhysToWorkMap* _physToWorkMap = nullptr;
//! Register assignment (WorkToPhys).
WorkToPhysMap* _workToPhysMap;
WorkToPhysMap* _workToPhysMap = nullptr;
//! Provided for clarity, most likely never called as we initialize a vector
//! of shared assignments to zero.
inline RASharedAssignment() noexcept
: _entryScratchGpRegs(0),
_liveIn(),
_physToWorkMap(nullptr),
_workToPhysMap(nullptr) {}
//! Most likely never called as we initialize a vector of shared assignments to zero.
inline RASharedAssignment() noexcept {}
inline uint32_t entryScratchGpRegs() const noexcept { return _entryScratchGpRegs; }
inline void addEntryScratchGpRegs(uint32_t mask) noexcept { _entryScratchGpRegs |= mask; }
@@ -655,13 +633,13 @@ public:
};
// ============================================================================
// [asmjit::RAPass]
// [asmjit::BaseRAPass]
// ============================================================================
//! Register allocation pass used by `BaseCompiler`.
class RAPass : public FuncPass {
class BaseRAPass : public FuncPass {
public:
ASMJIT_NONCOPYABLE(RAPass)
ASMJIT_NONCOPYABLE(BaseRAPass)
typedef FuncPass Base;
enum Weights : uint32_t {
@@ -672,58 +650,59 @@ public:
typedef RAAssignment::WorkToPhysMap WorkToPhysMap;
//! Allocator that uses zone passed to `runOnFunction()`.
ZoneAllocator _allocator;
ZoneAllocator _allocator {};
//! Emit helper.
BaseEmitHelper* _iEmitHelper = nullptr;
//! Logger, disabled if null.
Logger* _logger;
Logger* _logger = nullptr;
//! Debug logger, non-null only if `kOptionDebugPasses` option is set.
Logger* _debugLogger;
Logger* _debugLogger = nullptr;
//! Logger flags.
uint32_t _loggerFlags;
uint32_t _loggerFlags = 0;
//! Function being processed.
FuncNode* _func;
FuncNode* _func = nullptr;
//! Stop node.
BaseNode* _stop;
BaseNode* _stop = nullptr;
//! Node that is used to insert extra code after the function body.
BaseNode* _extraBlock;
BaseNode* _extraBlock = nullptr;
//! Blocks (first block is the entry, always exists).
RABlocks _blocks;
RABlocks _blocks {};
//! Function exit blocks (usually one, but can contain more).
RABlocks _exits;
RABlocks _exits {};
//! Post order view (POV).
RABlocks _pov;
RABlocks _pov {};
//! Number of instruction nodes.
uint32_t _instructionCount;
uint32_t _instructionCount = 0;
//! Number of created blocks (internal).
uint32_t _createdBlockCount;
uint32_t _createdBlockCount = 0;
//! SharedState blocks.
ZoneVector<RASharedAssignment> _sharedAssignments;
ZoneVector<RASharedAssignment> _sharedAssignments {};
//! Timestamp generator (incremental).
mutable uint64_t _lastTimestamp;
mutable uint64_t _lastTimestamp = 0;
//!< Architecture registers information.
const ArchRegs* _archRegsInfo;
//! Architecture traits.
RAArchTraits _archTraits;
const ArchTraits* _archTraits = nullptr;
//! Index to physical registers in `RAAssignment::PhysToWorkMap`.
RARegIndex _physRegIndex;
RARegIndex _physRegIndex = RARegIndex();
//! Count of physical registers in `RAAssignment::PhysToWorkMap`.
RARegCount _physRegCount;
RARegCount _physRegCount = RARegCount();
//! Total number of physical registers.
uint32_t _physRegTotal;
uint32_t _physRegTotal = 0;
//! Indexes of a possible scratch registers that can be selected if necessary.
uint8_t _scratchRegIndexes[2];
uint8_t _scratchRegIndexes[2] {};
//! Registers available for allocation.
RARegMask _availableRegs;
RARegMask _availableRegs = RARegMask();
//! Count of physical registers per group.
RARegCount _availableRegCount;
RARegCount _availableRegCount = RARegCount();
//! Registers clobbered by the function.
RARegMask _clobberedRegs;
RARegMask _clobberedRegs = RARegMask();
//! Work registers (registers used by the function).
RAWorkRegs _workRegs;
@@ -733,33 +712,33 @@ public:
//! Register allocation strategy per register group.
RAStrategy _strategy[BaseReg::kGroupVirt];
//! Global max live-count (from all blocks) per register group.
RALiveCount _globalMaxLiveCount;
RALiveCount _globalMaxLiveCount = RALiveCount();
//! Global live spans per register group.
LiveRegSpans* _globalLiveSpans[BaseReg::kGroupVirt];
LiveRegSpans* _globalLiveSpans[BaseReg::kGroupVirt] {};
//! Temporary stack slot.
Operand _temporaryMem;
Operand _temporaryMem = Operand();
//! Stack pointer.
BaseReg _sp;
BaseReg _sp = BaseReg();
//! Frame pointer.
BaseReg _fp;
BaseReg _fp = BaseReg();
//! Stack manager.
RAStackAllocator _stackAllocator;
RAStackAllocator _stackAllocator {};
//! Function arguments assignment.
FuncArgsAssignment _argsAssignment;
FuncArgsAssignment _argsAssignment {};
//! Some StackArgs have to be assigned to StackSlots.
uint32_t _numStackArgsToStackSlots;
uint32_t _numStackArgsToStackSlots = 0;
//! Maximum name-size computed from all WorkRegs.
uint32_t _maxWorkRegNameSize;
uint32_t _maxWorkRegNameSize = 0;
//! Temporary string builder used to format comments.
StringTmp<80> _tmpString;
//! \name Construction & Reset
//! \{
RAPass() noexcept;
virtual ~RAPass() noexcept;
BaseRAPass() noexcept;
virtual ~BaseRAPass() noexcept;
//! \}
@@ -949,7 +928,7 @@ public:
//! information that is essential for further analysis and register
//! allocation.
//!
//! Use `RACFGBuilder` template that provides the necessary boilerplate.
//! Use `RACFGBuilderT` template that provides the necessary boilerplate.
virtual Error buildCFG() noexcept = 0;
//! Called after the CFG is built.
@@ -1135,7 +1114,7 @@ public:
//! \name Function Prolog & Epilog
//! \{
Error updateStackFrame() noexcept;
virtual Error updateStackFrame() noexcept;
Error _markStackArgsToKeep() noexcept;
Error _updateStackArgs() noexcept;
Error insertPrologEpilog() noexcept;
@@ -1166,14 +1145,14 @@ public:
//! \name Emit
//! \{
virtual Error onEmitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept = 0;
virtual Error onEmitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept = 0;
virtual Error emitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept = 0;
virtual Error emitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept = 0;
virtual Error onEmitLoad(uint32_t workId, uint32_t dstPhysId) noexcept = 0;
virtual Error onEmitSave(uint32_t workId, uint32_t srcPhysId) noexcept = 0;
virtual Error emitLoad(uint32_t workId, uint32_t dstPhysId) noexcept = 0;
virtual Error emitSave(uint32_t workId, uint32_t srcPhysId) noexcept = 0;
virtual Error onEmitJump(const Label& label) noexcept = 0;
virtual Error onEmitPreCall(InvokeNode* invokeNode) noexcept = 0;
virtual Error emitJump(const Label& label) noexcept = 0;
virtual Error emitPreCall(InvokeNode* invokeNode) noexcept = 0;
//! \}
};

View File

@@ -271,63 +271,96 @@ static constexpr T fillTrailingBits(const T& x) noexcept {
//! \cond
namespace Internal {
static constexpr uint32_t constCtzImpl(uint32_t xAndNegX) noexcept {
return 31 - ((xAndNegX & 0x0000FFFFu) ? 16 : 0)
- ((xAndNegX & 0x00FF00FFu) ? 8 : 0)
- ((xAndNegX & 0x0F0F0F0Fu) ? 4 : 0)
- ((xAndNegX & 0x33333333u) ? 2 : 0)
- ((xAndNegX & 0x55555555u) ? 1 : 0);
namespace {
template<typename T>
struct BitScanData { T x; uint32_t n; };
template<typename T, uint32_t N>
struct BitScanCalc {
static constexpr BitScanData<T> advanceLeft(const BitScanData<T>& data, uint32_t n) noexcept {
return BitScanData<T> { data.x << n, data.n + n };
}
static constexpr uint32_t constCtzImpl(uint64_t xAndNegX) noexcept {
return 63 - ((xAndNegX & 0x00000000FFFFFFFFu) ? 32 : 0)
- ((xAndNegX & 0x0000FFFF0000FFFFu) ? 16 : 0)
- ((xAndNegX & 0x00FF00FF00FF00FFu) ? 8 : 0)
- ((xAndNegX & 0x0F0F0F0F0F0F0F0Fu) ? 4 : 0)
- ((xAndNegX & 0x3333333333333333u) ? 2 : 0)
- ((xAndNegX & 0x5555555555555555u) ? 1 : 0);
static constexpr BitScanData<T> advanceRight(const BitScanData<T>& data, uint32_t n) noexcept {
return BitScanData<T> { data.x >> n, data.n + n };
}
template<typename T>
static constexpr uint32_t constCtz(T x) noexcept {
return constCtzImpl(x & neg(x));
static constexpr BitScanData<T> clz(const BitScanData<T>& data) noexcept {
return BitScanCalc<T, N / 2>::clz(advanceLeft(data, data.x & (allOnes<T>() << (bitSizeOf<T>() - N)) ? uint32_t(0) : N));
}
static ASMJIT_INLINE uint32_t ctz(uint32_t x) noexcept {
#if defined(__GNUC__)
return uint32_t(__builtin_ctz(x));
#elif defined(_MSC_VER) && (ASMJIT_ARCH_X86 || ASMJIT_ARCH_ARM)
unsigned long i;
_BitScanForward(&i, x);
return uint32_t(i);
#else
return constCtz(x);
#endif
static constexpr BitScanData<T> ctz(const BitScanData<T>& data) noexcept {
return BitScanCalc<T, N / 2>::ctz(advanceRight(data, data.x & (allOnes<T>() >> (bitSizeOf<T>() - N)) ? uint32_t(0) : N));
}
};
template<typename T>
struct BitScanCalc<T, 0> {
static constexpr BitScanData<T> clz(const BitScanData<T>& ctx) noexcept {
return BitScanData<T> { 0, ctx.n - uint32_t(ctx.x >> (bitSizeOf<T>() - 1)) };
}
static ASMJIT_INLINE uint32_t ctz(uint64_t x) noexcept {
#if defined(__GNUC__)
return uint32_t(__builtin_ctzll(x));
#elif defined(_MSC_VER) && (ASMJIT_ARCH_X86 == 64 || ASMJIT_ARCH_ARM == 64)
unsigned long i;
_BitScanForward64(&i, x);
return uint32_t(i);
#else
return constCtz(x);
#endif
static constexpr BitScanData<T> ctz(const BitScanData<T>& ctx) noexcept {
return BitScanData<T> { 0, ctx.n - uint32_t(ctx.x & 0x1) };
}
};
template<typename T>
constexpr uint32_t clzFallback(const T& x) noexcept {
return BitScanCalc<T, bitSizeOf<T>() / 2u>::clz(BitScanData<T>{x, 1}).n;
}
template<typename T>
constexpr uint32_t ctzFallback(const T& x) noexcept {
return BitScanCalc<T, bitSizeOf<T>() / 2u>::ctz(BitScanData<T>{x, 1}).n;
}
template<typename T> constexpr uint32_t constClz(const T& x) noexcept { return clzFallback(asUInt(x)); }
template<typename T> constexpr uint32_t constCtz(const T& x) noexcept { return ctzFallback(asUInt(x)); }
template<typename T> inline uint32_t clzImpl(const T& x) noexcept { return constClz(x); }
template<typename T> inline uint32_t ctzImpl(const T& x) noexcept { return constCtz(x); }
#if !defined(ASMJIT_NO_INTRINSICS)
# if defined(__GNUC__)
template<> inline uint32_t clzImpl(const uint32_t& x) noexcept { return uint32_t(__builtin_clz(x)); }
template<> inline uint32_t clzImpl(const uint64_t& x) noexcept { return uint32_t(__builtin_clzll(x)); }
template<> inline uint32_t ctzImpl(const uint32_t& x) noexcept { return uint32_t(__builtin_ctz(x)); }
template<> inline uint32_t ctzImpl(const uint64_t& x) noexcept { return uint32_t(__builtin_ctzll(x)); }
# elif defined(_MSC_VER)
template<> inline uint32_t clzImpl(const uint32_t& x) noexcept { unsigned long i; _BitScanReverse(&i, x); return uint32_t(i ^ 31); }
template<> inline uint32_t ctzImpl(const uint32_t& x) noexcept { unsigned long i; _BitScanForward(&i, x); return uint32_t(i); }
# if ASMJIT_ARCH_X86 == 64 || ASMJIT_ARCH_ARM == 64
template<> inline uint32_t clzImpl(const uint64_t& x) noexcept { unsigned long i; _BitScanReverse64(&i, x); return uint32_t(i ^ 63); }
template<> inline uint32_t ctzImpl(const uint64_t& x) noexcept { unsigned long i; _BitScanForward64(&i, x); return uint32_t(i); }
# endif
# endif
#endif
} // {anonymous}
} // {Internal}
//! \endcond
//! Count leading zeros in `x` (returns a position of a first bit set in `x`).
//!
//! \note The input MUST NOT be zero, otherwise the result is undefined.
template<typename T>
static inline uint32_t clz(T x) noexcept { return Internal::clzImpl(asUInt(x)); }
//! Count leading zeros in `x` (constant expression).
template<typename T>
static constexpr inline uint32_t constClz(T x) noexcept { return Internal::constClz(asUInt(x)); }
//! Count trailing zeros in `x` (returns a position of a first bit set in `x`).
//!
//! \note The input MUST NOT be zero, otherwise the result is undefined.
template<typename T>
static inline uint32_t ctz(T x) noexcept { return Internal::ctz(asUInt(x)); }
static inline uint32_t ctz(T x) noexcept { return Internal::ctzImpl(asUInt(x)); }
//! Count trailing zeros in `x` (constant expression).
template<typename T>
static constexpr uint32_t constCtz(T x) noexcept { return Internal::constCtz(asUInt(x)); }
static constexpr inline uint32_t constCtz(T x) noexcept { return Internal::constCtz(asUInt(x)); }
// ============================================================================
// [asmjit::Support - PopCnt]
@@ -410,6 +443,30 @@ static constexpr T max(const T& a, const T& b) noexcept { return a < b ? b : a;
template<typename T, typename... Args>
static constexpr T max(const T& a, const T& b, Args&&... args) noexcept { return max(max(a, b), std::forward<Args>(args)...); }
// ============================================================================
// [asmjit::Support - Immediate Helpers]
// ============================================================================
namespace Internal {
template<typename T, bool IsFloat>
struct ImmConv {
static inline int64_t fromT(const T& x) noexcept { return int64_t(x); }
static inline T toT(int64_t x) noexcept { return T(uint64_t(x) & Support::allOnes<typename std::make_unsigned<T>::type>()); }
};
template<typename T>
struct ImmConv<T, true> {
static inline int64_t fromT(const T& x) noexcept { return int64_t(bitCast<int64_t>(double(x))); }
static inline T toT(int64_t x) noexcept { return T(bitCast<double>(x)); }
};
}
template<typename T>
static inline int64_t immediateFromT(const T& x) noexcept { return Internal::ImmConv<T, std::is_floating_point<T>::value>::fromT(x); }
template<typename T>
static inline T immediateToT(int64_t x) noexcept { return Internal::ImmConv<T, std::is_floating_point<T>::value>::toT(x); }
// ============================================================================
// [asmjit::Support - Overflow Arithmetic]
// ============================================================================
@@ -601,8 +658,16 @@ static constexpr bool isInt4(T x) noexcept {
typedef typename std::make_signed<T>::type S;
typedef typename std::make_unsigned<T>::type U;
return std::is_signed<T>::value ? isBetween<S>(S(x), -8, 7)
: U(x) <= U(7u);
return std::is_signed<T>::value ? isBetween<S>(S(x), -8, 7) : U(x) <= U(7u);
}
//! Checks whether the given integer `x` can be casted to a 7-bit signed integer.
template<typename T>
static constexpr bool isInt7(T x) noexcept {
typedef typename std::make_signed<T>::type S;
typedef typename std::make_unsigned<T>::type U;
return std::is_signed<T>::value ? isBetween<S>(S(x), -64, 63) : U(x) <= U(63u);
}
//! Checks whether the given integer `x` can be casted to an 8-bit signed integer.
@@ -611,8 +676,27 @@ static constexpr bool isInt8(T x) noexcept {
typedef typename std::make_signed<T>::type S;
typedef typename std::make_unsigned<T>::type U;
return std::is_signed<T>::value ? sizeof(T) <= 1 || isBetween<S>(S(x), -128, 127)
: U(x) <= U(127u);
return std::is_signed<T>::value ? sizeof(T) <= 1 || isBetween<S>(S(x), -128, 127) : U(x) <= U(127u);
}
//! Checks whether the given integer `x` can be casted to a 9-bit signed integer.
template<typename T>
static constexpr bool isInt9(T x) noexcept {
typedef typename std::make_signed<T>::type S;
typedef typename std::make_unsigned<T>::type U;
return std::is_signed<T>::value ? sizeof(T) <= 1 || isBetween<S>(S(x), -256, 255)
: sizeof(T) <= 1 || U(x) <= U(255u);
}
//! Checks whether the given integer `x` can be casted to a 10-bit signed integer.
template<typename T>
static constexpr bool isInt10(T x) noexcept {
typedef typename std::make_signed<T>::type S;
typedef typename std::make_unsigned<T>::type U;
return std::is_signed<T>::value ? sizeof(T) <= 1 || isBetween<S>(S(x), -512, 511)
: sizeof(T) <= 1 || U(x) <= U(511u);
}
//! Checks whether the given integer `x` can be casted to a 16-bit signed integer.
@@ -686,6 +770,16 @@ static constexpr bool isIntOrUInt32(T x) noexcept {
return sizeof(T) <= 4 ? true : (uint32_t(uint64_t(x) >> 32) + 1u) <= 1u;
}
static bool inline isEncodableOffset32(int32_t offset, uint32_t nBits) noexcept {
uint32_t nRev = 32 - nBits;
return Support::sar(Support::shl(offset, nRev), nRev) == offset;
}
static bool inline isEncodableOffset64(int64_t offset, uint32_t nBits) noexcept {
uint32_t nRev = 64 - nBits;
return Support::sar(Support::shl(offset, nRev), nRev) == offset;
}
// ============================================================================
// [asmjit::Support - ByteSwap]
// ============================================================================

View File

@@ -24,7 +24,7 @@
#ifndef ASMJIT_CORE_TARGET_H_INCLUDED
#define ASMJIT_CORE_TARGET_H_INCLUDED
#include "../core/arch.h"
#include "../core/archtraits.h"
#include "../core/func.h"
ASMJIT_BEGIN_NAMESPACE

View File

@@ -78,11 +78,11 @@ struct SizeOfTypeId {
};
const TypeData _typeData = {
#define VALUE(X) BaseOfTypeId<X>::kTypeId
#define VALUE(x) BaseOfTypeId<x>::kTypeId
{ ASMJIT_LOOKUP_TABLE_256(VALUE, 0) },
#undef VALUE
#define VALUE(X) SizeOfTypeId<X>::kTypeSize
#define VALUE(x) SizeOfTypeId<x>::kTypeSize
{ ASMJIT_LOOKUP_TABLE_256(VALUE, 0) }
#undef VALUE
};

View File

@@ -46,21 +46,18 @@ public:
typedef ptrdiff_t difference_type;
//! Vector data (untyped).
void* _data;
void* _data = nullptr;
//! Size of the vector.
size_type _size;
size_type _size = 0;
//! Capacity of the vector.
size_type _capacity;
size_type _capacity = 0;
protected:
//! \name Construction & Destruction
//! \{
//! Creates a new instance of `ZoneVectorBase`.
inline ZoneVectorBase() noexcept
: _data(nullptr),
_size(0),
_capacity(0) {}
inline ZoneVectorBase() noexcept {}
inline ZoneVectorBase(ZoneVectorBase&& other) noexcept
: _data(other._data),
@@ -436,11 +433,11 @@ public:
static constexpr uint32_t kBitWordSizeInBits = Support::kBitWordSizeInBits;
//! Bits.
BitWord* _data;
BitWord* _data = nullptr;
//! Size of the bit-vector (in bits).
uint32_t _size;
uint32_t _size = 0;
//! Capacity of the bit-vector (in bits).
uint32_t _capacity;
uint32_t _capacity = 0;
ASMJIT_NONCOPYABLE(ZoneBitVector)
@@ -473,10 +470,7 @@ public:
//! \name Construction & Destruction
//! \{
inline ZoneBitVector() noexcept
: _data(nullptr),
_size(0),
_capacity(0) {}
inline ZoneBitVector() noexcept {}
inline ZoneBitVector(ZoneBitVector&& other) noexcept
: _data(other._data),

View File

@@ -45,6 +45,7 @@
//! 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.
@@ -74,7 +75,7 @@
//! ### Memory Operands
//!
//! - \ref x86::Mem - X86/X64 memory operand that provides support for all
//! X86 and X64 addressing features, including absolute addresses, index
//! X86 and X64 addressing features including absolute addresses, index
//! scales, and segment override prefixes.
//!
//! ### Other

View File

@@ -1,137 +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"
#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

View 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.
#ifndef ASMJIT_X86_X86ARCHTRAITS_P_H_INCLUDED
#define ASMJIT_X86_X86ARCHTRAITS_P_H_INCLUDED
#include "../core/archtraits.h"
#include "../core/misc_p.h"
#include "../x86/x86operand.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! \cond INTERNAL
//! \addtogroup asmjit_x86
//! \{
// ============================================================================
// [asmjit::x86::x86ArchTraits
// ============================================================================
static const constexpr ArchTraits x86ArchTraits = {
Gp::kIdSp, // SP.
Gp::kIdBp, // FP.
0xFF, // LR.
0xFF, // PC.
{ 0, 0, 0 }, // Reserved.
1, // HW stack alignment.
0x7FFFFFFFu, // Min stack offset.
0x7FFFFFFFu, // Max stack offset.
// ISA features [Gp, Vec, Other0, Other1].
{
ArchTraits::kIsaFeatureSwap | ArchTraits::kIsaFeaturePushPop,
0,
0,
0
},
// RegInfo.
#define V(index) { x86::RegTraits<index>::kSignature }
{ ASMJIT_LOOKUP_TABLE_32(V, 0) },
#undef V
// RegTypeToTypeId.
#define V(index) x86::RegTraits<index>::kTypeId
{ ASMJIT_LOOKUP_TABLE_32(V, 0) },
#undef V
// TypeIdToRegType.
#define V(index) (index + Type::_kIdBaseStart == Type::kIdI8 ? Reg::kTypeGpbLo : \
index + Type::_kIdBaseStart == Type::kIdU8 ? Reg::kTypeGpbLo : \
index + Type::_kIdBaseStart == Type::kIdI16 ? Reg::kTypeGpw : \
index + Type::_kIdBaseStart == Type::kIdU16 ? Reg::kTypeGpw : \
index + Type::_kIdBaseStart == Type::kIdI32 ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdU32 ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdIntPtr ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdUIntPtr ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdF32 ? Reg::kTypeXmm : \
index + Type::_kIdBaseStart == Type::kIdF64 ? Reg::kTypeXmm : \
index + Type::_kIdBaseStart == Type::kIdMask8 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask16 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask32 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask64 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMmx32 ? Reg::kTypeMm : \
index + Type::_kIdBaseStart == Type::kIdMmx64 ? Reg::kTypeMm : Reg::kTypeNone)
{ ASMJIT_LOOKUP_TABLE_32(V, 0) }
#undef V
};
// ============================================================================
// [asmjit::x86::x64ArchTraits
// ============================================================================
static const constexpr ArchTraits x64ArchTraits = {
Gp::kIdSp, // SP.
Gp::kIdBp, // FP.
0xFF, // LR.
0xFF, // PC.
{ 0, 0, 0 }, // Reserved.
1, // HW stack alignment.
0x7FFFFFFFu, // Min stack offset.
0x7FFFFFFFu, // Max stack offset.
// ISA features [Gp, Vec, Other0, Other1].
{
ArchTraits::kIsaFeatureSwap | ArchTraits::kIsaFeaturePushPop,
0,
0,
0
},
// RegInfo.
#define V(index) { x86::RegTraits<index>::kSignature }
{ ASMJIT_LOOKUP_TABLE_32(V, 0) },
#undef V
// RegTypeToTypeId.
#define V(index) x86::RegTraits<index>::kTypeId
{ ASMJIT_LOOKUP_TABLE_32(V, 0) },
#undef V
// TypeIdToRegType.
#define V(index) (index + Type::_kIdBaseStart == Type::kIdI8 ? Reg::kTypeGpbLo : \
index + Type::_kIdBaseStart == Type::kIdU8 ? Reg::kTypeGpbLo : \
index + Type::_kIdBaseStart == Type::kIdI16 ? Reg::kTypeGpw : \
index + Type::_kIdBaseStart == Type::kIdU16 ? Reg::kTypeGpw : \
index + Type::_kIdBaseStart == Type::kIdI32 ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdU32 ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdI64 ? Reg::kTypeGpq : \
index + Type::_kIdBaseStart == Type::kIdU64 ? Reg::kTypeGpq : \
index + Type::_kIdBaseStart == Type::kIdIntPtr ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdUIntPtr ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdF32 ? Reg::kTypeXmm : \
index + Type::_kIdBaseStart == Type::kIdF64 ? Reg::kTypeXmm : \
index + Type::_kIdBaseStart == Type::kIdMask8 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask16 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask32 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask64 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMmx32 ? Reg::kTypeMm : \
index + Type::_kIdBaseStart == Type::kIdMmx64 ? Reg::kTypeMm : Reg::kTypeNone)
{ ASMJIT_LOOKUP_TABLE_32(V, 0) }
#undef V
};
//! \}
//! \endcond
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_X86_X86ARCHTRAITS_P_H_INCLUDED

View File

@@ -25,7 +25,7 @@
#ifdef ASMJIT_BUILD_X86
#include "../core/assembler.h"
#include "../core/codebufferwriter_p.h"
#include "../core/codewriter_p.h"
#include "../core/cpuinfo.h"
#include "../core/emitterutils_p.h"
#include "../core/formatter.h"
@@ -227,7 +227,7 @@ struct X86MemInfo_T {
// - REX - A possible combination of REX.[B|X|R|W] bits in REX prefix where
// REX.B and REX.X are possibly masked out, but REX.R and REX.W are
// kept as is.
#define VALUE(X) X86MemInfo_T<X>::kValue
#define VALUE(x) X86MemInfo_T<x>::kValue
static const uint8_t x86MemInfo[] = { ASMJIT_LOOKUP_TABLE_1024(VALUE, 0) };
#undef VALUE
@@ -240,23 +240,23 @@ static const uint8_t x86MemInfo[] = { ASMJIT_LOOKUP_TABLE_1024(VALUE, 0) };
// decide between VEX3 vs XOP.
// ____ ___
// [_OPCODE_|WvvvvLpp|RXBmmmmm|VEX3_XOP]
#define VALUE(X) ((X & 0x08) ? kX86ByteXop3 : kX86ByteVex3) | (0xF << 19) | (0x7 << 13)
#define VALUE(x) ((x & 0x08) ? kX86ByteXop3 : kX86ByteVex3) | (0xF << 19) | (0x7 << 13)
static const uint32_t x86VEXPrefix[] = { ASMJIT_LOOKUP_TABLE_16(VALUE, 0) };
#undef VALUE
// Table that contains LL opcode field addressed by a register size / 16. It's
// used to propagate L.256 or L.512 when YMM or ZMM registers are used,
// respectively.
#define VALUE(X) (X & (64 >> 4)) ? Opcode::kLL_2 : \
(X & (32 >> 4)) ? Opcode::kLL_1 : Opcode::kLL_0
#define VALUE(x) (x & (64 >> 4)) ? Opcode::kLL_2 : \
(x & (32 >> 4)) ? Opcode::kLL_1 : Opcode::kLL_0
static const uint32_t x86LLBySizeDiv16[] = { ASMJIT_LOOKUP_TABLE_16(VALUE, 0) };
#undef VALUE
// Table that contains LL opcode field addressed by a register size / 16. It's
// used to propagate L.256 or L.512 when YMM or ZMM registers are used,
// respectively.
#define VALUE(X) X == Reg::kTypeZmm ? Opcode::kLL_2 : \
X == Reg::kTypeYmm ? Opcode::kLL_1 : Opcode::kLL_0
#define VALUE(x) x == Reg::kTypeZmm ? Opcode::kLL_2 : \
x == Reg::kTypeYmm ? Opcode::kLL_1 : Opcode::kLL_0
static const uint32_t x86LLByRegType[] = { ASMJIT_LOOKUP_TABLE_16(VALUE, 0) };
#undef VALUE
@@ -278,7 +278,7 @@ struct X86CDisp8SHL_T {
};
};
#define VALUE(X) X86CDisp8SHL_T<X>::kValue
#define VALUE(x) X86CDisp8SHL_T<x>::kValue
static const uint32_t x86CDisp8SHL[] = { ASMJIT_LOOKUP_TABLE_32(VALUE, 0) };
#undef VALUE
@@ -310,7 +310,7 @@ struct X86Mod16BaseIndexTable_T {
};
};
#define VALUE(X) X86Mod16BaseIndexTable_T<X>::kValue
#define VALUE(x) X86Mod16BaseIndexTable_T<x>::kValue
static const uint8_t x86Mod16BaseIndexTable[] = { ASMJIT_LOOKUP_TABLE_64(VALUE, 0) };
#undef VALUE
@@ -375,10 +375,10 @@ static ASMJIT_INLINE uint32_t x86AltOpcodeOf(const InstDB::InstInfo* info) noexc
// [asmjit::X86BufferWriter]
// ============================================================================
class X86BufferWriter : public CodeBufferWriter {
class X86BufferWriter : public CodeWriter {
public:
ASMJIT_INLINE explicit X86BufferWriter(Assembler* a) noexcept
: CodeBufferWriter(a) {}
: CodeWriter(a) {}
ASMJIT_INLINE void emitPP(uint32_t opcode) noexcept {
uint32_t ppIndex = (opcode >> Opcode::kPP_Shift) &
@@ -509,7 +509,7 @@ static ASMJIT_INLINE uint32_t x86GetMovAbsAddrType(Assembler* self, X86BufferWri
uint32_t addrType = rmRel.addrType();
int64_t addrValue = rmRel.offset();
if (addrType == BaseMem::kAddrTypeDefault && !(options & Inst::kOptionModMR)) {
if (addrType == Mem::kAddrTypeDefault && !(options & Inst::kOptionModMR)) {
if (self->is64Bit()) {
uint64_t baseAddress = self->code()->baseAddress();
if (baseAddress != Globals::kNoBaseAddress && !rmRel.hasSegment()) {
@@ -519,15 +519,15 @@ static ASMJIT_INLINE uint32_t x86GetMovAbsAddrType(Assembler* self, X86BufferWri
uint64_t rel64 = uint64_t(addrValue) - rip64;
if (!Support::isInt32(int64_t(rel64)))
addrType = BaseMem::kAddrTypeAbs;
addrType = Mem::kAddrTypeAbs;
}
else {
if (!Support::isInt32(addrValue))
addrType = BaseMem::kAddrTypeAbs;
addrType = Mem::kAddrTypeAbs;
}
}
else {
addrType = BaseMem::kAddrTypeAbs;
addrType = Mem::kAddrTypeAbs;
}
}
@@ -1637,7 +1637,7 @@ CaseX86M_GPB_MulDiv:
// Handle a special form of `mov al|ax|eax|rax, [ptr64]` that doesn't use MOD.
if (opReg == Gp::kIdAx && !rmRel->as<Mem>().hasBaseOrIndex()) {
immValue = rmRel->as<Mem>().offset();
if (x86GetMovAbsAddrType(this, writer, o0.size(), options, rmRel->as<Mem>()) == BaseMem::kAddrTypeAbs) {
if (x86GetMovAbsAddrType(this, writer, o0.size(), options, rmRel->as<Mem>()) == Mem::kAddrTypeAbs) {
opcode += 0xA0;
goto EmitX86OpMovAbs;
}
@@ -1670,7 +1670,7 @@ CaseX86M_GPB_MulDiv:
// Handle a special form of `mov [ptr64], al|ax|eax|rax` that doesn't use MOD.
if (opReg == Gp::kIdAx && !rmRel->as<Mem>().hasBaseOrIndex()) {
immValue = rmRel->as<Mem>().offset();
if (x86GetMovAbsAddrType(this, writer, o1.size(), options, rmRel->as<Mem>()) == BaseMem::kAddrTypeAbs) {
if (x86GetMovAbsAddrType(this, writer, o1.size(), options, rmRel->as<Mem>()) == Mem::kAddrTypeAbs) {
opcode += 0xA2;
goto EmitX86OpMovAbs;
}
@@ -3991,7 +3991,7 @@ EmitModSib:
if (is32Bit()) {
// Explicit relative addressing doesn't work in 32-bit mode.
if (ASMJIT_UNLIKELY(addrType == BaseMem::kAddrTypeRel))
if (ASMJIT_UNLIKELY(addrType == Mem::kAddrTypeRel))
goto InvalidAddress;
writer.emit8(x86EncodeMod(0, opReg, 5));
@@ -4005,11 +4005,11 @@ EmitModSib:
// 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
// base the decision on the segment prefix and the address type.
if (addrType == BaseMem::kAddrTypeDefault) {
if (addrType == Mem::kAddrTypeDefault) {
if (baseAddress == Globals::kNoBaseAddress) {
// Prefer absolute addressing mode if the offset is 32-bit.
addrType = isOffsetI32 || isOffsetU32 ? BaseMem::kAddrTypeAbs
: BaseMem::kAddrTypeRel;
addrType = isOffsetI32 || isOffsetU32 ? Mem::kAddrTypeAbs
: Mem::kAddrTypeRel;
}
else {
// Prefer absolute addressing mode if FS|GS segment override is present.
@@ -4017,30 +4017,30 @@ EmitModSib:
// Prefer absolute addressing mode if this is LEA with 32-bit immediate.
bool isLea32 = (instId == Inst::kIdLea) && (isOffsetI32 || isOffsetU32);
addrType = hasFsGs || isLea32 ? BaseMem::kAddrTypeAbs
: BaseMem::kAddrTypeRel;
addrType = hasFsGs || isLea32 ? Mem::kAddrTypeAbs
: Mem::kAddrTypeRel;
}
}
if (addrType == BaseMem::kAddrTypeRel) {
if (addrType == Mem::kAddrTypeRel) {
uint32_t kModRel32Size = 5;
uint64_t virtualOffset = uint64_t(writer.offsetFrom(_bufferData)) + immSize + kModRel32Size;
if (baseAddress == Globals::kNoBaseAddress) {
if (baseAddress == Globals::kNoBaseAddress || _section->id() != 0) {
// Create a new RelocEntry as we cannot calculate the offset right now.
err = _code->newRelocEntry(&re, RelocEntry::kTypeAbsToRel, 4);
err = _code->newRelocEntry(&re, RelocEntry::kTypeAbsToRel);
if (ASMJIT_UNLIKELY(err))
goto Failed;
writer.emit8(x86EncodeMod(0, opReg, 5));
writer.emit32uLE(0);
re->_sourceSectionId = _section->id();
re->_sourceOffset = offset();
re->_leadingSize = uint8_t(writer.offsetFrom(_bufferPtr) - 4);
re->_trailingSize = uint8_t(immSize);
re->_format.resetToDataValue(4);
re->_format.setLeadingAndTrailingSize(writer.offsetFrom(_bufferPtr), immSize);
re->_payload = uint64_t(rmRel->as<Mem>().offset());
writer.emit32uLE(0);
writer.emitImmediate(uint64_t(immValue), immSize);
goto EmitDone;
}
@@ -4131,14 +4131,14 @@ EmitModSib_LabelRip_X86:
if (ASMJIT_UNLIKELY(!label))
goto InvalidLabel;
err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, 4);
err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs);
if (ASMJIT_UNLIKELY(err))
goto Failed;
re->_sourceSectionId = _section->id();
re->_sourceOffset = offset();
re->_leadingSize = uint8_t(writer.offsetFrom(_bufferPtr));
re->_trailingSize = uint8_t(immSize);
re->_format.resetToDataValue(4);
re->_format.setLeadingAndTrailingSize(writer.offsetFrom(_bufferPtr), immSize);
re->_payload = uint64_t(int64_t(relOffset));
if (label->isBound()) {
@@ -4156,16 +4156,16 @@ EmitModSib_LabelRip_X86:
}
else {
// [RIP->ABS].
err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, 4);
err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs);
if (ASMJIT_UNLIKELY(err))
goto Failed;
re->_sourceSectionId = _section->id();
re->_targetSectionId = _section->id();
re->_format.resetToDataValue(4);
re->_format.setLeadingAndTrailingSize(writer.offsetFrom(_bufferPtr), immSize);
re->_sourceOffset = offset();
re->_leadingSize = uint8_t(writer.offsetFrom(_bufferPtr));
re->_trailingSize = uint8_t(immSize);
re->_payload = re->_sourceOffset + re->_leadingSize + 4 + re->_trailingSize + uint64_t(int64_t(relOffset));
re->_payload = re->_sourceOffset + re->_format.regionSize() + uint64_t(int64_t(relOffset));
writer.emit32uLE(0);
}
@@ -4682,7 +4682,7 @@ EmitJmpCall:
}
}
err = _code->newRelocEntry(&re, RelocEntry::kTypeAbsToRel, 0);
err = _code->newRelocEntry(&re, RelocEntry::kTypeAbsToRel);
if (ASMJIT_UNLIKELY(err))
goto Failed;
@@ -4709,19 +4709,15 @@ EmitJmpCall:
writer.emit8If(0x0F, (opcode & Opcode::kMM_Mask) != 0); // Emit 0F prefix.
writer.emit8(opcode.v); // Emit opcode.
writer.emit8If(x86EncodeMod(3, opReg, 0), opReg != 0); // Emit MOD.
re->_format.resetToDataValue(4);
re->_format.setLeadingAndTrailingSize(writer.offsetFrom(_bufferPtr), immSize);
writer.emit32uLE(0); // Emit DISP32.
re->_valueSize = 4;
re->_leadingSize = uint8_t(writer.offsetFrom(_bufferPtr) - 4);
re->_trailingSize = uint8_t(immSize);
}
else {
writer.emit8(opCode8); // Emit opcode.
re->_format.resetToDataValue(4);
re->_format.setLeadingAndTrailingSize(writer.offsetFrom(_bufferPtr), immSize);
writer.emit8(0); // Emit DISP8 (zero).
re->_valueSize = 1;
re->_leadingSize = uint8_t(writer.offsetFrom(_bufferPtr) - 1);
re->_trailingSize = uint8_t(immSize);
}
goto EmitDone;
}
@@ -4762,19 +4758,18 @@ EmitRel:
// Chain with label.
size_t offset = size_t(writer.offsetFrom(_bufferData));
LabelLink* link = _code->newLabelLink(label, _section->id(), offset, relOffset);
OffsetFormat of;
of.resetToDataValue(relSize);
LabelLink* link = _code->newLabelLink(label, _section->id(), offset, relOffset, of);
if (ASMJIT_UNLIKELY(!link))
goto OutOfMemory;
if (re)
link->relocId = re->id();
// Emit label size as dummy data.
if (relSize == 1)
writer.emit8(0x01);
else // if (relSize == 4)
writer.emit32uLE(0x04040404);
// Emit dummy zeros, must be patched later when the reference becomes known.
writer.emitZeros(relSize);
}
writer.emitImmediate(uint64_t(immValue), immSize);
@@ -4798,14 +4793,10 @@ EmitDone:
return kErrorOk;
// --------------------------------------------------------------------------
// [Error Cases]
// [Error Handler]
// --------------------------------------------------------------------------
#define ERROR_HANDLER(ERROR) \
ERROR: \
err = DebugUtils::errored(kError##ERROR); \
goto Failed;
#define ERROR_HANDLER(ERR) ERR: err = DebugUtils::errored(kError##ERR); goto Failed;
ERROR_HANDLER(OutOfMemory)
ERROR_HANDLER(InvalidLabel)
ERROR_HANDLER(InvalidInstruction)
@@ -4825,8 +4816,7 @@ EmitDone:
ERROR_HANDLER(OperandSizeMismatch)
ERROR_HANDLER(AmbiguousOperandSize)
ERROR_HANDLER(NotConsecutiveRegs)
#undef ERROR_HANDLER
#undef ERROR_HANDLER
Failed:
#ifndef ASMJIT_NO_LOGGING
@@ -4858,7 +4848,7 @@ Error Assembler::align(uint32_t alignMode, uint32_t alignment) {
uint32_t i = uint32_t(Support::alignUpDiff<size_t>(offset(), alignment));
if (i > 0) {
CodeBufferWriter writer(this);
CodeWriter writer(this);
ASMJIT_PROPAGATE(writer.ensureSpace(this, i));
uint8_t pattern = 0x00;
@@ -4937,13 +4927,11 @@ Error Assembler::onAttach(CodeHolder* code) noexcept {
if (Environment::is32Bit(arch)) {
// 32 bit architecture - X86.
_gpRegInfo.setSignature(Gpd::kSignature);
_forcedInstOptions |= Inst::_kOptionInvalidRex;
_setAddressOverrideMask(kX86MemInfo_67H_X86);
}
else {
// 64 bit architecture - X64.
_gpRegInfo.setSignature(Gpq::kSignature);
_forcedInstOptions &= ~Inst::_kOptionInvalidRex;
_setAddressOverrideMask(kX86MemInfo_67H_X64);
}

View File

@@ -60,10 +60,7 @@ Error Builder::onAttach(CodeHolder* code) noexcept {
if (!Environment::isFamilyX86(arch))
return DebugUtils::errored(kErrorInvalidArch);
ASMJIT_PROPAGATE(Base::onAttach(code));
_gpRegInfo.setSignature(Environment::is32Bit(arch) ? uint32_t(Gpd::kSignature) : uint32_t(Gpq::kSignature));
return kErrorOk;
return Base::onAttach(code);
}
ASMJIT_END_SUB_NAMESPACE

View File

@@ -1,238 +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"
#ifdef ASMJIT_BUILD_X86
#include "../x86/x86callconv_p.h"
#include "../x86/x86operand.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
// ============================================================================
// [asmjit::x86::CallConvInternal - Init]
// ============================================================================
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 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;
constexpr uint32_t kGroupKReg = Reg::kGroupKReg;
constexpr uint32_t kZax = Gp::kIdAx;
constexpr uint32_t kZbx = Gp::kIdBx;
constexpr uint32_t kZcx = Gp::kIdCx;
constexpr uint32_t kZdx = Gp::kIdDx;
constexpr uint32_t kZsp = Gp::kIdSp;
constexpr uint32_t kZbp = Gp::kIdBp;
constexpr uint32_t kZsi = Gp::kIdSi;
constexpr uint32_t kZdi = Gp::kIdDi;
bool winABI = environment.isPlatformWindows() || environment.isAbiMSVC();
cc.setArch(environment.arch());
if (environment.is32Bit()) {
bool isStandardCallConv = true;
cc.setPreservedRegs(Reg::kGroupGp, Support::bitMask(Gp::kIdBx, Gp::kIdSp, Gp::kIdBp, Gp::kIdSi, Gp::kIdDi));
cc.setNaturalStackAlignment(4);
switch (ccId) {
case CallConv::kIdCDecl:
break;
case CallConv::kIdStdCall:
cc.setFlags(CallConv::kFlagCalleePopsStack);
break;
case CallConv::kIdFastCall:
cc.setFlags(CallConv::kFlagCalleePopsStack);
cc.setPassedOrder(kGroupGp, kZcx, kZdx);
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::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::kIdRegParm1:
cc.setPassedOrder(kGroupGp, kZax);
break;
case CallConv::kIdRegParm2:
cc.setPassedOrder(kGroupGp, kZax, kZdx);
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);
}
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);
// 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);
// Functions with variable arguments always use stack for MM and vector
// arguments.
cc.addFlags(CallConv::kFlagPassVecByStackIfVA);
}
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

View File

@@ -51,6 +51,7 @@ Error Compiler::finalize() {
a.addValidationOptions(validationOptions());
return serializeTo(&a);
}
// ============================================================================
// [asmjit::x86::Compiler - Events]
// ============================================================================
@@ -61,12 +62,8 @@ Error Compiler::onAttach(CodeHolder* code) noexcept {
return DebugUtils::errored(kErrorInvalidArch);
ASMJIT_PROPAGATE(Base::onAttach(code));
bool is32Bit = Environment::is32Bit(arch);
_gpRegInfo.setSignature(is32Bit ? uint32_t(Gpd::kSignature)
: uint32_t(Gpq::kSignature));
Error err = addPassT<X86RAPass>();
if (ASMJIT_UNLIKELY(err)) {
onDetach(code);
return err;

View File

@@ -0,0 +1,603 @@
// 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/formatter.h"
#include "../core/funcargscontext_p.h"
#include "../core/string.h"
#include "../core/support.h"
#include "../core/type.h"
#include "../core/radefs_p.h"
#include "../x86/x86emithelper_p.h"
#include "../x86/x86emitter.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
// ============================================================================
// [asmjit::x86::Internal - Helpers]
// ============================================================================
static ASMJIT_INLINE uint32_t getXmmMovInst(const FuncFrame& frame) {
bool avx = frame.isAvxEnabled();
bool aligned = frame.hasAlignedVecSR();
return aligned ? (avx ? Inst::kIdVmovaps : Inst::kIdMovaps)
: (avx ? Inst::kIdVmovups : Inst::kIdMovups);
}
//! Converts `size` to a 'kmov?' instructio.
static inline uint32_t kmovInstFromSize(uint32_t size) noexcept {
switch (size) {
case 1: return Inst::kIdKmovb;
case 2: return Inst::kIdKmovw;
case 4: return Inst::kIdKmovd;
case 8: return Inst::kIdKmovq;
default: return Inst::kIdNone;
}
}
// ============================================================================
// [asmjit::X86Internal - Emit Helpers]
// ============================================================================
ASMJIT_FAVOR_SIZE Error EmitHelper::emitRegMove(
const Operand_& dst_,
const Operand_& src_, uint32_t typeId, const char* comment) {
// Invalid or abstract TypeIds are not allowed.
ASMJIT_ASSERT(Type::isValid(typeId) && !Type::isAbstract(typeId));
Operand dst(dst_);
Operand src(src_);
uint32_t instId = Inst::kIdNone;
uint32_t memFlags = 0;
uint32_t overrideMemSize = 0;
enum MemFlags : uint32_t {
kDstMem = 0x1,
kSrcMem = 0x2
};
// Detect memory operands and patch them to have the same size as the register.
// BaseCompiler always sets memory size of allocs and spills, so it shouldn't
// be really necessary, however, after this function was separated from Compiler
// it's better to make sure that the size is always specified, as we can use
// 'movzx' and 'movsx' that rely on it.
if (dst.isMem()) { memFlags |= kDstMem; dst.as<Mem>().setSize(src.size()); }
if (src.isMem()) { memFlags |= kSrcMem; src.as<Mem>().setSize(dst.size()); }
switch (typeId) {
case Type::kIdI8:
case Type::kIdU8:
case Type::kIdI16:
case Type::kIdU16:
// Special case - 'movzx' load.
if (memFlags & kSrcMem) {
instId = Inst::kIdMovzx;
dst.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
}
else if (!memFlags) {
// Change both destination and source registers to GPD (safer, no dependencies).
dst.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
src.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
}
ASMJIT_FALLTHROUGH;
case Type::kIdI32:
case Type::kIdU32:
case Type::kIdI64:
case Type::kIdU64:
instId = Inst::kIdMov;
break;
case Type::kIdMmx32:
instId = Inst::kIdMovd;
if (memFlags) break;
ASMJIT_FALLTHROUGH;
case Type::kIdMmx64 : instId = Inst::kIdMovq ; break;
case Type::kIdMask8 : instId = Inst::kIdKmovb; break;
case Type::kIdMask16: instId = Inst::kIdKmovw; break;
case Type::kIdMask32: instId = Inst::kIdKmovd; break;
case Type::kIdMask64: instId = Inst::kIdKmovq; break;
default: {
uint32_t elementTypeId = Type::baseOf(typeId);
if (Type::isVec32(typeId) && memFlags) {
overrideMemSize = 4;
if (elementTypeId == Type::kIdF32)
instId = _avxEnabled ? Inst::kIdVmovss : Inst::kIdMovss;
else
instId = _avxEnabled ? Inst::kIdVmovd : Inst::kIdMovd;
break;
}
if (Type::isVec64(typeId) && memFlags) {
overrideMemSize = 8;
if (elementTypeId == Type::kIdF64)
instId = _avxEnabled ? Inst::kIdVmovsd : Inst::kIdMovsd;
else
instId = _avxEnabled ? Inst::kIdVmovq : Inst::kIdMovq;
break;
}
if (elementTypeId == Type::kIdF32)
instId = _avxEnabled ? Inst::kIdVmovaps : Inst::kIdMovaps;
else if (elementTypeId == Type::kIdF64)
instId = _avxEnabled ? Inst::kIdVmovapd : Inst::kIdMovapd;
else if (typeId <= Type::_kIdVec256End)
instId = _avxEnabled ? Inst::kIdVmovdqa : Inst::kIdMovdqa;
else if (elementTypeId <= Type::kIdU32)
instId = Inst::kIdVmovdqa32;
else
instId = Inst::kIdVmovdqa64;
break;
}
}
if (!instId)
return DebugUtils::errored(kErrorInvalidState);
if (overrideMemSize) {
if (dst.isMem()) dst.as<Mem>().setSize(overrideMemSize);
if (src.isMem()) src.as<Mem>().setSize(overrideMemSize);
}
_emitter->setInlineComment(comment);
return _emitter->emit(instId, dst, src);
}
ASMJIT_FAVOR_SIZE Error EmitHelper::emitArgMove(
const BaseReg& dst_, uint32_t dstTypeId,
const Operand_& src_, uint32_t srcTypeId, const char* comment) {
// Deduce optional `dstTypeId`, which may be `Type::kIdVoid` in some cases.
if (!dstTypeId) {
const ArchTraits& archTraits = ArchTraits::byArch(_emitter->arch());
dstTypeId = archTraits.regTypeToTypeId(dst_.type());
}
// Invalid or abstract TypeIds are not allowed.
ASMJIT_ASSERT(Type::isValid(dstTypeId) && !Type::isAbstract(dstTypeId));
ASMJIT_ASSERT(Type::isValid(srcTypeId) && !Type::isAbstract(srcTypeId));
Reg dst(dst_.as<Reg>());
Operand src(src_);
uint32_t dstSize = Type::sizeOf(dstTypeId);
uint32_t srcSize = Type::sizeOf(srcTypeId);
uint32_t instId = Inst::kIdNone;
// Not a real loop, just 'break' is nicer than 'goto'.
for (;;) {
if (Type::isInt(dstTypeId)) {
if (Type::isInt(srcTypeId)) {
instId = Inst::kIdMovsx;
uint32_t typeOp = (dstTypeId << 8) | srcTypeId;
// Sign extend by using 'movsx'.
if (typeOp == ((Type::kIdI16 << 8) | Type::kIdI8 ) ||
typeOp == ((Type::kIdI32 << 8) | Type::kIdI8 ) ||
typeOp == ((Type::kIdI32 << 8) | Type::kIdI16) ||
typeOp == ((Type::kIdI64 << 8) | Type::kIdI8 ) ||
typeOp == ((Type::kIdI64 << 8) | Type::kIdI16))
break;
// Sign extend by using 'movsxd'.
instId = Inst::kIdMovsxd;
if (typeOp == ((Type::kIdI64 << 8) | Type::kIdI32))
break;
}
if (Type::isInt(srcTypeId) || src_.isMem()) {
// Zero extend by using 'movzx' or 'mov'.
if (dstSize <= 4 && srcSize < 4) {
instId = Inst::kIdMovzx;
dst.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
}
else {
// We should have caught all possibilities where `srcSize` is less
// than 4, so we don't have to worry about 'movzx' anymore. Minimum
// size is enough to determine if we want 32-bit or 64-bit move.
instId = Inst::kIdMov;
srcSize = Support::min(srcSize, dstSize);
dst.setSignature(srcSize == 4 ? Reg::signatureOfT<Reg::kTypeGpd>()
: Reg::signatureOfT<Reg::kTypeGpq>());
if (src.isReg())
src.setSignature(dst.signature());
}
break;
}
// NOTE: The previous branch caught all memory sources, from here it's
// always register to register conversion, so catch the remaining cases.
srcSize = Support::min(srcSize, dstSize);
if (Type::isMmx(srcTypeId)) {
// 64-bit move.
instId = Inst::kIdMovq;
if (srcSize == 8)
break;
// 32-bit move.
instId = Inst::kIdMovd;
dst.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
break;
}
if (Type::isMask(srcTypeId)) {
instId = kmovInstFromSize(srcSize);
dst.setSignature(srcSize <= 4 ? Reg::signatureOfT<Reg::kTypeGpd>()
: Reg::signatureOfT<Reg::kTypeGpq>());
break;
}
if (Type::isVec(srcTypeId)) {
// 64-bit move.
instId = _avxEnabled ? Inst::kIdVmovq : Inst::kIdMovq;
if (srcSize == 8)
break;
// 32-bit move.
instId = _avxEnabled ? Inst::kIdVmovd : Inst::kIdMovd;
dst.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
break;
}
}
if (Type::isMmx(dstTypeId)) {
instId = Inst::kIdMovq;
srcSize = Support::min(srcSize, dstSize);
if (Type::isInt(srcTypeId) || src.isMem()) {
// 64-bit move.
if (srcSize == 8)
break;
// 32-bit move.
instId = Inst::kIdMovd;
if (src.isReg())
src.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
break;
}
if (Type::isMmx(srcTypeId))
break;
// This will hurt if AVX is enabled.
instId = Inst::kIdMovdq2q;
if (Type::isVec(srcTypeId))
break;
}
if (Type::isMask(dstTypeId)) {
srcSize = Support::min(srcSize, dstSize);
if (Type::isInt(srcTypeId) || Type::isMask(srcTypeId) || src.isMem()) {
instId = kmovInstFromSize(srcSize);
if (Reg::isGp(src) && srcSize <= 4)
src.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
break;
}
}
if (Type::isVec(dstTypeId)) {
// By default set destination to XMM, will be set to YMM|ZMM if needed.
dst.setSignature(Reg::signatureOfT<Reg::kTypeXmm>());
// This will hurt if AVX is enabled.
if (Reg::isMm(src)) {
// 64-bit move.
instId = Inst::kIdMovq2dq;
break;
}
// Argument conversion.
uint32_t dstElement = Type::baseOf(dstTypeId);
uint32_t srcElement = Type::baseOf(srcTypeId);
if (dstElement == Type::kIdF32 && srcElement == Type::kIdF64) {
srcSize = Support::min(dstSize * 2, srcSize);
dstSize = srcSize / 2;
if (srcSize <= 8)
instId = _avxEnabled ? Inst::kIdVcvtss2sd : Inst::kIdCvtss2sd;
else
instId = _avxEnabled ? Inst::kIdVcvtps2pd : Inst::kIdCvtps2pd;
if (dstSize == 32)
dst.setSignature(Reg::signatureOfT<Reg::kTypeYmm>());
if (src.isReg())
src.setSignature(Reg::signatureOfVecBySize(srcSize));
break;
}
if (dstElement == Type::kIdF64 && srcElement == Type::kIdF32) {
srcSize = Support::min(dstSize, srcSize * 2) / 2;
dstSize = srcSize * 2;
if (srcSize <= 4)
instId = _avxEnabled ? Inst::kIdVcvtsd2ss : Inst::kIdCvtsd2ss;
else
instId = _avxEnabled ? Inst::kIdVcvtpd2ps : Inst::kIdCvtpd2ps;
dst.setSignature(Reg::signatureOfVecBySize(dstSize));
if (src.isReg() && srcSize >= 32)
src.setSignature(Reg::signatureOfT<Reg::kTypeYmm>());
break;
}
srcSize = Support::min(srcSize, dstSize);
if (Reg::isGp(src) || src.isMem()) {
// 32-bit move.
if (srcSize <= 4) {
instId = _avxEnabled ? Inst::kIdVmovd : Inst::kIdMovd;
if (src.isReg())
src.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
break;
}
// 64-bit move.
if (srcSize == 8) {
instId = _avxEnabled ? Inst::kIdVmovq : Inst::kIdMovq;
break;
}
}
if (Reg::isVec(src) || src.isMem()) {
instId = _avxEnabled ? Inst::kIdVmovaps : Inst::kIdMovaps;
if (src.isMem() && srcSize < _emitter->environment().stackAlignment())
instId = _avxEnabled ? Inst::kIdVmovups : Inst::kIdMovups;
uint32_t signature = Reg::signatureOfVecBySize(srcSize);
dst.setSignature(signature);
if (src.isReg())
src.setSignature(signature);
break;
}
}
return DebugUtils::errored(kErrorInvalidState);
}
if (src.isMem())
src.as<Mem>().setSize(srcSize);
_emitter->setInlineComment(comment);
return _emitter->emit(instId, dst, src);
}
Error EmitHelper::emitRegSwap(
const BaseReg& a,
const BaseReg& b, const char* comment) {
if (a.isGp() && b.isGp()) {
_emitter->setInlineComment(comment);
return _emitter->emit(Inst::kIdXchg, a, b);
}
else
return DebugUtils::errored(kErrorInvalidState);
}
// ============================================================================
// [asmjit::X86Internal - Emit Prolog & Epilog]
// ============================================================================
static ASMJIT_INLINE void X86Internal_setupSaveRestoreInfo(uint32_t group, const FuncFrame& frame, Reg& xReg, uint32_t& xInst, uint32_t& xSize) noexcept {
switch (group) {
case Reg::kGroupVec:
xReg = xmm(0);
xInst = getXmmMovInst(frame);
xSize = xReg.size();
break;
case Reg::kGroupMm:
xReg = mm(0);
xInst = Inst::kIdMovq;
xSize = xReg.size();
break;
case Reg::kGroupKReg:
xReg = k(0);
xInst = Inst::kIdKmovq;
xSize = xReg.size();
break;
}
}
ASMJIT_FAVOR_SIZE Error EmitHelper::emitProlog(const FuncFrame& frame) {
Emitter* emitter = _emitter->as<Emitter>();
uint32_t gpSaved = frame.savedRegs(Reg::kGroupGp);
Gp zsp = emitter->zsp(); // ESP|RSP register.
Gp zbp = emitter->zbp(); // EBP|RBP register.
Gp gpReg = zsp; // General purpose register (temporary).
Gp saReg = zsp; // Stack-arguments base pointer.
// Emit: 'push zbp'
// 'mov zbp, zsp'.
if (frame.hasPreservedFP()) {
gpSaved &= ~Support::bitMask(Gp::kIdBp);
ASMJIT_PROPAGATE(emitter->push(zbp));
ASMJIT_PROPAGATE(emitter->mov(zbp, zsp));
}
// Emit: 'push gp' sequence.
{
Support::BitWordIterator<uint32_t> it(gpSaved);
while (it.hasNext()) {
gpReg.setId(it.next());
ASMJIT_PROPAGATE(emitter->push(gpReg));
}
}
// Emit: 'mov saReg, zsp'.
uint32_t saRegId = frame.saRegId();
if (saRegId != BaseReg::kIdBad && saRegId != Gp::kIdSp) {
saReg.setId(saRegId);
if (frame.hasPreservedFP()) {
if (saRegId != Gp::kIdBp)
ASMJIT_PROPAGATE(emitter->mov(saReg, zbp));
}
else {
ASMJIT_PROPAGATE(emitter->mov(saReg, zsp));
}
}
// Emit: 'and zsp, StackAlignment'.
if (frame.hasDynamicAlignment()) {
ASMJIT_PROPAGATE(emitter->and_(zsp, -int32_t(frame.finalStackAlignment())));
}
// Emit: 'sub zsp, StackAdjustment'.
if (frame.hasStackAdjustment()) {
ASMJIT_PROPAGATE(emitter->sub(zsp, frame.stackAdjustment()));
}
// Emit: 'mov [zsp + DAOffset], saReg'.
if (frame.hasDynamicAlignment() && frame.hasDAOffset()) {
Mem saMem = ptr(zsp, int32_t(frame.daOffset()));
ASMJIT_PROPAGATE(emitter->mov(saMem, saReg));
}
// Emit 'movxxx [zsp + X], {[x|y|z]mm, k}'.
{
Reg xReg;
Mem xBase = ptr(zsp, int32_t(frame.extraRegSaveOffset()));
uint32_t xInst;
uint32_t xSize;
for (uint32_t group = 1; group < BaseReg::kGroupVirt; group++) {
Support::BitWordIterator<uint32_t> it(frame.savedRegs(group));
if (it.hasNext()) {
X86Internal_setupSaveRestoreInfo(group, frame, xReg, xInst, xSize);
do {
xReg.setId(it.next());
ASMJIT_PROPAGATE(emitter->emit(xInst, xBase, xReg));
xBase.addOffsetLo32(int32_t(xSize));
} while (it.hasNext());
}
}
}
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error EmitHelper::emitEpilog(const FuncFrame& frame) {
Emitter* emitter = _emitter->as<Emitter>();
uint32_t i;
uint32_t regId;
uint32_t registerSize = emitter->registerSize();
uint32_t gpSaved = frame.savedRegs(Reg::kGroupGp);
Gp zsp = emitter->zsp(); // ESP|RSP register.
Gp zbp = emitter->zbp(); // EBP|RBP register.
Gp gpReg = emitter->zsp(); // General purpose register (temporary).
// Don't emit 'pop zbp' in the pop sequence, this case is handled separately.
if (frame.hasPreservedFP())
gpSaved &= ~Support::bitMask(Gp::kIdBp);
// Emit 'movxxx {[x|y|z]mm, k}, [zsp + X]'.
{
Reg xReg;
Mem xBase = ptr(zsp, int32_t(frame.extraRegSaveOffset()));
uint32_t xInst;
uint32_t xSize;
for (uint32_t group = 1; group < BaseReg::kGroupVirt; group++) {
Support::BitWordIterator<uint32_t> it(frame.savedRegs(group));
if (it.hasNext()) {
X86Internal_setupSaveRestoreInfo(group, frame, xReg, xInst, xSize);
do {
xReg.setId(it.next());
ASMJIT_PROPAGATE(emitter->emit(xInst, xReg, xBase));
xBase.addOffsetLo32(int32_t(xSize));
} while (it.hasNext());
}
}
}
// Emit 'emms' and/or 'vzeroupper'.
if (frame.hasMmxCleanup()) ASMJIT_PROPAGATE(emitter->emms());
if (frame.hasAvxCleanup()) ASMJIT_PROPAGATE(emitter->vzeroupper());
if (frame.hasPreservedFP()) {
// Emit 'mov zsp, zbp' or 'lea zsp, [zbp - x]'
int32_t count = int32_t(frame.pushPopSaveSize() - registerSize);
if (!count)
ASMJIT_PROPAGATE(emitter->mov(zsp, zbp));
else
ASMJIT_PROPAGATE(emitter->lea(zsp, ptr(zbp, -count)));
}
else {
if (frame.hasDynamicAlignment() && frame.hasDAOffset()) {
// Emit 'mov zsp, [zsp + DsaSlot]'.
Mem saMem = ptr(zsp, int32_t(frame.daOffset()));
ASMJIT_PROPAGATE(emitter->mov(zsp, saMem));
}
else if (frame.hasStackAdjustment()) {
// Emit 'add zsp, StackAdjustment'.
ASMJIT_PROPAGATE(emitter->add(zsp, int32_t(frame.stackAdjustment())));
}
}
// Emit 'pop gp' sequence.
if (gpSaved) {
i = gpSaved;
regId = 16;
do {
regId--;
if (i & 0x8000) {
gpReg.setId(regId);
ASMJIT_PROPAGATE(emitter->pop(gpReg));
}
i <<= 1;
} while (regId != 0);
}
// Emit 'pop zbp'.
if (frame.hasPreservedFP())
ASMJIT_PROPAGATE(emitter->pop(zbp));
// Emit 'ret' or 'ret x'.
if (frame.hasCalleeStackCleanup())
ASMJIT_PROPAGATE(emitter->emit(Inst::kIdRet, int(frame.calleeStackCleanup())));
else
ASMJIT_PROPAGATE(emitter->emit(Inst::kIdRet));
return kErrorOk;
}
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_BUILD_X86

View File

@@ -21,10 +21,15 @@
// 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
#ifndef ASMJIT_X86_X86EMITHELPER_P_H_INCLUDED
#define ASMJIT_X86_X86EMITHELPER_P_H_INCLUDED
#include "../core/arch.h"
#include "../core/api-config.h"
#include "../core/emithelper_p.h"
#include "../core/func.h"
#include "../x86/x86emitter.h"
#include "../x86/x86operand.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
@@ -33,19 +38,41 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! \{
// ============================================================================
// [asmjit::x86::ArchInternal]
// [asmjit::x86::EmitHelper]
// ============================================================================
//! X86-specific function API (calling conventions and other utilities).
namespace ArchInternal {
static ASMJIT_INLINE uint32_t vecTypeIdToRegType(uint32_t typeId) noexcept {
return typeId <= Type::_kIdVec128End ? Reg::kTypeXmm :
typeId <= Type::_kIdVec256End ? Reg::kTypeYmm : Reg::kTypeZmm;
}
Error typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfoOut) noexcept;
class EmitHelper : public BaseEmitHelper {
public:
bool _avxEnabled;
} // {ArchInternal}
inline explicit EmitHelper(BaseEmitter* emitter = nullptr, bool avxEnabled = false) noexcept
: BaseEmitHelper(emitter),
_avxEnabled(avxEnabled) {}
Error emitRegMove(
const Operand_& dst_,
const Operand_& src_, uint32_t typeId, const char* comment = nullptr) override;
Error emitArgMove(
const BaseReg& dst_, uint32_t dstTypeId,
const Operand_& src_, uint32_t srcTypeId, const char* comment = nullptr) override;
Error emitRegSwap(
const BaseReg& a,
const BaseReg& b, const char* comment = nullptr) override;
Error emitProlog(const FuncFrame& frame);
Error emitEpilog(const FuncFrame& frame);
};
//! \}
//! \endcond
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_X86_X86ARCHDATA_P_H_INCLUDED
#endif // ASMJIT_X86_X86EMITHELPER_P_H_INCLUDED

View File

@@ -32,144 +32,112 @@
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
#define ASMJIT_INST_0x(NAME, ID) \
inline Error NAME() { return _emitter()->emit(Inst::kId##ID); }
inline Error NAME() { return _emitter()->_emitI(Inst::kId##ID); }
#define ASMJIT_INST_1x(NAME, ID, T0) \
inline Error NAME(const T0& o0) { return _emitter()->emit(Inst::kId##ID, o0); }
inline Error NAME(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID, o0); }
#define ASMJIT_INST_1i(NAME, ID, T0) \
inline Error NAME(const T0& o0) { return _emitter()->emit(Inst::kId##ID, o0); } \
/** \cond */ \
inline Error NAME(int o0) { return _emitter()->emit(Inst::kId##ID, Support::asInt(o0)); } \
inline Error NAME(unsigned int o0) { return _emitter()->emit(Inst::kId##ID, Support::asInt(o0)); } \
inline Error NAME(int64_t o0) { return _emitter()->emit(Inst::kId##ID, Support::asInt(o0)); } \
inline Error NAME(uint64_t o0) { return _emitter()->emit(Inst::kId##ID, Support::asInt(o0)); } \
/** \endcond */
inline Error NAME(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID, o0); }
#define ASMJIT_INST_1c(NAME, ID, CONV, T0) \
inline Error NAME(uint32_t cc, const T0& o0) { return _emitter()->emit(CONV(cc), o0); } \
inline Error NAME##a(const T0& o0) { return _emitter()->emit(Inst::kId##ID##a, o0); } \
inline Error NAME##ae(const T0& o0) { return _emitter()->emit(Inst::kId##ID##ae, o0); } \
inline Error NAME##b(const T0& o0) { return _emitter()->emit(Inst::kId##ID##b, o0); } \
inline Error NAME##be(const T0& o0) { return _emitter()->emit(Inst::kId##ID##be, o0); } \
inline Error NAME##c(const T0& o0) { return _emitter()->emit(Inst::kId##ID##c, o0); } \
inline Error NAME##e(const T0& o0) { return _emitter()->emit(Inst::kId##ID##e, o0); } \
inline Error NAME##g(const T0& o0) { return _emitter()->emit(Inst::kId##ID##g, o0); } \
inline Error NAME##ge(const T0& o0) { return _emitter()->emit(Inst::kId##ID##ge, o0); } \
inline Error NAME##l(const T0& o0) { return _emitter()->emit(Inst::kId##ID##l, o0); } \
inline Error NAME##le(const T0& o0) { return _emitter()->emit(Inst::kId##ID##le, o0); } \
inline Error NAME##na(const T0& o0) { return _emitter()->emit(Inst::kId##ID##na, o0); } \
inline Error NAME##nae(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nae, o0); } \
inline Error NAME##nb(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nb, o0); } \
inline Error NAME##nbe(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nbe, o0); } \
inline Error NAME##nc(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nc, o0); } \
inline Error NAME##ne(const T0& o0) { return _emitter()->emit(Inst::kId##ID##ne, o0); } \
inline Error NAME##ng(const T0& o0) { return _emitter()->emit(Inst::kId##ID##ng, o0); } \
inline Error NAME##nge(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nge, o0); } \
inline Error NAME##nl(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nl, o0); } \
inline Error NAME##nle(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nle, o0); } \
inline Error NAME##no(const T0& o0) { return _emitter()->emit(Inst::kId##ID##no, o0); } \
inline Error NAME##np(const T0& o0) { return _emitter()->emit(Inst::kId##ID##np, o0); } \
inline Error NAME##ns(const T0& o0) { return _emitter()->emit(Inst::kId##ID##ns, o0); } \
inline Error NAME##nz(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nz, o0); } \
inline Error NAME##o(const T0& o0) { return _emitter()->emit(Inst::kId##ID##o, o0); } \
inline Error NAME##p(const T0& o0) { return _emitter()->emit(Inst::kId##ID##p, o0); } \
inline Error NAME##pe(const T0& o0) { return _emitter()->emit(Inst::kId##ID##pe, o0); } \
inline Error NAME##po(const T0& o0) { return _emitter()->emit(Inst::kId##ID##po, o0); } \
inline Error NAME##s(const T0& o0) { return _emitter()->emit(Inst::kId##ID##s, o0); } \
inline Error NAME##z(const T0& o0) { return _emitter()->emit(Inst::kId##ID##z, o0); }
inline Error NAME(uint32_t cc, const T0& o0) { return _emitter()->_emitI(CONV(cc), o0); } \
inline Error NAME##a(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##a, o0); } \
inline Error NAME##ae(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##ae, o0); } \
inline Error NAME##b(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##b, o0); } \
inline Error NAME##be(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##be, o0); } \
inline Error NAME##c(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##c, o0); } \
inline Error NAME##e(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##e, o0); } \
inline Error NAME##g(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##g, o0); } \
inline Error NAME##ge(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##ge, o0); } \
inline Error NAME##l(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##l, o0); } \
inline Error NAME##le(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##le, o0); } \
inline Error NAME##na(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##na, o0); } \
inline Error NAME##nae(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nae, o0); } \
inline Error NAME##nb(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nb, o0); } \
inline Error NAME##nbe(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nbe, o0); } \
inline Error NAME##nc(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nc, o0); } \
inline Error NAME##ne(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##ne, o0); } \
inline Error NAME##ng(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##ng, o0); } \
inline Error NAME##nge(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nge, o0); } \
inline Error NAME##nl(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nl, o0); } \
inline Error NAME##nle(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nle, o0); } \
inline Error NAME##no(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##no, o0); } \
inline Error NAME##np(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##np, o0); } \
inline Error NAME##ns(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##ns, o0); } \
inline Error NAME##nz(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nz, o0); } \
inline Error NAME##o(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##o, o0); } \
inline Error NAME##p(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##p, o0); } \
inline Error NAME##pe(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##pe, o0); } \
inline Error NAME##po(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##po, o0); } \
inline Error NAME##s(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##s, o0); } \
inline Error NAME##z(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##z, o0); }
#define ASMJIT_INST_2x(NAME, ID, T0, T1) \
inline Error NAME(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID, o0, o1); }
inline Error NAME(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID, o0, o1); }
#define ASMJIT_INST_2i(NAME, ID, T0, T1) \
inline Error NAME(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID, o0, o1); } \
/** \cond */ \
inline Error NAME(const T0& o0, int o1) { return _emitter()->emit(Inst::kId##ID, o0, Support::asInt(o1)); } \
inline Error NAME(const T0& o0, unsigned int o1) { return _emitter()->emit(Inst::kId##ID, o0, Support::asInt(o1)); } \
inline Error NAME(const T0& o0, int64_t o1) { return _emitter()->emit(Inst::kId##ID, o0, Support::asInt(o1)); } \
inline Error NAME(const T0& o0, uint64_t o1) { return _emitter()->emit(Inst::kId##ID, o0, Support::asInt(o1)); } \
/** \endcond */
inline Error NAME(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID, o0, o1); }
#define ASMJIT_INST_2c(NAME, ID, CONV, T0, T1) \
inline Error NAME(uint32_t cc, const T0& o0, const T1& o1) { return _emitter()->emit(CONV(cc), o0, o1); } \
inline Error NAME##a(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##a, o0, o1); } \
inline Error NAME##ae(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##ae, o0, o1); } \
inline Error NAME##b(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##b, o0, o1); } \
inline Error NAME##be(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##be, o0, o1); } \
inline Error NAME##c(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##c, o0, o1); } \
inline Error NAME##e(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##e, o0, o1); } \
inline Error NAME##g(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##g, o0, o1); } \
inline Error NAME##ge(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##ge, o0, o1); } \
inline Error NAME##l(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##l, o0, o1); } \
inline Error NAME##le(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##le, o0, o1); } \
inline Error NAME##na(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##na, o0, o1); } \
inline Error NAME##nae(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nae, o0, o1); } \
inline Error NAME##nb(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nb, o0, o1); } \
inline Error NAME##nbe(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nbe, o0, o1); } \
inline Error NAME##nc(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nc, o0, o1); } \
inline Error NAME##ne(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##ne, o0, o1); } \
inline Error NAME##ng(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##ng, o0, o1); } \
inline Error NAME##nge(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nge, o0, o1); } \
inline Error NAME##nl(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nl, o0, o1); } \
inline Error NAME##nle(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nle, o0, o1); } \
inline Error NAME##no(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##no, o0, o1); } \
inline Error NAME##np(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##np, o0, o1); } \
inline Error NAME##ns(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##ns, o0, o1); } \
inline Error NAME##nz(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nz, o0, o1); } \
inline Error NAME##o(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##o, o0, o1); } \
inline Error NAME##p(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##p, o0, o1); } \
inline Error NAME##pe(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##pe, o0, o1); } \
inline Error NAME##po(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##po, o0, o1); } \
inline Error NAME##s(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##s, o0, o1); } \
inline Error NAME##z(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##z, o0, o1); }
inline Error NAME(uint32_t cc, const T0& o0, const T1& o1) { return _emitter()->_emitI(CONV(cc), o0, o1); } \
inline Error NAME##a(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##a, o0, o1); } \
inline Error NAME##ae(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##ae, o0, o1); } \
inline Error NAME##b(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##b, o0, o1); } \
inline Error NAME##be(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##be, o0, o1); } \
inline Error NAME##c(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##c, o0, o1); } \
inline Error NAME##e(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##e, o0, o1); } \
inline Error NAME##g(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##g, o0, o1); } \
inline Error NAME##ge(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##ge, o0, o1); } \
inline Error NAME##l(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##l, o0, o1); } \
inline Error NAME##le(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##le, o0, o1); } \
inline Error NAME##na(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##na, o0, o1); } \
inline Error NAME##nae(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nae, o0, o1); } \
inline Error NAME##nb(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nb, o0, o1); } \
inline Error NAME##nbe(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nbe, o0, o1); } \
inline Error NAME##nc(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nc, o0, o1); } \
inline Error NAME##ne(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##ne, o0, o1); } \
inline Error NAME##ng(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##ng, o0, o1); } \
inline Error NAME##nge(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nge, o0, o1); } \
inline Error NAME##nl(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nl, o0, o1); } \
inline Error NAME##nle(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nle, o0, o1); } \
inline Error NAME##no(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##no, o0, o1); } \
inline Error NAME##np(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##np, o0, o1); } \
inline Error NAME##ns(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##ns, o0, o1); } \
inline Error NAME##nz(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nz, o0, o1); } \
inline Error NAME##o(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##o, o0, o1); } \
inline Error NAME##p(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##p, o0, o1); } \
inline Error NAME##pe(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##pe, o0, o1); } \
inline Error NAME##po(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##po, o0, o1); } \
inline Error NAME##s(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##s, o0, o1); } \
inline Error NAME##z(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##z, o0, o1); }
#define ASMJIT_INST_3x(NAME, ID, T0, T1, T2) \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2); }
inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2); }
#define ASMJIT_INST_3i(NAME, ID, T0, T1, T2) \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2); } \
/** \cond */ \
inline Error NAME(const T0& o0, const T1& o1, int o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, Support::asInt(o2)); } \
inline Error NAME(const T0& o0, const T1& o1, unsigned int o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, Support::asInt(o2)); } \
inline Error NAME(const T0& o0, const T1& o1, int64_t o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, Support::asInt(o2)); } \
inline Error NAME(const T0& o0, const T1& o1, uint64_t o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, Support::asInt(o2)); } \
/** \endcond */
inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2); }
#define ASMJIT_INST_3ii(NAME, ID, T0, T1, T2) \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2); } \
inline Error NAME(const T0& o0, int o1, int o2) { return _emitter()->emit(Inst::kId##ID, o0, Imm(o1), Support::asInt(o2)); }
inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2); }
#define ASMJIT_INST_4x(NAME, ID, T0, T1, T2, T3) \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3); }
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3); }
#define ASMJIT_INST_4i(NAME, ID, T0, T1, T2, T3) \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3); } \
/** \cond */ \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, int o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, Support::asInt(o3)); } \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, unsigned int o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, Support::asInt(o3)); } \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, int64_t o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, Support::asInt(o3)); } \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, uint64_t o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, Support::asInt(o3)); } \
/** \endcond */
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3); }
#define ASMJIT_INST_4ii(NAME, ID, T0, T1, T2, T3) \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3); } \
inline Error NAME(const T0& o0, const T1& o1, int o2, int o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, Imm(o2), Support::asInt(o3)); }
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3); }
#define ASMJIT_INST_5x(NAME, ID, T0, T1, T2, T3, T4) \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, o4); }
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3, o4); }
#define ASMJIT_INST_5i(NAME, ID, T0, T1, T2, T3, T4) \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, o4); } \
/** \cond */ \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, int o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, Support::asInt(o4)); } \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, unsigned int o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, Support::asInt(o4)); } \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, int64_t o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, Support::asInt(o4)); } \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, uint64_t o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, Support::asInt(o4)); } \
/** \endcond */
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3, o4); }
#define ASMJIT_INST_6x(NAME, ID, T0, T1, T2, T3, T4, T5) \
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4, const T5& o5) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, o4, o5); }
inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4, const T5& o5) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3, o4, o5); }
//! \addtogroup asmjit_x86
//! \{
@@ -224,16 +192,16 @@ struct EmitterExplicitT {
//! \{
//! Returns either GPD or GPQ register of the given `id` depending on the emitter's architecture.
inline Gp gpz(uint32_t id) const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), id); }
inline Gp gpz(uint32_t id) const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), id); }
inline Gp zax() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdAx); }
inline Gp zcx() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdCx); }
inline Gp zdx() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdDx); }
inline Gp zbx() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdBx); }
inline Gp zsp() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdSp); }
inline Gp zbp() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdBp); }
inline Gp zsi() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdSi); }
inline Gp zdi() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdDi); }
inline Gp zax() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdAx); }
inline Gp zcx() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdCx); }
inline Gp zdx() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdDx); }
inline Gp zbx() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdBx); }
inline Gp zsp() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdSp); }
inline Gp zbp() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdBp); }
inline Gp zsi() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdSi); }
inline Gp zdi() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdDi); }
//! \}
@@ -302,12 +270,12 @@ struct EmitterExplicitT {
//! \overload
inline Mem intptr_ptr_abs(uint64_t base) const noexcept {
uint32_t nativeGpSize = _emitter()->registerSize();
return Mem(base, nativeGpSize, BaseMem::kSignatureMemAbs);
return Mem(base, nativeGpSize, Mem::kSignatureMemAbs);
}
//! \overload
inline Mem intptr_ptr_abs(uint64_t base, const Gp& index, uint32_t shift = 0) const noexcept {
uint32_t nativeGpSize = _emitter()->registerSize();
return Mem(base, index, shift, nativeGpSize, BaseMem::kSignatureMemAbs);
return Mem(base, index, shift, nativeGpSize, Mem::kSignatureMemAbs);
}
//! \}

View File

@@ -168,6 +168,7 @@ public:
inline Features() noexcept
: BaseFeatures() {}
inline Features(const Features& other) noexcept
: BaseFeatures(other) {}

View File

@@ -338,6 +338,59 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
return sb.append(sFeatureString + sFeatureIndex[Support::min<uint32_t>(featureId, x86::Features::kCount)]);
}
// ============================================================================
// [asmjit::x86::FormatterInternal - Format Register]
// ============================================================================
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
if (Operand::isVirtId(rId)) {
if (emitter && emitter->emitterType() == BaseEmitter::kTypeCompiler) {
const BaseCompiler* cc = static_cast<const BaseCompiler*>(emitter);
if (cc->isVirtIdValid(rId)) {
VirtReg* vReg = cc->virtRegById(rId);
ASMJIT_ASSERT(vReg != nullptr);
const char* name = vReg->name();
if (name && name[0] != '\0')
ASMJIT_PROPAGATE(sb.append(name));
else
ASMJIT_PROPAGATE(sb.appendFormat("%%%u", unsigned(Operand::virtIdToIndex(rId))));
if (vReg->type() != rType && rType <= BaseReg::kTypeMax && (flags & FormatOptions::kFlagRegCasts) != 0) {
const RegFormatInfo::TypeEntry& typeEntry = info.typeEntries[rType];
if (typeEntry.index)
ASMJIT_PROPAGATE(sb.appendFormat("@%s", info.typeStrings + typeEntry.index));
}
return kErrorOk;
}
}
}
#else
DebugUtils::unused(emitter, flags);
#endif
if (ASMJIT_LIKELY(rType <= BaseReg::kTypeMax)) {
const RegFormatInfo::NameEntry& nameEntry = info.nameEntries[rType];
if (rId < nameEntry.specialCount)
return sb.append(info.nameStrings + nameEntry.specialIndex + rId * 4);
if (rId < nameEntry.count)
return sb.appendFormat(info.nameStrings + nameEntry.formatIndex, unsigned(rId));
const RegFormatInfo::TypeEntry& typeEntry = info.typeEntries[rType];
if (typeEntry.index)
return sb.appendFormat("%s@%u", info.typeStrings + typeEntry.index, rId);
}
return sb.appendFormat("<Reg-%u>?%u", rType, rId);
}
// ============================================================================
// [asmjit::x86::FormatterInternal - Format Operand]
// ============================================================================
@@ -363,8 +416,8 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand(
ASMJIT_PROPAGATE(sb.append('['));
switch (m.addrType()) {
case BaseMem::kAddrTypeAbs: ASMJIT_PROPAGATE(sb.append("abs ")); break;
case BaseMem::kAddrTypeRel: ASMJIT_PROPAGATE(sb.append("rel ")); break;
case Mem::kAddrTypeAbs: ASMJIT_PROPAGATE(sb.append("abs ")); break;
case Mem::kAddrTypeRel: ASMJIT_PROPAGATE(sb.append("rel ")); break;
}
char opSign = '\0';
@@ -763,59 +816,6 @@ ASMJIT_FAVOR_SIZE static Error FormatterInternal_explainConst(
}
}
// ============================================================================
// [asmjit::x86::FormatterInternal - Format Register]
// ============================================================================
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
if (Operand::isVirtId(rId)) {
if (emitter && emitter->emitterType() == BaseEmitter::kTypeCompiler) {
const BaseCompiler* cc = static_cast<const BaseCompiler*>(emitter);
if (cc->isVirtIdValid(rId)) {
VirtReg* vReg = cc->virtRegById(rId);
ASMJIT_ASSERT(vReg != nullptr);
const char* name = vReg->name();
if (name && name[0] != '\0')
ASMJIT_PROPAGATE(sb.append(name));
else
ASMJIT_PROPAGATE(sb.appendFormat("%%%u", unsigned(Operand::virtIdToIndex(rId))));
if (vReg->type() != rType && rType <= BaseReg::kTypeMax && (flags & FormatOptions::kFlagRegCasts) != 0) {
const RegFormatInfo::TypeEntry& typeEntry = info.typeEntries[rType];
if (typeEntry.index)
ASMJIT_PROPAGATE(sb.appendFormat("@%s", info.typeStrings + typeEntry.index));
}
return kErrorOk;
}
}
}
#else
DebugUtils::unused(emitter, flags);
#endif
if (ASMJIT_LIKELY(rType <= BaseReg::kTypeMax)) {
const RegFormatInfo::NameEntry& nameEntry = info.nameEntries[rType];
if (rId < nameEntry.specialCount)
return sb.append(info.nameStrings + nameEntry.specialIndex + rId * 4);
if (rId < nameEntry.count)
return sb.appendFormat(info.nameStrings + nameEntry.formatIndex, unsigned(rId));
const RegFormatInfo::TypeEntry& typeEntry = info.typeEntries[rType];
if (typeEntry.index)
return sb.appendFormat("%s@%u", info.typeStrings + typeEntry.index, rId);
}
return sb.appendFormat("Reg?%u@%u", rType, rId);
}
// ============================================================================
// [asmjit::x86::FormatterInternal - Format Instruction]
// ============================================================================

531
src/asmjit/x86/x86func.cpp Normal file
View File

@@ -0,0 +1,531 @@
// 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 "../x86/x86func_p.h"
#include "../x86/x86emithelper_p.h"
#include "../x86/x86operand.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
// ============================================================================
// [asmjit::x86::FuncInternal - Init]
// ============================================================================
namespace FuncInternal {
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 initCallConv(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;
constexpr uint32_t kGroupKReg = Reg::kGroupKReg;
constexpr uint32_t kZax = Gp::kIdAx;
constexpr uint32_t kZbx = Gp::kIdBx;
constexpr uint32_t kZcx = Gp::kIdCx;
constexpr uint32_t kZdx = Gp::kIdDx;
constexpr uint32_t kZsp = Gp::kIdSp;
constexpr uint32_t kZbp = Gp::kIdBp;
constexpr uint32_t kZsi = Gp::kIdSi;
constexpr uint32_t kZdi = Gp::kIdDi;
bool winABI = environment.isPlatformWindows() || environment.isAbiMSVC();
cc.setArch(environment.arch());
cc.setSaveRestoreRegSize(Reg::kGroupVec, 16);
cc.setSaveRestoreRegSize(Reg::kGroupMm, 8);
cc.setSaveRestoreRegSize(Reg::kGroupKReg, 8);
cc.setSaveRestoreAlignment(Reg::kGroupVec, 16);
cc.setSaveRestoreAlignment(Reg::kGroupMm, 8);
cc.setSaveRestoreAlignment(Reg::kGroupKReg, 8);
if (environment.is32Bit()) {
bool isStandardCallConv = true;
cc.setSaveRestoreRegSize(Reg::kGroupGp, 4);
cc.setSaveRestoreAlignment(Reg::kGroupGp, 4);
cc.setPreservedRegs(Reg::kGroupGp, Support::bitMask(Gp::kIdBx, Gp::kIdSp, Gp::kIdBp, Gp::kIdSi, Gp::kIdDi));
cc.setNaturalStackAlignment(4);
switch (ccId) {
case CallConv::kIdCDecl:
break;
case CallConv::kIdStdCall:
cc.setFlags(CallConv::kFlagCalleePopsStack);
break;
case CallConv::kIdFastCall:
cc.setFlags(CallConv::kFlagCalleePopsStack);
cc.setPassedOrder(kGroupGp, kZcx, kZdx);
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::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::kIdRegParm1:
cc.setPassedOrder(kGroupGp, kZax);
break;
case CallConv::kIdRegParm2:
cc.setPassedOrder(kGroupGp, kZax, kZdx);
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);
}
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);
// 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);
// Functions with variable arguments always use stack for MM and vector
// arguments.
cc.addFlags(CallConv::kFlagPassVecByStackIfVA);
}
if (ccId == CallConv::kIdCDecl) {
cc.addFlags(CallConv::kFlagVarArgCompatible);
}
}
else {
cc.setSaveRestoreRegSize(Reg::kGroupGp, 8);
cc.setSaveRestoreAlignment(Reg::kGroupGp, 8);
// 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;
}
ASMJIT_FAVOR_SIZE void unpackValues(FuncDetail& func, FuncValuePack& pack) noexcept {
uint32_t typeId = pack[0].typeId();
switch (typeId) {
case Type::kIdI64:
case Type::kIdU64: {
if (Environment::is32Bit(func.callConv().arch())) {
// Convert a 64-bit return value to two 32-bit return values.
pack[0].initTypeId(Type::kIdU32);
pack[1].initTypeId(typeId - 2);
break;
}
break;
}
}
}
ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept {
const CallConv& cc = func.callConv();
uint32_t arch = cc.arch();
uint32_t stackOffset = cc._spillZoneSize;
uint32_t argCount = func.argCount();
// Up to two return values can be returned in GP registers.
static const uint8_t gpReturnIndexes[4] = {
uint8_t(Gp::kIdAx),
uint8_t(Gp::kIdDx),
uint8_t(BaseReg::kIdBad),
uint8_t(BaseReg::kIdBad)
};
if (func.hasRet()) {
unpackValues(func, func._rets);
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
uint32_t typeId = func._rets[valueIndex].typeId();
// Terminate at the first void type (end of the pack).
if (!typeId)
break;
switch (typeId) {
case Type::kIdI64:
case Type::kIdU64: {
if (gpReturnIndexes[valueIndex] != BaseReg::kIdBad)
func._rets[valueIndex].initReg(Reg::kTypeGpq, gpReturnIndexes[valueIndex], typeId);
else
return DebugUtils::errored(kErrorInvalidState);
break;
}
case Type::kIdI8:
case Type::kIdI16:
case Type::kIdI32: {
if (gpReturnIndexes[valueIndex] != BaseReg::kIdBad)
func._rets[valueIndex].initReg(Reg::kTypeGpd, gpReturnIndexes[valueIndex], Type::kIdI32);
else
return DebugUtils::errored(kErrorInvalidState);
break;
}
case Type::kIdU8:
case Type::kIdU16:
case Type::kIdU32: {
if (gpReturnIndexes[valueIndex] != BaseReg::kIdBad)
func._rets[valueIndex].initReg(Reg::kTypeGpd, gpReturnIndexes[valueIndex], Type::kIdU32);
else
return DebugUtils::errored(kErrorInvalidState);
break;
}
case Type::kIdF32:
case Type::kIdF64: {
uint32_t regType = Environment::is32Bit(arch) ? Reg::kTypeSt : Reg::kTypeXmm;
func._rets[valueIndex].initReg(regType, valueIndex, typeId);
break;
}
case Type::kIdF80: {
// 80-bit floats are always returned by FP0.
func._rets[valueIndex].initReg(Reg::kTypeSt, valueIndex, typeId);
break;
}
case Type::kIdMmx32:
case Type::kIdMmx64: {
// MM registers are returned through XMM (SystemV) or GPQ (Win64).
uint32_t regType = Reg::kTypeMm;
uint32_t regIndex = valueIndex;
if (Environment::is64Bit(arch)) {
regType = cc.strategy() == CallConv::kStrategyDefault ? Reg::kTypeXmm : Reg::kTypeGpq;
regIndex = cc.strategy() == CallConv::kStrategyDefault ? valueIndex : gpReturnIndexes[valueIndex];
if (regIndex == BaseReg::kIdBad)
return DebugUtils::errored(kErrorInvalidState);
}
func._rets[valueIndex].initReg(regType, regIndex, typeId);
break;
}
default: {
func._rets[valueIndex].initReg(vecTypeIdToRegType(typeId), valueIndex, typeId);
break;
}
}
}
}
switch (cc.strategy()) {
case CallConv::kStrategyDefault: {
uint32_t gpzPos = 0;
uint32_t vecPos = 0;
for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
unpackValues(func, func._args[argIndex]);
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
FuncValue& arg = func._args[argIndex][valueIndex];
// Terminate if there are no more arguments in the pack.
if (!arg)
break;
uint32_t typeId = arg.typeId();
if (Type::isInt(typeId)) {
uint32_t regId = BaseReg::kIdBad;
if (gpzPos < CallConv::kMaxRegArgsPerGroup)
regId = cc._passedOrder[Reg::kGroupGp].id[gpzPos];
if (regId != BaseReg::kIdBad) {
uint32_t regType = (typeId <= Type::kIdU32) ? Reg::kTypeGpd : Reg::kTypeGpq;
arg.assignRegData(regType, regId);
func.addUsedRegs(Reg::kGroupGp, Support::bitMask(regId));
gpzPos++;
}
else {
uint32_t size = Support::max<uint32_t>(Type::sizeOf(typeId), registerSize);
arg.assignStackOffset(int32_t(stackOffset));
stackOffset += size;
}
continue;
}
if (Type::isFloat(typeId) || Type::isVec(typeId)) {
uint32_t regId = BaseReg::kIdBad;
if (vecPos < CallConv::kMaxRegArgsPerGroup)
regId = cc._passedOrder[Reg::kGroupVec].id[vecPos];
if (Type::isFloat(typeId)) {
// If this is a float, but `kFlagPassFloatsByVec` is false, we have
// to use stack instead. This should be only used by 32-bit calling
// conventions.
if (!cc.hasFlag(CallConv::kFlagPassFloatsByVec))
regId = BaseReg::kIdBad;
}
else {
// Pass vector registers via stack if this is a variable arguments
// function. This should be only used by 32-bit calling conventions.
if (signature.hasVarArgs() && cc.hasFlag(CallConv::kFlagPassVecByStackIfVA))
regId = BaseReg::kIdBad;
}
if (regId != BaseReg::kIdBad) {
arg.initTypeId(typeId);
arg.assignRegData(vecTypeIdToRegType(typeId), regId);
func.addUsedRegs(Reg::kGroupVec, Support::bitMask(regId));
vecPos++;
}
else {
uint32_t size = Type::sizeOf(typeId);
arg.assignStackOffset(int32_t(stackOffset));
stackOffset += size;
}
continue;
}
}
}
break;
}
case CallConv::kStrategyX64Windows:
case CallConv::kStrategyX64VectorCall: {
// Both X64 and VectorCall behave similarly - arguments are indexed
// from left to right. The position of the argument determines in
// which register the argument is allocated, so it's either GP or
// one of XMM/YMM/ZMM registers.
//
// [ X64 ] [VecCall]
// Index: #0 #1 #2 #3 #4 #5
//
// GP : RCX RDX R8 R9
// VEC : XMM0 XMM1 XMM2 XMM3 XMM4 XMM5
//
// For example function `f(int a, double b, int c, double d)` will be:
//
// (a) (b) (c) (d)
// RCX XMM1 R8 XMM3
//
// Unused vector registers are used by HVA.
bool isVectorCall = (cc.strategy() == CallConv::kStrategyX64VectorCall);
for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
unpackValues(func, func._args[argIndex]);
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
FuncValue& arg = func._args[argIndex][valueIndex];
// Terminate if there are no more arguments in the pack.
if (!arg)
break;
uint32_t typeId = arg.typeId();
uint32_t size = Type::sizeOf(typeId);
if (Type::isInt(typeId) || Type::isMmx(typeId)) {
uint32_t regId = BaseReg::kIdBad;
if (argIndex < CallConv::kMaxRegArgsPerGroup)
regId = cc._passedOrder[Reg::kGroupGp].id[argIndex];
if (regId != BaseReg::kIdBad) {
uint32_t regType = (size <= 4 && !Type::isMmx(typeId)) ? Reg::kTypeGpd : Reg::kTypeGpq;
arg.assignRegData(regType, regId);
func.addUsedRegs(Reg::kGroupGp, Support::bitMask(regId));
}
else {
arg.assignStackOffset(int32_t(stackOffset));
stackOffset += 8;
}
continue;
}
if (Type::isFloat(typeId) || Type::isVec(typeId)) {
uint32_t regId = BaseReg::kIdBad;
if (argIndex < CallConv::kMaxRegArgsPerGroup)
regId = cc._passedOrder[Reg::kGroupVec].id[argIndex];
if (regId != BaseReg::kIdBad) {
// X64-ABI doesn't allow vector types (XMM|YMM|ZMM) to be passed
// via registers, however, VectorCall was designed for that purpose.
if (Type::isFloat(typeId) || isVectorCall) {
uint32_t regType = vecTypeIdToRegType(typeId);
arg.assignRegData(regType, regId);
func.addUsedRegs(Reg::kGroupVec, Support::bitMask(regId));
continue;
}
}
// Passed via stack if the argument is float/double or indirectly.
// The trap is - if the argument is passed indirectly, the address
// can be passed via register, if the argument's index has GP one.
if (Type::isFloat(typeId)) {
arg.assignStackOffset(int32_t(stackOffset));
}
else {
uint32_t gpRegId = cc._passedOrder[Reg::kGroupGp].id[argIndex];
if (gpRegId != BaseReg::kIdBad)
arg.assignRegData(Reg::kTypeGpq, gpRegId);
else
arg.assignStackOffset(int32_t(stackOffset));
arg.addFlags(FuncValue::kFlagIsIndirect);
}
// Always 8 bytes (float/double/pointer).
stackOffset += 8;
continue;
}
}
}
break;
}
}
func._argStackSize = stackOffset;
return kErrorOk;
}
} // {FuncInternal}
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_BUILD_X86

View File

@@ -21,10 +21,10 @@
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_X86_X86CALLCONV_P_H_INCLUDED
#define ASMJIT_X86_X86CALLCONV_P_H_INCLUDED
#ifndef ASMJIT_X86_X86FUNC_P_H_INCLUDED
#define ASMJIT_X86_X86FUNC_P_H_INCLUDED
#include "../core/callconv.h"
#include "../core/func.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
@@ -33,20 +33,23 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! \{
// ============================================================================
// [asmjit::x86::CallConvInternal]
// [asmjit::x86::FuncInternal]
// ============================================================================
//! X86-specific function API (calling conventions and other utilities).
namespace CallConvInternal {
namespace FuncInternal {
//! Initialize `CallConv` structure (X86 specific).
Error init(CallConv& cc, uint32_t ccId, const Environment& environment) noexcept;
Error initCallConv(CallConv& cc, uint32_t ccId, const Environment& environment) noexcept;
} // {CallConvInternal}
//! Initialize `FuncDetail` (X86 specific).
Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept;
} // {FuncInternal}
//! \}
//! \endcond
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_X86_X86CALLCONV_P_H_INCLUDED
#endif // ASMJIT_X86_X86FUNC_P_H_INCLUDED

View File

@@ -24,7 +24,7 @@
#ifndef ASMJIT_X86_X86GLOBALS_H_INCLUDED
#define ASMJIT_X86_X86GLOBALS_H_INCLUDED
#include "../core/arch.h"
#include "../core/archtraits.h"
#include "../core/inst.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
@@ -1723,16 +1723,16 @@ namespace Condition {
static constexpr uint16_t cmovccTable[] = { ASMJIT_INST_FROM_COND(Inst::kIdCmov) };
#undef ASMJIT_INST_FROM_COND
//! Reverse a condition code (reverses the corresponding operands of a comparison).
//! Reverses a condition code (reverses the corresponding operands of a comparison).
static constexpr uint32_t reverse(uint32_t cond) noexcept { return reverseTable[cond]; }
//! Negate a condition code.
//! Negates a condition code.
static constexpr uint32_t negate(uint32_t cond) noexcept { return cond ^ 1u; }
//! Translate a condition code `cond` to a `jcc` instruction id.
//! Translates a condition code `cond` to a `jcc` instruction id.
static constexpr uint32_t toJcc(uint32_t cond) noexcept { return jccTable[cond]; }
//! Translate a condition code `cond` to a `setcc` instruction id.
//! Translates a condition code `cond` to a `setcc` instruction id.
static constexpr uint32_t toSetcc(uint32_t cond) noexcept { return setccTable[cond]; }
//! Translate a condition code `cond` to a `cmovcc` instruction id.
//! Translates a condition code `cond` to a `cmovcc` instruction id.
static constexpr uint32_t toCmovcc(uint32_t cond) noexcept { return cmovccTable[cond]; }
}

View File

@@ -126,64 +126,64 @@ struct X86ValidationData {
uint32_t allowedMemIndexRegs;
};
#define VALUE(X) \
(X == Reg::kTypeGpbLo) ? InstDB::kOpGpbLo : \
(X == Reg::kTypeGpbHi) ? InstDB::kOpGpbHi : \
(X == Reg::kTypeGpw ) ? InstDB::kOpGpw : \
(X == Reg::kTypeGpd ) ? InstDB::kOpGpd : \
(X == Reg::kTypeGpq ) ? InstDB::kOpGpq : \
(X == Reg::kTypeXmm ) ? InstDB::kOpXmm : \
(X == Reg::kTypeYmm ) ? InstDB::kOpYmm : \
(X == Reg::kTypeZmm ) ? InstDB::kOpZmm : \
(X == Reg::kTypeMm ) ? InstDB::kOpMm : \
(X == Reg::kTypeKReg ) ? InstDB::kOpKReg : \
(X == Reg::kTypeSReg ) ? InstDB::kOpSReg : \
(X == Reg::kTypeCReg ) ? InstDB::kOpCReg : \
(X == Reg::kTypeDReg ) ? InstDB::kOpDReg : \
(X == Reg::kTypeSt ) ? InstDB::kOpSt : \
(X == Reg::kTypeBnd ) ? InstDB::kOpBnd : \
(X == Reg::kTypeTmm ) ? InstDB::kOpTmm : \
(X == Reg::kTypeRip ) ? InstDB::kOpNone : InstDB::kOpNone
#define VALUE(x) \
(x == Reg::kTypeGpbLo) ? InstDB::kOpGpbLo : \
(x == Reg::kTypeGpbHi) ? InstDB::kOpGpbHi : \
(x == Reg::kTypeGpw ) ? InstDB::kOpGpw : \
(x == Reg::kTypeGpd ) ? InstDB::kOpGpd : \
(x == Reg::kTypeGpq ) ? InstDB::kOpGpq : \
(x == Reg::kTypeXmm ) ? InstDB::kOpXmm : \
(x == Reg::kTypeYmm ) ? InstDB::kOpYmm : \
(x == Reg::kTypeZmm ) ? InstDB::kOpZmm : \
(x == Reg::kTypeMm ) ? InstDB::kOpMm : \
(x == Reg::kTypeKReg ) ? InstDB::kOpKReg : \
(x == Reg::kTypeSReg ) ? InstDB::kOpSReg : \
(x == Reg::kTypeCReg ) ? InstDB::kOpCReg : \
(x == Reg::kTypeDReg ) ? InstDB::kOpDReg : \
(x == Reg::kTypeSt ) ? InstDB::kOpSt : \
(x == Reg::kTypeBnd ) ? InstDB::kOpBnd : \
(x == Reg::kTypeTmm ) ? InstDB::kOpTmm : \
(x == Reg::kTypeRip ) ? InstDB::kOpNone : InstDB::kOpNone
static const uint32_t _x86OpFlagFromRegType[Reg::kTypeMax + 1] = { ASMJIT_LOOKUP_TABLE_32(VALUE, 0) };
#undef VALUE
#define REG_MASK_FROM_REG_TYPE_X86(X) \
(X == Reg::kTypeGpbLo) ? 0x0000000Fu : \
(X == Reg::kTypeGpbHi) ? 0x0000000Fu : \
(X == Reg::kTypeGpw ) ? 0x000000FFu : \
(X == Reg::kTypeGpd ) ? 0x000000FFu : \
(X == Reg::kTypeGpq ) ? 0x000000FFu : \
(X == Reg::kTypeXmm ) ? 0x000000FFu : \
(X == Reg::kTypeYmm ) ? 0x000000FFu : \
(X == Reg::kTypeZmm ) ? 0x000000FFu : \
(X == Reg::kTypeMm ) ? 0x000000FFu : \
(X == Reg::kTypeKReg ) ? 0x000000FFu : \
(X == Reg::kTypeSReg ) ? 0x0000007Eu : \
(X == Reg::kTypeCReg ) ? 0x0000FFFFu : \
(X == Reg::kTypeDReg ) ? 0x000000FFu : \
(X == Reg::kTypeSt ) ? 0x000000FFu : \
(X == Reg::kTypeBnd ) ? 0x0000000Fu : \
(X == Reg::kTypeTmm ) ? 0x000000FFu : \
(X == Reg::kTypeRip ) ? 0x00000001u : 0u
#define REG_MASK_FROM_REG_TYPE_X86(x) \
(x == Reg::kTypeGpbLo) ? 0x0000000Fu : \
(x == Reg::kTypeGpbHi) ? 0x0000000Fu : \
(x == Reg::kTypeGpw ) ? 0x000000FFu : \
(x == Reg::kTypeGpd ) ? 0x000000FFu : \
(x == Reg::kTypeGpq ) ? 0x000000FFu : \
(x == Reg::kTypeXmm ) ? 0x000000FFu : \
(x == Reg::kTypeYmm ) ? 0x000000FFu : \
(x == Reg::kTypeZmm ) ? 0x000000FFu : \
(x == Reg::kTypeMm ) ? 0x000000FFu : \
(x == Reg::kTypeKReg ) ? 0x000000FFu : \
(x == Reg::kTypeSReg ) ? 0x0000007Eu : \
(x == Reg::kTypeCReg ) ? 0x0000FFFFu : \
(x == Reg::kTypeDReg ) ? 0x000000FFu : \
(x == Reg::kTypeSt ) ? 0x000000FFu : \
(x == Reg::kTypeBnd ) ? 0x0000000Fu : \
(x == Reg::kTypeTmm ) ? 0x000000FFu : \
(x == Reg::kTypeRip ) ? 0x00000001u : 0u
#define REG_MASK_FROM_REG_TYPE_X64(X) \
(X == Reg::kTypeGpbLo) ? 0x0000FFFFu : \
(X == Reg::kTypeGpbHi) ? 0x0000000Fu : \
(X == Reg::kTypeGpw ) ? 0x0000FFFFu : \
(X == Reg::kTypeGpd ) ? 0x0000FFFFu : \
(X == Reg::kTypeGpq ) ? 0x0000FFFFu : \
(X == Reg::kTypeXmm ) ? 0xFFFFFFFFu : \
(X == Reg::kTypeYmm ) ? 0xFFFFFFFFu : \
(X == Reg::kTypeZmm ) ? 0xFFFFFFFFu : \
(X == Reg::kTypeMm ) ? 0x000000FFu : \
(X == Reg::kTypeKReg ) ? 0x000000FFu : \
(X == Reg::kTypeSReg ) ? 0x0000007Eu : \
(X == Reg::kTypeCReg ) ? 0x0000FFFFu : \
(X == Reg::kTypeDReg ) ? 0x0000FFFFu : \
(X == Reg::kTypeSt ) ? 0x000000FFu : \
(X == Reg::kTypeBnd ) ? 0x0000000Fu : \
(X == Reg::kTypeTmm ) ? 0x000000FFu : \
(X == Reg::kTypeRip ) ? 0x00000001u : 0u
#define REG_MASK_FROM_REG_TYPE_X64(x) \
(x == Reg::kTypeGpbLo) ? 0x0000FFFFu : \
(x == Reg::kTypeGpbHi) ? 0x0000000Fu : \
(x == Reg::kTypeGpw ) ? 0x0000FFFFu : \
(x == Reg::kTypeGpd ) ? 0x0000FFFFu : \
(x == Reg::kTypeGpq ) ? 0x0000FFFFu : \
(x == Reg::kTypeXmm ) ? 0xFFFFFFFFu : \
(x == Reg::kTypeYmm ) ? 0xFFFFFFFFu : \
(x == Reg::kTypeZmm ) ? 0xFFFFFFFFu : \
(x == Reg::kTypeMm ) ? 0x000000FFu : \
(x == Reg::kTypeKReg ) ? 0x000000FFu : \
(x == Reg::kTypeSReg ) ? 0x0000007Eu : \
(x == Reg::kTypeCReg ) ? 0x0000FFFFu : \
(x == Reg::kTypeDReg ) ? 0x0000FFFFu : \
(x == Reg::kTypeSt ) ? 0x000000FFu : \
(x == Reg::kTypeBnd ) ? 0x0000000Fu : \
(x == Reg::kTypeTmm ) ? 0x000000FFu : \
(x == Reg::kTypeRip ) ? 0x00000001u : 0u
static const X86ValidationData _x86ValidationData = {
{ ASMJIT_LOOKUP_TABLE_32(REG_MASK_FROM_REG_TYPE_X86, 0) },

View File

@@ -366,21 +366,21 @@ ASMJIT_VARAPI const CommonInfo _commonInfoTable[];
//! Instruction information (X86).
struct InstInfo {
//! Index to `_nameData`.
//! Index to \ref _nameData.
uint32_t _nameDataIndex : 14;
//! Index to `_commonInfoTable`.
//! Index to \ref _commonInfoTable.
uint32_t _commonInfoIndex : 10;
//! Index to `InstDB::_commonInfoTableB`.
//! Index to \ref _commonInfoTableB.
uint32_t _commonInfoIndexB : 8;
//! Instruction encoding, see `InstDB::EncodingId`.
//! Instruction encoding (internal encoding identifier used by \ref Assembler).
uint8_t _encoding;
//! Main opcode value (0.255).
//! Main opcode value (0..255).
uint8_t _mainOpcodeValue;
//! Index to `InstDB::_mainOpcodeTable` that is combined with `_mainOpcodeValue`
//! Index to \ref _mainOpcodeTable` that is combined with \ref _mainOpcodeValue
//! to form the final opcode.
uint8_t _mainOpcodeIndex;
//! Index to `InstDB::_altOpcodeTable` that contains a full alternative opcode.
//! Index to \ref _altOpcodeTable that contains a full alternative opcode.
uint8_t _altOpcodeIndex;
// --------------------------------------------------------------------------
@@ -456,7 +456,7 @@ struct InstInfo {
ASMJIT_VARAPI const InstInfo _instInfoTable[];
inline const InstInfo& infoById(uint32_t instId) noexcept {
static inline const InstInfo& infoById(uint32_t instId) noexcept {
ASMJIT_ASSERT(Inst::isDefinedId(instId));
return _instInfoTable[instId];
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,87 +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.
#ifndef ASMJIT_X86_X86INTERNAL_P_H_INCLUDED
#define ASMJIT_X86_X86INTERNAL_P_H_INCLUDED
#include "../core/api-config.h"
#include "../core/func.h"
#include "../x86/x86emitter.h"
#include "../x86/x86operand.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! \cond INTERNAL
//! \addtogroup asmjit_x86
//! \{
// ============================================================================
// [asmjit::X86Internal]
// ============================================================================
//! X86 utilities used at multiple places, not part of public API, not exported.
struct X86Internal {
//! Initialize `FuncDetail` (X86 specific).
static Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept;
//! Initialize `FuncFrame` (X86 specific).
static Error initFuncFrame(FuncFrame& frame, const FuncDetail& signature) noexcept;
//! Finalize `FuncFrame` (X86 specific).
static Error finalizeFuncFrame(FuncFrame& frame) noexcept;
static Error argsToFuncFrame(const FuncArgsAssignment& args, FuncFrame& frame) noexcept;
//! Emit function prolog.
static Error emitProlog(Emitter* emitter, const FuncFrame& frame);
//! Emit function epilog.
static Error emitEpilog(Emitter* emitter, const FuncFrame& frame);
//! Emit a pure move operation between two registers or the same type or
//! between a register and its home slot. This function does not handle
//! register conversion.
static Error emitRegMove(Emitter* emitter,
const Operand_& dst_,
const Operand_& src_, uint32_t typeId, bool avxEnabled, const char* comment = nullptr);
//! Emit move from a function argument (either register or stack) to a register.
//!
//! This function can handle the necessary conversion from one argument to
//! another, and from one register type to another, if it's possible. Any
//! attempt of conversion that requires third register of a different group
//! (for example conversion from K to MMX) will fail.
static Error emitArgMove(Emitter* emitter,
const Reg& dst_, uint32_t dstTypeId,
const Operand_& src_, uint32_t srcTypeId, bool avxEnabled, const char* comment = nullptr);
static Error emitArgsAssignment(Emitter* emitter, const FuncFrame& frame, const FuncArgsAssignment& args);
};
//! \}
//! \endcond
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_X86_X86INTERNAL_P_H_INCLUDED

View File

@@ -29,29 +29,6 @@
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
// ============================================================================
// [asmjit::x86::OpData]
// ============================================================================
const OpData opData = {
{
// RegInfo[]
#define VALUE(X) { RegTraits<X>::kSignature }
{ ASMJIT_LOOKUP_TABLE_32(VALUE, 0) },
#undef VALUE
// RegCount[]
#define VALUE(X) RegTraits<X>::kCount
{ ASMJIT_LOOKUP_TABLE_32(VALUE, 0) },
#undef VALUE
// RegTypeToTypeId[]
#define VALUE(X) RegTraits<X>::kTypeId
{ ASMJIT_LOOKUP_TABLE_32(VALUE, 0) }
#undef VALUE
}
};
// ============================================================================
// [asmjit::x86::Operand - Unit]
// ============================================================================
@@ -167,7 +144,7 @@ UNIT(x86_operand) {
EXPECT(zmm7.ymm() == ymm7);
EXPECT(zmm7.zmm() == zmm7);
INFO("Checking x86::FpMm register properties");
INFO("Checking x86::Mm register properties");
EXPECT(Mm().isReg() == true);
EXPECT(mm2.isReg() == true);
EXPECT(mm2.id() == 2);

View File

@@ -24,7 +24,7 @@
#ifndef ASMJIT_X86_X86OPERAND_H_INCLUDED
#define ASMJIT_X86_X86OPERAND_H_INCLUDED
#include "../core/arch.h"
#include "../core/archtraits.h"
#include "../core/operand.h"
#include "../core/type.h"
#include "../x86/x86globals.h"
@@ -61,23 +61,23 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
} \
\
static constexpr Mem FUNC##_abs(uint64_t base) noexcept { \
return Mem(base, SIZE, BaseMem::kSignatureMemAbs); \
return Mem(base, SIZE, Mem::kSignatureMemAbs); \
} \
static constexpr Mem FUNC##_abs(uint64_t base, const Gp& index, uint32_t shift = 0) noexcept { \
return Mem(base, index, shift, SIZE, BaseMem::kSignatureMemAbs); \
return Mem(base, index, shift, SIZE, Mem::kSignatureMemAbs); \
} \
static constexpr Mem FUNC##_abs(uint64_t base, const Vec& index, uint32_t shift = 0) noexcept { \
return Mem(base, index, shift, SIZE, BaseMem::kSignatureMemAbs); \
return Mem(base, index, shift, SIZE, Mem::kSignatureMemAbs); \
} \
\
static constexpr Mem FUNC##_rel(uint64_t base) noexcept { \
return Mem(base, SIZE, BaseMem::kSignatureMemRel); \
return Mem(base, SIZE, Mem::kSignatureMemRel); \
} \
static constexpr Mem FUNC##_rel(uint64_t base, const Gp& index, uint32_t shift = 0) noexcept { \
return Mem(base, index, shift, SIZE, BaseMem::kSignatureMemRel); \
return Mem(base, index, shift, SIZE, Mem::kSignatureMemRel); \
} \
static constexpr Mem FUNC##_rel(uint64_t base, const Vec& index, uint32_t shift = 0) noexcept { \
return Mem(base, index, shift, SIZE, BaseMem::kSignatureMemRel); \
return Mem(base, index, shift, SIZE, Mem::kSignatureMemRel); \
}
//! \addtogroup asmjit_x86
@@ -231,39 +231,39 @@ public:
//! Tests whether the register is a GPB register (8-bit).
constexpr bool isGpb() const noexcept { return size() == 1; }
//! Tests whether the register is a low GPB register (8-bit).
constexpr bool isGpbLo() const noexcept { return hasSignature(RegTraits<kTypeGpbLo>::kSignature); }
constexpr bool isGpbLo() const noexcept { return hasBaseSignature(RegTraits<kTypeGpbLo>::kSignature); }
//! Tests whether the register is a high GPB register (8-bit).
constexpr bool isGpbHi() const noexcept { return hasSignature(RegTraits<kTypeGpbHi>::kSignature); }
constexpr bool isGpbHi() const noexcept { return hasBaseSignature(RegTraits<kTypeGpbHi>::kSignature); }
//! Tests whether the register is a GPW register (16-bit).
constexpr bool isGpw() const noexcept { return hasSignature(RegTraits<kTypeGpw>::kSignature); }
constexpr bool isGpw() const noexcept { return hasBaseSignature(RegTraits<kTypeGpw>::kSignature); }
//! Tests whether the register is a GPD register (32-bit).
constexpr bool isGpd() const noexcept { return hasSignature(RegTraits<kTypeGpd>::kSignature); }
constexpr bool isGpd() const noexcept { return hasBaseSignature(RegTraits<kTypeGpd>::kSignature); }
//! Tests whether the register is a GPQ register (64-bit).
constexpr bool isGpq() const noexcept { return hasSignature(RegTraits<kTypeGpq>::kSignature); }
constexpr bool isGpq() const noexcept { return hasBaseSignature(RegTraits<kTypeGpq>::kSignature); }
//! Tests whether the register is an XMM register (128-bit).
constexpr bool isXmm() const noexcept { return hasSignature(RegTraits<kTypeXmm>::kSignature); }
constexpr bool isXmm() const noexcept { return hasBaseSignature(RegTraits<kTypeXmm>::kSignature); }
//! Tests whether the register is a YMM register (256-bit).
constexpr bool isYmm() const noexcept { return hasSignature(RegTraits<kTypeYmm>::kSignature); }
constexpr bool isYmm() const noexcept { return hasBaseSignature(RegTraits<kTypeYmm>::kSignature); }
//! Tests whether the register is a ZMM register (512-bit).
constexpr bool isZmm() const noexcept { return hasSignature(RegTraits<kTypeZmm>::kSignature); }
constexpr bool isZmm() const noexcept { return hasBaseSignature(RegTraits<kTypeZmm>::kSignature); }
//! Tests whether the register is an MMX register (64-bit).
constexpr bool isMm() const noexcept { return hasSignature(RegTraits<kTypeMm>::kSignature); }
constexpr bool isMm() const noexcept { return hasBaseSignature(RegTraits<kTypeMm>::kSignature); }
//! Tests whether the register is a K register (64-bit).
constexpr bool isKReg() const noexcept { return hasSignature(RegTraits<kTypeKReg>::kSignature); }
constexpr bool isKReg() const noexcept { return hasBaseSignature(RegTraits<kTypeKReg>::kSignature); }
//! Tests whether the register is a segment register.
constexpr bool isSReg() const noexcept { return hasSignature(RegTraits<kTypeSReg>::kSignature); }
constexpr bool isSReg() const noexcept { return hasBaseSignature(RegTraits<kTypeSReg>::kSignature); }
//! Tests whether the register is a control register.
constexpr bool isCReg() const noexcept { return hasSignature(RegTraits<kTypeCReg>::kSignature); }
constexpr bool isCReg() const noexcept { return hasBaseSignature(RegTraits<kTypeCReg>::kSignature); }
//! Tests whether the register is a debug register.
constexpr bool isDReg() const noexcept { return hasSignature(RegTraits<kTypeDReg>::kSignature); }
constexpr bool isDReg() const noexcept { return hasBaseSignature(RegTraits<kTypeDReg>::kSignature); }
//! Tests whether the register is an FPU register (80-bit).
constexpr bool isSt() const noexcept { return hasSignature(RegTraits<kTypeSt>::kSignature); }
constexpr bool isSt() const noexcept { return hasBaseSignature(RegTraits<kTypeSt>::kSignature); }
//! Tests whether the register is a bound register.
constexpr bool isBnd() const noexcept { return hasSignature(RegTraits<kTypeBnd>::kSignature); }
constexpr bool isBnd() const noexcept { return hasBaseSignature(RegTraits<kTypeBnd>::kSignature); }
//! Tests whether the register is a TMM register.
constexpr bool isTmm() const noexcept { return hasSignature(RegTraits<kTypeTmm>::kSignature); }
constexpr bool isTmm() const noexcept { return hasBaseSignature(RegTraits<kTypeTmm>::kSignature); }
//! Tests whether the register is RIP.
constexpr bool isRip() const noexcept { return hasSignature(RegTraits<kTypeRip>::kSignature); }
constexpr bool isRip() const noexcept { return hasBaseSignature(RegTraits<kTypeRip>::kSignature); }
template<uint32_t REG_TYPE>
inline void setRegT(uint32_t rId) noexcept {
@@ -277,15 +277,16 @@ public:
setId(rId);
}
static inline uint32_t groupOf(uint32_t rType) noexcept;
static inline uint32_t groupOf(uint32_t rType) noexcept { return _archTraits[Environment::kArchX86].regTypeToGroup(rType); }
static inline uint32_t typeIdOf(uint32_t rType) noexcept { return _archTraits[Environment::kArchX86].regTypeToTypeId(rType); }
static inline uint32_t signatureOf(uint32_t rType) noexcept { return _archTraits[Environment::kArchX86].regTypeToSignature(rType); }
template<uint32_t REG_TYPE>
static inline uint32_t groupOfT() noexcept { return RegTraits<REG_TYPE>::kGroup; }
static inline uint32_t typeIdOf(uint32_t rType) noexcept;
template<uint32_t REG_TYPE>
static inline uint32_t typeIdOfT() noexcept { return RegTraits<REG_TYPE>::kTypeId; }
static inline uint32_t signatureOf(uint32_t rType) noexcept;
template<uint32_t REG_TYPE>
static inline uint32_t signatureOfT() noexcept { return RegTraits<REG_TYPE>::kSignature; }
@@ -302,9 +303,9 @@ public:
//! Tests whether the `op` operand is either a low or high 8-bit GPB register.
static inline bool isGpb(const Operand_& op) noexcept {
// Check operand type, register group, and size. Not interested in register type.
const uint32_t kSgn = (Operand::kOpReg << kSignatureOpShift ) |
(1 << kSignatureSizeShift) ;
return (op.signature() & (kSignatureOpMask | kSignatureSizeMask)) == kSgn;
const uint32_t kSgn = (Operand::kOpReg << kSignatureOpTypeShift) |
(1 << kSignatureSizeShift ) ;
return (op.signature() & (kSignatureOpTypeMask | kSignatureSizeMask)) == kSgn;
}
static inline bool isGpbLo(const Operand_& op) noexcept { return op.as<Reg>().isGpbLo(); }
@@ -401,7 +402,7 @@ class Vec : public Reg {
//! Casts this register to a register that has half the size (or XMM if it's already XMM).
inline Vec half() const noexcept {
return Vec(type() == kTypeZmm ? signatureOf(kTypeYmm) : signatureOf(kTypeXmm), id());
return Vec::fromSignatureAndId(type() == kTypeZmm ? signatureOfT<kTypeYmm>() : signatureOfT<kTypeXmm>(), id());
}
};
@@ -544,78 +545,78 @@ static constexpr Bnd bnd(uint32_t rId) noexcept { return Bnd(rId); }
//! Creates a TMM register operand.
static constexpr Tmm tmm(uint32_t rId) noexcept { return Tmm(rId); }
static constexpr Gp al = Gp(GpbLo::kSignature, Gp::kIdAx);
static constexpr Gp bl = Gp(GpbLo::kSignature, Gp::kIdBx);
static constexpr Gp cl = Gp(GpbLo::kSignature, Gp::kIdCx);
static constexpr Gp dl = Gp(GpbLo::kSignature, Gp::kIdDx);
static constexpr Gp spl = Gp(GpbLo::kSignature, Gp::kIdSp);
static constexpr Gp bpl = Gp(GpbLo::kSignature, Gp::kIdBp);
static constexpr Gp sil = Gp(GpbLo::kSignature, Gp::kIdSi);
static constexpr Gp dil = Gp(GpbLo::kSignature, Gp::kIdDi);
static constexpr Gp r8b = Gp(GpbLo::kSignature, Gp::kIdR8);
static constexpr Gp r9b = Gp(GpbLo::kSignature, Gp::kIdR9);
static constexpr Gp r10b = Gp(GpbLo::kSignature, Gp::kIdR10);
static constexpr Gp r11b = Gp(GpbLo::kSignature, Gp::kIdR11);
static constexpr Gp r12b = Gp(GpbLo::kSignature, Gp::kIdR12);
static constexpr Gp r13b = Gp(GpbLo::kSignature, Gp::kIdR13);
static constexpr Gp r14b = Gp(GpbLo::kSignature, Gp::kIdR14);
static constexpr Gp r15b = Gp(GpbLo::kSignature, Gp::kIdR15);
static constexpr Gp al = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdAx));
static constexpr Gp bl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdBx));
static constexpr Gp cl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdCx));
static constexpr Gp dl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdDx));
static constexpr Gp spl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdSp));
static constexpr Gp bpl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdBp));
static constexpr Gp sil = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdSi));
static constexpr Gp dil = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdDi));
static constexpr Gp r8b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR8));
static constexpr Gp r9b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR9));
static constexpr Gp r10b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR10));
static constexpr Gp r11b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR11));
static constexpr Gp r12b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR12));
static constexpr Gp r13b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR13));
static constexpr Gp r14b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR14));
static constexpr Gp r15b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR15));
static constexpr Gp ah = Gp(GpbHi::kSignature, Gp::kIdAx);
static constexpr Gp bh = Gp(GpbHi::kSignature, Gp::kIdBx);
static constexpr Gp ch = Gp(GpbHi::kSignature, Gp::kIdCx);
static constexpr Gp dh = Gp(GpbHi::kSignature, Gp::kIdDx);
static constexpr Gp ah = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdAx));
static constexpr Gp bh = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdBx));
static constexpr Gp ch = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdCx));
static constexpr Gp dh = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdDx));
static constexpr Gp ax = Gp(Gpw::kSignature, Gp::kIdAx);
static constexpr Gp bx = Gp(Gpw::kSignature, Gp::kIdBx);
static constexpr Gp cx = Gp(Gpw::kSignature, Gp::kIdCx);
static constexpr Gp dx = Gp(Gpw::kSignature, Gp::kIdDx);
static constexpr Gp sp = Gp(Gpw::kSignature, Gp::kIdSp);
static constexpr Gp bp = Gp(Gpw::kSignature, Gp::kIdBp);
static constexpr Gp si = Gp(Gpw::kSignature, Gp::kIdSi);
static constexpr Gp di = Gp(Gpw::kSignature, Gp::kIdDi);
static constexpr Gp r8w = Gp(Gpw::kSignature, Gp::kIdR8);
static constexpr Gp r9w = Gp(Gpw::kSignature, Gp::kIdR9);
static constexpr Gp r10w = Gp(Gpw::kSignature, Gp::kIdR10);
static constexpr Gp r11w = Gp(Gpw::kSignature, Gp::kIdR11);
static constexpr Gp r12w = Gp(Gpw::kSignature, Gp::kIdR12);
static constexpr Gp r13w = Gp(Gpw::kSignature, Gp::kIdR13);
static constexpr Gp r14w = Gp(Gpw::kSignature, Gp::kIdR14);
static constexpr Gp r15w = Gp(Gpw::kSignature, Gp::kIdR15);
static constexpr Gp ax = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdAx));
static constexpr Gp bx = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdBx));
static constexpr Gp cx = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdCx));
static constexpr Gp dx = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdDx));
static constexpr Gp sp = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdSp));
static constexpr Gp bp = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdBp));
static constexpr Gp si = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdSi));
static constexpr Gp di = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdDi));
static constexpr Gp r8w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR8));
static constexpr Gp r9w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR9));
static constexpr Gp r10w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR10));
static constexpr Gp r11w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR11));
static constexpr Gp r12w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR12));
static constexpr Gp r13w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR13));
static constexpr Gp r14w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR14));
static constexpr Gp r15w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR15));
static constexpr Gp eax = Gp(Gpd::kSignature, Gp::kIdAx);
static constexpr Gp ebx = Gp(Gpd::kSignature, Gp::kIdBx);
static constexpr Gp ecx = Gp(Gpd::kSignature, Gp::kIdCx);
static constexpr Gp edx = Gp(Gpd::kSignature, Gp::kIdDx);
static constexpr Gp esp = Gp(Gpd::kSignature, Gp::kIdSp);
static constexpr Gp ebp = Gp(Gpd::kSignature, Gp::kIdBp);
static constexpr Gp esi = Gp(Gpd::kSignature, Gp::kIdSi);
static constexpr Gp edi = Gp(Gpd::kSignature, Gp::kIdDi);
static constexpr Gp r8d = Gp(Gpd::kSignature, Gp::kIdR8);
static constexpr Gp r9d = Gp(Gpd::kSignature, Gp::kIdR9);
static constexpr Gp r10d = Gp(Gpd::kSignature, Gp::kIdR10);
static constexpr Gp r11d = Gp(Gpd::kSignature, Gp::kIdR11);
static constexpr Gp r12d = Gp(Gpd::kSignature, Gp::kIdR12);
static constexpr Gp r13d = Gp(Gpd::kSignature, Gp::kIdR13);
static constexpr Gp r14d = Gp(Gpd::kSignature, Gp::kIdR14);
static constexpr Gp r15d = Gp(Gpd::kSignature, Gp::kIdR15);
static constexpr Gp eax = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdAx));
static constexpr Gp ebx = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdBx));
static constexpr Gp ecx = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdCx));
static constexpr Gp edx = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdDx));
static constexpr Gp esp = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdSp));
static constexpr Gp ebp = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdBp));
static constexpr Gp esi = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdSi));
static constexpr Gp edi = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdDi));
static constexpr Gp r8d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR8));
static constexpr Gp r9d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR9));
static constexpr Gp r10d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR10));
static constexpr Gp r11d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR11));
static constexpr Gp r12d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR12));
static constexpr Gp r13d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR13));
static constexpr Gp r14d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR14));
static constexpr Gp r15d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR15));
static constexpr Gp rax = Gp(Gpq::kSignature, Gp::kIdAx);
static constexpr Gp rbx = Gp(Gpq::kSignature, Gp::kIdBx);
static constexpr Gp rcx = Gp(Gpq::kSignature, Gp::kIdCx);
static constexpr Gp rdx = Gp(Gpq::kSignature, Gp::kIdDx);
static constexpr Gp rsp = Gp(Gpq::kSignature, Gp::kIdSp);
static constexpr Gp rbp = Gp(Gpq::kSignature, Gp::kIdBp);
static constexpr Gp rsi = Gp(Gpq::kSignature, Gp::kIdSi);
static constexpr Gp rdi = Gp(Gpq::kSignature, Gp::kIdDi);
static constexpr Gp r8 = Gp(Gpq::kSignature, Gp::kIdR8);
static constexpr Gp r9 = Gp(Gpq::kSignature, Gp::kIdR9);
static constexpr Gp r10 = Gp(Gpq::kSignature, Gp::kIdR10);
static constexpr Gp r11 = Gp(Gpq::kSignature, Gp::kIdR11);
static constexpr Gp r12 = Gp(Gpq::kSignature, Gp::kIdR12);
static constexpr Gp r13 = Gp(Gpq::kSignature, Gp::kIdR13);
static constexpr Gp r14 = Gp(Gpq::kSignature, Gp::kIdR14);
static constexpr Gp r15 = Gp(Gpq::kSignature, Gp::kIdR15);
static constexpr Gp rax = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdAx));
static constexpr Gp rbx = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdBx));
static constexpr Gp rcx = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdCx));
static constexpr Gp rdx = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdDx));
static constexpr Gp rsp = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdSp));
static constexpr Gp rbp = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdBp));
static constexpr Gp rsi = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdSi));
static constexpr Gp rdi = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdDi));
static constexpr Gp r8 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR8));
static constexpr Gp r9 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR9));
static constexpr Gp r10 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR10));
static constexpr Gp r11 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR11));
static constexpr Gp r12 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR12));
static constexpr Gp r13 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR13));
static constexpr Gp r14 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR14));
static constexpr Gp r15 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR15));
static constexpr Xmm xmm0 = Xmm(0);
static constexpr Xmm xmm1 = Xmm(1);
@@ -815,28 +816,65 @@ using namespace regs;
//! Memory operand.
class Mem : public BaseMem {
public:
//! Additional bits of operand's signature used by `Mem`.
//! Additional bits of operand's signature used by `x86::Mem`.
enum AdditionalBits : uint32_t {
kSignatureMemSegmentShift = 16,
// Memory address type (2 bits).
// |........|........|XX......|........|
kSignatureMemAddrTypeShift = 14,
kSignatureMemAddrTypeMask = 0x03u << kSignatureMemAddrTypeShift,
// Memory shift amount (2 bits).
// |........|......XX|........|........|
kSignatureMemShiftValueShift = 16,
kSignatureMemShiftValueMask = 0x03u << kSignatureMemShiftValueShift,
// Memory segment reg (3 bits).
// |........|...XXX..|........|........|
kSignatureMemSegmentShift = 18,
kSignatureMemSegmentMask = 0x07u << kSignatureMemSegmentShift,
kSignatureMemShiftShift = 19,
kSignatureMemShiftMask = 0x03u << kSignatureMemShiftShift,
// Memory broadcast type (3 bits).
// |........|XXX.....|........|........|
kSignatureMemBroadcastShift = 21,
kSignatureMemBroadcastMask = 0x7u << kSignatureMemBroadcastShift
};
//! Address type.
enum AddrType : uint32_t {
//! Default address type, Assembler will select the best type when necessary.
kAddrTypeDefault = 0,
//! Absolute address type.
kAddrTypeAbs = 1,
//! Relative address type.
kAddrTypeRel = 2
};
//! Memory broadcast type.
enum Broadcast : uint32_t {
//! Broadcast {1to1}.
kBroadcast1To1 = 0,
//! Broadcast {1to2}.
kBroadcast1To2 = 1,
//! Broadcast {1to4}.
kBroadcast1To4 = 2,
//! Broadcast {1to8}.
kBroadcast1To8 = 3,
//! Broadcast {1to16}.
kBroadcast1To16 = 4,
//! Broadcast {1to32}.
kBroadcast1To32 = 5,
//! Broadcast {1to64}.
kBroadcast1To64 = 6
};
//! \cond
//! Shortcuts.
enum SignatureMem : uint32_t {
kSignatureMemAbs = kAddrTypeAbs << kSignatureMemAddrTypeShift,
kSignatureMemRel = kAddrTypeRel << kSignatureMemAddrTypeShift
};
//! \endcond
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
@@ -859,19 +897,19 @@ public:
: BaseMem(Decomposed { Label::kLabelTag, base.id(), 0, 0, off, size, flags }) {}
constexpr Mem(const Label& base, const BaseReg& index, uint32_t shift, int32_t off, uint32_t size = 0, uint32_t flags = 0) noexcept
: BaseMem(Decomposed { Label::kLabelTag, base.id(), index.type(), index.id(), off, size, flags | (shift << kSignatureMemShiftShift) }) {}
: BaseMem(Decomposed { Label::kLabelTag, base.id(), index.type(), index.id(), off, size, flags | (shift << kSignatureMemShiftValueShift) }) {}
constexpr Mem(const BaseReg& base, int32_t off, uint32_t size = 0, uint32_t flags = 0) noexcept
: BaseMem(Decomposed { base.type(), base.id(), 0, 0, off, size, flags }) {}
constexpr Mem(const BaseReg& base, const BaseReg& index, uint32_t shift, int32_t off, uint32_t size = 0, uint32_t flags = 0) noexcept
: BaseMem(Decomposed { base.type(), base.id(), index.type(), index.id(), off, size, flags | (shift << kSignatureMemShiftShift) }) {}
: BaseMem(Decomposed { base.type(), base.id(), index.type(), index.id(), off, size, flags | (shift << kSignatureMemShiftValueShift) }) {}
constexpr explicit Mem(uint64_t base, uint32_t size = 0, uint32_t flags = 0) noexcept
: BaseMem(Decomposed { 0, uint32_t(base >> 32), 0, 0, int32_t(uint32_t(base & 0xFFFFFFFFu)), size, flags }) {}
constexpr Mem(uint64_t base, const BaseReg& index, uint32_t shift = 0, uint32_t size = 0, uint32_t flags = 0) noexcept
: BaseMem(Decomposed { 0, uint32_t(base >> 32), index.type(), index.id(), int32_t(uint32_t(base & 0xFFFFFFFFu)), size, flags | (shift << kSignatureMemShiftShift) }) {}
: BaseMem(Decomposed { 0, uint32_t(base >> 32), index.type(), index.id(), int32_t(uint32_t(base & 0xFFFFFFFFu)), size, flags | (shift << kSignatureMemShiftValueShift) }) {}
constexpr Mem(Globals::Init_, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) noexcept
: BaseMem(Globals::Init, u0, u1, u2, u3) {}
@@ -918,6 +956,25 @@ public:
setShift(shift);
}
//! 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 the memory operand has a segment override.
constexpr bool hasSegment() const noexcept { return _hasSignaturePart<kSignatureMemSegmentMask>(); }
//! Returns the associated segment override as `SReg` operand.
@@ -933,13 +990,13 @@ public:
inline void resetSegment() noexcept { _setSignaturePart<kSignatureMemSegmentMask>(0); }
//! Tests whether the memory operand has shift (aka scale) value.
constexpr bool hasShift() const noexcept { return _hasSignaturePart<kSignatureMemShiftMask>(); }
constexpr bool hasShift() const noexcept { return _hasSignaturePart<kSignatureMemShiftValueMask>(); }
//! Returns the memory operand's shift (aka scale) value.
constexpr uint32_t shift() const noexcept { return _getSignaturePart<kSignatureMemShiftMask>(); }
constexpr uint32_t shift() const noexcept { return _getSignaturePart<kSignatureMemShiftValueMask>(); }
//! Sets the memory operand's shift (aka scale) value.
inline void setShift(uint32_t shift) noexcept { _setSignaturePart<kSignatureMemShiftMask>(shift); }
inline void setShift(uint32_t shift) noexcept { _setSignaturePart<kSignatureMemShiftValueMask>(shift); }
//! Resets the memory operand's shift (aka scale) value to zero.
inline void resetShift() noexcept { _setSignaturePart<kSignatureMemShiftMask>(0); }
inline void resetShift() noexcept { _setSignaturePart<kSignatureMemShiftValueMask>(0); }
//! Tests whether the memory operand has broadcast {1tox}.
constexpr bool hasBroadcast() const noexcept { return _hasSignaturePart<kSignatureMemBroadcastMask>(); }
@@ -1003,28 +1060,28 @@ static constexpr Mem ptr(uint64_t base, const Vec& index, uint32_t shift = 0, ui
//! Creates `[base]` absolute memory operand (absolute).
static constexpr Mem ptr_abs(uint64_t base, uint32_t size = 0) noexcept {
return Mem(base, size, BaseMem::kSignatureMemAbs);
return Mem(base, size, Mem::kSignatureMemAbs);
}
//! Creates `[base + (index.reg << shift)]` absolute memory operand (absolute).
static constexpr Mem ptr_abs(uint64_t base, const Reg& index, uint32_t shift = 0, uint32_t size = 0) noexcept {
return Mem(base, index, shift, size, BaseMem::kSignatureMemAbs);
return Mem(base, index, shift, size, Mem::kSignatureMemAbs);
}
//! Creates `[base + (index.reg << shift)]` absolute memory operand (absolute).
static constexpr Mem ptr_abs(uint64_t base, const Vec& index, uint32_t shift = 0, uint32_t size = 0) noexcept {
return Mem(base, index, shift, size, BaseMem::kSignatureMemAbs);
return Mem(base, index, shift, size, Mem::kSignatureMemAbs);
}
//! Creates `[base]` relative memory operand (relative).
static constexpr Mem ptr_rel(uint64_t base, uint32_t size = 0) noexcept {
return Mem(base, size, BaseMem::kSignatureMemRel);
return Mem(base, size, Mem::kSignatureMemRel);
}
//! Creates `[base + (index.reg << shift)]` relative memory operand (relative).
static constexpr Mem ptr_rel(uint64_t base, const Reg& index, uint32_t shift = 0, uint32_t size = 0) noexcept {
return Mem(base, index, shift, size, BaseMem::kSignatureMemRel);
return Mem(base, index, shift, size, Mem::kSignatureMemRel);
}
//! Creates `[base + (index.reg << shift)]` relative memory operand (relative).
static constexpr Mem ptr_rel(uint64_t base, const Vec& index, uint32_t shift = 0, uint32_t size = 0) noexcept {
return Mem(base, index, shift, size, BaseMem::kSignatureMemRel);
return Mem(base, index, shift, size, Mem::kSignatureMemRel);
}
// Definition of memory operand constructors that use platform independent naming.
@@ -1051,34 +1108,6 @@ ASMJIT_MEM_PTR(xmmword_ptr, 16)
ASMJIT_MEM_PTR(ymmword_ptr, 32)
ASMJIT_MEM_PTR(zmmword_ptr, 64)
// ============================================================================
// [asmjit::x86::OpData]
// ============================================================================
struct OpData {
//! Information about all architecture registers.
ArchRegs archRegs;
};
ASMJIT_VARAPI const OpData opData;
//! \cond
// ... Reg methods that require `opData`.
inline uint32_t Reg::groupOf(uint32_t rType) noexcept {
ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
return opData.archRegs.regInfo[rType].group();
}
inline uint32_t Reg::typeIdOf(uint32_t rType) noexcept {
ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
return opData.archRegs.regTypeToTypeId[rType];
}
inline uint32_t Reg::signatureOf(uint32_t rType) noexcept {
ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
return opData.archRegs.regInfo[rType].signature();
}
//! \endcond
//! \}
ASMJIT_END_SUB_NAMESPACE

View File

@@ -31,7 +31,7 @@
#include "../x86/x86compiler.h"
#include "../x86/x86instapi_p.h"
#include "../x86/x86instdb_p.h"
#include "../x86/x86internal_p.h"
#include "../x86/x86emithelper_p.h"
#include "../x86/x86rapass_p.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
@@ -86,20 +86,20 @@ static ASMJIT_INLINE uint32_t raMemIndexRwFlags(uint32_t flags) noexcept {
}
// ============================================================================
// [asmjit::x86::X86RACFGBuilder]
// [asmjit::x86::RACFGBuilder]
// ============================================================================
class X86RACFGBuilder : public RACFGBuilder<X86RACFGBuilder> {
class RACFGBuilder : public RACFGBuilderT<RACFGBuilder> {
public:
uint32_t _arch;
bool _is64Bit;
bool _avxEnabled;
inline X86RACFGBuilder(X86RAPass* pass) noexcept
: RACFGBuilder<X86RACFGBuilder>(pass),
inline RACFGBuilder(X86RAPass* pass) noexcept
: RACFGBuilderT<RACFGBuilder>(pass),
_arch(pass->cc()->arch()),
_is64Bit(pass->registerSize() == 8),
_avxEnabled(pass->_avxEnabled) {
_avxEnabled(pass->avxEnabled()) {
}
inline Compiler* cc() const noexcept { return static_cast<Compiler*>(_cc); }
@@ -123,10 +123,10 @@ public:
};
// ============================================================================
// [asmjit::x86::X86RACFGBuilder - OnInst]
// [asmjit::x86::RACFGBuilder - OnInst]
// ============================================================================
Error X86RACFGBuilder::onInst(InstNode* inst, uint32_t& controlType, RAInstBuilder& ib) noexcept {
Error RACFGBuilder::onInst(InstNode* inst, uint32_t& controlType, RAInstBuilder& ib) noexcept {
InstRWInfo rwInfo;
uint32_t instId = inst->id();
@@ -400,10 +400,10 @@ Error X86RACFGBuilder::onInst(InstNode* inst, uint32_t& controlType, RAInstBuild
}
// ============================================================================
// [asmjit::x86::X86RACFGBuilder - OnCall]
// [asmjit::x86::RACFGBuilder - OnInvoke]
// ============================================================================
Error X86RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept {
Error RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept {
const FuncDetail& fd = invokeNode->detail();
uint32_t argCount = invokeNode->argCount();
@@ -503,7 +503,7 @@ Error X86RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept {
if (workReg->group() != Reg::kGroupVec)
return DebugUtils::errored(kErrorInvalidAssignment);
Reg dst = Reg(workReg->signature(), workReg->virtId());
Reg dst = Reg::fromSignatureAndId(workReg->signature(), workReg->virtId());
Mem mem;
uint32_t typeId = Type::baseOf(workReg->typeId());
@@ -543,7 +543,7 @@ Error X86RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept {
}
}
// This block has function invokeNode(s).
// This block has function call(s).
_curBlock->addFlags(RABlock::kFlagHasFuncCalls);
_pass->func()->frame().addAttributes(FuncFrame::kAttrHasFuncCalls);
_pass->func()->frame().updateCallStackSize(fd.argStackSize());
@@ -551,7 +551,7 @@ Error X86RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept {
return kErrorOk;
}
Error X86RACFGBuilder::onInvoke(InvokeNode* invokeNode, RAInstBuilder& ib) noexcept {
Error RACFGBuilder::onInvoke(InvokeNode* invokeNode, RAInstBuilder& ib) noexcept {
uint32_t argCount = invokeNode->argCount();
const FuncDetail& fd = invokeNode->detail();
@@ -629,7 +629,7 @@ Error X86RACFGBuilder::onInvoke(InvokeNode* invokeNode, RAInstBuilder& ib) noexc
}
// ============================================================================
// [asmjit::x86::X86RACFGBuilder - MoveVecToPtr]
// [asmjit::x86::RACFGBuilder - MoveVecToPtr]
// ============================================================================
static uint32_t x86VecRegSignatureBySize(uint32_t size) noexcept {
@@ -641,7 +641,7 @@ static uint32_t x86VecRegSignatureBySize(uint32_t size) noexcept {
return Xmm::kSignature;
}
Error X86RACFGBuilder::moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg, const Vec& src, BaseReg* out) noexcept {
Error RACFGBuilder::moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg, const Vec& src, BaseReg* out) noexcept {
DebugUtils::unused(invokeNode);
ASMJIT_ASSERT(arg.isReg());
@@ -656,7 +656,7 @@ Error X86RACFGBuilder::moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg
_funcNode->frame().updateCallStackAlignment(argSize);
invokeNode->detail()._argStackSize = argStackOffset + argSize;
Vec vecReg(x86VecRegSignatureBySize(argSize), src.id());
Vec vecReg = Vec::fromSignatureAndId(x86VecRegSignatureBySize(argSize), src.id());
Mem vecPtr = ptr(_pass->_sp.as<Gp>(), int32_t(argStackOffset));
uint32_t vMovInstId = choose(Inst::kIdMovaps, Inst::kIdVmovaps);
@@ -666,7 +666,7 @@ Error X86RACFGBuilder::moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg
ASMJIT_PROPAGATE(cc()->_newReg(out, cc()->_gpRegInfo.type(), nullptr));
VirtReg* vReg = cc()->virtRegById(out->id());
vReg->setWeight(RAPass::kCallArgWeight);
vReg->setWeight(BaseRAPass::kCallArgWeight);
ASMJIT_PROPAGATE(cc()->lea(out->as<Gp>(), vecPtr));
ASMJIT_PROPAGATE(cc()->emit(vMovInstId, ptr(out->as<Gp>()), vecReg));
@@ -680,10 +680,10 @@ Error X86RACFGBuilder::moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg
}
// ============================================================================
// [asmjit::x86::X86RACFGBuilder - MoveImmToRegArg]
// [asmjit::x86::RACFGBuilder - MoveImmToRegArg]
// ============================================================================
Error X86RACFGBuilder::moveImmToRegArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_, BaseReg* out) noexcept {
Error RACFGBuilder::moveImmToRegArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_, BaseReg* out) noexcept {
DebugUtils::unused(invokeNode);
ASMJIT_ASSERT(arg.isReg());
@@ -718,16 +718,16 @@ MovU32:
}
ASMJIT_PROPAGATE(cc()->_newReg(out, rTypeId, nullptr));
cc()->virtRegById(out->id())->setWeight(RAPass::kCallArgWeight);
cc()->virtRegById(out->id())->setWeight(BaseRAPass::kCallArgWeight);
return cc()->mov(out->as<x86::Gp>(), imm);
}
// ============================================================================
// [asmjit::x86::X86RACFGBuilder - MoveImmToStackArg]
// [asmjit::x86::RACFGBuilder - MoveImmToStackArg]
// ============================================================================
Error X86RACFGBuilder::moveImmToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_) noexcept {
Error RACFGBuilder::moveImmToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_) noexcept {
DebugUtils::unused(invokeNode);
ASMJIT_ASSERT(arg.isStack());
@@ -786,10 +786,10 @@ MovU32:
}
// ============================================================================
// [asmjit::x86::X86RACFGBuilder - MoveRegToStackArg]
// [asmjit::x86::RACFGBuilder - MoveRegToStackArg]
// ============================================================================
Error X86RACFGBuilder::moveRegToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const BaseReg& reg) noexcept {
Error RACFGBuilder::moveRegToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const BaseReg& reg) noexcept {
DebugUtils::unused(invokeNode);
ASMJIT_ASSERT(arg.isStack());
@@ -992,10 +992,10 @@ MovXmmQ:
}
// ============================================================================
// [asmjit::x86::X86RACFGBuilder - OnReg]
// [asmjit::x86::RACFGBuilder - OnReg]
// ============================================================================
Error X86RACFGBuilder::onBeforeRet(FuncRetNode* funcRet) noexcept {
Error RACFGBuilder::onBeforeRet(FuncRetNode* funcRet) noexcept {
const FuncDetail& funcDetail = _pass->func()->detail();
const Operand* opArray = funcRet->operands();
uint32_t opCount = funcRet->opCount();
@@ -1020,7 +1020,7 @@ Error X86RACFGBuilder::onBeforeRet(FuncRetNode* funcRet) noexcept {
if (workReg->group() != Reg::kGroupVec)
return DebugUtils::errored(kErrorInvalidAssignment);
Reg src = Reg(workReg->signature(), workReg->virtId());
Reg src = Reg::fromSignatureAndId(workReg->signature(), workReg->virtId());
Mem mem;
uint32_t typeId = Type::baseOf(workReg->typeId());
@@ -1052,7 +1052,7 @@ Error X86RACFGBuilder::onBeforeRet(FuncRetNode* funcRet) noexcept {
return kErrorOk;
}
Error X86RACFGBuilder::onRet(FuncRetNode* funcRet, RAInstBuilder& ib) noexcept {
Error RACFGBuilder::onRet(FuncRetNode* funcRet, RAInstBuilder& ib) noexcept {
const FuncDetail& funcDetail = _pass->func()->detail();
const Operand* opArray = funcRet->operands();
uint32_t opCount = funcRet->opCount();
@@ -1096,8 +1096,7 @@ Error X86RACFGBuilder::onRet(FuncRetNode* funcRet, RAInstBuilder& ib) noexcept {
// ============================================================================
X86RAPass::X86RAPass() noexcept
: RAPass(),
_avxEnabled(false) {}
: BaseRAPass() { _iEmitHelper = &_emitHelper; }
X86RAPass::~X86RAPass() noexcept {}
// ============================================================================
@@ -1108,9 +1107,10 @@ void X86RAPass::onInit() noexcept {
uint32_t arch = cc()->arch();
uint32_t baseRegCount = Environment::is32Bit(arch) ? 8u : 16u;
_archRegsInfo = &opData.archRegs;
_archTraits[Reg::kGroupGp] |= RAArchTraits::kHasSwap;
_emitHelper._emitter = _cb;
_emitHelper._avxEnabled = _func->frame().isAvxEnabled();
_archTraits = &ArchTraits::byArch(arch);
_physRegCount.set(Reg::kGroupGp , baseRegCount);
_physRegCount.set(Reg::kGroupVec , baseRegCount);
_physRegCount.set(Reg::kGroupMm , 8);
@@ -1135,7 +1135,6 @@ void X86RAPass::onInit() noexcept {
_sp = cc()->zsp();
_fp = cc()->zbp();
_avxEnabled = _func->frame().isAvxEnabled();
}
void X86RAPass::onDone() noexcept {}
@@ -1145,17 +1144,17 @@ void X86RAPass::onDone() noexcept {}
// ============================================================================
Error X86RAPass::buildCFG() noexcept {
return X86RACFGBuilder(this).run();
return RACFGBuilder(this).run();
}
// ============================================================================
// [asmjit::x86::X86RAPass - OnEmit]
// ============================================================================
Error X86RAPass::onEmitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept {
Error X86RAPass::emitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept {
RAWorkReg* wReg = workRegById(workId);
BaseReg dst(wReg->info().signature(), dstPhysId);
BaseReg src(wReg->info().signature(), srcPhysId);
BaseReg dst = BaseReg::fromSignatureAndId(wReg->info().signature(), dstPhysId);
BaseReg src = BaseReg::fromSignatureAndId(wReg->info().signature(), srcPhysId);
const char* comment = nullptr;
@@ -1166,10 +1165,10 @@ Error X86RAPass::onEmitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhy
}
#endif
return X86Internal::emitRegMove(cc()->as<Emitter>(), dst, src, wReg->typeId(), _avxEnabled, comment);
return _emitHelper.emitRegMove(dst, src, wReg->typeId(), comment);
}
Error X86RAPass::onEmitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept {
Error X86RAPass::emitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept {
RAWorkReg* waReg = workRegById(aWorkId);
RAWorkReg* wbReg = workRegById(bWorkId);
@@ -1184,13 +1183,15 @@ Error X86RAPass::onEmitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId
}
#endif
return cc()->emit(Inst::kIdXchg, Reg(sign, aPhysId), Reg(sign, bPhysId));
return cc()->emit(Inst::kIdXchg,
Reg::fromSignatureAndId(sign, aPhysId),
Reg::fromSignatureAndId(sign, bPhysId));
}
Error X86RAPass::onEmitLoad(uint32_t workId, uint32_t dstPhysId) noexcept {
Error X86RAPass::emitLoad(uint32_t workId, uint32_t dstPhysId) noexcept {
RAWorkReg* wReg = workRegById(workId);
BaseReg dstReg(wReg->info().signature(), dstPhysId);
BaseMem srcMem(workRegAsMem(wReg));
BaseReg dstReg = BaseReg::fromSignatureAndId(wReg->info().signature(), dstPhysId);
BaseMem srcMem = BaseMem(workRegAsMem(wReg));
const char* comment = nullptr;
@@ -1201,13 +1202,13 @@ Error X86RAPass::onEmitLoad(uint32_t workId, uint32_t dstPhysId) noexcept {
}
#endif
return X86Internal::emitRegMove(cc()->as<Emitter>(), dstReg, srcMem, wReg->typeId(), _avxEnabled, comment);
return _emitHelper.emitRegMove(dstReg, srcMem, wReg->typeId(), comment);
}
Error X86RAPass::onEmitSave(uint32_t workId, uint32_t srcPhysId) noexcept {
Error X86RAPass::emitSave(uint32_t workId, uint32_t srcPhysId) noexcept {
RAWorkReg* wReg = workRegById(workId);
BaseMem dstMem(workRegAsMem(wReg));
BaseReg srcReg(wReg->info().signature(), srcPhysId);
BaseMem dstMem = BaseMem(workRegAsMem(wReg));
BaseReg srcReg = BaseReg::fromSignatureAndId(wReg->info().signature(), srcPhysId);
const char* comment = nullptr;
@@ -1218,14 +1219,14 @@ Error X86RAPass::onEmitSave(uint32_t workId, uint32_t srcPhysId) noexcept {
}
#endif
return X86Internal::emitRegMove(cc()->as<Emitter>(), dstMem, srcReg, wReg->typeId(), _avxEnabled, comment);
return _emitHelper.emitRegMove(dstMem, srcReg, wReg->typeId(), comment);
}
Error X86RAPass::onEmitJump(const Label& label) noexcept {
Error X86RAPass::emitJump(const Label& label) noexcept {
return cc()->jmp(label);
}
Error X86RAPass::onEmitPreCall(InvokeNode* invokeNode) noexcept {
Error X86RAPass::emitPreCall(InvokeNode* invokeNode) noexcept {
if (invokeNode->detail().hasVarArgs() && cc()->is64Bit()) {
const FuncDetail& fd = invokeNode->detail();
uint32_t argCount = invokeNode->argCount();

View File

@@ -32,14 +32,12 @@
#include "../core/rapass_p.h"
#include "../x86/x86assembler.h"
#include "../x86/x86compiler.h"
#include "../x86/x86emithelper_p.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! \cond INTERNAL
//! \brief X86/X64 register allocation.
//! \addtogroup asmjit_ra
//! \addtogroup asmjit_x86
//! \{
// ============================================================================
@@ -50,12 +48,12 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//!
//! Takes care of generating function prologs and epilogs, and also performs
//! register allocation.
class X86RAPass : public RAPass {
class X86RAPass : public BaseRAPass {
public:
ASMJIT_NONCOPYABLE(X86RAPass)
typedef RAPass Base;
typedef BaseRAPass Base;
bool _avxEnabled;
EmitHelper _emitHelper;
// --------------------------------------------------------------------------
// [Construction / Destruction]
@@ -71,12 +69,17 @@ public:
//! Returns the compiler casted to `x86::Compiler`.
inline Compiler* cc() const noexcept { return static_cast<Compiler*>(_cb); }
//! Returns emit helper.
inline EmitHelper* emitHelper() noexcept { return &_emitHelper; }
// --------------------------------------------------------------------------
// [Utilities]
// --------------------------------------------------------------------------
inline bool avxEnabled() const noexcept { return _emitHelper._avxEnabled; }
inline uint32_t choose(uint32_t sseInstId, uint32_t avxInstId) noexcept {
return _avxEnabled ? avxInstId : sseInstId;
return avxEnabled() ? avxInstId : sseInstId;
}
// --------------------------------------------------------------------------
@@ -96,14 +99,14 @@ public:
// [Emit]
// --------------------------------------------------------------------------
Error onEmitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept override;
Error onEmitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept override;
Error emitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept override;
Error emitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept override;
Error onEmitLoad(uint32_t workId, uint32_t dstPhysId) noexcept override;
Error onEmitSave(uint32_t workId, uint32_t srcPhysId) noexcept override;
Error emitLoad(uint32_t workId, uint32_t dstPhysId) noexcept override;
Error emitSave(uint32_t workId, uint32_t srcPhysId) noexcept override;
Error onEmitJump(const Label& label) noexcept override;
Error onEmitPreCall(InvokeNode* invokeNode) noexcept override;
Error emitJump(const Label& label) noexcept override;
Error emitPreCall(InvokeNode* invokeNode) noexcept override;
};
//! \}

View File

@@ -0,0 +1,247 @@
// 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 <asmjit/core.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory>
#include <vector>
#include "./cmdline.h"
#include "./asmjit_test_compiler.h"
#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86
#include <asmjit/x86.h>
void compiler_add_x86_tests(TestApp& app);
#endif
#if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM == 64
#include <asmjit/a64.h>
void compiler_add_a64_tests(TestApp& app);
#endif
#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86
#define ASMJIT_HAVE_WORKING_JIT
#endif
#if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM == 64
#define ASMJIT_HAVE_WORKING_JIT
#endif
using namespace asmjit;
// ============================================================================
// [TestApp]
// ============================================================================
static const char* archAsString(uint32_t arch) {
switch (arch) {
case Environment::kArchX86: return "X86";
case Environment::kArchX64: return "X64";
case Environment::kArchARM: return "A32";
case Environment::kArchThumb: return "T32";
case Environment::kArchAArch64: return "A64";
default: return "Unknown";
}
}
int TestApp::handleArgs(int argc, const char* const* argv) {
CmdLine cmd(argc, argv);
if (cmd.hasArg("--verbose")) _verbose = true;
if (cmd.hasArg("--dump-asm")) _dumpAsm = true;
if (cmd.hasArg("--dump-hex")) _dumpHex = true;
return 0;
}
void TestApp::showInfo() {
printf("AsmJit Compiler Test-Suite v%u.%u.%u [Arch=%s]:\n",
unsigned((ASMJIT_LIBRARY_VERSION >> 16) ),
unsigned((ASMJIT_LIBRARY_VERSION >> 8) & 0xFF),
unsigned((ASMJIT_LIBRARY_VERSION ) & 0xFF),
archAsString(Environment::kArchHost));
printf(" [%s] Verbose (use --verbose to turn verbose output ON)\n", _verbose ? "x" : " ");
printf(" [%s] DumpAsm (use --dump-asm to turn assembler dumps ON)\n", _dumpAsm ? "x" : " ");
printf(" [%s] DumpHex (use --dump-hex to dump binary in hexadecimal)\n", _dumpHex ? "x" : " ");
printf("\n");
}
int TestApp::run() {
#ifndef ASMJIT_HAVE_WORKING_JIT
return 0;
#else
#ifndef ASMJIT_NO_LOGGING
uint32_t kFormatFlags = FormatOptions::kFlagMachineCode |
FormatOptions::kFlagExplainImms |
FormatOptions::kFlagRegCasts |
FormatOptions::kFlagAnnotations |
FormatOptions::kFlagDebugPasses |
FormatOptions::kFlagDebugRA ;
FileLogger fileLogger(stdout);
fileLogger.addFlags(kFormatFlags);
StringLogger stringLogger;
stringLogger.addFlags(kFormatFlags);
#endif
for (std::unique_ptr<TestCase>& test : _tests) {
JitRuntime runtime;
CodeHolder code;
SimpleErrorHandler errorHandler;
code.init(runtime.environment());
code.setErrorHandler(&errorHandler);
#ifndef ASMJIT_NO_LOGGING
if (_verbose) {
code.setLogger(&fileLogger);
}
else {
stringLogger.clear();
code.setLogger(&stringLogger);
}
#endif
printf("[Test] %s", test->name());
#ifndef ASMJIT_NO_LOGGING
if (_verbose) printf("\n");
#endif
#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86
x86::Compiler cc(&code);
#endif
#if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM == 64
arm::Compiler cc(&code);
#endif
test->compile(cc);
void* func = nullptr;
Error err = errorHandler._err;
if (!err)
err = cc.finalize();
#ifndef ASMJIT_NO_LOGGING
if (_dumpAsm) {
if (!_verbose) printf("\n");
String sb;
Formatter::formatNodeList(sb, kFormatFlags, &cc);
printf("%s", sb.data());
}
#endif
if (err == kErrorOk)
err = runtime.add(&func, &code);
if (err == kErrorOk && _dumpHex) {
String sb;
sb.appendHex((void*)func, code.codeSize());
printf("\n (HEX: %s)\n", sb.data());
}
if (_verbose)
fflush(stdout);
if (err == kErrorOk) {
_outputSize += code.codeSize();
StringTmp<128> result;
StringTmp<128> expect;
if (test->run(func, result, expect)) {
if (!_verbose) printf(" [OK]\n");
}
else {
if (!_verbose) printf(" [FAILED]\n");
#ifndef ASMJIT_NO_LOGGING
if (!_verbose) printf("%s", stringLogger.data());
#endif
printf("[Status]\n");
printf(" Returned: %s\n", result.data());
printf(" Expected: %s\n", expect.data());
_nFailed++;
}
if (_dumpAsm)
printf("\n");
runtime.release(func);
}
else {
if (!_verbose) printf(" [FAILED]\n");
#ifndef ASMJIT_NO_LOGGING
if (!_verbose) printf("%s", stringLogger.data());
#endif
printf("[Status]\n");
printf(" ERROR 0x%08X: %s\n", unsigned(err), errorHandler._message.data());
_nFailed++;
}
}
if (_nFailed == 0)
printf("\nSuccess:\n All %u tests passed\n", unsigned(_tests.size()));
else
printf("\nFailure:\n %u %s of %u failed\n", _nFailed, _nFailed == 1 ? "test" : "tests", unsigned(_tests.size()));
printf(" OutputSize=%zu\n", _outputSize);
printf("\n");
return _nFailed == 0 ? 0 : 1;
#endif
}
// ============================================================================
// [Main]
// ============================================================================
int main(int argc, char* argv[]) {
TestApp app;
app.handleArgs(argc, argv);
app.showInfo();
#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86
compiler_add_x86_tests(app);
#endif
#if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM == 64
compiler_add_a64_tests(app);
#endif
return app.run();
}

103
test/asmjit_test_compiler.h Normal file
View File

@@ -0,0 +1,103 @@
// 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_TEST_COMPILER_H_INCLUDED
#define ASMJIT_TEST_COMPILER_H_INCLUDED
#include <asmjit/core.h>
#include <memory>
#include <vector>
// ============================================================================
// [SimpleErrorHandler]
// ============================================================================
class SimpleErrorHandler : public asmjit::ErrorHandler {
public:
SimpleErrorHandler()
: _err(asmjit::kErrorOk) {}
virtual void handleError(asmjit::Error err, const char* message, asmjit::BaseEmitter* origin) {
asmjit::DebugUtils::unused(origin);
_err = err;
_message.assign(message);
}
asmjit::Error _err;
asmjit::String _message;
};
// ============================================================================
// [TestCase]
// ============================================================================
//! A test case interface for testing AsmJit's Compiler.
class TestCase {
public:
TestCase(const char* name = nullptr) {
if (name)
_name.assign(name);
}
virtual ~TestCase() {}
inline const char* name() const { return _name.data(); }
virtual void compile(asmjit::BaseCompiler& cc) = 0;
virtual bool run(void* func, asmjit::String& result, asmjit::String& expect) = 0;
asmjit::String _name;
};
// ============================================================================
// [TestApp]
// ============================================================================
class TestApp {
public:
std::vector<std::unique_ptr<TestCase>> _tests;
unsigned _nFailed = 0;
size_t _outputSize = 0;
bool _verbose = false;
bool _dumpAsm = false;
bool _dumpHex = false;
TestApp() noexcept {}
~TestApp() noexcept {}
void add(TestCase* test) noexcept {
_tests.push_back(std::unique_ptr<TestCase>(test));
}
template<class T>
inline void addT() { T::add(*this); }
int handleArgs(int argc, const char* const* argv);
void showInfo();
int run();
};
#endif // ASMJIT_TEST_COMPILER_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@@ -48,7 +48,7 @@ static const char* archToString(uint32_t arch) noexcept {
case Environment::kArchARM : return "ARM";
case Environment::kArchThumb : return "Thumb";
case Environment::kArchAArch64 : return "AArch64";
case Environment::kArchMIPS_LE : return "MIPS";
case Environment::kArchMIPS32_LE: return "MIPS32";
case Environment::kArchMIPS64_LE: return "MIPS64";
default: return "Unknown";
}

View File

@@ -42,7 +42,7 @@ static const char* archToString(uint32_t arch) noexcept {
case Environment::kArchARM : return "ARM";
case Environment::kArchThumb : return "Thumb";
case Environment::kArchAArch64 : return "AArch64";
case Environment::kArchMIPS_LE : return "MIPS";
case Environment::kArchMIPS32_LE: return "MIPS";
case Environment::kArchMIPS64_LE: return "MIPS64";
default: return "Unknown";
}

View File

@@ -79,6 +79,41 @@ static void printInfo(uint32_t arch, const BaseInst& inst, const Operand_* opera
sb.append("\n");
}
if (rw.readFlags() | rw.writeFlags()) {
sb.append("Flags: \n");
struct FlagMap {
uint32_t flag;
char name[4];
};
static const FlagMap flagMap[] = {
{ x86::Status::kCF, "CF" },
{ x86::Status::kOF, "OF" },
{ x86::Status::kSF, "SF" },
{ x86::Status::kZF, "ZF" },
{ x86::Status::kAF, "AF" },
{ x86::Status::kPF, "PF" },
{ x86::Status::kDF, "DF" },
{ x86::Status::kIF, "IF" },
{ x86::Status::kAC, "AC" },
{ x86::Status::kC0, "C0" },
{ x86::Status::kC1, "C1" },
{ x86::Status::kC2, "C2" },
{ x86::Status::kC3, "C3" }
};
sb.append(" ");
for (uint32_t f = 0; f < 13; f++) {
char c = accessLetter((rw.readFlags() & flagMap[f].flag) != 0,
(rw.writeFlags() & flagMap[f].flag) != 0);
if (c != '_')
sb.appendFormat("%s=%c ", flagMap[f].name, c);
}
sb.append("\n");
}
// CPU Features
// ------------

83
test/cmdline.h Normal file
View File

@@ -0,0 +1,83 @@
// 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_TEST_CMDLINE_H_INCLUDED
#define ASMJIT_TEST_CMDLINE_H_INCLUDED
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
// ============================================================================
// [CmdLine]
// ============================================================================
class CmdLine {
public:
int _argc;
const char* const* _argv;
CmdLine(int argc, const char* const* argv)
: _argc(argc),
_argv(argv) {}
bool hasArg(const char* key) const {
for (int i = 1; i < _argc; i++)
if (strcmp(key, _argv[i]) == 0)
return true;
return false;
}
const char* valueOf(const char* key, const char* defaultValue) const {
size_t keySize = strlen(key);
for (int i = 1; i < _argc; i++) {
const char* val = _argv[i];
if (strlen(val) >= keySize + 1 && val[keySize] == '=' && memcmp(val, key, keySize) == 0)
return val + keySize + 1;
}
return defaultValue;
}
int valueAsInt(const char* key, int defaultValue) const {
const char* val = valueOf(key, nullptr);
if (val == nullptr || val[0] == '\0')
return defaultValue;
return atoi(val);
}
unsigned valueAsUInt(const char* key, unsigned defaultValue) const {
const char* val = valueOf(key, nullptr);
if (val == nullptr || val[0] == '\0')
return defaultValue;
int v = atoi(val);
if (v < 0)
return defaultValue;
else
return unsigned(v);
}
};
#endif // ASMJIT_TEST_CMDLINE_H_INCLUDED

View File

@@ -33,7 +33,7 @@ if [ -f ${BUILD_DIR}/asmjit_test_x86_instinfo ]; then
eval "$RUN_CMD ${BUILD_DIR}/asmjit_test_x86_instinfo"
fi
if [ -f ${BUILD_DIR}asmjit_test_x86_cc ]; then
if [ -f ${BUILD_DIR}asmjit_test_compiler ]; then
echo ""
eval "$RUN_CMD ${BUILD_DIR}/asmjit_test_x86_cc"
eval "$RUN_CMD ${BUILD_DIR}/asmjit_test_compiler"
fi