mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-17 12:34:35 +03:00
Preparation for AArch64 support
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]
|
||||
// ============================================================================
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
164
src/asmjit/core/archcommons.h
Normal file
164
src/asmjit/core/archcommons.h
Normal 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
|
||||
155
src/asmjit/core/archtraits.cpp
Normal file
155
src/asmjit/core/archtraits.cpp
Normal 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
|
||||
174
src/asmjit/core/archtraits.h
Normal file
174
src/asmjit/core/archtraits.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
//! \{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
//! \{
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
//! \}
|
||||
|
||||
|
||||
151
src/asmjit/core/codewriter.cpp
Normal file
151
src/asmjit/core/codewriter.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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, ®Info);
|
||||
|
||||
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);
|
||||
|
||||
@@ -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]
|
||||
// ============================================================================
|
||||
|
||||
170
src/asmjit/core/compilerdefs.h
Normal file
170
src/asmjit/core/compilerdefs.h
Normal 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
|
||||
|
||||
@@ -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"
|
||||
|
||||
351
src/asmjit/core/emithelper.cpp
Normal file
351
src/asmjit/core/emithelper.cpp
Normal 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
|
||||
83
src/asmjit/core/emithelper_p.h
Normal file
83
src/asmjit/core/emithelper_p.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
315
src/asmjit/core/funcargscontext.cpp
Normal file
315
src/asmjit/core/funcargscontext.cpp
Normal 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
|
||||
224
src/asmjit/core/funcargscontext_p.h
Normal file
224
src/asmjit/core/funcargscontext_p.h
Normal 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
|
||||
@@ -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@
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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); }
|
||||
|
||||
//! \}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
//! \}
|
||||
};
|
||||
|
||||
@@ -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]
|
||||
// ============================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
150
src/asmjit/x86/x86archtraits_p.h
Normal file
150
src/asmjit/x86/x86archtraits_p.h
Normal file
@@ -0,0 +1,150 @@
|
||||
// AsmJit - Machine code generation for C++
|
||||
//
|
||||
// * Official AsmJit Home Page: https://asmjit.com
|
||||
// * Official Github Repository: https://github.com/asmjit/asmjit
|
||||
//
|
||||
// Copyright (c) 2008-2020 The AsmJit Authors
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
603
src/asmjit/x86/x86emithelper.cpp
Normal file
603
src/asmjit/x86/x86emithelper.cpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
//! \}
|
||||
|
||||
@@ -168,6 +168,7 @@ public:
|
||||
|
||||
inline Features() noexcept
|
||||
: BaseFeatures() {}
|
||||
|
||||
inline Features(const Features& other) noexcept
|
||||
: BaseFeatures(other) {}
|
||||
|
||||
|
||||
@@ -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
531
src/asmjit/x86/x86func.cpp
Normal 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
|
||||
@@ -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
|
||||
@@ -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]; }
|
||||
}
|
||||
|
||||
|
||||
@@ -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) },
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
247
test/asmjit_test_compiler.cpp
Normal file
247
test/asmjit_test_compiler.cpp
Normal 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
103
test/asmjit_test_compiler.h
Normal 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
|
||||
4238
test/asmjit_test_compiler_x86.cpp
Normal file
4238
test/asmjit_test_compiler_x86.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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
83
test/cmdline.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user