Preparation for AArch64 support

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

View File

@@ -45,10 +45,6 @@ if (NOT DEFINED ASMJIT_STATIC)
set(ASMJIT_STATIC ${ASMJIT_EMBED}) set(ASMJIT_STATIC ${ASMJIT_EMBED})
endif() endif()
if (NOT DEFINED ASMJIT_BUILD_ARM)
set(ASMJIT_BUILD_ARM FALSE)
endif()
if (NOT DEFINED ASMJIT_BUILD_X86) if (NOT DEFINED ASMJIT_BUILD_X86)
set(ASMJIT_BUILD_X86 FALSE) set(ASMJIT_BUILD_X86 FALSE)
endif() 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_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_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_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] # [AsmJit - Project]
@@ -140,6 +135,7 @@ function(asmjit_add_target target target_type)
add_library(${target} ${target_type} ${X_SOURCES}) add_library(${target} ${target_type} ${X_SOURCES})
endif() endif()
set_target_properties(${target} PROPERTIES DEFINE_SYMBOL "")
target_link_libraries(${target} PRIVATE ${X_LIBRARIES}) target_link_libraries(${target} PRIVATE ${X_LIBRARIES})
# target_link_options was added in cmake v3.13, don't use it for now... # 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 foreach(build_option ASMJIT_STATIC
ASMJIT_BUILD_X86 ASMJIT_BUILD_X86
#ASMJIT_BUILD_ARM
ASMJIT_BUILD_A64
ASMJIT_NO_DEPRECATED ASMJIT_NO_DEPRECATED
ASMJIT_NO_JIT ASMJIT_NO_JIT
ASMJIT_NO_LOGGING ASMJIT_NO_LOGGING
@@ -291,25 +285,28 @@ set(ASMJIT_SRC_LIST
asmjit/core.h asmjit/core.h
asmjit/core/api-build_p.h asmjit/core/api-build_p.h
asmjit/core/api-config.h asmjit/core/api-config.h
asmjit/core/arch.cpp asmjit/core/archtraits.cpp
asmjit/core/arch.h asmjit/core/archtraits.h
asmjit/core/archcommons.h
asmjit/core/assembler.cpp asmjit/core/assembler.cpp
asmjit/core/assembler.h asmjit/core/assembler.h
asmjit/core/builder.cpp asmjit/core/builder.cpp
asmjit/core/builder.h asmjit/core/builder.h
asmjit/core/callconv.cpp
asmjit/core/callconv.h
asmjit/core/codebuffer.h asmjit/core/codebuffer.h
asmjit/core/codebufferwriter_p.h
asmjit/core/codeholder.cpp asmjit/core/codeholder.cpp
asmjit/core/codeholder.h asmjit/core/codeholder.h
asmjit/core/codewriter.cpp
asmjit/core/codewriter_p.h
asmjit/core/compiler.cpp asmjit/core/compiler.cpp
asmjit/core/compiler.h asmjit/core/compiler.h
asmjit/core/compilerdefs.h
asmjit/core/constpool.cpp asmjit/core/constpool.cpp
asmjit/core/constpool.h asmjit/core/constpool.h
asmjit/core/cpuinfo.cpp asmjit/core/cpuinfo.cpp
asmjit/core/cpuinfo.h asmjit/core/cpuinfo.h
asmjit/core/datatypes.h asmjit/core/datatypes.h
asmjit/core/emithelper.cpp
asmjit/core/emithelper_p.h
asmjit/core/emitter.cpp asmjit/core/emitter.cpp
asmjit/core/emitter.h asmjit/core/emitter.h
asmjit/core/emitterutils.cpp asmjit/core/emitterutils.cpp
@@ -323,6 +320,8 @@ set(ASMJIT_SRC_LIST
asmjit/core/formatter.h asmjit/core/formatter.h
asmjit/core/func.cpp asmjit/core/func.cpp
asmjit/core/func.h asmjit/core/func.h
asmjit/core/funcargscontext.cpp
asmjit/core/funcargscontext_p.h
asmjit/core/globals.cpp asmjit/core/globals.cpp
asmjit/core/globals.h asmjit/core/globals.h
asmjit/core/inst.cpp asmjit/core/inst.cpp
@@ -372,24 +371,23 @@ set(ASMJIT_SRC_LIST
asmjit/core/zonevector.h asmjit/core/zonevector.h
asmjit/x86.h asmjit/x86.h
asmjit/x86/x86archdata.cpp asmjit/x86/x86archtraits_p.h
asmjit/x86/x86archdata_p.h
asmjit/x86/x86assembler.cpp asmjit/x86/x86assembler.cpp
asmjit/x86/x86assembler.h asmjit/x86/x86assembler.h
asmjit/x86/x86builder.cpp asmjit/x86/x86builder.cpp
asmjit/x86/x86builder.h asmjit/x86/x86builder.h
asmjit/x86/x86callconv.cpp
asmjit/x86/x86callconv_p.h
asmjit/x86/x86compiler.cpp asmjit/x86/x86compiler.cpp
asmjit/x86/x86compiler.h asmjit/x86/x86compiler.h
asmjit/x86/x86emithelper.cpp
asmjit/x86/x86emithelper_p.h
asmjit/x86/x86emitter.h asmjit/x86/x86emitter.h
asmjit/x86/x86features.cpp asmjit/x86/x86features.cpp
asmjit/x86/x86features.h asmjit/x86/x86features.h
asmjit/x86/x86formatter.cpp asmjit/x86/x86formatter.cpp
asmjit/x86/x86formatter_p.h asmjit/x86/x86formatter_p.h
asmjit/x86/x86func.cpp
asmjit/x86/x86func_p.h
asmjit/x86/x86globals.h asmjit/x86/x86globals.h
asmjit/x86/x86internal.cpp
asmjit/x86/x86internal_p.h
asmjit/x86/x86instdb.cpp asmjit/x86/x86instdb.cpp
asmjit/x86/x86instdb.h asmjit/x86/x86instdb.h
asmjit/x86/x86instdb_p.h asmjit/x86/x86instdb_p.h
@@ -518,15 +516,17 @@ if (NOT ASMJIT_EMBED)
endif() endif()
if (NOT (ASMJIT_NO_BUILDER OR ASMJIT_NO_COMPILER)) 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 "") set(sse2_flags "")
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
asmjit_detect_cflags(sse2_flags "-arch:SSE2") asmjit_detect_cflags(sse2_flags "-arch:SSE2")
else() else()
asmjit_detect_cflags(sse2_flags "-msse2") asmjit_detect_cflags(sse2_flags "-msse2")
endif() endif()
asmjit_add_target(asmjit_test_x86_cc TEST asmjit_add_target(asmjit_test_compiler TEST
SOURCES test/asmjit_test_x86_cc.cpp SOURCES test/asmjit_test_compiler.cpp
test/asmjit_test_compiler_x86.cpp
test/asmjit_test_compiler.h
LIBRARIES asmjit::asmjit LIBRARIES asmjit::asmjit
CFLAGS ${ASMJIT_PRIVATE_CFLAGS} ${sse2_flags} CFLAGS ${ASMJIT_PRIVATE_CFLAGS} ${sse2_flags}
CFLAGS_DBG ${ASMJIT_PRIVATE_CFLAGS_DBG} CFLAGS_DBG ${ASMJIT_PRIVATE_CFLAGS_DBG}

View File

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

View File

@@ -68,9 +68,6 @@ namespace asmjit {
//! Defined to build X86/X64 backend. //! Defined to build X86/X64 backend.
#define ASMJIT_BUILD_X86 #define ASMJIT_BUILD_X86
//! Defined to build ARM/AArch64 backend.
#define ASMJIT_BUILD_ARM
//! Defined to build host backend autodetected at compile-time. //! Defined to build host backend autodetected at compile-time.
#define ASMJIT_BUILD_HOST #define ASMJIT_BUILD_HOST
@@ -102,7 +99,6 @@ namespace asmjit {
#define ASMJIT_NO_INTROSPECTION #define ASMJIT_NO_INTROSPECTION
// Avoid doxygen preprocessor using feature-selection definitions. // Avoid doxygen preprocessor using feature-selection definitions.
#undef ASMJIT_NO_DEPRECATED
#undef ASMJIT_NO_BUILDER #undef ASMJIT_NO_BUILDER
#undef ASMJIT_NO_COMPILER #undef ASMJIT_NO_COMPILER
#undef ASMJIT_NO_JIT #undef ASMJIT_NO_JIT
@@ -116,6 +112,13 @@ namespace asmjit {
} // {asmjit} } // {asmjit}
#endif // _DOXYGEN #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] // [asmjit::Dependencies]
// ============================================================================ // ============================================================================
@@ -253,10 +256,6 @@ namespace asmjit {
#if ASMJIT_ARCH_X86 && !defined(ASMJIT_BUILD_X86) #if ASMJIT_ARCH_X86 && !defined(ASMJIT_BUILD_X86)
#define ASMJIT_BUILD_X86 #define ASMJIT_BUILD_X86
#endif #endif
#if ASMJIT_ARCH_ARM && !defined(ASMJIT_BUILD_ARM)
// #define ASMJIT_BUILD_ARM
#endif
#endif #endif
// Define 'ASMJIT_BUILD_HOST' if we know that host architecture will be built. // Define 'ASMJIT_BUILD_HOST' if we know that host architecture will be built.
@@ -264,10 +263,6 @@ namespace asmjit {
#define ASMJIT_BUILD_HOST #define ASMJIT_BUILD_HOST
#endif #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] // [asmjit::Build - Globals - C++ Compiler and Features Detection]
// ============================================================================ // ============================================================================

View File

@@ -1,59 +0,0 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "../core/api-build_p.h"
#include "../core/arch.h"
#ifdef ASMJIT_BUILD_X86
#include "../x86/x86archdata_p.h"
#endif
#ifdef ASMJIT_BUILD_ARM
#include "../arm/armarchdata_p.h"
#endif
ASMJIT_BEGIN_NAMESPACE
// ============================================================================
// [asmjit::ArchUtils]
// ============================================================================
ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfoOut) noexcept {
// Zero the output in case the input is invalid.
*typeIdOut = 0;
regInfoOut->reset();
#ifdef ASMJIT_BUILD_X86
if (Environment::isFamilyX86(arch))
return x86::ArchInternal::typeIdToRegInfo(arch, typeId, typeIdOut, regInfoOut);
#endif
#ifdef ASMJIT_BUILD_ARM
if (Environment::isFamilyARM(arch))
return arm::ArchInternal::typeIdToRegInfo(arch, typeId, typeIdOut, regInfoOut);
#endif
return DebugUtils::errored(kErrorInvalidArch);
}
ASMJIT_END_NAMESPACE

View File

@@ -1,64 +0,0 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_CORE_ARCH_H_INCLUDED
#define ASMJIT_CORE_ARCH_H_INCLUDED
#include "../core/environment.h"
#include "../core/operand.h"
ASMJIT_BEGIN_NAMESPACE
//! \addtogroup asmjit_core
//! \{
// ============================================================================
// [asmjit::ArchRegs]
// ============================================================================
//! Information about registers of a CPU architecture.
struct ArchRegs {
//! Register information and signatures indexed by `BaseReg::RegType`.
RegInfo regInfo[BaseReg::kTypeMax + 1];
//! Count (maximum) of registers per `BaseReg::RegType`.
uint8_t regCount[BaseReg::kTypeMax + 1];
//! Converts RegType to TypeId, see `Type::Id`.
uint8_t regTypeToTypeId[BaseReg::kTypeMax + 1];
};
// ============================================================================
// [asmjit::ArchUtils]
// ============================================================================
//! Architecture utilities.
namespace ArchUtils {
ASMJIT_API Error typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfo) noexcept;
} // {ArchUtils}
//! \}
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_ARCH_H_INCLUDED

View File

@@ -0,0 +1,164 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_CORE_ARCHCOMMONS_H_INCLUDED
#define ASMJIT_CORE_ARCHCOMMONS_H_INCLUDED
// This file provides architecture-specific classes that are required in the
// core library. For example Imm operand allows to be created from arm::Shift
// in a const-expr way, so the arm::Shift must be provided. So this header
// file provides everything architecture-specific that is used by the Core API.
#include "../core/globals.h"
// ============================================================================
// [asmjit::arm]
// ============================================================================
ASMJIT_BEGIN_SUB_NAMESPACE(arm)
//! \addtogroup asmjit_arm
//! \{
//! Represents ARM immediate shift operation type and value.
class Shift {
public:
//! Operation predicate (ARM) describes either SHIFT or EXTEND operation.
//!
//! \note The constants are AsmJit specific. The first 5 values describe real
//! constants on ARM32 and AArch64 hardware, however, the addition constants
//! that describe extend modes are specific to AsmJit and would be translated
//! to the AArch64 specific constants by the assembler.
enum Op : uint32_t {
//! Shift left logical operation (default).
//!
//! Available to all ARM architectures.
kOpLSL = 0x00u,
//! Shift right logical operation.
//!
//! Available to all ARM architectures.
kOpLSR = 0x01u,
//! Shift right arithmetic operation.
//!
//! Available to all ARM architectures.
kOpASR = 0x02u,
//! Rotate right operation.
//!
//! \note Not available in AArch64 mode.
kOpROR = 0x03u,
//! Rotate right with carry operation (encoded as `kShiftROR` with zero).
//!
//! \note Not available in AArch64 mode.
kOpRRX = 0x04u,
//! Shift left by filling low order bits with ones.
kOpMSL = 0x05u,
//! UXTN extend register operation (AArch64 only).
kOpUXTB = 0x06u,
//! UXTH extend register operation (AArch64 only).
kOpUXTH = 0x07u,
//! UXTW extend register operation (AArch64 only).
kOpUXTW = 0x08u,
//! UXTX extend register operation (AArch64 only).
kOpUXTX = 0x09u,
//! SXTB extend register operation (AArch64 only).
kOpSXTB = 0x0Au,
//! SXTH extend register operation (AArch64 only).
kOpSXTH = 0x0Bu,
//! SXTW extend register operation (AArch64 only).
kOpSXTW = 0x0Cu,
//! SXTX extend register operation (AArch64 only).
kOpSXTX = 0x0Du
// NOTE: 0xE and 0xF are used by memory operand to specify POST|PRE offset mode.
};
//! Shift operation.
uint32_t _op;
//! Shift Value.
uint32_t _value;
//! Default constructed Shift is not initialized.
inline Shift() noexcept = default;
//! Copy constructor (default)
constexpr Shift(const Shift& other) noexcept = default;
//! Constructs Shift from operation `op` and shift `value`.
constexpr Shift(uint32_t op, uint32_t value) noexcept
: _op(op),
_value(value) {}
//! Returns the shift operation.
constexpr uint32_t op() const noexcept { return _op; }
//! Returns the shift smount.
constexpr uint32_t value() const noexcept { return _value; }
//! Sets shift operation to `op`.
inline void setOp(uint32_t op) noexcept { _op = op; }
//! Sets shift amount to `value`.
inline void setValue(uint32_t value) noexcept { _value = value; }
};
//! Constructs a `LSL #value` shift (logical shift left).
static constexpr Shift lsl(uint32_t value) noexcept { return Shift(Shift::kOpLSL, value); }
//! Constructs a `LSR #value` shift (logical shift right).
static constexpr Shift lsr(uint32_t value) noexcept { return Shift(Shift::kOpLSR, value); }
//! Constructs a `ASR #value` shift (arithmetic shift right).
static constexpr Shift asr(uint32_t value) noexcept { return Shift(Shift::kOpASR, value); }
//! Constructs a `ROR #value` shift (rotate right).
static constexpr Shift ror(uint32_t value) noexcept { return Shift(Shift::kOpROR, value); }
//! Constructs a `RRX` shift (rotate with carry by 1).
static constexpr Shift rrx() noexcept { return Shift(Shift::kOpRRX, 0); }
//! Constructs a `MSL #value` shift (logical shift left filling ones).
static constexpr Shift msl(uint32_t value) noexcept { return Shift(Shift::kOpMSL, value); }
//! Constructs a `UXTB #value` extend and shift (unsigned byte extend).
static constexpr Shift uxtb(uint32_t value) noexcept { return Shift(Shift::kOpUXTB, value); }
//! Constructs a `UXTH #value` extend and shift (unsigned hword extend).
static constexpr Shift uxth(uint32_t value) noexcept { return Shift(Shift::kOpUXTH, value); }
//! Constructs a `UXTW #value` extend and shift (unsigned word extend).
static constexpr Shift uxtw(uint32_t value) noexcept { return Shift(Shift::kOpUXTW, value); }
//! Constructs a `UXTX #value` extend and shift (unsigned dword extend).
static constexpr Shift uxtx(uint32_t value) noexcept { return Shift(Shift::kOpUXTX, value); }
//! Constructs a `SXTB #value` extend and shift (signed byte extend).
static constexpr Shift sxtb(uint32_t value) noexcept { return Shift(Shift::kOpSXTB, value); }
//! Constructs a `SXTH #value` extend and shift (signed hword extend).
static constexpr Shift sxth(uint32_t value) noexcept { return Shift(Shift::kOpSXTH, value); }
//! Constructs a `SXTW #value` extend and shift (signed word extend).
static constexpr Shift sxtw(uint32_t value) noexcept { return Shift(Shift::kOpSXTW, value); }
//! Constructs a `SXTX #value` extend and shift (signed dword extend).
static constexpr Shift sxtx(uint32_t value) noexcept { return Shift(Shift::kOpSXTX, value); }
//! \}
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_CORE_ARCHCOMMONS_H_INCLUDED

View File

@@ -0,0 +1,155 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "../core/api-build_p.h"
#include "../core/archtraits.h"
#include "../core/misc_p.h"
#ifdef ASMJIT_BUILD_X86
#include "../x86/x86archtraits_p.h"
#endif
#ifdef ASMJIT_BUILD_ARM
#include "../arm/armarchtraits_p.h"
#endif
ASMJIT_BEGIN_NAMESPACE
// ============================================================================
// [asmjit::ArchTraits]
// ============================================================================
static const constexpr ArchTraits noArchTraits = {
0xFF, // SP.
0xFF, // FP.
0xFF, // LR.
0xFF, // PC.
{ 0, 0, 0 }, // Reserved.
0, // HW stack alignment.
0, // Min stack offset.
0, // Max stack offset.
{ 0, 0, 0, 0}, // ISA features [Gp, Vec, Other0, Other1].
{ { 0 } }, // RegTypeToSignature.
{ 0 }, // RegTypeToTypeId.
{ 0 } // TypeIdToRegType.
};
ASMJIT_VARAPI const ArchTraits _archTraits[Environment::kArchCount] = {
// No architecture.
noArchTraits,
// X86/X86 architectures.
#ifdef ASMJIT_BUILD_X86
x86::x86ArchTraits,
x86::x64ArchTraits,
#else
noArchTraits,
noArchTraits,
#endif
// RISCV32/RISCV64 architectures.
noArchTraits,
noArchTraits,
// ARM architecture
noArchTraits,
// AArch64 architecture.
#ifdef ASMJIT_BUILD_ARM
arm::a64ArchTraits,
#else
noArchTraits,
#endif
// ARM/Thumb architecture.
noArchTraits,
// Reserved.
noArchTraits,
// MIPS32/MIPS64
noArchTraits,
noArchTraits
};
// ============================================================================
// [asmjit::ArchUtils]
// ============================================================================
ASMJIT_FAVOR_SIZE Error ArchUtils::typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfoOut) noexcept {
const ArchTraits& archTraits = ArchTraits::byArch(arch);
// Passed RegType instead of TypeId?
if (typeId <= BaseReg::kTypeMax)
typeId = archTraits.regTypeToTypeId(typeId);
if (ASMJIT_UNLIKELY(!Type::isValid(typeId)))
return DebugUtils::errored(kErrorInvalidTypeId);
// First normalize architecture dependent types.
if (Type::isAbstract(typeId)) {
bool is32Bit = Environment::is32Bit(arch);
if (typeId == Type::kIdIntPtr)
typeId = is32Bit ? Type::kIdI32 : Type::kIdI64;
else
typeId = is32Bit ? Type::kIdU32 : Type::kIdU64;
}
// Type size helps to construct all groups of registers.
// TypeId is invalid if the size is zero.
uint32_t size = Type::sizeOf(typeId);
if (ASMJIT_UNLIKELY(!size))
return DebugUtils::errored(kErrorInvalidTypeId);
if (ASMJIT_UNLIKELY(typeId == Type::kIdF80))
return DebugUtils::errored(kErrorInvalidUseOfF80);
uint32_t regType = 0;
if (typeId >= Type::_kIdBaseStart && typeId < Type::_kIdVec32Start) {
regType = archTraits._typeIdToRegType[typeId - Type::_kIdBaseStart];
if (!regType) {
if (typeId == Type::kIdI64 || typeId == Type::kIdU64)
return DebugUtils::errored(kErrorInvalidUseOfGpq);
else
return DebugUtils::errored(kErrorInvalidTypeId);
}
}
else {
if (size <= 8 && archTraits._regInfo[BaseReg::kTypeVec64].isValid())
regType = BaseReg::kTypeVec64;
else if (size <= 16 && archTraits._regInfo[BaseReg::kTypeVec128].isValid())
regType = BaseReg::kTypeVec128;
else if (size == 32 && archTraits._regInfo[BaseReg::kTypeVec256].isValid())
regType = BaseReg::kTypeVec256;
else if (archTraits._regInfo[BaseReg::kTypeVec512].isValid())
regType = BaseReg::kTypeVec512;
else
return DebugUtils::errored(kErrorInvalidTypeId);
}
*typeIdOut = typeId;
regInfoOut->reset(archTraits.regTypeToSignature(regType));
return kErrorOk;
}
ASMJIT_END_NAMESPACE

View File

@@ -0,0 +1,174 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_CORE_ARCHTRAITS_H_INCLUDED
#define ASMJIT_CORE_ARCHTRAITS_H_INCLUDED
#include "../core/environment.h"
#include "../core/operand.h"
#include "../core/type.h"
ASMJIT_BEGIN_NAMESPACE
//! \addtogroup asmjit_core
//! \{
// ============================================================================
// [asmjit::ArchTraits]
// ============================================================================
//! Architecture traits used by Function API and Compiler's register allocator.
struct ArchTraits {
//! ISA features for each register group.
enum IsaFeatures : uint32_t {
//! ISA features a register swap by using a single instruction.
kIsaFeatureSwap = 0x01u,
//! ISA features a push/pop like instruction for this register group.
kIsaFeaturePushPop = 0x02u,
};
//! Stack pointer register id.
uint8_t _spRegId;
//! Frame pointer register id.
uint8_t _fpRegId;
//! Link register id.
uint8_t _linkRegId;
//! Instruction pointer (or program counter) register id, if accessible.
uint8_t _ipRegId;
// Reserved.
uint8_t _reserved[3];
//! Hardware stack alignment requirement.
uint8_t _hwStackAlignment;
//! Minimum addressable offset on stack guaranteed for all instructions.
uint32_t _minStackOffset;
//! Maximum addressable offset on stack depending on specific instruction.
uint32_t _maxStackOffset;
//! Flags for each virtual register group (always covers GP and Vec groups).
uint8_t _isaFlags[BaseReg::kGroupVirt];
//! Maps register type into a signature, that provides group, size and can
//! be used to construct register operands.
RegInfo _regInfo[BaseReg::kTypeMax + 1];
//! Maps a register to type-id, see \ref Type::Id.
uint8_t _regTypeToTypeId[BaseReg::kTypeMax + 1];
//! Maps base TypeId values (from TypeId::_kIdBaseStart) to register types, see \ref Type::Id.
uint8_t _typeIdToRegType[32];
//! Resets all members to zeros.
inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
//! \name Accessors
//! \{
//! Returns stack pointer register id.
inline constexpr uint32_t spRegId() const noexcept { return _spRegId; }
//! Returns stack frame register id.
inline constexpr uint32_t fpRegId() const noexcept { return _fpRegId; }
//! Returns link register id, if the architecture provides it.
inline constexpr uint32_t linkRegId() const noexcept { return _linkRegId; }
//! Returns instruction pointer register id, if the architecture provides it.
inline constexpr uint32_t ipRegId() const noexcept { return _ipRegId; }
//! Returns a hardware stack alignment requirement.
//!
//! \note This is a hardware constraint. Architectures that don't constrain
//! it would return the lowest alignment (1), however, some architectures may
//! constrain the alignment, for example AArch64 requires 16-byte alignment.
inline constexpr uint32_t hwStackAlignment() const noexcept { return _hwStackAlignment; }
//! Tests whether the architecture provides link register, which is used across
//! function calls. If the link register is not provided then a function call
//! pushes the return address on stack (X86/X64).
inline constexpr bool hasLinkReg() const noexcept { return _linkRegId != BaseReg::kIdBad; }
//! Returns minimum addressable offset on stack guaranteed for all instructions.
inline constexpr uint32_t minStackOffset() const noexcept { return _minStackOffset; }
//! Returns maximum addressable offset on stack depending on specific instruction.
inline constexpr uint32_t maxStackOffset() const noexcept { return _maxStackOffset; }
//! Returns ISA flags of the given register `group`.
inline constexpr uint32_t isaFlags(uint32_t group) const noexcept { return _isaFlags[group]; }
//! Tests whether the given register `group` has the given `flag` set.
inline constexpr bool hasIsaFlag(uint32_t group, uint32_t flag) const noexcept { return (_isaFlags[group] & flag) != 0; }
//! Tests whether the ISA provides register swap instruction for the given register `group`.
inline constexpr bool hasSwap(uint32_t group) const noexcept { return hasIsaFlag(group, kIsaFeatureSwap); }
//! Tests whether the ISA provides push/pop instructions for the given register `group`.
inline constexpr bool hasPushPop(uint32_t group) const noexcept { return hasIsaFlag(group, kIsaFeaturePushPop); }
inline uint32_t hasRegType(uint32_t rType) const noexcept {
return rType <= BaseReg::kTypeMax && _regInfo[rType].signature() != 0;
}
inline uint32_t regTypeToSignature(uint32_t rType) const noexcept {
ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
return _regInfo[rType].signature();
}
inline uint32_t regTypeToGroup(uint32_t rType) const noexcept {
ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
return _regInfo[rType].group();
}
inline uint32_t regTypeToSize(uint32_t rType) const noexcept {
ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
return _regInfo[rType].size();
}
inline uint32_t regTypeToTypeId(uint32_t rType) const noexcept {
ASMJIT_ASSERT(rType <= BaseReg::kTypeMax);
return _regTypeToTypeId[rType];
}
//! \}
//! \name Statics
//! \{
//! Returns a const reference to `ArchTraits` for the given architecture `arch`.
static inline const ArchTraits& byArch(uint32_t arch) noexcept;
//! \}
};
ASMJIT_VARAPI const ArchTraits _archTraits[Environment::kArchCount];
inline const ArchTraits& ArchTraits::byArch(uint32_t arch) noexcept { return _archTraits[arch & ~Environment::kArchBigEndianMask]; }
// ============================================================================
// [asmjit::ArchUtils]
// ============================================================================
//! Architecture utilities.
namespace ArchUtils {
ASMJIT_API Error typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfo) noexcept;
} // {ArchUtils}
//! \}
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_ARCHTRAITS_H_INCLUDED

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,59 +0,0 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "../core/api-build_p.h"
#include "../core/arch.h"
#include "../core/func.h"
#include "../core/type.h"
#ifdef ASMJIT_BUILD_X86
#include "../x86/x86callconv_p.h"
#endif
#ifdef ASMJIT_BUILD_ARM
#include "../arm/armcallconv_p.h"
#endif
ASMJIT_BEGIN_NAMESPACE
// ============================================================================
// [asmjit::CallConv - Init / Reset]
// ============================================================================
ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId, const Environment& environment) noexcept {
reset();
#ifdef ASMJIT_BUILD_X86
if (environment.isFamilyX86())
return x86::CallConvInternal::init(*this, ccId, environment);
#endif
#ifdef ASMJIT_BUILD_ARM
if (environment.isFamilyARM())
return arm::CallConvInternal::init(*this, ccIdv, environment);
#endif
return DebugUtils::errored(kErrorInvalidArgument);
}
ASMJIT_END_NAMESPACE

View File

@@ -1,374 +0,0 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_CORE_CALLCONV_H_INCLUDED
#define ASMJIT_CORE_CALLCONV_H_INCLUDED
#include "../core/arch.h"
#include "../core/operand.h"
#include "../core/support.h"
ASMJIT_BEGIN_NAMESPACE
//! \addtogroup asmjit_function
//! \{
// ============================================================================
// [asmjit::CallConv]
// ============================================================================
//! Function calling convention.
//!
//! Function calling convention is a scheme that defines how function parameters
//! are passed and how function returns its result. AsmJit defines a variety of
//! architecture and OS specific calling conventions and also provides a compile
//! time detection to make the code-generation easier.
struct CallConv {
//! Calling convention id, see `Id`.
uint8_t _id;
//! Architecture identifier, see \ref Environment::Arch.
uint8_t _arch;
//! Register assignment strategy.
uint8_t _strategy;
//! Flags.
uint8_t _flags;
//! Red zone size (AMD64 == 128 bytes).
uint8_t _redZoneSize;
//! Spill zone size (WIN64 == 32 bytes).
uint8_t _spillZoneSize;
//! Natural stack alignment as defined by OS/ABI.
uint8_t _naturalStackAlignment;
uint8_t _reserved[1];
//! Mask of all passed registers, per group.
uint32_t _passedRegs[BaseReg::kGroupVirt];
//! Mask of all preserved registers, per group.
uint32_t _preservedRegs[BaseReg::kGroupVirt];
//! Internal limits of AsmJit's CallConv.
enum Limits : uint32_t {
kMaxRegArgsPerGroup = 16
};
//! Passed registers' order.
union RegOrder {
//! Passed registers, ordered.
uint8_t id[kMaxRegArgsPerGroup];
uint32_t packed[(kMaxRegArgsPerGroup + 3) / 4];
};
//! Passed registers' order, per register group.
RegOrder _passedOrder[BaseReg::kGroupVirt];
//! Calling convention id.
//!
//! Calling conventions can be divided into the following groups:
//!
//! - Universal - calling conventions are applicable to any target. They
//! will be converted to a target dependent calling convention at runtime
//! by \ref init(). The purpose of these conventions is to make using
//! functions less target dependent and closer to how they are declared
//! in C and C++.
//!
//! - Target specific - calling conventions that are used by a particular
//! architecture and ABI. For example Windows 64-bit calling convention
//! and AMD64 SystemV calling convention.
enum Id : uint32_t {
//! None or invalid (can't be used).
kIdNone = 0,
// ------------------------------------------------------------------------
// [Universal Calling Conventions]
// ------------------------------------------------------------------------
//! Standard function call or explicit `__cdecl` where it can be specified.
//!
//! This is a universal convention, which is used to initialize specific
//! calling connventions based on architecture, platform, and its ABI.
kIdCDecl = 1,
//! `__stdcall` on targets that support this calling convention.
//!
//! \note This calling convention is only supported on 32-bit X86. If used
//! on environment that doesn't support this calling convention \ref kIdCDecl
//! will be used instead.
kIdStdCall = 2,
//! `__fastcall` on targets that support this calling convention.
//!
//! \note This calling convention is only supported on 32-bit X86. If used
//! on environment that doesn't support this calling convention \ref kIdCDecl
//! will be used instead.
kIdFastCall = 3,
//! `__vectorcall` on targets that support this calling convention.
//!
//! \note This calling convention is only supported on 32-bit and 64-bit
//! X86 architecture on Windows platform. If used on environment that doesn't
//! support this calling convention \ref kIdCDecl will be used instead.
kIdVectorCall = 4,
//! `__thiscall` on targets that support this calling convention.
//!
//! \note This calling convention is only supported on 32-bit X86 Windows
//! platform. If used on environment that doesn't support this calling
//! convention \ref kIdCDecl will be used instead.
kIdThisCall = 5,
//! `__attribute__((regparm(1)))` convention (GCC and Clang).
kIdRegParm1 = 6,
//! `__attribute__((regparm(2)))` convention (GCC and Clang).
kIdRegParm2 = 7,
//! `__attribute__((regparm(3)))` convention (GCC and Clang).
kIdRegParm3 = 8,
//! Soft-float calling convention (ARM).
//!
//! Floating point arguments are passed via general purpose registers.
kIdSoftFloat = 9,
//! Hard-float calling convention (ARM).
//!
//! Floating point arguments are passed via SIMD registers.
kIdHardFloat = 10,
//! AsmJit specific calling convention designed for calling functions
//! inside a multimedia code that don't use many registers internally,
//! but are long enough to be called and not inlined. These functions are
//! usually used to calculate trigonometric functions, logarithms, etc...
kIdLightCall2 = 16,
kIdLightCall3 = 17,
kIdLightCall4 = 18,
// ------------------------------------------------------------------------
// [ABI-Specific Calling Conventions]
// ------------------------------------------------------------------------
kIdX64SystemV = 32,
kIdX64Windows = 33,
// ------------------------------------------------------------------------
// [Host]
// ------------------------------------------------------------------------
kIdHost =
#if ASMJIT_ARCH_ARM == 32 && defined(__SOFTFP__)
kIdSoftFloat
#elif ASMJIT_ARCH_ARM == 32 && !defined(__SOFTFP__)
kIdHardFloat
#else
kIdCDecl
#endif
#ifndef ASMJIT_NO_DEPRECATE
, kIdHostCDecl = kIdCDecl
, kIdHostStdCall = kIdStdCall
, kIdHostFastCall = kIdFastCall
, kIdHostLightCall2 = kIdLightCall2
, kIdHostLightCall3 = kIdLightCall3
, kIdHostLightCall4 = kIdLightCall4
#endif // !ASMJIT_NO_DEPRECATE
};
//! Strategy used to assign registers to function arguments.
//!
//! This is AsmJit specific. It basically describes how AsmJit should convert
//! the function arguments defined by `FuncSignature` into register IDs and
//! stack offsets. The default strategy `kStrategyDefault` assigns registers
//! and then stack whereas `kStrategyWin64` strategy does register shadowing
//! as defined by WIN64 calling convention - it applies to 64-bit calling
//! conventions only.
enum Strategy : uint32_t {
//! Default register assignment strategy.
kStrategyDefault = 0,
//! Windows 64-bit ABI register assignment strategy.
kStrategyX64Windows = 1,
//! Windows 64-bit __vectorcall register assignment strategy.
kStrategyX64VectorCall = 2,
//! Number of assignment strategies.
kStrategyCount = 3
};
//! Calling convention flags.
enum Flags : uint32_t {
//! Callee is responsible for cleaning up the stack.
kFlagCalleePopsStack = 0x01u,
//! Pass vector arguments indirectly (as a pointer).
kFlagIndirectVecArgs = 0x02u,
//! Pass F32 and F64 arguments by VEC128 register.
kFlagPassFloatsByVec = 0x04u,
//! Pass MMX and vector arguments by stack if the function has variable arguments.
kFlagPassVecByStackIfVA = 0x08u,
//! MMX registers are passed and returned via GP registers.
kFlagPassMmxByGp = 0x10u,
//! MMX registers are passed and returned via XMM registers.
kFlagPassMmxByXmm = 0x20u,
//! Calling convention can be used with variable arguments.
kFlagVarArgCompatible = 0x80u
};
//! \name Construction & Destruction
//! \{
//! Initializes this calling convention to the given `ccId` based on the
//! `environment`.
//!
//! See \ref Id and \ref Environment for more details.
ASMJIT_API Error init(uint32_t ccId, const Environment& environment) noexcept;
//! Resets this CallConv struct into a defined state.
//!
//! It's recommended to reset the \ref CallConv struct in case you would
//! like create a custom calling convention as it prevents from using an
//! uninitialized data (CallConv doesn't have a constructor that would
//! initialize it, it's just a struct).
inline void reset() noexcept {
memset(this, 0, sizeof(*this));
memset(_passedOrder, 0xFF, sizeof(_passedOrder));
}
//! \}
//! \name Accessors
//! \{
//! Returns the calling convention id, see `Id`.
inline uint32_t id() const noexcept { return _id; }
//! Sets the calling convention id, see `Id`.
inline void setId(uint32_t id) noexcept { _id = uint8_t(id); }
//! Returns the calling function architecture id.
inline uint32_t arch() const noexcept { return _arch; }
//! Sets the calling function architecture id.
inline void setArch(uint32_t arch) noexcept { _arch = uint8_t(arch); }
//! Returns the strategy used to assign registers to arguments, see `Strategy`.
inline uint32_t strategy() const noexcept { return _strategy; }
//! Sets the strategy used to assign registers to arguments, see `Strategy`.
inline void setStrategy(uint32_t strategy) noexcept { _strategy = uint8_t(strategy); }
//! Tests whether the calling convention has the given `flag` set.
inline bool hasFlag(uint32_t flag) const noexcept { return (uint32_t(_flags) & flag) != 0; }
//! Returns the calling convention flags, see `Flags`.
inline uint32_t flags() const noexcept { return _flags; }
//! Adds the calling convention flags, see `Flags`.
inline void setFlags(uint32_t flag) noexcept { _flags = uint8_t(flag); };
//! Adds the calling convention flags, see `Flags`.
inline void addFlags(uint32_t flags) noexcept { _flags = uint8_t(_flags | flags); };
//! Tests whether this calling convention specifies 'RedZone'.
inline bool hasRedZone() const noexcept { return _redZoneSize != 0; }
//! Tests whether this calling convention specifies 'SpillZone'.
inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; }
//! Returns size of 'RedZone'.
inline uint32_t redZoneSize() const noexcept { return _redZoneSize; }
//! Returns size of 'SpillZone'.
inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; }
//! Sets size of 'RedZone'.
inline void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = uint8_t(size); }
//! Sets size of 'SpillZone'.
inline void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = uint8_t(size); }
//! Returns a natural stack alignment.
inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; }
//! Sets a natural stack alignment.
//!
//! This function can be used to override the default stack alignment in case
//! that you know that it's alignment is different. For example it allows to
//! implement custom calling conventions that guarantee higher stack alignment.
inline void setNaturalStackAlignment(uint32_t value) noexcept { _naturalStackAlignment = uint8_t(value); }
//! Returns the order of passed registers of the given `group`, see \ref BaseReg::RegGroup.
inline const uint8_t* passedOrder(uint32_t group) const noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
return _passedOrder[group].id;
}
//! Returns the mask of passed registers of the given `group`, see \ref BaseReg::RegGroup.
inline uint32_t passedRegs(uint32_t group) const noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
return _passedRegs[group];
}
inline void _setPassedPacked(uint32_t group, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
_passedOrder[group].packed[0] = p0;
_passedOrder[group].packed[1] = p1;
_passedOrder[group].packed[2] = p2;
_passedOrder[group].packed[3] = p3;
}
//! Resets the order and mask of passed registers.
inline void setPassedToNone(uint32_t group) noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
_setPassedPacked(group, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
_passedRegs[group] = 0u;
}
//! Sets the order and mask of passed registers.
inline void setPassedOrder(uint32_t group, uint32_t a0, uint32_t a1 = 0xFF, uint32_t a2 = 0xFF, uint32_t a3 = 0xFF, uint32_t a4 = 0xFF, uint32_t a5 = 0xFF, uint32_t a6 = 0xFF, uint32_t a7 = 0xFF) noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
// NOTE: This should always be called with all arguments known at compile time,
// so even if it looks scary it should be translated into few instructions.
_setPassedPacked(group, Support::bytepack32_4x8(a0, a1, a2, a3),
Support::bytepack32_4x8(a4, a5, a6, a7),
0xFFFFFFFFu,
0xFFFFFFFFu);
_passedRegs[group] = (a0 != 0xFF ? 1u << a0 : 0u) |
(a1 != 0xFF ? 1u << a1 : 0u) |
(a2 != 0xFF ? 1u << a2 : 0u) |
(a3 != 0xFF ? 1u << a3 : 0u) |
(a4 != 0xFF ? 1u << a4 : 0u) |
(a5 != 0xFF ? 1u << a5 : 0u) |
(a6 != 0xFF ? 1u << a6 : 0u) |
(a7 != 0xFF ? 1u << a7 : 0u) ;
}
//! Returns preserved register mask of the given `group`, see \ref BaseReg::RegGroup.
inline uint32_t preservedRegs(uint32_t group) const noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
return _preservedRegs[group];
}
//! Sets preserved register mask of the given `group`, see \ref BaseReg::RegGroup.
inline void setPreservedRegs(uint32_t group, uint32_t regs) noexcept {
ASMJIT_ASSERT(group < BaseReg::kGroupVirt);
_preservedRegs[group] = regs;
}
//! \}
};
//! \}
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_CALLCONV_H_INCLUDED

View File

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

View File

@@ -24,7 +24,7 @@
#ifndef ASMJIT_CORE_CODEHOLDER_H_INCLUDED #ifndef ASMJIT_CORE_CODEHOLDER_H_INCLUDED
#define 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/codebuffer.h"
#include "../core/datatypes.h" #include "../core/datatypes.h"
#include "../core/errorhandler.h" #include "../core/errorhandler.h"
@@ -68,6 +68,83 @@ enum AlignMode : uint32_t {
kAlignCount = 3 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] // [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] // [asmjit::LabelLink]
// ============================================================================ // ============================================================================
@@ -181,83 +455,8 @@ struct LabelLink {
size_t offset; size_t offset;
//! Inlined rel8/rel32. //! Inlined rel8/rel32.
intptr_t rel; intptr_t rel;
}; //! Offset format information.
OffsetFormat format;
// ============================================================================
// [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;
}
}; };
// ============================================================================ // ============================================================================
@@ -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] // [asmjit::AddressTableEntry]
// ============================================================================ // ============================================================================
@@ -843,7 +966,7 @@ public:
//! Creates a new label-link used to store information about yet unbound labels. //! Creates a new label-link used to store information about yet unbound labels.
//! //!
//! Returns `null` if the allocation failed. //! 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 //! 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 //! 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`. //! Returns a RelocEntry of the given `id`.
inline RelocEntry* relocEntry(uint32_t id) const noexcept { return _relocations[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. //! Additional fields can be set after the relocation entry was created.
ASMJIT_API Error newRelocEntry(RelocEntry** dst, uint32_t relocType, uint32_t valueSize) noexcept; ASMJIT_API Error newRelocEntry(RelocEntry** dst, uint32_t relocType) noexcept;
//! \} //! \}

View File

@@ -0,0 +1,151 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "../core/api-build_p.h"
#include "../core/codeholder.h"
#include "../core/codewriter_p.h"
ASMJIT_BEGIN_NAMESPACE
bool CodeWriterUtils::encodeOffset32(uint32_t* dst, int64_t offset64, const OffsetFormat& format) noexcept {
uint32_t bitCount = format.immBitCount();
uint32_t bitShift = format.immBitShift();
uint32_t discardLsb = format.immDiscardLsb();
if (!bitCount || bitCount > format.valueSize() * 8u)
return false;
if (discardLsb) {
ASMJIT_ASSERT(discardLsb <= 32);
if ((offset64 & Support::lsbMask<uint32_t>(discardLsb)) != 0)
return false;
offset64 >>= discardLsb;
}
if (!Support::isInt32(offset64))
return false;
int32_t offset32 = int32_t(offset64);
if (!Support::isEncodableOffset32(offset32, bitCount))
return false;
switch (format.type()) {
case OffsetFormat::kTypeCommon: {
*dst = (uint32_t(offset32) & Support::lsbMask<uint32_t>(bitCount)) << bitShift;
return true;
}
case OffsetFormat::kTypeAArch64_ADR:
case OffsetFormat::kTypeAArch64_ADRP: {
// Sanity checks.
if (format.valueSize() != 4 || bitCount != 21 || bitShift != 5)
return false;
uint32_t immLo = uint32_t(offset32) & 0x3u;
uint32_t immHi = uint32_t(offset32 >> 2) & Support::lsbMask<uint32_t>(19);
*dst = (immLo << 29) | (immHi << 5);
return true;
}
default:
return false;
}
}
bool CodeWriterUtils::encodeOffset64(uint64_t* dst, int64_t offset64, const OffsetFormat& format) noexcept {
uint32_t bitCount = format.immBitCount();
uint32_t discardLsb = format.immDiscardLsb();
if (!bitCount || bitCount > format.valueSize() * 8u)
return false;
if (discardLsb) {
ASMJIT_ASSERT(discardLsb <= 32);
if ((offset64 & Support::lsbMask<uint32_t>(discardLsb)) != 0)
return false;
offset64 >>= discardLsb;
}
if (!Support::isEncodableOffset64(offset64, bitCount))
return false;
switch (format.type()) {
case OffsetFormat::kTypeCommon: {
*dst = (uint64_t(offset64) & Support::lsbMask<uint64_t>(bitCount)) << format.immBitShift();
return true;
}
default:
return false;
}
}
bool CodeWriterUtils::writeOffset(void* dst, int64_t offset64, const OffsetFormat& format) noexcept {
// Offset the destination by ValueOffset so the `dst` points to the
// patched word instead of the beginning of the patched region.
dst = static_cast<char*>(dst) + format.valueOffset();
switch (format.valueSize()) {
case 1: {
uint32_t mask;
if (!encodeOffset32(&mask, offset64, format))
return false;
Support::writeU8(dst, Support::readU8(dst) | mask);
return true;
}
case 2: {
uint32_t mask;
if (!encodeOffset32(&mask, offset64, format))
return false;
Support::writeU16uLE(dst, Support::readU16uLE(dst) | mask);
return true;
}
case 4: {
uint32_t mask;
if (!encodeOffset32(&mask, offset64, format))
return false;
Support::writeU32uLE(dst, Support::readU32uLE(dst) | mask);
return true;
}
case 8: {
uint64_t mask;
if (!encodeOffset64(&mask, offset64, format))
return false;
Support::writeU64uLE(dst, Support::readU64uLE(dst) | mask);
return true;
}
default:
return false;
}
}
ASMJIT_END_NAMESPACE

View File

@@ -35,15 +35,21 @@ ASMJIT_BEGIN_NAMESPACE
//! \{ //! \{
// ============================================================================ // ============================================================================
// [asmjit::CodeBufferWriter] // [Forward Declarations]
// ============================================================================ // ============================================================================
//! Helper that is used to write into a `CodeBuffer` held by `BaseAssembler`. struct OffsetFormat;
class CodeBufferWriter {
// ============================================================================
// [asmjit::CodeWriter]
// ============================================================================
//! Helper that is used to write into a \ref CodeBuffer held by \ref BaseAssembler.
class CodeWriter {
public: public:
uint8_t* _cursor; uint8_t* _cursor;
ASMJIT_INLINE explicit CodeBufferWriter(BaseAssembler* a) noexcept ASMJIT_INLINE explicit CodeWriter(BaseAssembler* a) noexcept
: _cursor(a->_bufferPtr) {} : _cursor(a->_bufferPtr) {}
ASMJIT_INLINE Error ensureSpace(BaseAssembler* a, size_t n) noexcept { 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 //! \endcond

View File

@@ -282,11 +282,10 @@ Error BaseCompiler::newVirtReg(VirtReg** out, uint32_t typeId, uint32_t signatur
} }
Error BaseCompiler::_newReg(BaseReg* out, uint32_t typeId, const char* name) { Error BaseCompiler::_newReg(BaseReg* out, uint32_t typeId, const char* name) {
RegInfo regInfo;
out->reset(); out->reset();
RegInfo regInfo;
Error err = ArchUtils::typeIdToRegInfo(arch(), typeId, &typeId, &regInfo); Error err = ArchUtils::typeIdToRegInfo(arch(), typeId, &typeId, &regInfo);
if (ASMJIT_UNLIKELY(err)) if (ASMJIT_UNLIKELY(err))
return reportError(err); return reportError(err);
@@ -569,6 +568,10 @@ JumpAnnotation* BaseCompiler::newJumpAnnotation() {
Error BaseCompiler::onAttach(CodeHolder* code) noexcept { Error BaseCompiler::onAttach(CodeHolder* code) noexcept {
ASMJIT_PROPAGATE(Base::onAttach(code)); 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>(); Error err = addPassT<GlobalConstPoolPass>();
if (ASMJIT_UNLIKELY(err)) { if (ASMJIT_UNLIKELY(err)) {
onDetach(code); onDetach(code);

View File

@@ -30,6 +30,7 @@
#include "../core/assembler.h" #include "../core/assembler.h"
#include "../core/builder.h" #include "../core/builder.h"
#include "../core/constpool.h" #include "../core/constpool.h"
#include "../core/compilerdefs.h"
#include "../core/func.h" #include "../core/func.h"
#include "../core/inst.h" #include "../core/inst.h"
#include "../core/operand.h" #include "../core/operand.h"
@@ -43,11 +44,7 @@ ASMJIT_BEGIN_NAMESPACE
// [Forward Declarations] // [Forward Declarations]
// ============================================================================ // ============================================================================
struct RATiedReg;
class RAWorkReg;
class JumpAnnotation; class JumpAnnotation;
class JumpNode; class JumpNode;
class FuncNode; class FuncNode;
class FuncRetNode; class FuncRetNode;
@@ -56,131 +53,6 @@ class InvokeNode;
//! \addtogroup asmjit_compiler //! \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] // [asmjit::BaseCompiler]
// ============================================================================ // ============================================================================

View File

@@ -0,0 +1,170 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_CORE_COMPILERDEFS_H_INCLUDED
#define ASMJIT_CORE_COMPILERDEFS_H_INCLUDED
#include "../core/api-config.h"
#include "../core/operand.h"
#include "../core/zonestring.h"
ASMJIT_BEGIN_NAMESPACE
// ============================================================================
// [Forward Declarations]
// ============================================================================
class RAWorkReg;
//! \addtogroup asmjit_compiler
//! \{
// ============================================================================
// [asmjit::VirtReg]
// ============================================================================
//! Virtual register data, managed by \ref BaseCompiler.
class VirtReg {
public:
ASMJIT_NONCOPYABLE(VirtReg)
//! Virtual register id.
uint32_t _id = 0;
//! Virtual register info (signature).
RegInfo _info = {};
//! Virtual register size (can be smaller than `regInfo._size`).
uint32_t _virtSize = 0;
//! Virtual register alignment (for spilling).
uint8_t _alignment = 0;
//! Type-id.
uint8_t _typeId = 0;
//! Virtual register weight for alloc/spill decisions.
uint8_t _weight = 1;
//! True if this is a fixed register, never reallocated.
uint8_t _isFixed : 1;
//! True if the virtual register is only used as a stack (never accessed as register).
uint8_t _isStack : 1;
uint8_t _reserved : 6;
//! Virtual register name (user provided or automatically generated).
ZoneString<16> _name {};
// -------------------------------------------------------------------------
// The following members are used exclusively by RAPass. They are initialized
// when the VirtReg is created to NULL pointers and then changed during RAPass
// execution. RAPass sets them back to NULL before it returns.
// -------------------------------------------------------------------------
//! Reference to `RAWorkReg`, used during register allocation.
RAWorkReg* _workReg = nullptr;
//! \name Construction & Destruction
//! \{
inline VirtReg(uint32_t id, uint32_t signature, uint32_t virtSize, uint32_t alignment, uint32_t typeId) noexcept
: _id(id),
_info { signature },
_virtSize(virtSize),
_alignment(uint8_t(alignment)),
_typeId(uint8_t(typeId)),
_isFixed(false),
_isStack(false),
_reserved(0) {}
//! \}
//! \name Accessors
//! \{
//! Returns the virtual register id.
inline uint32_t id() const noexcept { return _id; }
//! Returns the virtual register name.
inline const char* name() const noexcept { return _name.data(); }
//! Returns the size of the virtual register name.
inline uint32_t nameSize() const noexcept { return _name.size(); }
//! Returns a register information that wraps the register signature.
inline const RegInfo& info() const noexcept { return _info; }
//! Returns a virtual register type (maps to the physical register type as well).
inline uint32_t type() const noexcept { return _info.type(); }
//! Returns a virtual register group (maps to the physical register group as well).
inline uint32_t group() const noexcept { return _info.group(); }
//! Returns a real size of the register this virtual register maps to.
//!
//! For example if this is a 128-bit SIMD register used for a scalar single
//! precision floating point value then its virtSize would be 4, however, the
//! `regSize` would still say 16 (128-bits), because it's the smallest size
//! of that register type.
inline uint32_t regSize() const noexcept { return _info.size(); }
//! Returns a register signature of this virtual register.
inline uint32_t signature() const noexcept { return _info.signature(); }
//! Returns the virtual register size.
//!
//! The virtual register size describes how many bytes the virtual register
//! needs to store its content. It can be smaller than the physical register
//! size, see `regSize()`.
inline uint32_t virtSize() const noexcept { return _virtSize; }
//! Returns the virtual register alignment.
inline uint32_t alignment() const noexcept { return _alignment; }
//! Returns the virtual register type id, see `Type::Id`.
inline uint32_t typeId() const noexcept { return _typeId; }
//! Returns the virtual register weight - the register allocator can use it
//! as explicit hint for alloc/spill decisions.
inline uint32_t weight() const noexcept { return _weight; }
//! Sets the virtual register weight (0 to 255) - the register allocator can
//! use it as explicit hint for alloc/spill decisions and initial bin-packing.
inline void setWeight(uint32_t weight) noexcept { _weight = uint8_t(weight); }
//! Returns whether the virtual register is always allocated to a fixed
//! physical register (and never reallocated).
//!
//! \note This is only used for special purposes and it's mostly internal.
inline bool isFixed() const noexcept { return bool(_isFixed); }
//! Returns whether the virtual register is indeed a stack that only uses
//! the virtual register id for making it accessible.
//!
//! \note It's an error if a stack is accessed as a register.
inline bool isStack() const noexcept { return bool(_isStack); }
inline bool hasWorkReg() const noexcept { return _workReg != nullptr; }
inline RAWorkReg* workReg() const noexcept { return _workReg; }
inline void setWorkReg(RAWorkReg* workReg) noexcept { _workReg = workReg; }
inline void resetWorkReg() noexcept { _workReg = nullptr; }
//! \}
};
//! \}
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_COMPILERDEFS_H_INCLUDED

View File

@@ -24,7 +24,7 @@
#ifndef ASMJIT_CORE_CPUINFO_H_INCLUDED #ifndef ASMJIT_CORE_CPUINFO_H_INCLUDED
#define 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/features.h"
#include "../core/globals.h" #include "../core/globals.h"
#include "../core/string.h" #include "../core/string.h"

View File

@@ -0,0 +1,351 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "../core/api-build_p.h"
#include "../core/archtraits.h"
#include "../core/emithelper_p.h"
#include "../core/formatter.h"
#include "../core/funcargscontext_p.h"
#include "../core/radefs_p.h"
// Can be used for debugging...
// #define ASMJIT_DUMP_ARGS_ASSIGNMENT
ASMJIT_BEGIN_NAMESPACE
// ============================================================================
// [asmjit::BaseEmitHelper - Formatting]
// ============================================================================
#ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
static void dumpFuncValue(String& sb, uint32_t arch, const FuncValue& value) noexcept {
Formatter::formatTypeId(sb, value.typeId());
sb.append('@');
if (value.isIndirect())
sb.append('[');
if (value.isReg())
Formatter::formatRegister(sb, 0, nullptr, arch, value.regType(), value.regId());
else if (value.isStack())
sb.appendFormat("[%d]", value.stackOffset());
else
sb.append("<none>");
if (value.isIndirect())
sb.append(']');
}
static void dumpAssignment(String& sb, const FuncArgsContext& ctx) noexcept {
typedef FuncArgsContext::Var Var;
uint32_t arch = ctx.arch();
uint32_t varCount = ctx.varCount();
for (uint32_t i = 0; i < varCount; i++) {
const Var& var = ctx.var(i);
const FuncValue& dst = var.out;
const FuncValue& cur = var.cur;
sb.appendFormat("Var%u: ", i);
dumpFuncValue(sb, arch, dst);
sb.append(" <- ");
dumpFuncValue(sb, arch, cur);
if (var.isDone())
sb.append(" {Done}");
sb.append('\n');
}
}
#endif
// ============================================================================
// [asmjit::BaseEmitHelper - EmitArgsAssignment]
// ============================================================================
ASMJIT_FAVOR_SIZE Error BaseEmitHelper::emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args) {
typedef FuncArgsContext::Var Var;
typedef FuncArgsContext::WorkData WorkData;
enum WorkFlags : uint32_t {
kWorkNone = 0x00,
kWorkDidSome = 0x01,
kWorkPending = 0x02,
kWorkPostponed = 0x04
};
uint32_t arch = frame.arch();
const ArchTraits& archTraits = ArchTraits::byArch(arch);
RAConstraints constraints;
FuncArgsContext ctx;
ASMJIT_PROPAGATE(constraints.init(arch));
ASMJIT_PROPAGATE(ctx.initWorkData(frame, args, &constraints));
#ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
{
String sb;
dumpAssignment(sb, ctx);
printf("%s\n", sb.data());
}
#endif
uint32_t varCount = ctx._varCount;
WorkData* workData = ctx._workData;
uint32_t saVarId = ctx._saVarId;
BaseReg sp = BaseReg::fromSignatureAndId(_emitter->_gpRegInfo.signature(), archTraits.spRegId());
BaseReg sa = sp;
if (frame.hasDynamicAlignment()) {
if (frame.hasPreservedFP())
sa.setId(archTraits.fpRegId());
else
sa.setId(saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId());
}
// --------------------------------------------------------------------------
// Register to stack and stack to stack moves must be first as now we have
// the biggest chance of having as many as possible unassigned registers.
// --------------------------------------------------------------------------
if (ctx._stackDstMask) {
// Base address of all arguments passed by stack.
BaseMem baseArgPtr(sa, int32_t(frame.saOffset(sa.id())));
BaseMem baseStackPtr(sp, 0);
for (uint32_t varId = 0; varId < varCount; varId++) {
Var& var = ctx._vars[varId];
if (!var.out.isStack())
continue;
FuncValue& cur = var.cur;
FuncValue& out = var.out;
ASMJIT_ASSERT(cur.isReg() || cur.isStack());
BaseReg reg;
BaseMem dstStackPtr = baseStackPtr.cloneAdjusted(out.stackOffset());
BaseMem srcStackPtr = baseArgPtr.cloneAdjusted(cur.stackOffset());
if (cur.isIndirect()) {
if (cur.isStack()) {
// TODO: Indirect stack.
return DebugUtils::errored(kErrorInvalidAssignment);
}
else {
srcStackPtr.setBaseId(cur.regId());
}
}
if (cur.isReg() && !cur.isIndirect()) {
WorkData& wd = workData[archTraits.regTypeToGroup(cur.regType())];
uint32_t rId = cur.regId();
reg.setSignatureAndId(archTraits.regTypeToSignature(cur.regType()), rId);
wd.unassign(varId, rId);
}
else {
// Stack to reg move - tricky since we move stack to stack we can decide which
// register to use. In general we follow the rule that IntToInt moves will use
// GP regs with possibility to signature or zero extend, and all other moves will
// either use GP or VEC regs depending on the size of the move.
RegInfo rInfo = getSuitableRegForMemToMemMove(arch, out.typeId(), cur.typeId());
if (ASMJIT_UNLIKELY(!rInfo.isValid()))
return DebugUtils::errored(kErrorInvalidState);
WorkData& wd = workData[rInfo.group()];
uint32_t availableRegs = wd.availableRegs();
if (ASMJIT_UNLIKELY(!availableRegs))
return DebugUtils::errored(kErrorInvalidState);
uint32_t rId = Support::ctz(availableRegs);
reg.setSignatureAndId(rInfo.signature(), rId);
ASMJIT_PROPAGATE(emitArgMove(reg, out.typeId(), srcStackPtr, cur.typeId()));
}
if (cur.isIndirect() && cur.isReg())
workData[BaseReg::kGroupGp].unassign(varId, cur.regId());
// Register to stack move.
ASMJIT_PROPAGATE(emitRegMove(dstStackPtr, reg, cur.typeId()));
var.markDone();
}
}
// --------------------------------------------------------------------------
// Shuffle all registers that are currently assigned accordingly to target
// assignment.
// --------------------------------------------------------------------------
uint32_t workFlags = kWorkNone;
for (;;) {
for (uint32_t varId = 0; varId < varCount; varId++) {
Var& var = ctx._vars[varId];
if (var.isDone() || !var.cur.isReg())
continue;
FuncValue& cur = var.cur;
FuncValue& out = var.out;
uint32_t curGroup = archTraits.regTypeToGroup(cur.regType());
uint32_t outGroup = archTraits.regTypeToGroup(out.regType());
uint32_t curId = cur.regId();
uint32_t outId = out.regId();
if (curGroup != outGroup) {
// TODO: Conversion is not supported.
return DebugUtils::errored(kErrorInvalidAssignment);
}
else {
WorkData& wd = workData[outGroup];
if (!wd.isAssigned(outId)) {
EmitMove:
ASMJIT_PROPAGATE(
emitArgMove(
BaseReg::fromSignatureAndId(archTraits.regTypeToSignature(out.regType()), outId), out.typeId(),
BaseReg::fromSignatureAndId(archTraits.regTypeToSignature(cur.regType()), curId), cur.typeId()));
wd.reassign(varId, outId, curId);
cur.initReg(out.regType(), outId, out.typeId());
if (outId == out.regId())
var.markDone();
workFlags |= kWorkDidSome | kWorkPending;
}
else {
uint32_t altId = wd._physToVarId[outId];
Var& altVar = ctx._vars[altId];
if (!altVar.out.isInitialized() || (altVar.out.isReg() && altVar.out.regId() == curId)) {
// Only few architectures provide swap operations, and only for few register groups.
if (archTraits.hasSwap(curGroup)) {
uint32_t highestType = Support::max(cur.regType(), altVar.cur.regType());
if (Support::isBetween<uint32_t>(highestType, BaseReg::kTypeGp8Lo, BaseReg::kTypeGp16))
highestType = BaseReg::kTypeGp32;
uint32_t signature = archTraits.regTypeToSignature(highestType);
ASMJIT_PROPAGATE(
emitRegSwap(BaseReg::fromSignatureAndId(signature, outId),
BaseReg::fromSignatureAndId(signature, curId)));
wd.swap(varId, curId, altId, outId);
cur.setRegId(outId);
var.markDone();
altVar.cur.setRegId(curId);
if (altVar.out.isInitialized())
altVar.markDone();
workFlags |= kWorkDidSome;
}
else {
// If there is a scratch register it can be used to perform the swap.
uint32_t availableRegs = wd.availableRegs();
if (availableRegs) {
uint32_t inOutRegs = wd.dstRegs();
if (availableRegs & ~inOutRegs)
availableRegs &= ~inOutRegs;
outId = Support::ctz(availableRegs);
goto EmitMove;
}
else {
workFlags |= kWorkPending;
}
}
}
else {
workFlags |= kWorkPending;
}
}
}
}
if (!(workFlags & kWorkPending))
break;
// If we did nothing twice it means that something is really broken.
if ((workFlags & (kWorkDidSome | kWorkPostponed)) == kWorkPostponed)
return DebugUtils::errored(kErrorInvalidState);
workFlags = (workFlags & kWorkDidSome) ? kWorkNone : kWorkPostponed;
}
// --------------------------------------------------------------------------
// Load arguments passed by stack into registers. This is pretty simple and
// it never requires multiple iterations like the previous phase.
// --------------------------------------------------------------------------
if (ctx._hasStackSrc) {
uint32_t iterCount = 1;
if (frame.hasDynamicAlignment() && !frame.hasPreservedFP())
sa.setId(saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId());
// Base address of all arguments passed by stack.
BaseMem baseArgPtr(sa, int32_t(frame.saOffset(sa.id())));
for (uint32_t iter = 0; iter < iterCount; iter++) {
for (uint32_t varId = 0; varId < varCount; varId++) {
Var& var = ctx._vars[varId];
if (var.isDone())
continue;
if (var.cur.isStack()) {
ASMJIT_ASSERT(var.out.isReg());
uint32_t outId = var.out.regId();
uint32_t outType = var.out.regType();
uint32_t group = archTraits.regTypeToGroup(outType);
WorkData& wd = ctx._workData[group];
if (outId == sa.id() && group == BaseReg::kGroupGp) {
// This register will be processed last as we still need `saRegId`.
if (iterCount == 1) {
iterCount++;
continue;
}
wd.unassign(wd._physToVarId[outId], outId);
}
BaseReg dstReg = BaseReg::fromSignatureAndId(archTraits.regTypeToSignature(outType), outId);
BaseMem srcMem = baseArgPtr.cloneAdjusted(var.cur.stackOffset());
ASMJIT_PROPAGATE(emitArgMove(
dstReg, var.out.typeId(),
srcMem, var.cur.typeId()));
wd.assign(varId, outId);
var.cur.initReg(outType, outId, var.cur.typeId(), FuncValue::kFlagIsDone);
}
}
}
}
return kErrorOk;
}
ASMJIT_END_NAMESPACE

View File

@@ -0,0 +1,83 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_CORE_EMITHELPER_P_H_INCLUDED
#define ASMJIT_CORE_EMITHELPER_P_H_INCLUDED
#include "../core/emitter.h"
#include "../core/operand.h"
#include "../core/type.h"
ASMJIT_BEGIN_NAMESPACE
//! \cond INTERNAL
//! \addtogroup asmjit_core
//! \{
// ============================================================================
// [asmjit::BaseEmitHelper]
// ============================================================================
//! Helper class that provides utilities for each supported architecture.
class BaseEmitHelper {
public:
BaseEmitter* _emitter;
inline explicit BaseEmitHelper(BaseEmitter* emitter = nullptr) noexcept
: _emitter(emitter) {}
inline BaseEmitter* emitter() const noexcept { return _emitter; }
inline void setEmitter(BaseEmitter* emitter) noexcept { _emitter = emitter; }
//! Emits a pure move operation between two registers or the same type or
//! between a register and its home slot. This function does not handle
//! register conversion.
virtual Error emitRegMove(
const Operand_& dst_,
const Operand_& src_, uint32_t typeId, const char* comment = nullptr) = 0;
//! Emits swap between two registers.
virtual Error emitRegSwap(
const BaseReg& a,
const BaseReg& b, const char* comment = nullptr) = 0;
//! Emits move from a function argument (either register or stack) to a register.
//!
//! This function can handle the necessary conversion from one argument to
//! another, and from one register type to another, if it's possible. Any
//! attempt of conversion that requires third register of a different group
//! (for example conversion from K to MMX on X86/X64) will fail.
virtual Error emitArgMove(
const BaseReg& dst_, uint32_t dstTypeId,
const Operand_& src_, uint32_t srcTypeId, const char* comment = nullptr) = 0;
Error emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args);
};
//! \}
//! \endcond
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_EMITHELPER_P_H_INCLUDED

View File

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

View File

@@ -24,7 +24,7 @@
#ifndef ASMJIT_CORE_EMITTER_H_INCLUDED #ifndef ASMJIT_CORE_EMITTER_H_INCLUDED
#define 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/codeholder.h"
#include "../core/inst.h" #include "../core/inst.h"
#include "../core/operand.h" #include "../core/operand.h"
@@ -54,43 +54,43 @@ public:
ASMJIT_BASE_CLASS(BaseEmitter) ASMJIT_BASE_CLASS(BaseEmitter)
//! See \ref EmitterType. //! See \ref EmitterType.
uint8_t _emitterType; uint8_t _emitterType = 0;
//! See \ref BaseEmitter::EmitterFlags. //! See \ref BaseEmitter::EmitterFlags.
uint8_t _emitterFlags; uint8_t _emitterFlags = 0;
//! Validation flags in case validation is used, see \ref InstAPI::ValidationFlags. //! Validation flags in case validation is used, see \ref InstAPI::ValidationFlags.
//! //!
//! \note Validation flags are specific to the emitter and they are setup at //! \note Validation flags are specific to the emitter and they are setup at
//! construction time and then never changed. //! construction time and then never changed.
uint8_t _validationFlags; uint8_t _validationFlags = 0;
//! Validation options, see \ref ValidationOptions. //! Validation options, see \ref ValidationOptions.
uint8_t _validationOptions; uint8_t _validationOptions = 0;
//! Encoding options, see \ref EncodingOptions. //! Encoding options, see \ref EncodingOptions.
uint32_t _encodingOptions; uint32_t _encodingOptions = 0;
//! Forced instruction options, combined with \ref _instOptions by \ref emit(). //! 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. //! Internal private data used freely by any emitter.
uint32_t _privateData; uint32_t _privateData = 0;
//! CodeHolder the emitter is attached to. //! CodeHolder the emitter is attached to.
CodeHolder* _code; CodeHolder* _code = nullptr;
//! Attached \ref Logger. //! Attached \ref Logger.
Logger* _logger; Logger* _logger = nullptr;
//! Attached \ref ErrorHandler. //! Attached \ref ErrorHandler.
ErrorHandler* _errorHandler; ErrorHandler* _errorHandler = nullptr;
//! Describes the target environment, matches \ref CodeHolder::environment(). //! Describes the target environment, matches \ref CodeHolder::environment().
Environment _environment; Environment _environment {};
//! Native GP register signature and signature related information. //! Native GP register signature and signature related information.
RegInfo _gpRegInfo; RegInfo _gpRegInfo {};
//! Next instruction options (affects the next instruction). //! 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). //! 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). //! Inline comment of the next instruction (affects the next instruction).
const char* _inlineComment; const char* _inlineComment = nullptr;
//! Emitter type. //! Emitter type.
enum EmitterType : uint32_t { enum EmitterType : uint32_t {
@@ -494,6 +494,11 @@ public:
//! Creates a new named label. //! 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; 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 `Label` by `name`.
//! //!
//! Returns invalid Label in case that the name is invalid or label was not found. //! Returns invalid Label in case that the name is invalid or label was not found.

View File

@@ -101,16 +101,16 @@ public:
// 8 is not used, even numbers are 64-bit architectures. // 8 is not used, even numbers are 64-bit architectures.
//! 32-bit MIPS architecture in (little endian). //! 32-bit MIPS architecture in (little endian).
kArchMIPS_LE = 9, kArchMIPS32_LE = 9,
//! 32-bit MIPS architecture in (big endian). //! 32-bit MIPS architecture in (big endian).
kArchMIPS_BE = kArchMIPS_LE | kArchBigEndianMask, kArchMIPS32_BE = kArchMIPS32_LE | kArchBigEndianMask,
//! 64-bit MIPS architecture in (little endian). //! 64-bit MIPS architecture in (little endian).
kArchMIPS64_LE = 10, kArchMIPS64_LE = 10,
//! 64-bit MIPS architecture in (big endian). //! 64-bit MIPS architecture in (big endian).
kArchMIPS64_BE = kArchMIPS64_LE | kArchBigEndianMask, kArchMIPS64_BE = kArchMIPS64_LE | kArchBigEndianMask,
//! Count of architectures. //! Count of architectures.
kArchCount kArchCount = 11
}; };
//! Sub-architecture. //! Sub-architecture.
@@ -246,8 +246,8 @@ public:
ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_LE ? kArchAArch64 : ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_LE ? kArchAArch64 :
ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_BE ? kArchAArch64_BE : ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_BE ? kArchAArch64_BE :
ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_LE ? kArchMIPS_LE : ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_LE ? kArchMIPS32_LE :
ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_BE ? kArchMIPS_BE : ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_BE ? kArchMIPS32_BE :
ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_LE ? kArchMIPS64_LE : ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_LE ? kArchMIPS64_LE :
ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_BE ? kArchMIPS64_BE : ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_BE ? kArchMIPS64_BE :
@@ -410,6 +410,16 @@ public:
_reserved = 0; _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. //! Tests whether the architecture is 32-bit.
inline bool is32Bit() const noexcept { return is32Bit(_arch); } inline bool is32Bit() const noexcept { return is32Bit(_arch); }
//! Tests whether the architecture is 64-bit. //! Tests whether the architecture is 64-bit.
@@ -422,11 +432,11 @@ public:
//! Tests whether this architecture is of X86 family. //! Tests whether this architecture is of X86 family.
inline bool isFamilyX86() const noexcept { return isFamilyX86(_arch); } 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); } 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); } 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); } inline bool isFamilyMIPS() const noexcept { return isFamilyMIPS(_arch); }
//! Tests whether the environment platform is Windows. //! Tests whether the environment platform is Windows.
@@ -486,6 +496,11 @@ public:
//! \name Static Utilities //! \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. //! Tests whether the given architecture `arch` is 32-bit.
static inline bool is32Bit(uint32_t arch) noexcept { static inline bool is32Bit(uint32_t arch) noexcept {
return (arch & kArch32BitMask) == kArch32BitMask; return (arch & kArch32BitMask) == kArch32BitMask;
@@ -506,6 +521,12 @@ public:
return (arch & kArchBigEndianMask) == kArchBigEndianMask; 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. //! Tests whether the given architecture family is X86 or X64.
static inline bool isFamilyX86(uint32_t arch) noexcept { static inline bool isFamilyX86(uint32_t arch) noexcept {
return arch == kArchX86 || return arch == kArchX86 ||
@@ -529,7 +550,7 @@ public:
//! Tests whether the given architecture family is MISP or MIPS64. //! Tests whether the given architecture family is MISP or MIPS64.
static inline bool isFamilyMIPS(uint32_t arch) noexcept { static inline bool isFamilyMIPS(uint32_t arch) noexcept {
arch &= ~kArchBigEndianMask; arch &= ~kArchBigEndianMask;
return arch == kArchMIPS_LE || return arch == kArchMIPS32_LE ||
arch == kArchMIPS64_LE; arch == kArchMIPS64_LE;
} }

View File

@@ -22,22 +22,42 @@
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
#include "../core/api-build_p.h" #include "../core/api-build_p.h"
#include "../core/arch.h" #include "../core/archtraits.h"
#include "../core/func.h" #include "../core/func.h"
#include "../core/operand.h"
#include "../core/type.h" #include "../core/type.h"
#include "../core/funcargscontext_p.h"
#ifdef ASMJIT_BUILD_X86 #ifdef ASMJIT_BUILD_X86
#include "../x86/x86internal_p.h" #include "../x86/x86func_p.h"
#include "../x86/x86operand.h"
#endif #endif
#ifdef ASMJIT_BUILD_ARM #ifdef ASMJIT_BUILD_ARM
#include "../arm/arminternal_p.h" #include "../arm/armfunc_p.h"
#include "../arm/armoperand.h"
#endif #endif
ASMJIT_BEGIN_NAMESPACE 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] // [asmjit::FuncDetail - Init / Reset]
// ============================================================================ // ============================================================================
@@ -69,12 +89,12 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const E
#ifdef ASMJIT_BUILD_X86 #ifdef ASMJIT_BUILD_X86
if (environment.isFamilyX86()) if (environment.isFamilyX86())
return x86::X86Internal::initFuncDetail(*this, signature, registerSize); return x86::FuncInternal::initFuncDetail(*this, signature, registerSize);
#endif #endif
#ifdef ASMJIT_BUILD_ARM #ifdef ASMJIT_BUILD_ARM
if (environment.isFamilyARM()) if (environment.isFamilyARM())
return arm::ArmInternal::initFuncDetail(*this, signature, registerSize); return arm::FuncInternal::initFuncDetail(*this, signature, registerSize);
#endif #endif
// We should never bubble here as if `cc.init()` succeeded then there has to // 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 { ASMJIT_FAVOR_SIZE Error FuncFrame::init(const FuncDetail& func) noexcept {
#ifdef ASMJIT_BUILD_X86 uint32_t arch = func.callConv().arch();
if (Environment::isFamilyX86(func.callConv().arch())) if (!Environment::isValidArch(arch))
return x86::X86Internal::initFuncFrame(*this, func); return DebugUtils::errored(kErrorInvalidArch);
#endif
#ifdef ASMJIT_BUILD_ARM const ArchTraits& archTraits = ArchTraits::byArch(arch);
if (Environment::isFamilyARM(func.callConv().arch()))
return arm::ArmInternal::initFuncFrame(*this, func);
#endif
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 { ASMJIT_FAVOR_SIZE Error FuncFrame::finalize() noexcept {
#ifdef ASMJIT_BUILD_X86 if (!Environment::isValidArch(arch()))
if (Environment::isFamilyX86(arch())) return DebugUtils::errored(kErrorInvalidArch);
return x86::X86Internal::finalizeFuncFrame(*this);
#endif
#ifdef ASMJIT_BUILD_ARM const ArchTraits& archTraits = ArchTraits::byArch(arch());
if (Environment::isFamilyARM(arch()))
return arm::ArmInternal::finalizeFuncFrame(*this);
#endif
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) if (!func)
return DebugUtils::errored(kErrorInvalidState); return DebugUtils::errored(kErrorInvalidState);
#ifdef ASMJIT_BUILD_X86 RAConstraints constraints;
if (Environment::isFamilyX86(arch)) ASMJIT_PROPAGATE(constraints.init(arch));
return x86::X86Internal::argsToFuncFrame(*this, frame);
#endif
#ifdef ASMJIT_BUILD_ARM FuncArgsContext ctx;
if (Environment::isFamilyARM(arch)) ASMJIT_PROPAGATE(ctx.initWorkData(frame, *this, &constraints));
return arm::ArmInternal::argsToFuncFrame(*this, frame); ASMJIT_PROPAGATE(ctx.markDstRegsDirty(frame));
#endif ASMJIT_PROPAGATE(ctx.markScratchRegs(frame));
ASMJIT_PROPAGATE(ctx.markStackArgsReg(frame));
return DebugUtils::errored(kErrorInvalidArch); return kErrorOk;
} }
ASMJIT_END_NAMESPACE ASMJIT_END_NAMESPACE

View File

@@ -24,8 +24,7 @@
#ifndef ASMJIT_CORE_FUNC_H_INCLUDED #ifndef ASMJIT_CORE_FUNC_H_INCLUDED
#define ASMJIT_CORE_FUNC_H_INCLUDED #define ASMJIT_CORE_FUNC_H_INCLUDED
#include "../core/arch.h" #include "../core/archtraits.h"
#include "../core/callconv.h"
#include "../core/environment.h" #include "../core/environment.h"
#include "../core/operand.h" #include "../core/operand.h"
#include "../core/type.h" #include "../core/type.h"
@@ -36,6 +35,365 @@ ASMJIT_BEGIN_NAMESPACE
//! \addtogroup asmjit_function //! \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] // [asmjit::FuncSignature]
// ============================================================================ // ============================================================================
@@ -629,15 +987,19 @@ public:
uint32_t _dirtyRegs[BaseReg::kGroupVirt]; uint32_t _dirtyRegs[BaseReg::kGroupVirt];
//! Registers that must be preserved (copied from CallConv). //! Registers that must be preserved (copied from CallConv).
uint32_t _preservedRegs[BaseReg::kGroupVirt]; 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. //! Stack size required to save registers with push/pop.
uint16_t _gpSaveSize; uint16_t _pushPopSaveSize;
//! Final Stack size required to save other than GP regs. //! Stack size required to save extra registers that cannot use push/pop.
uint16_t _nonGpSaveSize; uint16_t _extraRegSaveSize;
//! Final offset where saved GP regs are stored. //! Offset where registers saved/restored via push/pop are stored
uint32_t _gpSaveOffset; uint32_t _pushPopSaveOffset;
//! Final offset where saved other than GP regs are stored. //! Offset where extra ragisters that cannot use push/pop are stored.
uint32_t _nonGpSaveOffset; uint32_t _extraRegSaveOffset;
//! \name Construction & Destruction //! \name Construction & Destruction
//! \{ //! \{
@@ -881,20 +1243,35 @@ public:
return _preservedRegs[group]; 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 bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; }
inline uint32_t saRegId() const noexcept { return _saRegId; } inline uint32_t saRegId() const noexcept { return _saRegId; }
inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); } inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); }
inline void resetSARegId() { setSARegId(BaseReg::kIdBad); } inline void resetSARegId() { setSARegId(BaseReg::kIdBad); }
//! Returns stack size required to save GP registers. //! Returns stack size required to save/restore registers via push/pop.
inline uint32_t gpSaveSize() const noexcept { return _gpSaveSize; } inline uint32_t pushPopSaveSize() const noexcept { return _pushPopSaveSize; }
//! Returns stack size required to save other than GP registers (MM, XMM|YMM|ZMM, K, VFP, etc...). //! Returns an offset to the stack where registers are saved via push/pop.
inline uint32_t nonGpSaveSize() const noexcept { return _nonGpSaveSize; } inline uint32_t pushPopSaveOffset() const noexcept { return _pushPopSaveOffset; }
//! Returns an offset to the stack where general purpose registers are saved. //! Returns stack size required to save/restore extra registers that don't
inline uint32_t gpSaveOffset() const noexcept { return _gpSaveOffset; } //! use push/pop/
//! Returns an offset to the stack where other than GP registers are saved. //!
inline uint32_t nonGpSaveOffset() const noexcept { return _nonGpSaveOffset; } //! \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. //! Tests whether the functions contains stack adjustment.
inline bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; } inline bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; }

View File

@@ -0,0 +1,315 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "../core/api-build_p.h"
#include "../core/funcargscontext_p.h"
ASMJIT_BEGIN_NAMESPACE
//! \cond INTERNAL
//! \addtogroup asmjit_core
//! \{
FuncArgsContext::FuncArgsContext() noexcept {
for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++)
_workData[group].reset();
}
ASMJIT_FAVOR_SIZE Error FuncArgsContext::initWorkData(const FuncFrame& frame, const FuncArgsAssignment& args, const RAConstraints* constraints) noexcept {
// The code has to be updated if this changes.
ASMJIT_ASSERT(BaseReg::kGroupVirt == 4);
uint32_t i;
uint32_t arch = frame.arch();
const FuncDetail& func = *args.funcDetail();
_archTraits = &ArchTraits::byArch(arch);
_constraints = constraints;
_arch = uint8_t(arch);
// Initialize `_archRegs`.
for (i = 0; i < BaseReg::kGroupVirt; i++)
_workData[i]._archRegs = _constraints->availableRegs(i);
if (frame.hasPreservedFP())
_workData[BaseReg::kGroupGp]._archRegs &= ~Support::bitMask(archTraits().fpRegId());
// Extract information from all function arguments/assignments and build Var[] array.
uint32_t varId = 0;
for (uint32_t argIndex = 0; argIndex < Globals::kMaxFuncArgs; argIndex++) {
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
const FuncValue& dst_ = args.arg(argIndex, valueIndex);
if (!dst_.isAssigned())
continue;
const FuncValue& src_ = func.arg(argIndex, valueIndex);
if (ASMJIT_UNLIKELY(!src_.isAssigned()))
return DebugUtils::errored(kErrorInvalidState);
Var& var = _vars[varId];
var.init(src_, dst_);
FuncValue& src = var.cur;
FuncValue& dst = var.out;
uint32_t dstGroup = 0xFFFFFFFFu;
uint32_t dstId = BaseReg::kIdBad;
WorkData* dstWd = nullptr;
// Not supported.
if (src.isIndirect())
return DebugUtils::errored(kErrorInvalidAssignment);
if (dst.isReg()) {
uint32_t dstType = dst.regType();
if (ASMJIT_UNLIKELY(!archTraits().hasRegType(dstType)))
return DebugUtils::errored(kErrorInvalidRegType);
// Copy TypeId from source if the destination doesn't have it. The RA
// used by BaseCompiler would never leave TypeId undefined, but users
// of FuncAPI can just assign phys regs without specifying the type.
if (!dst.hasTypeId())
dst.setTypeId(archTraits().regTypeToTypeId(dst.regType()));
dstGroup = archTraits().regTypeToGroup(dstType);
if (ASMJIT_UNLIKELY(dstGroup >= BaseReg::kGroupVirt))
return DebugUtils::errored(kErrorInvalidRegGroup);
dstWd = &_workData[dstGroup];
dstId = dst.regId();
if (ASMJIT_UNLIKELY(dstId >= 32 || !Support::bitTest(dstWd->archRegs(), dstId)))
return DebugUtils::errored(kErrorInvalidPhysId);
if (ASMJIT_UNLIKELY(Support::bitTest(dstWd->dstRegs(), dstId)))
return DebugUtils::errored(kErrorOverlappedRegs);
dstWd->_dstRegs |= Support::bitMask(dstId);
dstWd->_dstShuf |= Support::bitMask(dstId);
dstWd->_usedRegs |= Support::bitMask(dstId);
}
else {
if (!dst.hasTypeId())
dst.setTypeId(src.typeId());
RegInfo regInfo = getSuitableRegForMemToMemMove(arch, dst.typeId(), src.typeId());
if (ASMJIT_UNLIKELY(!regInfo.isValid()))
return DebugUtils::errored(kErrorInvalidState);
_stackDstMask = uint8_t(_stackDstMask | Support::bitMask(regInfo.group()));
}
if (src.isReg()) {
uint32_t srcId = src.regId();
uint32_t srcGroup = archTraits().regTypeToGroup(src.regType());
if (dstGroup == srcGroup) {
dstWd->assign(varId, srcId);
// The best case, register is allocated where it is expected to be.
if (dstId == srcId)
var.markDone();
}
else {
if (ASMJIT_UNLIKELY(srcGroup >= BaseReg::kGroupVirt))
return DebugUtils::errored(kErrorInvalidState);
WorkData& srcData = _workData[srcGroup];
srcData.assign(varId, srcId);
}
}
else {
if (dstWd)
dstWd->_numStackArgs++;
_hasStackSrc = true;
}
varId++;
}
}
// Initialize WorkData::workRegs.
for (i = 0; i < BaseReg::kGroupVirt; i++) {
_workData[i]._workRegs = (_workData[i].archRegs() & (frame.dirtyRegs(i) | ~frame.preservedRegs(i))) | _workData[i].dstRegs() | _workData[i].assignedRegs();
}
// Create a variable that represents `SARegId` if necessary.
bool saRegRequired = _hasStackSrc && frame.hasDynamicAlignment() && !frame.hasPreservedFP();
WorkData& gpRegs = _workData[BaseReg::kGroupGp];
uint32_t saCurRegId = frame.saRegId();
uint32_t saOutRegId = args.saRegId();
if (saCurRegId != BaseReg::kIdBad) {
// Check if the provided `SARegId` doesn't collide with input registers.
if (ASMJIT_UNLIKELY(gpRegs.isAssigned(saCurRegId)))
return DebugUtils::errored(kErrorOverlappedRegs);
}
if (saOutRegId != BaseReg::kIdBad) {
// Check if the provided `SARegId` doesn't collide with argument assignments.
if (ASMJIT_UNLIKELY(Support::bitTest(gpRegs.dstRegs(), saOutRegId)))
return DebugUtils::errored(kErrorOverlappedRegs);
saRegRequired = true;
}
if (saRegRequired) {
uint32_t ptrTypeId = Environment::is32Bit(arch) ? Type::kIdU32 : Type::kIdU64;
uint32_t ptrRegType = Environment::is32Bit(arch) ? BaseReg::kTypeGp32 : BaseReg::kTypeGp64;
_saVarId = uint8_t(varId);
_hasPreservedFP = frame.hasPreservedFP();
Var& var = _vars[varId];
var.reset();
if (saCurRegId == BaseReg::kIdBad) {
if (saOutRegId != BaseReg::kIdBad && !gpRegs.isAssigned(saOutRegId)) {
saCurRegId = saOutRegId;
}
else {
uint32_t availableRegs = gpRegs.availableRegs();
if (!availableRegs)
availableRegs = gpRegs.archRegs() & ~gpRegs.workRegs();
if (ASMJIT_UNLIKELY(!availableRegs))
return DebugUtils::errored(kErrorNoMorePhysRegs);
saCurRegId = Support::ctz(availableRegs);
}
}
var.cur.initReg(ptrRegType, saCurRegId, ptrTypeId);
gpRegs.assign(varId, saCurRegId);
gpRegs._workRegs |= Support::bitMask(saCurRegId);
if (saOutRegId != BaseReg::kIdBad) {
var.out.initReg(ptrRegType, saOutRegId, ptrTypeId);
gpRegs._dstRegs |= Support::bitMask(saOutRegId);
gpRegs._workRegs |= Support::bitMask(saOutRegId);
}
else {
var.markDone();
}
varId++;
}
_varCount = varId;
// Detect register swaps.
for (varId = 0; varId < _varCount; varId++) {
Var& var = _vars[varId];
if (var.cur.isReg() && var.out.isReg()) {
uint32_t srcId = var.cur.regId();
uint32_t dstId = var.out.regId();
uint32_t group = archTraits().regTypeToGroup(var.cur.regType());
if (group != archTraits().regTypeToGroup(var.out.regType()))
continue;
WorkData& wd = _workData[group];
if (wd.isAssigned(dstId)) {
Var& other = _vars[wd._physToVarId[dstId]];
if (archTraits().regTypeToGroup(other.out.regType()) == group && other.out.regId() == srcId) {
wd._numSwaps++;
_regSwapsMask = uint8_t(_regSwapsMask | Support::bitMask(group));
}
}
}
}
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error FuncArgsContext::markDstRegsDirty(FuncFrame& frame) noexcept {
for (uint32_t i = 0; i < BaseReg::kGroupVirt; i++) {
WorkData& wd = _workData[i];
uint32_t regs = wd.usedRegs() | wd._dstShuf;
wd._workRegs |= regs;
frame.addDirtyRegs(i, regs);
}
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error FuncArgsContext::markScratchRegs(FuncFrame& frame) noexcept {
uint32_t groupMask = 0;
// Handle stack to stack moves.
groupMask |= _stackDstMask;
// Handle register swaps.
groupMask |= _regSwapsMask & ~Support::bitMask(BaseReg::kGroupGp);
if (!groupMask)
return kErrorOk;
// Selects one dirty register per affected group that can be used as a scratch register.
for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) {
if (Support::bitTest(groupMask, group)) {
WorkData& wd = _workData[group];
// Initially, pick some clobbered or dirty register.
uint32_t workRegs = wd.workRegs();
uint32_t regs = workRegs & ~(wd.usedRegs() | wd._dstShuf);
// If that didn't work out pick some register which is not in 'used'.
if (!regs)
regs = workRegs & ~wd.usedRegs();
// If that didn't work out pick any other register that is allocable.
// This last resort case will, however, result in marking one more
// register dirty.
if (!regs)
regs = wd.archRegs() & ~workRegs;
// If that didn't work out we will have to use XORs instead of MOVs.
if (!regs)
continue;
uint32_t regMask = Support::blsi(regs);
wd._workRegs |= regMask;
frame.addDirtyRegs(group, regMask);
}
}
return kErrorOk;
}
ASMJIT_FAVOR_SIZE Error FuncArgsContext::markStackArgsReg(FuncFrame& frame) noexcept {
if (_saVarId != kVarIdNone) {
const Var& var = _vars[_saVarId];
frame.setSARegId(var.cur.regId());
}
else if (frame.hasPreservedFP()) {
frame.setSARegId(archTraits().fpRegId());
}
return kErrorOk;
}
//! \}
//! \endcond
ASMJIT_END_NAMESPACE

View File

@@ -0,0 +1,224 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_CORE_FUNCARGSCONTEXT_P_H_INCLUDED
#define ASMJIT_CORE_FUNCARGSCONTEXT_P_H_INCLUDED
#include "../core/archtraits.h"
#include "../core/environment.h"
#include "../core/func.h"
#include "../core/operand.h"
#include "../core/radefs_p.h"
#include "../core/support.h"
ASMJIT_BEGIN_NAMESPACE
//! \cond INTERNAL
//! \addtogroup asmjit_core
//! \{
// ============================================================================
// [TODO: Place somewhere else]
// ============================================================================
static inline RegInfo getSuitableRegForMemToMemMove(uint32_t arch, uint32_t dstTypeId, uint32_t srcTypeId) noexcept {
const ArchTraits& archTraits = ArchTraits::byArch(arch);
uint32_t dstSize = Type::sizeOf(dstTypeId);
uint32_t srcSize = Type::sizeOf(srcTypeId);
uint32_t maxSize = Support::max<uint32_t>(dstSize, srcSize);
uint32_t regSize = Environment::registerSizeFromArch(arch);
uint32_t signature = 0;
if (maxSize <= regSize || (Type::isInt(dstTypeId) && Type::isInt(srcTypeId)))
signature = maxSize <= 4 ? archTraits.regTypeToSignature(BaseReg::kTypeGp32)
: archTraits.regTypeToSignature(BaseReg::kTypeGp64);
else if (maxSize <= 8 && archTraits.hasRegType(BaseReg::kTypeVec64))
signature = archTraits.regTypeToSignature(BaseReg::kTypeVec64);
else if (maxSize <= 16 && archTraits.hasRegType(BaseReg::kTypeVec128))
signature = archTraits.regTypeToSignature(BaseReg::kTypeVec128);
else if (maxSize <= 32 && archTraits.hasRegType(BaseReg::kTypeVec256))
signature = archTraits.regTypeToSignature(BaseReg::kTypeVec256);
else if (maxSize <= 64 && archTraits.hasRegType(BaseReg::kTypeVec512))
signature = archTraits.regTypeToSignature(BaseReg::kTypeVec512);
return RegInfo { signature };
}
// ============================================================================
// [asmjit::FuncArgsContext]
// ============================================================================
class FuncArgsContext {
public:
enum VarId : uint32_t {
kVarIdNone = 0xFF
};
//! Contains information about a single argument or SA register that may need shuffling.
struct Var {
FuncValue cur;
FuncValue out;
inline void init(const FuncValue& cur_, const FuncValue& out_) noexcept {
cur = cur_;
out = out_;
}
//! Reset the value to its unassigned state.
inline void reset() noexcept {
cur.reset();
out.reset();
}
inline bool isDone() const noexcept { return cur.isDone(); }
inline void markDone() noexcept { cur.addFlags(FuncValue::kFlagIsDone); }
};
struct WorkData {
//! All allocable registers provided by the architecture.
uint32_t _archRegs;
//! All registers that can be used by the shuffler.
uint32_t _workRegs;
//! Registers used by the shuffler (all).
uint32_t _usedRegs;
//! Assigned registers.
uint32_t _assignedRegs;
//! Destination registers assigned to arguments or SA.
uint32_t _dstRegs;
//! Destination registers that require shuffling.
uint32_t _dstShuf;
//! Number of register swaps.
uint8_t _numSwaps;
//! Number of stack loads.
uint8_t _numStackArgs;
//! Reserved (only used as padding).
uint8_t _reserved[6];
//! Physical ID to variable ID mapping.
uint8_t _physToVarId[32];
inline void reset() noexcept {
_archRegs = 0;
_workRegs = 0;
_usedRegs = 0;
_assignedRegs = 0;
_dstRegs = 0;
_dstShuf = 0;
_numSwaps = 0;
_numStackArgs = 0;
memset(_reserved, 0, sizeof(_reserved));
memset(_physToVarId, kVarIdNone, 32);
}
inline bool isAssigned(uint32_t regId) const noexcept {
ASMJIT_ASSERT(regId < 32);
return Support::bitTest(_assignedRegs, regId);
}
inline void assign(uint32_t varId, uint32_t regId) noexcept {
ASMJIT_ASSERT(!isAssigned(regId));
ASMJIT_ASSERT(_physToVarId[regId] == kVarIdNone);
_physToVarId[regId] = uint8_t(varId);
_assignedRegs ^= Support::bitMask(regId);
}
inline void reassign(uint32_t varId, uint32_t newId, uint32_t oldId) noexcept {
ASMJIT_ASSERT( isAssigned(oldId));
ASMJIT_ASSERT(!isAssigned(newId));
ASMJIT_ASSERT(_physToVarId[oldId] == varId);
ASMJIT_ASSERT(_physToVarId[newId] == kVarIdNone);
_physToVarId[oldId] = uint8_t(kVarIdNone);
_physToVarId[newId] = uint8_t(varId);
_assignedRegs ^= Support::bitMask(newId) ^ Support::bitMask(oldId);
}
inline void swap(uint32_t aVarId, uint32_t aRegId, uint32_t bVarId, uint32_t bRegId) noexcept {
ASMJIT_ASSERT(isAssigned(aRegId));
ASMJIT_ASSERT(isAssigned(bRegId));
ASMJIT_ASSERT(_physToVarId[aRegId] == aVarId);
ASMJIT_ASSERT(_physToVarId[bRegId] == bVarId);
_physToVarId[aRegId] = uint8_t(bVarId);
_physToVarId[bRegId] = uint8_t(aVarId);
}
inline void unassign(uint32_t varId, uint32_t regId) noexcept {
ASMJIT_ASSERT(isAssigned(regId));
ASMJIT_ASSERT(_physToVarId[regId] == varId);
DebugUtils::unused(varId);
_physToVarId[regId] = uint8_t(kVarIdNone);
_assignedRegs ^= Support::bitMask(regId);
}
inline uint32_t archRegs() const noexcept { return _archRegs; }
inline uint32_t workRegs() const noexcept { return _workRegs; }
inline uint32_t usedRegs() const noexcept { return _usedRegs; }
inline uint32_t assignedRegs() const noexcept { return _assignedRegs; }
inline uint32_t dstRegs() const noexcept { return _dstRegs; }
inline uint32_t availableRegs() const noexcept { return _workRegs & ~_assignedRegs; }
};
//! Architecture traits.
const ArchTraits* _archTraits = nullptr;
const RAConstraints* _constraints = nullptr;
//! Architecture identifier.
uint8_t _arch = 0;
//! Has arguments passed via stack (SRC).
bool _hasStackSrc = false;
//! Has preserved frame-pointer (FP).
bool _hasPreservedFP = false;
//! Has arguments assigned to stack (DST).
uint8_t _stackDstMask = 0;
//! Register swap groups (bit-mask).
uint8_t _regSwapsMask = 0;
uint8_t _saVarId = kVarIdNone;
uint32_t _varCount = 0;
WorkData _workData[BaseReg::kGroupVirt];
Var _vars[Globals::kMaxFuncArgs * Globals::kMaxValuePack + 1];
FuncArgsContext() noexcept;
inline const ArchTraits& archTraits() const noexcept { return *_archTraits; }
inline uint32_t arch() const noexcept { return _arch; }
inline uint32_t varCount() const noexcept { return _varCount; }
inline size_t indexOf(const Var* var) const noexcept { return (size_t)(var - _vars); }
inline Var& var(size_t varId) noexcept { return _vars[varId]; }
inline const Var& var(size_t varId) const noexcept { return _vars[varId]; }
Error initWorkData(const FuncFrame& frame, const FuncArgsAssignment& args, const RAConstraints* constraints) noexcept;
Error markScratchRegs(FuncFrame& frame) noexcept;
Error markDstRegsDirty(FuncFrame& frame) noexcept;
Error markStackArgsReg(FuncFrame& frame) noexcept;
};
//! \}
//! \endcond
ASMJIT_END_NAMESPACE
#endif // ASMJIT_CORE_FUNCARGSCONTEXT_P_H_INCLUDED

View File

@@ -67,6 +67,7 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
"InvalidRegGroup\0" "InvalidRegGroup\0"
"InvalidPhysId\0" "InvalidPhysId\0"
"InvalidVirtId\0" "InvalidVirtId\0"
"InvalidElementIndex\0"
"InvalidPrefixCombination\0" "InvalidPrefixCombination\0"
"InvalidLockPrefix\0" "InvalidLockPrefix\0"
"InvalidXAcquirePrefix\0" "InvalidXAcquirePrefix\0"
@@ -108,9 +109,9 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
static const uint16_t sErrorIndex[] = { static const uint16_t sErrorIndex[] = {
0, 3, 15, 31, 44, 56, 71, 90, 108, 123, 132, 148, 165, 178, 192, 210, 230, 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, 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, 518, 538, 563, 581, 603, 625, 642, 659, 675, 691, 707, 724, 739, 754, 774,
794, 827, 847, 862, 879, 898, 919, 939, 953, 974, 988, 1006, 1022, 1038, 794, 814, 847, 867, 882, 899, 918, 939, 959, 973, 994, 1008, 1026, 1042,
1057, 1072, 1088, 1103, 1118, 1148, 1172, 1191 1058, 1077, 1092, 1108, 1123, 1138, 1168, 1192, 1211
}; };
// @EnumStringEnd@ // @EnumStringEnd@

View File

@@ -296,27 +296,29 @@ enum ErrorCode : uint32_t {
kErrorInvalidPhysId, kErrorInvalidPhysId,
//! Invalid virtual register id. //! Invalid virtual register id.
kErrorInvalidVirtId, kErrorInvalidVirtId,
//! Invalid prefix combination. //! Invalid element index (ARM).
kErrorInvalidElementIndex,
//! Invalid prefix combination (X86|X64).
kErrorInvalidPrefixCombination, kErrorInvalidPrefixCombination,
//! Invalid LOCK prefix. //! Invalid LOCK prefix (X86|X64).
kErrorInvalidLockPrefix, kErrorInvalidLockPrefix,
//! Invalid XACQUIRE prefix. //! Invalid XACQUIRE prefix (X86|X64).
kErrorInvalidXAcquirePrefix, kErrorInvalidXAcquirePrefix,
//! Invalid XRELEASE prefix. //! Invalid XRELEASE prefix (X86|X64).
kErrorInvalidXReleasePrefix, kErrorInvalidXReleasePrefix,
//! Invalid REP prefix. //! Invalid REP prefix (X86|X64).
kErrorInvalidRepPrefix, kErrorInvalidRepPrefix,
//! Invalid REX prefix. //! Invalid REX prefix (X86|X64).
kErrorInvalidRexPrefix, kErrorInvalidRexPrefix,
//! Invalid {...} register. //! Invalid {...} register (X86|X64).
kErrorInvalidExtraReg, kErrorInvalidExtraReg,
//! Invalid {k} use (not supported by the instruction). //! Invalid {k} use (not supported by the instruction) (X86|X64).
kErrorInvalidKMaskUse, kErrorInvalidKMaskUse,
//! Invalid {k}{z} use (not supported by the instruction). //! Invalid {k}{z} use (not supported by the instruction) (X86|X64).
kErrorInvalidKZeroUse, 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, 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, kErrorInvalidEROrSAE,
//! Invalid address used (not encodable). //! Invalid address used (not encodable).
kErrorInvalidAddress, kErrorInvalidAddress,

View File

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

View File

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

View File

@@ -35,11 +35,19 @@ ASMJIT_BEGIN_NAMESPACE
// Only useful on non-x86 architectures. // Only useful on non-x86 architectures.
static inline void JitRuntime_flushInstructionCache(const void* p, size_t size) noexcept { 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`. // Windows has a built-in support in `kernel32.dll`.
::FlushInstructionCache(::GetCurrentProcess(), p, size); ::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); DebugUtils::unused(p, size);
# endif
#endif #endif
} }

View File

@@ -32,9 +32,11 @@ ASMJIT_BEGIN_NAMESPACE
//! \addtogroup asmjit_utilities //! \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_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_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_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_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) #define ASMJIT_LOOKUP_TABLE_256(T, I) ASMJIT_LOOKUP_TABLE_128(T, I), ASMJIT_LOOKUP_TABLE_128(T, I + 128)

View File

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

View File

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

View File

@@ -54,6 +54,17 @@ public:
}; };
struct Layout { 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 { inline void reset() noexcept {
physIndex.reset(); physIndex.reset();
physCount.reset(); physCount.reset();
@@ -61,54 +72,52 @@ public:
workCount = 0; workCount = 0;
workRegs = nullptr; 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 { struct PhysToWorkMap {
static inline size_t sizeOf(uint32_t count) noexcept { //! Assigned registers (each bit represents one physical reg).
return sizeof(PhysToWorkMap) - sizeof(uint32_t) + size_t(count) * sizeof(uint32_t); 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(); assigned.reset();
dirty.reset(); dirty.reset();
for (uint32_t i = 0; i < count; i++) for (size_t i = 0; i < count; i++)
workIds[i] = kWorkNone; 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); size_t size = sizeOf(count);
memcpy(this, other, size); 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 { 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); return size_t(count) * sizeof(uint8_t);
} }
inline void reset(uint32_t count) noexcept { inline void reset(size_t count) noexcept {
for (uint32_t i = 0; i < count; i++) for (size_t i = 0; i < count; i++)
physIds[i] = kPhysNone; 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); size_t size = sizeOf(count);
if (ASMJIT_LIKELY(size)) if (ASMJIT_LIKELY(size))
memcpy(this, other, size); memcpy(this, other, size);
} }
uint8_t physIds[1 /* ... */]; //!< WorkReg to PhysReg mapping
}; };
//! Physical registers layout. //! Physical registers layout.

View File

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

View File

@@ -25,11 +25,12 @@
#define ASMJIT_CORE_RADEFS_P_H_INCLUDED #define ASMJIT_CORE_RADEFS_P_H_INCLUDED
#include "../core/api-config.h" #include "../core/api-config.h"
#ifndef ASMJIT_NO_COMPILER #include "../core/archtraits.h"
#include "../core/compilerdefs.h"
#include "../core/compiler.h"
#include "../core/logger.h" #include "../core/logger.h"
#include "../core/operand.h"
#include "../core/support.h" #include "../core/support.h"
#include "../core/type.h"
#include "../core/zone.h" #include "../core/zone.h"
#include "../core/zonevector.h" #include "../core/zonevector.h"
@@ -64,13 +65,52 @@ ASMJIT_BEGIN_NAMESPACE
// [Forward Declarations] // [Forward Declarations]
// ============================================================================ // ============================================================================
class RAPass; class BaseRAPass;
class RABlock; class RABlock;
class BaseNode;
struct RAStackSlot; struct RAStackSlot;
typedef ZoneVector<RABlock*> RABlocks; typedef ZoneVector<RABlock*> RABlocks;
typedef ZoneVector<RAWorkReg*> RAWorkRegs; 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] // [asmjit::RAStrategy]
// ============================================================================ // ============================================================================
@@ -93,45 +133,6 @@ struct RAStrategy {
inline bool isComplex() const noexcept { return _type >= kStrategyComplex; } 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] // [asmjit::RARegCount]
@@ -317,8 +318,9 @@ struct RARegMask {
//! before the register allocator tries to do its job. For example to use fast //! 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 //! register allocation inside a block or loop it cannot have clobbered and/or
//! fixed registers, etc... //! fixed registers, etc...
struct RARegsStats { class RARegsStats {
uint32_t _packed; public:
uint32_t _packed = 0;
enum Index : uint32_t { enum Index : uint32_t {
kIndexUsed = 0, kIndexUsed = 0,
@@ -355,12 +357,12 @@ struct RARegsStats {
//! Count of live registers, per group. //! Count of live registers, per group.
class RALiveCount { class RALiveCount {
public: public:
uint32_t n[BaseReg::kGroupVirt]; uint32_t n[BaseReg::kGroupVirt] {};
//! \name Construction & Destruction //! \name Construction & Destruction
//! \{ //! \{
inline RALiveCount() noexcept { reset(); } inline RALiveCount() noexcept = default;
inline RALiveCount(const RALiveCount& other) noexcept = default; inline RALiveCount(const RALiveCount& other) noexcept = default;
inline void init(const RALiveCount& other) noexcept { inline void init(const RALiveCount& other) noexcept {
@@ -674,19 +676,9 @@ public:
//! Statistics about a register liveness. //! Statistics about a register liveness.
class RALiveStats { class RALiveStats {
public: public:
uint32_t _width; uint32_t _width = 0;
float _freq; float _freq = 0.0f;
float _priority; float _priority = 0.0f;
//! \name Construction & Destruction
//! \{
inline RALiveStats()
: _width(0),
_freq(0.0f),
_priority(0.0f) {}
//! \}
//! \name Accessors //! \name Accessors
//! \{ //! \{
@@ -928,46 +920,46 @@ public:
ASMJIT_NONCOPYABLE(RAWorkReg) ASMJIT_NONCOPYABLE(RAWorkReg)
//! RAPass specific ID used during analysis and allocation. //! RAPass specific ID used during analysis and allocation.
uint32_t _workId; uint32_t _workId = 0;
//! Copy of ID used by `VirtReg`. //! Copy of ID used by \ref VirtReg.
uint32_t _virtId; uint32_t _virtId = 0;
//! Permanent association with `VirtReg`. //! Permanent association with \ref VirtReg.
VirtReg* _virtReg; VirtReg* _virtReg = nullptr;
//! Temporary association with `RATiedReg`. //! Temporary association with \ref RATiedReg.
RATiedReg* _tiedReg; RATiedReg* _tiedReg = nullptr;
//! Stack slot associated with the register. //! Stack slot associated with the register.
RAStackSlot* _stackSlot; RAStackSlot* _stackSlot = nullptr;
//! Copy of a signature used by `VirtReg`. //! Copy of a signature used by \ref VirtReg.
RegInfo _info; RegInfo _info {};
//! RAPass specific flags used during analysis and allocation. //! 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. //! 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 //! IDs of all physical registers that are clobbered during the lifetime of
//! this WorkReg. //! this WorkReg.
//! //!
//! This mask should be updated by `RAPass::buildLiveness()`, because it's //! This mask should be updated by `RAPass::buildLiveness()`, because it's
//! global and should be updated after unreachable code has been removed. //! 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. //! 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). //! Argument index (or `kNoArgIndex` if none).
uint8_t _argIndex; uint8_t _argIndex = kNoArgIndex;
//! Argument value index in the pack (0 by default). //! 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). //! 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). //! Global hint register ID (provided by RA or user).
uint8_t _hintRegId; uint8_t _hintRegId = BaseReg::kIdBad;
//! Live spans of the `VirtReg`. //! Live spans of the `VirtReg`.
LiveRegSpans _liveSpans; LiveRegSpans _liveSpans {};
//! Live statistics. //! Live statistics.
RALiveStats _liveStats; RALiveStats _liveStats {};
//! All nodes that read/write this VirtReg/WorkReg. //! All nodes that read/write this VirtReg/WorkReg.
ZoneVector<BaseNode*> _refs; ZoneVector<BaseNode*> _refs;
@@ -1000,20 +992,7 @@ public:
: _workId(workId), : _workId(workId),
_virtId(vReg->id()), _virtId(vReg->id()),
_virtReg(vReg), _virtReg(vReg),
_tiedReg(nullptr), _info(vReg->info()) {}
_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() {}
//! \} //! \}
@@ -1095,5 +1074,4 @@ public:
ASMJIT_END_NAMESPACE ASMJIT_END_NAMESPACE
#endif // !ASMJIT_NO_COMPILER
#endif // ASMJIT_CORE_RADEFS_P_H_INCLUDED #endif // ASMJIT_CORE_RADEFS_P_H_INCLUDED

View File

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

View File

@@ -50,13 +50,13 @@ public:
typedef RAAssignment::PhysToWorkMap PhysToWorkMap; typedef RAAssignment::PhysToWorkMap PhysToWorkMap;
typedef RAAssignment::WorkToPhysMap WorkToPhysMap; typedef RAAssignment::WorkToPhysMap WorkToPhysMap;
//! Link to `RAPass`. //! Link to `BaseRAPass`.
RAPass* _pass; BaseRAPass* _pass;
//! Link to `BaseCompiler`. //! Link to `BaseCompiler`.
BaseCompiler* _cc; BaseCompiler* _cc;
//! Architecture traits. //! Architecture traits.
RAArchTraits _archTraits; const ArchTraits* _archTraits;
//! Registers available to the allocator. //! Registers available to the allocator.
RARegMask _availableRegs; RARegMask _availableRegs;
//! Registers clobbered by the allocator. //! Registers clobbered by the allocator.
@@ -82,7 +82,7 @@ public:
//! \name Construction & Destruction //! \name Construction & Destruction
//! \{ //! \{
inline RALocalAllocator(RAPass* pass) noexcept inline RALocalAllocator(BaseRAPass* pass) noexcept
: _pass(pass), : _pass(pass),
_cc(pass->cc()), _cc(pass->cc()),
_archTraits(pass->_archTraits), _archTraits(pass->_archTraits),
@@ -219,7 +219,7 @@ public:
inline Error onMoveReg(uint32_t group, uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept { inline Error onMoveReg(uint32_t group, uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept {
if (dstPhysId == srcPhysId) return kErrorOk; if (dstPhysId == srcPhysId) return kErrorOk;
_curAssignment.reassign(group, workId, dstPhysId, srcPhysId); _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. //! 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. //! \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 { 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); _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 //! Emits a load from [VirtReg/WorkReg]'s spill slot to a physical register
//! and makes it assigned and clean. //! and makes it assigned and clean.
inline Error onLoadReg(uint32_t group, uint32_t workId, uint32_t physId) noexcept { inline Error onLoadReg(uint32_t group, uint32_t workId, uint32_t physId) noexcept {
_curAssignment.assign(group, workId, physId, RAAssignment::kClean); _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, //! 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); ASMJIT_ASSERT(_curAssignment.physToWorkId(group, physId) == workId);
_curAssignment.makeClean(group, workId, physId); _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. //! Assigns a register, the content of it is undefined at this point.

View File

@@ -70,50 +70,17 @@ Error RABlock::prependSuccessor(RABlock* successor) noexcept {
} }
// ============================================================================ // ============================================================================
// [asmjit::RAPass - Construction / Destruction] // [asmjit::BaseRAPass - Construction / Destruction]
// ============================================================================ // ============================================================================
RAPass::RAPass() noexcept BaseRAPass::BaseRAPass() noexcept : FuncPass("BaseRAPass") {}
: FuncPass("RAPass"), BaseRAPass::~BaseRAPass() noexcept {}
_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 {}
// ============================================================================ // ============================================================================
// [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(); ZoneAllocator* allocator = self->allocator();
self->_blocks.reset(); self->_blocks.reset();
@@ -126,8 +93,7 @@ static void RAPass_reset(RAPass* self, FuncDetail* funcDetail) noexcept {
self->_sharedAssignments.reset(); self->_sharedAssignments.reset();
self->_lastTimestamp = 0; self->_lastTimestamp = 0;
self->_archRegsInfo = nullptr; self->_archTraits = nullptr;
self->_archTraits.reset();
self->_physRegIndex.reset(); self->_physRegIndex.reset();
self->_physRegCount.reset(); self->_physRegCount.reset();
self->_physRegTotal = 0; self->_physRegTotal = 0;
@@ -154,7 +120,7 @@ static void RAPass_reset(RAPass* self, FuncDetail* funcDetail) noexcept {
self->_maxWorkRegNameSize = 0; 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. // Zero everything so it cannot be used by accident.
for (RAWorkReg* wReg : self->_workRegs) { for (RAWorkReg* wReg : self->_workRegs) {
VirtReg* vReg = wReg->virtReg(); 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); _allocator.reset(zone);
#ifndef ASMJIT_NO_LOGGING #ifndef ASMJIT_NO_LOGGING
@@ -223,7 +189,7 @@ Error RAPass::runOnFunction(Zone* zone, Logger* logger, FuncNode* func) {
return err; return err;
} }
Error RAPass::onPerformAllSteps() noexcept { Error BaseRAPass::onPerformAllSteps() noexcept {
ASMJIT_PROPAGATE(buildCFG()); ASMJIT_PROPAGATE(buildCFG());
ASMJIT_PROPAGATE(buildViews()); ASMJIT_PROPAGATE(buildViews());
ASMJIT_PROPAGATE(removeUnreachableBlocks()); 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); RABlock* block = zone()->newT<RABlock>(this);
if (ASMJIT_UNLIKELY(!block)) if (ASMJIT_UNLIKELY(!block))
return nullptr; return nullptr;
@@ -264,7 +230,7 @@ RABlock* RAPass::newBlock(BaseNode* initialNode) noexcept {
return block; return block;
} }
RABlock* RAPass::newBlockOrExistingAt(LabelNode* cbLabel, BaseNode** stoppedAt) noexcept { RABlock* BaseRAPass::newBlockOrExistingAt(LabelNode* cbLabel, BaseNode** stoppedAt) noexcept {
if (cbLabel->hasPassData()) if (cbLabel->hasPassData())
return cbLabel->passData<RABlock>(); return cbLabel->passData<RABlock>();
@@ -351,7 +317,7 @@ RABlock* RAPass::newBlockOrExistingAt(LabelNode* cbLabel, BaseNode** stoppedAt)
return block; return block;
} }
Error RAPass::addBlock(RABlock* block) noexcept { Error BaseRAPass::addBlock(RABlock* block) noexcept {
ASMJIT_PROPAGATE(_blocks.willGrow(allocator())); ASMJIT_PROPAGATE(_blocks.willGrow(allocator()));
block->_blockId = blockCount(); 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()) if (sharedAssignmentsMap.empty())
return kErrorOk; 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 { class RABlockVisitItem {
@@ -425,7 +391,7 @@ public:
uint32_t _index; uint32_t _index;
}; };
Error RAPass::buildViews() noexcept { Error BaseRAPass::buildViews() noexcept {
#ifndef ASMJIT_NO_LOGGING #ifndef ASMJIT_NO_LOGGING
Logger* logger = debugLogger(); Logger* logger = debugLogger();
ASMJIT_RA_LOG_FORMAT("[RAPass::BuildViews]\n"); 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 { 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". // Based on "A Simple, Fast Dominance Algorithm".
Error RAPass::buildDominators() noexcept { Error BaseRAPass::buildDominators() noexcept {
#ifndef ASMJIT_NO_LOGGING #ifndef ASMJIT_NO_LOGGING
Logger* logger = debugLogger(); Logger* logger = debugLogger();
ASMJIT_RA_LOG_FORMAT("[RAPass::BuildDominators]\n"); ASMJIT_RA_LOG_FORMAT("[RAPass::BuildDominators]\n");
@@ -556,7 +522,7 @@ Error RAPass::buildDominators() noexcept {
return kErrorOk; 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(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(b != nullptr); // called, as both `a` and `b` must be valid blocks.
ASMJIT_ASSERT(a != b); // Checked by `dominates()` and `strictlyDominates()`. 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; 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(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(b != nullptr); // called, as both `a` and `b` must be valid blocks.
ASMJIT_ASSERT(a != b); // Checked by `dominates()` and `properlyDominates()`. 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 numAllBlocks = blockCount();
uint32_t numReachableBlocks = reachableBlockCount(); uint32_t numReachableBlocks = reachableBlockCount();
@@ -661,13 +627,13 @@ Error RAPass::removeUnreachableBlocks() noexcept {
return kErrorOk; return kErrorOk;
} }
BaseNode* RAPass::findSuccessorStartingAt(BaseNode* node) noexcept { BaseNode* BaseRAPass::findSuccessorStartingAt(BaseNode* node) noexcept {
while (node && (node->isInformative() || node->hasNoEffect())) while (node && (node->isInformative() || node->hasNoEffect()))
node = node->next(); node = node->next();
return node; return node;
} }
bool RAPass::isNextTo(BaseNode* node, BaseNode* target) noexcept { bool BaseRAPass::isNextTo(BaseNode* node, BaseNode* target) noexcept {
for (;;) { for (;;) {
node = node->next(); node = node->next();
if (node == target) 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. // Checked by `asWorkReg()` - must be true.
ASMJIT_ASSERT(vReg->_workReg == nullptr); ASMJIT_ASSERT(vReg->_workReg == nullptr);
@@ -715,7 +681,7 @@ Error RAPass::_asWorkReg(VirtReg* vReg, RAWorkReg** out) noexcept {
return kErrorOk; return kErrorOk;
} }
RAAssignment::WorkToPhysMap* RAPass::newWorkToPhysMap() noexcept { RAAssignment::WorkToPhysMap* BaseRAPass::newWorkToPhysMap() noexcept {
uint32_t count = workRegCount(); uint32_t count = workRegCount();
size_t size = WorkToPhysMap::sizeOf(count); size_t size = WorkToPhysMap::sizeOf(count);
@@ -734,7 +700,7 @@ RAAssignment::WorkToPhysMap* RAPass::newWorkToPhysMap() noexcept {
return map; return map;
} }
RAAssignment::PhysToWorkMap* RAPass::newPhysToWorkMap() noexcept { RAAssignment::PhysToWorkMap* BaseRAPass::newPhysToWorkMap() noexcept {
uint32_t count = physRegTotal(); uint32_t count = physRegTotal();
size_t size = PhysToWorkMap::sizeOf(count); 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 { 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 #ifndef ASMJIT_NO_LOGGING
Logger* logger = debugLogger(); Logger* logger = debugLogger();
StringTmp<512> sb; StringTmp<512> sb;
@@ -1106,7 +1072,7 @@ ASMJIT_FAVOR_SPEED Error RAPass::buildLiveness() noexcept {
return kErrorOk; return kErrorOk;
} }
Error RAPass::assignArgIndexToWorkRegs() noexcept { Error BaseRAPass::assignArgIndexToWorkRegs() noexcept {
ZoneBitVector& liveIn = entryBlock()->liveIn(); ZoneBitVector& liveIn = entryBlock()->liveIn();
uint32_t argCount = func()->argCount(); uint32_t argCount = func()->argCount();
@@ -1130,7 +1096,7 @@ Error RAPass::assignArgIndexToWorkRegs() noexcept {
workReg->setArgIndex(argIndex, valueIndex); workReg->setArgIndex(argIndex, valueIndex);
const FuncValue& arg = func()->detail().arg(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()); workReg->setHintRegId(arg.regId());
} }
} }
@@ -1138,8 +1104,9 @@ Error RAPass::assignArgIndexToWorkRegs() noexcept {
return kErrorOk; return kErrorOk;
} }
// ============================================================================ // ============================================================================
// [asmjit::RAPass - Allocation - Global] // [asmjit::BaseRAPass - Allocation - Global]
// ============================================================================ // ============================================================================
#ifndef ASMJIT_NO_LOGGING #ifndef ASMJIT_NO_LOGGING
@@ -1156,7 +1123,7 @@ static void RAPass_dumpSpans(String& sb, uint32_t index, const LiveRegSpans& liv
} }
#endif #endif
Error RAPass::runGlobalAllocator() noexcept { Error BaseRAPass::runGlobalAllocator() noexcept {
ASMJIT_PROPAGATE(initGlobalLiveSpans()); ASMJIT_PROPAGATE(initGlobalLiveSpans());
for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) { for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) {
@@ -1166,16 +1133,19 @@ Error RAPass::runGlobalAllocator() noexcept {
return kErrorOk; 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++) { for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) {
size_t physCount = _physRegCount[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)) if (ASMJIT_UNLIKELY(!liveSpans))
return DebugUtils::errored(kErrorOutOfMemory); return DebugUtils::errored(kErrorOutOfMemory);
for (size_t physId = 0; physId < physCount; physId++) for (size_t physId = 0; physId < physCount; physId++)
new(&liveSpans[physId]) LiveRegSpans(); new(&liveSpans[physId]) LiveRegSpans();
}
_globalLiveSpans[group] = liveSpans; _globalLiveSpans[group] = liveSpans;
} }
@@ -1183,7 +1153,7 @@ ASMJIT_FAVOR_SPEED Error RAPass::initGlobalLiveSpans() noexcept {
return kErrorOk; 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) if (workRegCount(group) == 0)
return kErrorOk; 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); RALocalAllocator lra(this);
ASMJIT_PROPAGATE(lra.init()); ASMJIT_PROPAGATE(lra.init());
@@ -1396,7 +1366,7 @@ Error RAPass::runLocalAllocator() noexcept {
ASMJIT_PROPAGATE(lra.allocInst(inst)); ASMJIT_PROPAGATE(lra.allocInst(inst));
if (inst->type() == BaseNode::kNodeInvoke) if (inst->type() == BaseNode::kNodeInvoke)
ASMJIT_PROPAGATE(onEmitPreCall(inst->as<InvokeNode>())); ASMJIT_PROPAGATE(emitPreCall(inst->as<InvokeNode>()));
else else
ASMJIT_PROPAGATE(lra.spillAfterAllocation(inst)); ASMJIT_PROPAGATE(lra.spillAfterAllocation(inst));
} }
@@ -1458,7 +1428,7 @@ Error RAPass::runLocalAllocator() noexcept {
return kErrorOk; 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()) { if (block->hasSharedAssignmentId()) {
uint32_t sharedAssignmentId = block->sharedAssignmentId(); uint32_t sharedAssignmentId = block->sharedAssignmentId();
@@ -1514,7 +1484,7 @@ Error RAPass::setBlockEntryAssignment(RABlock* block, const RABlock* fromBlock,
return blockEntryAssigned(as); 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()); ASMJIT_ASSERT(_sharedAssignments[sharedAssignmentId].empty());
PhysToWorkMap* physToWorkMap = clonePhysToWorkMap(fromAssignment.physToWorkMap()); PhysToWorkMap* physToWorkMap = clonePhysToWorkMap(fromAssignment.physToWorkMap());
@@ -1582,7 +1552,7 @@ Error RAPass::setSharedAssignment(uint32_t sharedAssignmentId, const RAAssignmen
return blockEntryAssigned(as); 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 // Complex allocation strategy requires to record register assignments upon
// block entry (or per shared state). // block entry (or per shared state).
for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) { 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); ASMJIT_ASSERT(alignment <= 64);
if (_temporaryMem.isNone()) { 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 // Update some StackFrame information that we updated during allocation. The
// only information we don't have at the moment is final local stack size, // only information we don't have at the moment is final local stack size,
// which is calculated last. // which is calculated last.
@@ -1668,7 +1638,7 @@ Error RAPass::updateStackFrame() noexcept {
return kErrorOk; return kErrorOk;
} }
Error RAPass::_markStackArgsToKeep() noexcept { Error BaseRAPass::_markStackArgsToKeep() noexcept {
FuncFrame& frame = func()->frame(); FuncFrame& frame = func()->frame();
bool hasSAReg = frame.hasPreservedFP() || !frame.hasDynamicAlignment(); bool hasSAReg = frame.hasPreservedFP() || !frame.hasDynamicAlignment();
@@ -1707,7 +1677,7 @@ Error RAPass::_markStackArgsToKeep() noexcept {
return kErrorOk; return kErrorOk;
} }
Error RAPass::_updateStackArgs() noexcept { Error BaseRAPass::_updateStackArgs() noexcept {
FuncFrame& frame = func()->frame(); FuncFrame& frame = func()->frame();
RAWorkRegs& workRegs = _workRegs; RAWorkRegs& workRegs = _workRegs;
uint32_t numWorkRegs = workRegCount(); uint32_t numWorkRegs = workRegCount();
@@ -1741,12 +1711,12 @@ Error RAPass::_updateStackArgs() noexcept {
return kErrorOk; return kErrorOk;
} }
Error RAPass::insertPrologEpilog() noexcept { Error BaseRAPass::insertPrologEpilog() noexcept {
FuncFrame& frame = _func->frame(); FuncFrame& frame = _func->frame();
cc()->_setCursor(func()); cc()->_setCursor(func());
ASMJIT_PROPAGATE(cc()->emitProlog(frame)); ASMJIT_PROPAGATE(cc()->emitProlog(frame));
ASMJIT_PROPAGATE(cc()->emitArgsAssignment(frame, _argsAssignment)); ASMJIT_PROPAGATE(_iEmitHelper->emitArgsAssignment(frame, _argsAssignment));
cc()->_setCursor(func()->exitNode()); cc()->_setCursor(func()->exitNode());
ASMJIT_PROPAGATE(cc()->emitEpilog(frame)); 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 #ifndef ASMJIT_NO_LOGGING
Logger* logger = debugLogger(); Logger* logger = debugLogger();
ASMJIT_RA_LOG_FORMAT("[RAPass::Rewrite]\n"); ASMJIT_RA_LOG_FORMAT("[RAPass::Rewrite]\n");
@@ -1767,7 +1737,7 @@ Error RAPass::rewrite() noexcept {
return _rewrite(_func, _stop); 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(); uint32_t virtCount = cc()->_vRegArray.size();
BaseNode* node = first; BaseNode* node = first;
@@ -1814,7 +1784,7 @@ ASMJIT_FAVOR_SPEED Error RAPass::_rewrite(BaseNode* first, BaseNode* stop) noexc
RABlock* block = raInst->block(); RABlock* block = raInst->block();
if (!isNextTo(node, _func->exitNode())) { if (!isNextTo(node, _func->exitNode())) {
cc()->_setCursor(node->prev()); cc()->_setCursor(node->prev());
ASMJIT_PROPAGATE(onEmitJump(_func->exitNode()->label())); ASMJIT_PROPAGATE(emitJump(_func->exitNode()->label()));
} }
BaseNode* prev = node->prev(); 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 #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(); const RATiedReg* tiedRegs = raInst->tiedRegs();
uint32_t tiedCount = raInst->tiedCount(); 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; uint32_t loggerFlags = _loggerFlags;
StringTmp<1024> sb; StringTmp<1024> sb;
@@ -1930,7 +1900,7 @@ ASMJIT_FAVOR_SIZE Error RAPass::annotateCode() noexcept {
return kErrorOk; 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++) { for (uint32_t i = 0, size = blocks.size(); i < size; i++) {
const RABlock* block = blocks[i]; const RABlock* block = blocks[i];
if (i != 0) if (i != 0)
@@ -1941,7 +1911,7 @@ ASMJIT_FAVOR_SIZE Error RAPass::_dumpBlockIds(String& sb, const RABlocks& blocks
return kErrorOk; 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++) { for (uint32_t liveType = 0; liveType < RABlock::kLiveCount; liveType++) {
const char* bitsName = liveType == RABlock::kLiveIn ? "IN " : const char* bitsName = liveType == RABlock::kLiveIn ? "IN " :
liveType == RABlock::kLiveOut ? "OUT " : liveType == RABlock::kLiveOut ? "OUT " :
@@ -1973,7 +1943,7 @@ ASMJIT_FAVOR_SIZE Error RAPass::_dumpBlockLiveness(String& sb, const RABlock* bl
return kErrorOk; 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 numWorkRegs = _workRegs.size();
uint32_t maxSize = _maxWorkRegNameSize; uint32_t maxSize = _maxWorkRegNameSize;

View File

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

View File

@@ -271,63 +271,96 @@ static constexpr T fillTrailingBits(const T& x) noexcept {
//! \cond //! \cond
namespace Internal { namespace Internal {
static constexpr uint32_t constCtzImpl(uint32_t xAndNegX) noexcept { namespace {
return 31 - ((xAndNegX & 0x0000FFFFu) ? 16 : 0)
- ((xAndNegX & 0x00FF00FFu) ? 8 : 0) template<typename T>
- ((xAndNegX & 0x0F0F0F0Fu) ? 4 : 0) struct BitScanData { T x; uint32_t n; };
- ((xAndNegX & 0x33333333u) ? 2 : 0)
- ((xAndNegX & 0x55555555u) ? 1 : 0); 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 { static constexpr BitScanData<T> advanceRight(const BitScanData<T>& data, uint32_t n) noexcept {
return 63 - ((xAndNegX & 0x00000000FFFFFFFFu) ? 32 : 0) return BitScanData<T> { data.x >> n, data.n + n };
- ((xAndNegX & 0x0000FFFF0000FFFFu) ? 16 : 0)
- ((xAndNegX & 0x00FF00FF00FF00FFu) ? 8 : 0)
- ((xAndNegX & 0x0F0F0F0F0F0F0F0Fu) ? 4 : 0)
- ((xAndNegX & 0x3333333333333333u) ? 2 : 0)
- ((xAndNegX & 0x5555555555555555u) ? 1 : 0);
} }
template<typename T> static constexpr BitScanData<T> clz(const BitScanData<T>& data) noexcept {
static constexpr uint32_t constCtz(T x) noexcept { return BitScanCalc<T, N / 2>::clz(advanceLeft(data, data.x & (allOnes<T>() << (bitSizeOf<T>() - N)) ? uint32_t(0) : N));
return constCtzImpl(x & neg(x));
} }
static ASMJIT_INLINE uint32_t ctz(uint32_t x) noexcept { static constexpr BitScanData<T> ctz(const BitScanData<T>& data) noexcept {
#if defined(__GNUC__) return BitScanCalc<T, N / 2>::ctz(advanceRight(data, data.x & (allOnes<T>() >> (bitSizeOf<T>() - N)) ? uint32_t(0) : N));
return uint32_t(__builtin_ctz(x)); }
#elif defined(_MSC_VER) && (ASMJIT_ARCH_X86 || ASMJIT_ARCH_ARM) };
unsigned long i;
_BitScanForward(&i, x); template<typename T>
return uint32_t(i); struct BitScanCalc<T, 0> {
#else static constexpr BitScanData<T> clz(const BitScanData<T>& ctx) noexcept {
return constCtz(x); return BitScanData<T> { 0, ctx.n - uint32_t(ctx.x >> (bitSizeOf<T>() - 1)) };
#endif
} }
static ASMJIT_INLINE uint32_t ctz(uint64_t x) noexcept { static constexpr BitScanData<T> ctz(const BitScanData<T>& ctx) noexcept {
#if defined(__GNUC__) return BitScanData<T> { 0, ctx.n - uint32_t(ctx.x & 0x1) };
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
} }
};
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 //! \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`). //! 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. //! \note The input MUST NOT be zero, otherwise the result is undefined.
template<typename T> 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). //! Count trailing zeros in `x` (constant expression).
template<typename T> 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] // [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> 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)...); } 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] // [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_signed<T>::type S;
typedef typename std::make_unsigned<T>::type U; typedef typename std::make_unsigned<T>::type U;
return std::is_signed<T>::value ? isBetween<S>(S(x), -8, 7) return std::is_signed<T>::value ? isBetween<S>(S(x), -8, 7) : U(x) <= U(7u);
: 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. //! 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_signed<T>::type S;
typedef typename std::make_unsigned<T>::type U; typedef typename std::make_unsigned<T>::type U;
return std::is_signed<T>::value ? sizeof(T) <= 1 || isBetween<S>(S(x), -128, 127) return std::is_signed<T>::value ? sizeof(T) <= 1 || isBetween<S>(S(x), -128, 127) : U(x) <= U(127u);
: 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. //! 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; 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] // [asmjit::Support - ByteSwap]
// ============================================================================ // ============================================================================

View File

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

View File

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

View File

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

View File

@@ -45,6 +45,7 @@
//! functions provided are part of all X86 emitters. //! functions provided are part of all X86 emitters.
//! - \ref x86::EmitterImplicitT - Provides all instructions that use //! - \ref x86::EmitterImplicitT - Provides all instructions that use
//! implicit operands, these cannot be used with \ref x86::Compiler. //! implicit operands, these cannot be used with \ref x86::Compiler.
//!
//! - Instruction representation: //! - Instruction representation:
//! - \ref x86::Inst::Id - instruction identifiers. //! - \ref x86::Inst::Id - instruction identifiers.
//! - \ref x86::Inst::Options - instruction options. //! - \ref x86::Inst::Options - instruction options.
@@ -74,7 +75,7 @@
//! ### Memory Operands //! ### Memory Operands
//! //!
//! - \ref x86::Mem - X86/X64 memory operand that provides support for all //! - \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. //! scales, and segment override prefixes.
//! //!
//! ### Other //! ### Other

View File

@@ -1,137 +0,0 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "../core/api-build_p.h"
#ifdef ASMJIT_BUILD_X86
#include "../core/support.h"
#include "../core/type.h"
#include "../x86/x86archdata_p.h"
#include "../x86/x86operand.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
// ============================================================================
// [asmjit::x86::ArchInternal]
// ============================================================================
namespace ArchInternal {
Error typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfoOut) noexcept {
// Passed RegType instead of TypeId?
if (typeId <= BaseReg::kTypeMax)
typeId = opData.archRegs.regTypeToTypeId[typeId];
if (ASMJIT_UNLIKELY(!Type::isValid(typeId)))
return DebugUtils::errored(kErrorInvalidTypeId);
// First normalize architecture dependent types.
if (Type::isAbstract(typeId)) {
bool is32Bit = arch == Environment::kArchX86;
if (typeId == Type::kIdIntPtr)
typeId = is32Bit ? Type::kIdI32 : Type::kIdI64;
else
typeId = is32Bit ? Type::kIdU32 : Type::kIdU64;
}
// Type size helps to construct all groups of registers.
// TypeId is invalid if the size is zero.
uint32_t size = Type::sizeOf(typeId);
if (ASMJIT_UNLIKELY(!size))
return DebugUtils::errored(kErrorInvalidTypeId);
if (ASMJIT_UNLIKELY(typeId == Type::kIdF80))
return DebugUtils::errored(kErrorInvalidUseOfF80);
uint32_t regType = 0;
switch (typeId) {
case Type::kIdI8:
case Type::kIdU8:
regType = Reg::kTypeGpbLo;
break;
case Type::kIdI16:
case Type::kIdU16:
regType = Reg::kTypeGpw;
break;
case Type::kIdI32:
case Type::kIdU32:
regType = Reg::kTypeGpd;
break;
case Type::kIdI64:
case Type::kIdU64:
if (arch == Environment::kArchX86)
return DebugUtils::errored(kErrorInvalidUseOfGpq);
regType = Reg::kTypeGpq;
break;
// F32 and F64 are always promoted to use vector registers.
case Type::kIdF32:
typeId = Type::kIdF32x1;
regType = Reg::kTypeXmm;
break;
case Type::kIdF64:
typeId = Type::kIdF64x1;
regType = Reg::kTypeXmm;
break;
// Mask registers {k}.
case Type::kIdMask8:
case Type::kIdMask16:
case Type::kIdMask32:
case Type::kIdMask64:
regType = Reg::kTypeKReg;
break;
// MMX registers.
case Type::kIdMmx32:
case Type::kIdMmx64:
regType = Reg::kTypeMm;
break;
// XMM|YMM|ZMM registers.
default:
if (size <= 16)
regType = Reg::kTypeXmm;
else if (size == 32)
regType = Reg::kTypeYmm;
else
regType = Reg::kTypeZmm;
break;
}
*typeIdOut = typeId;
regInfoOut->reset(opData.archRegs.regInfo[regType].signature());
return kErrorOk;
}
} // {ArchInternal}
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_BUILD_X86

View File

@@ -0,0 +1,150 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_X86_X86ARCHTRAITS_P_H_INCLUDED
#define ASMJIT_X86_X86ARCHTRAITS_P_H_INCLUDED
#include "../core/archtraits.h"
#include "../core/misc_p.h"
#include "../x86/x86operand.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! \cond INTERNAL
//! \addtogroup asmjit_x86
//! \{
// ============================================================================
// [asmjit::x86::x86ArchTraits
// ============================================================================
static const constexpr ArchTraits x86ArchTraits = {
Gp::kIdSp, // SP.
Gp::kIdBp, // FP.
0xFF, // LR.
0xFF, // PC.
{ 0, 0, 0 }, // Reserved.
1, // HW stack alignment.
0x7FFFFFFFu, // Min stack offset.
0x7FFFFFFFu, // Max stack offset.
// ISA features [Gp, Vec, Other0, Other1].
{
ArchTraits::kIsaFeatureSwap | ArchTraits::kIsaFeaturePushPop,
0,
0,
0
},
// RegInfo.
#define V(index) { x86::RegTraits<index>::kSignature }
{ ASMJIT_LOOKUP_TABLE_32(V, 0) },
#undef V
// RegTypeToTypeId.
#define V(index) x86::RegTraits<index>::kTypeId
{ ASMJIT_LOOKUP_TABLE_32(V, 0) },
#undef V
// TypeIdToRegType.
#define V(index) (index + Type::_kIdBaseStart == Type::kIdI8 ? Reg::kTypeGpbLo : \
index + Type::_kIdBaseStart == Type::kIdU8 ? Reg::kTypeGpbLo : \
index + Type::_kIdBaseStart == Type::kIdI16 ? Reg::kTypeGpw : \
index + Type::_kIdBaseStart == Type::kIdU16 ? Reg::kTypeGpw : \
index + Type::_kIdBaseStart == Type::kIdI32 ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdU32 ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdIntPtr ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdUIntPtr ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdF32 ? Reg::kTypeXmm : \
index + Type::_kIdBaseStart == Type::kIdF64 ? Reg::kTypeXmm : \
index + Type::_kIdBaseStart == Type::kIdMask8 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask16 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask32 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask64 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMmx32 ? Reg::kTypeMm : \
index + Type::_kIdBaseStart == Type::kIdMmx64 ? Reg::kTypeMm : Reg::kTypeNone)
{ ASMJIT_LOOKUP_TABLE_32(V, 0) }
#undef V
};
// ============================================================================
// [asmjit::x86::x64ArchTraits
// ============================================================================
static const constexpr ArchTraits x64ArchTraits = {
Gp::kIdSp, // SP.
Gp::kIdBp, // FP.
0xFF, // LR.
0xFF, // PC.
{ 0, 0, 0 }, // Reserved.
1, // HW stack alignment.
0x7FFFFFFFu, // Min stack offset.
0x7FFFFFFFu, // Max stack offset.
// ISA features [Gp, Vec, Other0, Other1].
{
ArchTraits::kIsaFeatureSwap | ArchTraits::kIsaFeaturePushPop,
0,
0,
0
},
// RegInfo.
#define V(index) { x86::RegTraits<index>::kSignature }
{ ASMJIT_LOOKUP_TABLE_32(V, 0) },
#undef V
// RegTypeToTypeId.
#define V(index) x86::RegTraits<index>::kTypeId
{ ASMJIT_LOOKUP_TABLE_32(V, 0) },
#undef V
// TypeIdToRegType.
#define V(index) (index + Type::_kIdBaseStart == Type::kIdI8 ? Reg::kTypeGpbLo : \
index + Type::_kIdBaseStart == Type::kIdU8 ? Reg::kTypeGpbLo : \
index + Type::_kIdBaseStart == Type::kIdI16 ? Reg::kTypeGpw : \
index + Type::_kIdBaseStart == Type::kIdU16 ? Reg::kTypeGpw : \
index + Type::_kIdBaseStart == Type::kIdI32 ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdU32 ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdI64 ? Reg::kTypeGpq : \
index + Type::_kIdBaseStart == Type::kIdU64 ? Reg::kTypeGpq : \
index + Type::_kIdBaseStart == Type::kIdIntPtr ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdUIntPtr ? Reg::kTypeGpd : \
index + Type::_kIdBaseStart == Type::kIdF32 ? Reg::kTypeXmm : \
index + Type::_kIdBaseStart == Type::kIdF64 ? Reg::kTypeXmm : \
index + Type::_kIdBaseStart == Type::kIdMask8 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask16 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask32 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMask64 ? Reg::kTypeKReg : \
index + Type::_kIdBaseStart == Type::kIdMmx32 ? Reg::kTypeMm : \
index + Type::_kIdBaseStart == Type::kIdMmx64 ? Reg::kTypeMm : Reg::kTypeNone)
{ ASMJIT_LOOKUP_TABLE_32(V, 0) }
#undef V
};
//! \}
//! \endcond
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_X86_X86ARCHTRAITS_P_H_INCLUDED

View File

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

View File

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

View File

@@ -1,238 +0,0 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "../core/api-build_p.h"
#ifdef ASMJIT_BUILD_X86
#include "../x86/x86callconv_p.h"
#include "../x86/x86operand.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
// ============================================================================
// [asmjit::x86::CallConvInternal - Init]
// ============================================================================
namespace CallConvInternal {
static inline bool shouldThreatAsCDeclIn64BitMode(uint32_t ccId) noexcept {
return ccId == CallConv::kIdCDecl ||
ccId == CallConv::kIdStdCall ||
ccId == CallConv::kIdThisCall ||
ccId == CallConv::kIdFastCall ||
ccId == CallConv::kIdRegParm1 ||
ccId == CallConv::kIdRegParm2 ||
ccId == CallConv::kIdRegParm3;
}
ASMJIT_FAVOR_SIZE Error init(CallConv& cc, uint32_t ccId, const Environment& environment) noexcept {
constexpr uint32_t kGroupGp = Reg::kGroupGp;
constexpr uint32_t kGroupVec = Reg::kGroupVec;
constexpr uint32_t kGroupMm = Reg::kGroupMm;
constexpr uint32_t kGroupKReg = Reg::kGroupKReg;
constexpr uint32_t kZax = Gp::kIdAx;
constexpr uint32_t kZbx = Gp::kIdBx;
constexpr uint32_t kZcx = Gp::kIdCx;
constexpr uint32_t kZdx = Gp::kIdDx;
constexpr uint32_t kZsp = Gp::kIdSp;
constexpr uint32_t kZbp = Gp::kIdBp;
constexpr uint32_t kZsi = Gp::kIdSi;
constexpr uint32_t kZdi = Gp::kIdDi;
bool winABI = environment.isPlatformWindows() || environment.isAbiMSVC();
cc.setArch(environment.arch());
if (environment.is32Bit()) {
bool isStandardCallConv = true;
cc.setPreservedRegs(Reg::kGroupGp, Support::bitMask(Gp::kIdBx, Gp::kIdSp, Gp::kIdBp, Gp::kIdSi, Gp::kIdDi));
cc.setNaturalStackAlignment(4);
switch (ccId) {
case CallConv::kIdCDecl:
break;
case CallConv::kIdStdCall:
cc.setFlags(CallConv::kFlagCalleePopsStack);
break;
case CallConv::kIdFastCall:
cc.setFlags(CallConv::kFlagCalleePopsStack);
cc.setPassedOrder(kGroupGp, kZcx, kZdx);
break;
case CallConv::kIdVectorCall:
cc.setFlags(CallConv::kFlagCalleePopsStack);
cc.setPassedOrder(kGroupGp, kZcx, kZdx);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5);
break;
case CallConv::kIdThisCall:
// NOTE: Even MINGW (starting with GCC 4.7.0) now uses __thiscall on MS Windows,
// so we won't bail to any other calling convention if __thiscall was specified.
if (winABI) {
cc.setFlags(CallConv::kFlagCalleePopsStack);
cc.setPassedOrder(kGroupGp, kZcx);
}
else {
ccId = CallConv::kIdCDecl;
}
break;
case CallConv::kIdRegParm1:
cc.setPassedOrder(kGroupGp, kZax);
break;
case CallConv::kIdRegParm2:
cc.setPassedOrder(kGroupGp, kZax, kZdx);
break;
case CallConv::kIdRegParm3:
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx);
break;
case CallConv::kIdLightCall2:
case CallConv::kIdLightCall3:
case CallConv::kIdLightCall4: {
uint32_t n = (ccId - CallConv::kIdLightCall2) + 2;
cc.setFlags(CallConv::kFlagPassFloatsByVec);
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx, kZsi, kZdi);
cc.setPassedOrder(kGroupMm, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPassedOrder(kGroupKReg, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPreservedRegs(kGroupGp, Support::lsbMask<uint32_t>(8));
cc.setPreservedRegs(kGroupVec, Support::lsbMask<uint32_t>(8) & ~Support::lsbMask<uint32_t>(n));
cc.setNaturalStackAlignment(16);
isStandardCallConv = false;
break;
}
default:
return DebugUtils::errored(kErrorInvalidArgument);
}
if (isStandardCallConv) {
// MMX arguments is something where compiler vendors disagree. For example
// GCC and MSVC would pass first three via registers and the rest via stack,
// however Clang passes all via stack. Returning MMX registers is even more
// fun, where GCC uses MM0, but Clang uses EAX:EDX pair. I'm not sure it's
// something we should be worried about as MMX is deprecated anyway.
cc.setPassedOrder(kGroupMm, 0, 1, 2);
// Vector arguments (XMM|YMM|ZMM) are passed via registers. However, if the
// function is variadic then they have to be passed via stack.
cc.setPassedOrder(kGroupVec, 0, 1, 2);
// Functions with variable arguments always use stack for MM and vector
// arguments.
cc.addFlags(CallConv::kFlagPassVecByStackIfVA);
}
if (ccId == CallConv::kIdCDecl) {
cc.addFlags(CallConv::kFlagVarArgCompatible);
}
}
else {
// Preprocess the calling convention into a common id as many conventions
// are normally ignored even by C/C++ compilers and treated as `__cdecl`.
if (shouldThreatAsCDeclIn64BitMode(ccId))
ccId = winABI ? CallConv::kIdX64Windows : CallConv::kIdX64SystemV;
switch (ccId) {
case CallConv::kIdX64SystemV: {
cc.setFlags(CallConv::kFlagPassFloatsByVec |
CallConv::kFlagPassMmxByXmm |
CallConv::kFlagVarArgCompatible);
cc.setNaturalStackAlignment(16);
cc.setRedZoneSize(128);
cc.setPassedOrder(kGroupGp, kZdi, kZsi, kZdx, kZcx, 8, 9);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPreservedRegs(kGroupGp, Support::bitMask(kZbx, kZsp, kZbp, 12, 13, 14, 15));
break;
}
case CallConv::kIdX64Windows: {
cc.setStrategy(CallConv::kStrategyX64Windows);
cc.setFlags(CallConv::kFlagPassFloatsByVec |
CallConv::kFlagIndirectVecArgs |
CallConv::kFlagPassMmxByGp |
CallConv::kFlagVarArgCompatible);
cc.setNaturalStackAlignment(16);
// Maximum 4 arguments in registers, each adds 8 bytes to the spill zone.
cc.setSpillZoneSize(4 * 8);
cc.setPassedOrder(kGroupGp, kZcx, kZdx, 8, 9);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3);
cc.setPreservedRegs(kGroupGp, Support::bitMask(kZbx, kZsp, kZbp, kZsi, kZdi, 12, 13, 14, 15));
cc.setPreservedRegs(kGroupVec, Support::bitMask(6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
break;
}
case CallConv::kIdVectorCall: {
cc.setStrategy(CallConv::kStrategyX64VectorCall);
cc.setFlags(CallConv::kFlagPassFloatsByVec |
CallConv::kFlagPassMmxByGp );
cc.setNaturalStackAlignment(16);
// Maximum 6 arguments in registers, each adds 8 bytes to the spill zone.
cc.setSpillZoneSize(6 * 8);
cc.setPassedOrder(kGroupGp, kZcx, kZdx, 8, 9);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5);
cc.setPreservedRegs(kGroupGp, Support::bitMask(kZbx, kZsp, kZbp, kZsi, kZdi, 12, 13, 14, 15));
cc.setPreservedRegs(kGroupVec, Support::bitMask(6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
break;
}
case CallConv::kIdLightCall2:
case CallConv::kIdLightCall3:
case CallConv::kIdLightCall4: {
uint32_t n = (ccId - CallConv::kIdLightCall2) + 2;
cc.setFlags(CallConv::kFlagPassFloatsByVec);
cc.setNaturalStackAlignment(16);
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx, kZsi, kZdi);
cc.setPassedOrder(kGroupMm, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPassedOrder(kGroupKReg, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPreservedRegs(kGroupGp, Support::lsbMask<uint32_t>(16));
cc.setPreservedRegs(kGroupVec, ~Support::lsbMask<uint32_t>(n));
break;
}
default:
return DebugUtils::errored(kErrorInvalidArgument);
}
}
cc.setId(ccId);
return kErrorOk;
}
} // {CallConvInternal}
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_BUILD_X86

View File

@@ -51,6 +51,7 @@ Error Compiler::finalize() {
a.addValidationOptions(validationOptions()); a.addValidationOptions(validationOptions());
return serializeTo(&a); return serializeTo(&a);
} }
// ============================================================================ // ============================================================================
// [asmjit::x86::Compiler - Events] // [asmjit::x86::Compiler - Events]
// ============================================================================ // ============================================================================
@@ -61,12 +62,8 @@ Error Compiler::onAttach(CodeHolder* code) noexcept {
return DebugUtils::errored(kErrorInvalidArch); return DebugUtils::errored(kErrorInvalidArch);
ASMJIT_PROPAGATE(Base::onAttach(code)); 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>(); Error err = addPassT<X86RAPass>();
if (ASMJIT_UNLIKELY(err)) { if (ASMJIT_UNLIKELY(err)) {
onDetach(code); onDetach(code);
return err; return err;

View File

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

View File

@@ -21,10 +21,15 @@
// misrepresented as being the original software. // misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_X86_X86ARCHDATA_P_H_INCLUDED #ifndef ASMJIT_X86_X86EMITHELPER_P_H_INCLUDED
#define ASMJIT_X86_X86ARCHDATA_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) 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). static ASMJIT_INLINE uint32_t vecTypeIdToRegType(uint32_t typeId) noexcept {
namespace ArchInternal { 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 //! \endcond
ASMJIT_END_SUB_NAMESPACE ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_X86_X86ARCHDATA_P_H_INCLUDED #endif // ASMJIT_X86_X86EMITHELPER_P_H_INCLUDED

View File

@@ -32,144 +32,112 @@
ASMJIT_BEGIN_SUB_NAMESPACE(x86) ASMJIT_BEGIN_SUB_NAMESPACE(x86)
#define ASMJIT_INST_0x(NAME, ID) \ #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) \ #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) \ #define ASMJIT_INST_1i(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); }
/** \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 */
#define ASMJIT_INST_1c(NAME, ID, CONV, T0) \ #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(uint32_t cc, const T0& o0) { return _emitter()->_emitI(CONV(cc), o0); } \
inline Error NAME##a(const T0& o0) { return _emitter()->emit(Inst::kId##ID##a, 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()->emit(Inst::kId##ID##ae, 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()->emit(Inst::kId##ID##b, 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()->emit(Inst::kId##ID##be, 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()->emit(Inst::kId##ID##c, 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()->emit(Inst::kId##ID##e, 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()->emit(Inst::kId##ID##g, 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()->emit(Inst::kId##ID##ge, 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()->emit(Inst::kId##ID##l, 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()->emit(Inst::kId##ID##le, 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()->emit(Inst::kId##ID##na, 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()->emit(Inst::kId##ID##nae, 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()->emit(Inst::kId##ID##nb, 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()->emit(Inst::kId##ID##nbe, 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()->emit(Inst::kId##ID##nc, 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()->emit(Inst::kId##ID##ne, 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()->emit(Inst::kId##ID##ng, 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()->emit(Inst::kId##ID##nge, 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()->emit(Inst::kId##ID##nl, 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()->emit(Inst::kId##ID##nle, 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()->emit(Inst::kId##ID##no, 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()->emit(Inst::kId##ID##np, 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()->emit(Inst::kId##ID##ns, 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()->emit(Inst::kId##ID##nz, 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()->emit(Inst::kId##ID##o, 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()->emit(Inst::kId##ID##p, 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()->emit(Inst::kId##ID##pe, 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()->emit(Inst::kId##ID##po, 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()->emit(Inst::kId##ID##s, 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()->emit(Inst::kId##ID##z, o0); } inline Error NAME##z(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##z, o0); }
#define ASMJIT_INST_2x(NAME, ID, T0, T1) \ #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) \ #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); } \ inline Error NAME(const T0& o0, const T1& o1) { return _emitter()->_emitI(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 */
#define ASMJIT_INST_2c(NAME, ID, CONV, T0, T1) \ #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(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()->emit(Inst::kId##ID##a, 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()->emit(Inst::kId##ID##ae, 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()->emit(Inst::kId##ID##b, 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()->emit(Inst::kId##ID##be, 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()->emit(Inst::kId##ID##c, 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()->emit(Inst::kId##ID##e, 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()->emit(Inst::kId##ID##g, 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()->emit(Inst::kId##ID##ge, 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()->emit(Inst::kId##ID##l, 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()->emit(Inst::kId##ID##le, 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()->emit(Inst::kId##ID##na, 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()->emit(Inst::kId##ID##nae, 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()->emit(Inst::kId##ID##nb, 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()->emit(Inst::kId##ID##nbe, 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()->emit(Inst::kId##ID##nc, 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()->emit(Inst::kId##ID##ne, 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()->emit(Inst::kId##ID##ng, 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()->emit(Inst::kId##ID##nge, 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()->emit(Inst::kId##ID##nl, 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()->emit(Inst::kId##ID##nle, 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()->emit(Inst::kId##ID##no, 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()->emit(Inst::kId##ID##np, 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()->emit(Inst::kId##ID##ns, 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()->emit(Inst::kId##ID##nz, 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()->emit(Inst::kId##ID##o, 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()->emit(Inst::kId##ID##p, 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()->emit(Inst::kId##ID##pe, 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()->emit(Inst::kId##ID##po, 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()->emit(Inst::kId##ID##s, 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()->emit(Inst::kId##ID##z, 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) \ #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) \ #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); } \ inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->_emitI(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 */
#define ASMJIT_INST_3ii(NAME, ID, T0, T1, T2) \ #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, const T1& o1, const T2& o2) { return _emitter()->_emitI(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)); }
#define ASMJIT_INST_4x(NAME, ID, T0, T1, T2, T3) \ #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) \ #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); } \ 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); }
/** \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 */
#define ASMJIT_INST_4ii(NAME, ID, T0, T1, T2, T3) \ #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, const T2& o2, const T3& o3) { return _emitter()->_emitI(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)); }
#define ASMJIT_INST_5x(NAME, ID, T0, T1, T2, T3, T4) \ #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) \ #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); } \ 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); }
/** \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 */
#define ASMJIT_INST_6x(NAME, ID, T0, T1, T2, T3, T4, T5) \ #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 //! \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. //! 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 zax() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdAx); }
inline Gp zcx() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdCx); } inline Gp zcx() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdCx); }
inline Gp zdx() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdDx); } inline Gp zdx() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdDx); }
inline Gp zbx() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdBx); } inline Gp zbx() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdBx); }
inline Gp zsp() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdSp); } inline Gp zsp() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdSp); }
inline Gp zbp() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdBp); } inline Gp zbp() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdBp); }
inline Gp zsi() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdSi); } inline Gp zsi() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdSi); }
inline Gp zdi() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdDi); } inline Gp zdi() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdDi); }
//! \} //! \}
@@ -302,12 +270,12 @@ struct EmitterExplicitT {
//! \overload //! \overload
inline Mem intptr_ptr_abs(uint64_t base) const noexcept { inline Mem intptr_ptr_abs(uint64_t base) const noexcept {
uint32_t nativeGpSize = _emitter()->registerSize(); uint32_t nativeGpSize = _emitter()->registerSize();
return Mem(base, nativeGpSize, BaseMem::kSignatureMemAbs); return Mem(base, nativeGpSize, Mem::kSignatureMemAbs);
} }
//! \overload //! \overload
inline Mem intptr_ptr_abs(uint64_t base, const Gp& index, uint32_t shift = 0) const noexcept { inline Mem intptr_ptr_abs(uint64_t base, const Gp& index, uint32_t shift = 0) const noexcept {
uint32_t nativeGpSize = _emitter()->registerSize(); uint32_t nativeGpSize = _emitter()->registerSize();
return Mem(base, index, shift, nativeGpSize, BaseMem::kSignatureMemAbs); return Mem(base, index, shift, nativeGpSize, Mem::kSignatureMemAbs);
} }
//! \} //! \}

View File

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

View File

@@ -338,6 +338,59 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept
return sb.append(sFeatureString + sFeatureIndex[Support::min<uint32_t>(featureId, x86::Features::kCount)]); 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] // [asmjit::x86::FormatterInternal - Format Operand]
// ============================================================================ // ============================================================================
@@ -363,8 +416,8 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand(
ASMJIT_PROPAGATE(sb.append('[')); ASMJIT_PROPAGATE(sb.append('['));
switch (m.addrType()) { switch (m.addrType()) {
case BaseMem::kAddrTypeAbs: ASMJIT_PROPAGATE(sb.append("abs ")); break; case Mem::kAddrTypeAbs: ASMJIT_PROPAGATE(sb.append("abs ")); break;
case BaseMem::kAddrTypeRel: ASMJIT_PROPAGATE(sb.append("rel ")); break; case Mem::kAddrTypeRel: ASMJIT_PROPAGATE(sb.append("rel ")); break;
} }
char opSign = '\0'; 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] // [asmjit::x86::FormatterInternal - Format Instruction]
// ============================================================================ // ============================================================================

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

@@ -0,0 +1,531 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include "../core/api-build_p.h"
#ifdef ASMJIT_BUILD_X86
#include "../x86/x86func_p.h"
#include "../x86/x86emithelper_p.h"
#include "../x86/x86operand.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
// ============================================================================
// [asmjit::x86::FuncInternal - Init]
// ============================================================================
namespace FuncInternal {
static inline bool shouldThreatAsCDeclIn64BitMode(uint32_t ccId) noexcept {
return ccId == CallConv::kIdCDecl ||
ccId == CallConv::kIdStdCall ||
ccId == CallConv::kIdThisCall ||
ccId == CallConv::kIdFastCall ||
ccId == CallConv::kIdRegParm1 ||
ccId == CallConv::kIdRegParm2 ||
ccId == CallConv::kIdRegParm3;
}
ASMJIT_FAVOR_SIZE Error initCallConv(CallConv& cc, uint32_t ccId, const Environment& environment) noexcept {
constexpr uint32_t kGroupGp = Reg::kGroupGp;
constexpr uint32_t kGroupVec = Reg::kGroupVec;
constexpr uint32_t kGroupMm = Reg::kGroupMm;
constexpr uint32_t kGroupKReg = Reg::kGroupKReg;
constexpr uint32_t kZax = Gp::kIdAx;
constexpr uint32_t kZbx = Gp::kIdBx;
constexpr uint32_t kZcx = Gp::kIdCx;
constexpr uint32_t kZdx = Gp::kIdDx;
constexpr uint32_t kZsp = Gp::kIdSp;
constexpr uint32_t kZbp = Gp::kIdBp;
constexpr uint32_t kZsi = Gp::kIdSi;
constexpr uint32_t kZdi = Gp::kIdDi;
bool winABI = environment.isPlatformWindows() || environment.isAbiMSVC();
cc.setArch(environment.arch());
cc.setSaveRestoreRegSize(Reg::kGroupVec, 16);
cc.setSaveRestoreRegSize(Reg::kGroupMm, 8);
cc.setSaveRestoreRegSize(Reg::kGroupKReg, 8);
cc.setSaveRestoreAlignment(Reg::kGroupVec, 16);
cc.setSaveRestoreAlignment(Reg::kGroupMm, 8);
cc.setSaveRestoreAlignment(Reg::kGroupKReg, 8);
if (environment.is32Bit()) {
bool isStandardCallConv = true;
cc.setSaveRestoreRegSize(Reg::kGroupGp, 4);
cc.setSaveRestoreAlignment(Reg::kGroupGp, 4);
cc.setPreservedRegs(Reg::kGroupGp, Support::bitMask(Gp::kIdBx, Gp::kIdSp, Gp::kIdBp, Gp::kIdSi, Gp::kIdDi));
cc.setNaturalStackAlignment(4);
switch (ccId) {
case CallConv::kIdCDecl:
break;
case CallConv::kIdStdCall:
cc.setFlags(CallConv::kFlagCalleePopsStack);
break;
case CallConv::kIdFastCall:
cc.setFlags(CallConv::kFlagCalleePopsStack);
cc.setPassedOrder(kGroupGp, kZcx, kZdx);
break;
case CallConv::kIdVectorCall:
cc.setFlags(CallConv::kFlagCalleePopsStack);
cc.setPassedOrder(kGroupGp, kZcx, kZdx);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5);
break;
case CallConv::kIdThisCall:
// NOTE: Even MINGW (starting with GCC 4.7.0) now uses __thiscall on MS Windows,
// so we won't bail to any other calling convention if __thiscall was specified.
if (winABI) {
cc.setFlags(CallConv::kFlagCalleePopsStack);
cc.setPassedOrder(kGroupGp, kZcx);
}
else {
ccId = CallConv::kIdCDecl;
}
break;
case CallConv::kIdRegParm1:
cc.setPassedOrder(kGroupGp, kZax);
break;
case CallConv::kIdRegParm2:
cc.setPassedOrder(kGroupGp, kZax, kZdx);
break;
case CallConv::kIdRegParm3:
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx);
break;
case CallConv::kIdLightCall2:
case CallConv::kIdLightCall3:
case CallConv::kIdLightCall4: {
uint32_t n = (ccId - CallConv::kIdLightCall2) + 2;
cc.setFlags(CallConv::kFlagPassFloatsByVec);
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx, kZsi, kZdi);
cc.setPassedOrder(kGroupMm, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPassedOrder(kGroupKReg, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPreservedRegs(kGroupGp, Support::lsbMask<uint32_t>(8));
cc.setPreservedRegs(kGroupVec, Support::lsbMask<uint32_t>(8) & ~Support::lsbMask<uint32_t>(n));
cc.setNaturalStackAlignment(16);
isStandardCallConv = false;
break;
}
default:
return DebugUtils::errored(kErrorInvalidArgument);
}
if (isStandardCallConv) {
// MMX arguments is something where compiler vendors disagree. For example
// GCC and MSVC would pass first three via registers and the rest via stack,
// however Clang passes all via stack. Returning MMX registers is even more
// fun, where GCC uses MM0, but Clang uses EAX:EDX pair. I'm not sure it's
// something we should be worried about as MMX is deprecated anyway.
cc.setPassedOrder(kGroupMm, 0, 1, 2);
// Vector arguments (XMM|YMM|ZMM) are passed via registers. However, if the
// function is variadic then they have to be passed via stack.
cc.setPassedOrder(kGroupVec, 0, 1, 2);
// Functions with variable arguments always use stack for MM and vector
// arguments.
cc.addFlags(CallConv::kFlagPassVecByStackIfVA);
}
if (ccId == CallConv::kIdCDecl) {
cc.addFlags(CallConv::kFlagVarArgCompatible);
}
}
else {
cc.setSaveRestoreRegSize(Reg::kGroupGp, 8);
cc.setSaveRestoreAlignment(Reg::kGroupGp, 8);
// Preprocess the calling convention into a common id as many conventions
// are normally ignored even by C/C++ compilers and treated as `__cdecl`.
if (shouldThreatAsCDeclIn64BitMode(ccId))
ccId = winABI ? CallConv::kIdX64Windows : CallConv::kIdX64SystemV;
switch (ccId) {
case CallConv::kIdX64SystemV: {
cc.setFlags(CallConv::kFlagPassFloatsByVec |
CallConv::kFlagPassMmxByXmm |
CallConv::kFlagVarArgCompatible);
cc.setNaturalStackAlignment(16);
cc.setRedZoneSize(128);
cc.setPassedOrder(kGroupGp, kZdi, kZsi, kZdx, kZcx, 8, 9);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPreservedRegs(kGroupGp, Support::bitMask(kZbx, kZsp, kZbp, 12, 13, 14, 15));
break;
}
case CallConv::kIdX64Windows: {
cc.setStrategy(CallConv::kStrategyX64Windows);
cc.setFlags(CallConv::kFlagPassFloatsByVec |
CallConv::kFlagIndirectVecArgs |
CallConv::kFlagPassMmxByGp |
CallConv::kFlagVarArgCompatible);
cc.setNaturalStackAlignment(16);
// Maximum 4 arguments in registers, each adds 8 bytes to the spill zone.
cc.setSpillZoneSize(4 * 8);
cc.setPassedOrder(kGroupGp, kZcx, kZdx, 8, 9);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3);
cc.setPreservedRegs(kGroupGp, Support::bitMask(kZbx, kZsp, kZbp, kZsi, kZdi, 12, 13, 14, 15));
cc.setPreservedRegs(kGroupVec, Support::bitMask(6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
break;
}
case CallConv::kIdVectorCall: {
cc.setStrategy(CallConv::kStrategyX64VectorCall);
cc.setFlags(CallConv::kFlagPassFloatsByVec |
CallConv::kFlagPassMmxByGp );
cc.setNaturalStackAlignment(16);
// Maximum 6 arguments in registers, each adds 8 bytes to the spill zone.
cc.setSpillZoneSize(6 * 8);
cc.setPassedOrder(kGroupGp, kZcx, kZdx, 8, 9);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5);
cc.setPreservedRegs(kGroupGp, Support::bitMask(kZbx, kZsp, kZbp, kZsi, kZdi, 12, 13, 14, 15));
cc.setPreservedRegs(kGroupVec, Support::bitMask(6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
break;
}
case CallConv::kIdLightCall2:
case CallConv::kIdLightCall3:
case CallConv::kIdLightCall4: {
uint32_t n = (ccId - CallConv::kIdLightCall2) + 2;
cc.setFlags(CallConv::kFlagPassFloatsByVec);
cc.setNaturalStackAlignment(16);
cc.setPassedOrder(kGroupGp, kZax, kZdx, kZcx, kZsi, kZdi);
cc.setPassedOrder(kGroupMm, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPassedOrder(kGroupVec, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPassedOrder(kGroupKReg, 0, 1, 2, 3, 4, 5, 6, 7);
cc.setPreservedRegs(kGroupGp, Support::lsbMask<uint32_t>(16));
cc.setPreservedRegs(kGroupVec, ~Support::lsbMask<uint32_t>(n));
break;
}
default:
return DebugUtils::errored(kErrorInvalidArgument);
}
}
cc.setId(ccId);
return kErrorOk;
}
ASMJIT_FAVOR_SIZE void unpackValues(FuncDetail& func, FuncValuePack& pack) noexcept {
uint32_t typeId = pack[0].typeId();
switch (typeId) {
case Type::kIdI64:
case Type::kIdU64: {
if (Environment::is32Bit(func.callConv().arch())) {
// Convert a 64-bit return value to two 32-bit return values.
pack[0].initTypeId(Type::kIdU32);
pack[1].initTypeId(typeId - 2);
break;
}
break;
}
}
}
ASMJIT_FAVOR_SIZE Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept {
const CallConv& cc = func.callConv();
uint32_t arch = cc.arch();
uint32_t stackOffset = cc._spillZoneSize;
uint32_t argCount = func.argCount();
// Up to two return values can be returned in GP registers.
static const uint8_t gpReturnIndexes[4] = {
uint8_t(Gp::kIdAx),
uint8_t(Gp::kIdDx),
uint8_t(BaseReg::kIdBad),
uint8_t(BaseReg::kIdBad)
};
if (func.hasRet()) {
unpackValues(func, func._rets);
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
uint32_t typeId = func._rets[valueIndex].typeId();
// Terminate at the first void type (end of the pack).
if (!typeId)
break;
switch (typeId) {
case Type::kIdI64:
case Type::kIdU64: {
if (gpReturnIndexes[valueIndex] != BaseReg::kIdBad)
func._rets[valueIndex].initReg(Reg::kTypeGpq, gpReturnIndexes[valueIndex], typeId);
else
return DebugUtils::errored(kErrorInvalidState);
break;
}
case Type::kIdI8:
case Type::kIdI16:
case Type::kIdI32: {
if (gpReturnIndexes[valueIndex] != BaseReg::kIdBad)
func._rets[valueIndex].initReg(Reg::kTypeGpd, gpReturnIndexes[valueIndex], Type::kIdI32);
else
return DebugUtils::errored(kErrorInvalidState);
break;
}
case Type::kIdU8:
case Type::kIdU16:
case Type::kIdU32: {
if (gpReturnIndexes[valueIndex] != BaseReg::kIdBad)
func._rets[valueIndex].initReg(Reg::kTypeGpd, gpReturnIndexes[valueIndex], Type::kIdU32);
else
return DebugUtils::errored(kErrorInvalidState);
break;
}
case Type::kIdF32:
case Type::kIdF64: {
uint32_t regType = Environment::is32Bit(arch) ? Reg::kTypeSt : Reg::kTypeXmm;
func._rets[valueIndex].initReg(regType, valueIndex, typeId);
break;
}
case Type::kIdF80: {
// 80-bit floats are always returned by FP0.
func._rets[valueIndex].initReg(Reg::kTypeSt, valueIndex, typeId);
break;
}
case Type::kIdMmx32:
case Type::kIdMmx64: {
// MM registers are returned through XMM (SystemV) or GPQ (Win64).
uint32_t regType = Reg::kTypeMm;
uint32_t regIndex = valueIndex;
if (Environment::is64Bit(arch)) {
regType = cc.strategy() == CallConv::kStrategyDefault ? Reg::kTypeXmm : Reg::kTypeGpq;
regIndex = cc.strategy() == CallConv::kStrategyDefault ? valueIndex : gpReturnIndexes[valueIndex];
if (regIndex == BaseReg::kIdBad)
return DebugUtils::errored(kErrorInvalidState);
}
func._rets[valueIndex].initReg(regType, regIndex, typeId);
break;
}
default: {
func._rets[valueIndex].initReg(vecTypeIdToRegType(typeId), valueIndex, typeId);
break;
}
}
}
}
switch (cc.strategy()) {
case CallConv::kStrategyDefault: {
uint32_t gpzPos = 0;
uint32_t vecPos = 0;
for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
unpackValues(func, func._args[argIndex]);
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
FuncValue& arg = func._args[argIndex][valueIndex];
// Terminate if there are no more arguments in the pack.
if (!arg)
break;
uint32_t typeId = arg.typeId();
if (Type::isInt(typeId)) {
uint32_t regId = BaseReg::kIdBad;
if (gpzPos < CallConv::kMaxRegArgsPerGroup)
regId = cc._passedOrder[Reg::kGroupGp].id[gpzPos];
if (regId != BaseReg::kIdBad) {
uint32_t regType = (typeId <= Type::kIdU32) ? Reg::kTypeGpd : Reg::kTypeGpq;
arg.assignRegData(regType, regId);
func.addUsedRegs(Reg::kGroupGp, Support::bitMask(regId));
gpzPos++;
}
else {
uint32_t size = Support::max<uint32_t>(Type::sizeOf(typeId), registerSize);
arg.assignStackOffset(int32_t(stackOffset));
stackOffset += size;
}
continue;
}
if (Type::isFloat(typeId) || Type::isVec(typeId)) {
uint32_t regId = BaseReg::kIdBad;
if (vecPos < CallConv::kMaxRegArgsPerGroup)
regId = cc._passedOrder[Reg::kGroupVec].id[vecPos];
if (Type::isFloat(typeId)) {
// If this is a float, but `kFlagPassFloatsByVec` is false, we have
// to use stack instead. This should be only used by 32-bit calling
// conventions.
if (!cc.hasFlag(CallConv::kFlagPassFloatsByVec))
regId = BaseReg::kIdBad;
}
else {
// Pass vector registers via stack if this is a variable arguments
// function. This should be only used by 32-bit calling conventions.
if (signature.hasVarArgs() && cc.hasFlag(CallConv::kFlagPassVecByStackIfVA))
regId = BaseReg::kIdBad;
}
if (regId != BaseReg::kIdBad) {
arg.initTypeId(typeId);
arg.assignRegData(vecTypeIdToRegType(typeId), regId);
func.addUsedRegs(Reg::kGroupVec, Support::bitMask(regId));
vecPos++;
}
else {
uint32_t size = Type::sizeOf(typeId);
arg.assignStackOffset(int32_t(stackOffset));
stackOffset += size;
}
continue;
}
}
}
break;
}
case CallConv::kStrategyX64Windows:
case CallConv::kStrategyX64VectorCall: {
// Both X64 and VectorCall behave similarly - arguments are indexed
// from left to right. The position of the argument determines in
// which register the argument is allocated, so it's either GP or
// one of XMM/YMM/ZMM registers.
//
// [ X64 ] [VecCall]
// Index: #0 #1 #2 #3 #4 #5
//
// GP : RCX RDX R8 R9
// VEC : XMM0 XMM1 XMM2 XMM3 XMM4 XMM5
//
// For example function `f(int a, double b, int c, double d)` will be:
//
// (a) (b) (c) (d)
// RCX XMM1 R8 XMM3
//
// Unused vector registers are used by HVA.
bool isVectorCall = (cc.strategy() == CallConv::kStrategyX64VectorCall);
for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
unpackValues(func, func._args[argIndex]);
for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
FuncValue& arg = func._args[argIndex][valueIndex];
// Terminate if there are no more arguments in the pack.
if (!arg)
break;
uint32_t typeId = arg.typeId();
uint32_t size = Type::sizeOf(typeId);
if (Type::isInt(typeId) || Type::isMmx(typeId)) {
uint32_t regId = BaseReg::kIdBad;
if (argIndex < CallConv::kMaxRegArgsPerGroup)
regId = cc._passedOrder[Reg::kGroupGp].id[argIndex];
if (regId != BaseReg::kIdBad) {
uint32_t regType = (size <= 4 && !Type::isMmx(typeId)) ? Reg::kTypeGpd : Reg::kTypeGpq;
arg.assignRegData(regType, regId);
func.addUsedRegs(Reg::kGroupGp, Support::bitMask(regId));
}
else {
arg.assignStackOffset(int32_t(stackOffset));
stackOffset += 8;
}
continue;
}
if (Type::isFloat(typeId) || Type::isVec(typeId)) {
uint32_t regId = BaseReg::kIdBad;
if (argIndex < CallConv::kMaxRegArgsPerGroup)
regId = cc._passedOrder[Reg::kGroupVec].id[argIndex];
if (regId != BaseReg::kIdBad) {
// X64-ABI doesn't allow vector types (XMM|YMM|ZMM) to be passed
// via registers, however, VectorCall was designed for that purpose.
if (Type::isFloat(typeId) || isVectorCall) {
uint32_t regType = vecTypeIdToRegType(typeId);
arg.assignRegData(regType, regId);
func.addUsedRegs(Reg::kGroupVec, Support::bitMask(regId));
continue;
}
}
// Passed via stack if the argument is float/double or indirectly.
// The trap is - if the argument is passed indirectly, the address
// can be passed via register, if the argument's index has GP one.
if (Type::isFloat(typeId)) {
arg.assignStackOffset(int32_t(stackOffset));
}
else {
uint32_t gpRegId = cc._passedOrder[Reg::kGroupGp].id[argIndex];
if (gpRegId != BaseReg::kIdBad)
arg.assignRegData(Reg::kTypeGpq, gpRegId);
else
arg.assignStackOffset(int32_t(stackOffset));
arg.addFlags(FuncValue::kFlagIsIndirect);
}
// Always 8 bytes (float/double/pointer).
stackOffset += 8;
continue;
}
}
}
break;
}
}
func._argStackSize = stackOffset;
return kErrorOk;
}
} // {FuncInternal}
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_BUILD_X86

View File

@@ -21,10 +21,10 @@
// misrepresented as being the original software. // misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_X86_X86CALLCONV_P_H_INCLUDED #ifndef ASMJIT_X86_X86FUNC_P_H_INCLUDED
#define ASMJIT_X86_X86CALLCONV_P_H_INCLUDED #define ASMJIT_X86_X86FUNC_P_H_INCLUDED
#include "../core/callconv.h" #include "../core/func.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86) 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). //! X86-specific function API (calling conventions and other utilities).
namespace CallConvInternal { namespace FuncInternal {
//! Initialize `CallConv` structure (X86 specific). //! 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 //! \endcond
ASMJIT_END_SUB_NAMESPACE ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_X86_X86CALLCONV_P_H_INCLUDED #endif // ASMJIT_X86_X86FUNC_P_H_INCLUDED

View File

@@ -24,7 +24,7 @@
#ifndef ASMJIT_X86_X86GLOBALS_H_INCLUDED #ifndef ASMJIT_X86_X86GLOBALS_H_INCLUDED
#define ASMJIT_X86_X86GLOBALS_H_INCLUDED #define ASMJIT_X86_X86GLOBALS_H_INCLUDED
#include "../core/arch.h" #include "../core/archtraits.h"
#include "../core/inst.h" #include "../core/inst.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86) ASMJIT_BEGIN_SUB_NAMESPACE(x86)
@@ -1723,16 +1723,16 @@ namespace Condition {
static constexpr uint16_t cmovccTable[] = { ASMJIT_INST_FROM_COND(Inst::kIdCmov) }; static constexpr uint16_t cmovccTable[] = { ASMJIT_INST_FROM_COND(Inst::kIdCmov) };
#undef ASMJIT_INST_FROM_COND #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]; } 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; } 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]; } 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]; } 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]; } static constexpr uint32_t toCmovcc(uint32_t cond) noexcept { return cmovccTable[cond]; }
} }

View File

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

View File

@@ -366,21 +366,21 @@ ASMJIT_VARAPI const CommonInfo _commonInfoTable[];
//! Instruction information (X86). //! Instruction information (X86).
struct InstInfo { struct InstInfo {
//! Index to `_nameData`. //! Index to \ref _nameData.
uint32_t _nameDataIndex : 14; uint32_t _nameDataIndex : 14;
//! Index to `_commonInfoTable`. //! Index to \ref _commonInfoTable.
uint32_t _commonInfoIndex : 10; uint32_t _commonInfoIndex : 10;
//! Index to `InstDB::_commonInfoTableB`. //! Index to \ref _commonInfoTableB.
uint32_t _commonInfoIndexB : 8; uint32_t _commonInfoIndexB : 8;
//! Instruction encoding, see `InstDB::EncodingId`. //! Instruction encoding (internal encoding identifier used by \ref Assembler).
uint8_t _encoding; uint8_t _encoding;
//! Main opcode value (0.255). //! Main opcode value (0..255).
uint8_t _mainOpcodeValue; 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. //! to form the final opcode.
uint8_t _mainOpcodeIndex; 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; uint8_t _altOpcodeIndex;
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@@ -456,7 +456,7 @@ struct InstInfo {
ASMJIT_VARAPI const InstInfo _instInfoTable[]; 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)); ASMJIT_ASSERT(Inst::isDefinedId(instId));
return _instInfoTable[instId]; return _instInfoTable[instId];
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,87 +0,0 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_X86_X86INTERNAL_P_H_INCLUDED
#define ASMJIT_X86_X86INTERNAL_P_H_INCLUDED
#include "../core/api-config.h"
#include "../core/func.h"
#include "../x86/x86emitter.h"
#include "../x86/x86operand.h"
ASMJIT_BEGIN_SUB_NAMESPACE(x86)
//! \cond INTERNAL
//! \addtogroup asmjit_x86
//! \{
// ============================================================================
// [asmjit::X86Internal]
// ============================================================================
//! X86 utilities used at multiple places, not part of public API, not exported.
struct X86Internal {
//! Initialize `FuncDetail` (X86 specific).
static Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept;
//! Initialize `FuncFrame` (X86 specific).
static Error initFuncFrame(FuncFrame& frame, const FuncDetail& signature) noexcept;
//! Finalize `FuncFrame` (X86 specific).
static Error finalizeFuncFrame(FuncFrame& frame) noexcept;
static Error argsToFuncFrame(const FuncArgsAssignment& args, FuncFrame& frame) noexcept;
//! Emit function prolog.
static Error emitProlog(Emitter* emitter, const FuncFrame& frame);
//! Emit function epilog.
static Error emitEpilog(Emitter* emitter, const FuncFrame& frame);
//! Emit a pure move operation between two registers or the same type or
//! between a register and its home slot. This function does not handle
//! register conversion.
static Error emitRegMove(Emitter* emitter,
const Operand_& dst_,
const Operand_& src_, uint32_t typeId, bool avxEnabled, const char* comment = nullptr);
//! Emit move from a function argument (either register or stack) to a register.
//!
//! This function can handle the necessary conversion from one argument to
//! another, and from one register type to another, if it's possible. Any
//! attempt of conversion that requires third register of a different group
//! (for example conversion from K to MMX) will fail.
static Error emitArgMove(Emitter* emitter,
const Reg& dst_, uint32_t dstTypeId,
const Operand_& src_, uint32_t srcTypeId, bool avxEnabled, const char* comment = nullptr);
static Error emitArgsAssignment(Emitter* emitter, const FuncFrame& frame, const FuncArgsAssignment& args);
};
//! \}
//! \endcond
ASMJIT_END_SUB_NAMESPACE
#endif // ASMJIT_X86_X86INTERNAL_P_H_INCLUDED

View File

@@ -29,29 +29,6 @@
ASMJIT_BEGIN_SUB_NAMESPACE(x86) ASMJIT_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] // [asmjit::x86::Operand - Unit]
// ============================================================================ // ============================================================================
@@ -167,7 +144,7 @@ UNIT(x86_operand) {
EXPECT(zmm7.ymm() == ymm7); EXPECT(zmm7.ymm() == ymm7);
EXPECT(zmm7.zmm() == zmm7); EXPECT(zmm7.zmm() == zmm7);
INFO("Checking x86::FpMm register properties"); INFO("Checking x86::Mm register properties");
EXPECT(Mm().isReg() == true); EXPECT(Mm().isReg() == true);
EXPECT(mm2.isReg() == true); EXPECT(mm2.isReg() == true);
EXPECT(mm2.id() == 2); EXPECT(mm2.id() == 2);

View File

@@ -24,7 +24,7 @@
#ifndef ASMJIT_X86_X86OPERAND_H_INCLUDED #ifndef ASMJIT_X86_X86OPERAND_H_INCLUDED
#define 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/operand.h"
#include "../core/type.h" #include "../core/type.h"
#include "../x86/x86globals.h" #include "../x86/x86globals.h"
@@ -61,23 +61,23 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86)
} \ } \
\ \
static constexpr Mem FUNC##_abs(uint64_t base) noexcept { \ 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 { \ 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 { \ 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 { \ 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 { \ 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 { \ 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 //! \addtogroup asmjit_x86
@@ -231,39 +231,39 @@ public:
//! Tests whether the register is a GPB register (8-bit). //! Tests whether the register is a GPB register (8-bit).
constexpr bool isGpb() const noexcept { return size() == 1; } constexpr bool isGpb() const noexcept { return size() == 1; }
//! Tests whether the register is a low GPB register (8-bit). //! 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). //! 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). //! 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). //! 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). //! 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). //! 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). //! 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). //! 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). //! 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). //! 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. //! 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. //! 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. //! 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). //! 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. //! 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. //! 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. //! 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> template<uint32_t REG_TYPE>
inline void setRegT(uint32_t rId) noexcept { inline void setRegT(uint32_t rId) noexcept {
@@ -277,15 +277,16 @@ public:
setId(rId); 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> template<uint32_t REG_TYPE>
static inline uint32_t groupOfT() noexcept { return RegTraits<REG_TYPE>::kGroup; } 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> template<uint32_t REG_TYPE>
static inline uint32_t typeIdOfT() noexcept { return RegTraits<REG_TYPE>::kTypeId; } 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> template<uint32_t REG_TYPE>
static inline uint32_t signatureOfT() noexcept { return RegTraits<REG_TYPE>::kSignature; } 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. //! Tests whether the `op` operand is either a low or high 8-bit GPB register.
static inline bool isGpb(const Operand_& op) noexcept { static inline bool isGpb(const Operand_& op) noexcept {
// Check operand type, register group, and size. Not interested in register type. // Check operand type, register group, and size. Not interested in register type.
const uint32_t kSgn = (Operand::kOpReg << kSignatureOpShift ) | const uint32_t kSgn = (Operand::kOpReg << kSignatureOpTypeShift) |
(1 << kSignatureSizeShift) ; (1 << kSignatureSizeShift ) ;
return (op.signature() & (kSignatureOpMask | kSignatureSizeMask)) == kSgn; return (op.signature() & (kSignatureOpTypeMask | kSignatureSizeMask)) == kSgn;
} }
static inline bool isGpbLo(const Operand_& op) noexcept { return op.as<Reg>().isGpbLo(); } 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). //! Casts this register to a register that has half the size (or XMM if it's already XMM).
inline Vec half() const noexcept { 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. //! Creates a TMM register operand.
static constexpr Tmm tmm(uint32_t rId) noexcept { return Tmm(rId); } static constexpr Tmm tmm(uint32_t rId) noexcept { return Tmm(rId); }
static constexpr Gp al = Gp(GpbLo::kSignature, Gp::kIdAx); static constexpr Gp al = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdAx));
static constexpr Gp bl = Gp(GpbLo::kSignature, Gp::kIdBx); static constexpr Gp bl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdBx));
static constexpr Gp cl = Gp(GpbLo::kSignature, Gp::kIdCx); static constexpr Gp cl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdCx));
static constexpr Gp dl = Gp(GpbLo::kSignature, Gp::kIdDx); static constexpr Gp dl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdDx));
static constexpr Gp spl = Gp(GpbLo::kSignature, Gp::kIdSp); static constexpr Gp spl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdSp));
static constexpr Gp bpl = Gp(GpbLo::kSignature, Gp::kIdBp); static constexpr Gp bpl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdBp));
static constexpr Gp sil = Gp(GpbLo::kSignature, Gp::kIdSi); static constexpr Gp sil = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdSi));
static constexpr Gp dil = Gp(GpbLo::kSignature, Gp::kIdDi); static constexpr Gp dil = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdDi));
static constexpr Gp r8b = Gp(GpbLo::kSignature, Gp::kIdR8); static constexpr Gp r8b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR8));
static constexpr Gp r9b = Gp(GpbLo::kSignature, Gp::kIdR9); static constexpr Gp r9b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR9));
static constexpr Gp r10b = Gp(GpbLo::kSignature, Gp::kIdR10); static constexpr Gp r10b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR10));
static constexpr Gp r11b = Gp(GpbLo::kSignature, Gp::kIdR11); static constexpr Gp r11b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR11));
static constexpr Gp r12b = Gp(GpbLo::kSignature, Gp::kIdR12); static constexpr Gp r12b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR12));
static constexpr Gp r13b = Gp(GpbLo::kSignature, Gp::kIdR13); static constexpr Gp r13b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR13));
static constexpr Gp r14b = Gp(GpbLo::kSignature, Gp::kIdR14); static constexpr Gp r14b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR14));
static constexpr Gp r15b = Gp(GpbLo::kSignature, Gp::kIdR15); static constexpr Gp r15b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR15));
static constexpr Gp ah = Gp(GpbHi::kSignature, Gp::kIdAx); static constexpr Gp ah = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdAx));
static constexpr Gp bh = Gp(GpbHi::kSignature, Gp::kIdBx); static constexpr Gp bh = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdBx));
static constexpr Gp ch = Gp(GpbHi::kSignature, Gp::kIdCx); static constexpr Gp ch = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdCx));
static constexpr Gp dh = Gp(GpbHi::kSignature, Gp::kIdDx); static constexpr Gp dh = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdDx));
static constexpr Gp ax = Gp(Gpw::kSignature, Gp::kIdAx); static constexpr Gp ax = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdAx));
static constexpr Gp bx = Gp(Gpw::kSignature, Gp::kIdBx); static constexpr Gp bx = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdBx));
static constexpr Gp cx = Gp(Gpw::kSignature, Gp::kIdCx); static constexpr Gp cx = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdCx));
static constexpr Gp dx = Gp(Gpw::kSignature, Gp::kIdDx); static constexpr Gp dx = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdDx));
static constexpr Gp sp = Gp(Gpw::kSignature, Gp::kIdSp); static constexpr Gp sp = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdSp));
static constexpr Gp bp = Gp(Gpw::kSignature, Gp::kIdBp); static constexpr Gp bp = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdBp));
static constexpr Gp si = Gp(Gpw::kSignature, Gp::kIdSi); static constexpr Gp si = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdSi));
static constexpr Gp di = Gp(Gpw::kSignature, Gp::kIdDi); static constexpr Gp di = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdDi));
static constexpr Gp r8w = Gp(Gpw::kSignature, Gp::kIdR8); static constexpr Gp r8w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR8));
static constexpr Gp r9w = Gp(Gpw::kSignature, Gp::kIdR9); static constexpr Gp r9w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR9));
static constexpr Gp r10w = Gp(Gpw::kSignature, Gp::kIdR10); static constexpr Gp r10w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR10));
static constexpr Gp r11w = Gp(Gpw::kSignature, Gp::kIdR11); static constexpr Gp r11w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR11));
static constexpr Gp r12w = Gp(Gpw::kSignature, Gp::kIdR12); static constexpr Gp r12w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR12));
static constexpr Gp r13w = Gp(Gpw::kSignature, Gp::kIdR13); static constexpr Gp r13w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR13));
static constexpr Gp r14w = Gp(Gpw::kSignature, Gp::kIdR14); static constexpr Gp r14w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR14));
static constexpr Gp r15w = Gp(Gpw::kSignature, Gp::kIdR15); static constexpr Gp r15w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR15));
static constexpr Gp eax = Gp(Gpd::kSignature, Gp::kIdAx); static constexpr Gp eax = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdAx));
static constexpr Gp ebx = Gp(Gpd::kSignature, Gp::kIdBx); static constexpr Gp ebx = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdBx));
static constexpr Gp ecx = Gp(Gpd::kSignature, Gp::kIdCx); static constexpr Gp ecx = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdCx));
static constexpr Gp edx = Gp(Gpd::kSignature, Gp::kIdDx); static constexpr Gp edx = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdDx));
static constexpr Gp esp = Gp(Gpd::kSignature, Gp::kIdSp); static constexpr Gp esp = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdSp));
static constexpr Gp ebp = Gp(Gpd::kSignature, Gp::kIdBp); static constexpr Gp ebp = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdBp));
static constexpr Gp esi = Gp(Gpd::kSignature, Gp::kIdSi); static constexpr Gp esi = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdSi));
static constexpr Gp edi = Gp(Gpd::kSignature, Gp::kIdDi); static constexpr Gp edi = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdDi));
static constexpr Gp r8d = Gp(Gpd::kSignature, Gp::kIdR8); static constexpr Gp r8d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR8));
static constexpr Gp r9d = Gp(Gpd::kSignature, Gp::kIdR9); static constexpr Gp r9d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR9));
static constexpr Gp r10d = Gp(Gpd::kSignature, Gp::kIdR10); static constexpr Gp r10d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR10));
static constexpr Gp r11d = Gp(Gpd::kSignature, Gp::kIdR11); static constexpr Gp r11d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR11));
static constexpr Gp r12d = Gp(Gpd::kSignature, Gp::kIdR12); static constexpr Gp r12d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR12));
static constexpr Gp r13d = Gp(Gpd::kSignature, Gp::kIdR13); static constexpr Gp r13d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR13));
static constexpr Gp r14d = Gp(Gpd::kSignature, Gp::kIdR14); static constexpr Gp r14d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR14));
static constexpr Gp r15d = Gp(Gpd::kSignature, Gp::kIdR15); static constexpr Gp r15d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR15));
static constexpr Gp rax = Gp(Gpq::kSignature, Gp::kIdAx); static constexpr Gp rax = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdAx));
static constexpr Gp rbx = Gp(Gpq::kSignature, Gp::kIdBx); static constexpr Gp rbx = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdBx));
static constexpr Gp rcx = Gp(Gpq::kSignature, Gp::kIdCx); static constexpr Gp rcx = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdCx));
static constexpr Gp rdx = Gp(Gpq::kSignature, Gp::kIdDx); static constexpr Gp rdx = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdDx));
static constexpr Gp rsp = Gp(Gpq::kSignature, Gp::kIdSp); static constexpr Gp rsp = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdSp));
static constexpr Gp rbp = Gp(Gpq::kSignature, Gp::kIdBp); static constexpr Gp rbp = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdBp));
static constexpr Gp rsi = Gp(Gpq::kSignature, Gp::kIdSi); static constexpr Gp rsi = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdSi));
static constexpr Gp rdi = Gp(Gpq::kSignature, Gp::kIdDi); static constexpr Gp rdi = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdDi));
static constexpr Gp r8 = Gp(Gpq::kSignature, Gp::kIdR8); static constexpr Gp r8 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR8));
static constexpr Gp r9 = Gp(Gpq::kSignature, Gp::kIdR9); static constexpr Gp r9 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR9));
static constexpr Gp r10 = Gp(Gpq::kSignature, Gp::kIdR10); static constexpr Gp r10 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR10));
static constexpr Gp r11 = Gp(Gpq::kSignature, Gp::kIdR11); static constexpr Gp r11 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR11));
static constexpr Gp r12 = Gp(Gpq::kSignature, Gp::kIdR12); static constexpr Gp r12 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR12));
static constexpr Gp r13 = Gp(Gpq::kSignature, Gp::kIdR13); static constexpr Gp r13 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR13));
static constexpr Gp r14 = Gp(Gpq::kSignature, Gp::kIdR14); static constexpr Gp r14 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR14));
static constexpr Gp r15 = Gp(Gpq::kSignature, Gp::kIdR15); static constexpr Gp r15 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR15));
static constexpr Xmm xmm0 = Xmm(0); static constexpr Xmm xmm0 = Xmm(0);
static constexpr Xmm xmm1 = Xmm(1); static constexpr Xmm xmm1 = Xmm(1);
@@ -815,28 +816,65 @@ using namespace regs;
//! Memory operand. //! Memory operand.
class Mem : public BaseMem { class Mem : public BaseMem {
public: public:
//! Additional bits of operand's signature used by `Mem`. //! Additional bits of operand's signature used by `x86::Mem`.
enum AdditionalBits : uint32_t { 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, kSignatureMemSegmentMask = 0x07u << kSignatureMemSegmentShift,
kSignatureMemShiftShift = 19, // Memory broadcast type (3 bits).
kSignatureMemShiftMask = 0x03u << kSignatureMemShiftShift, // |........|XXX.....|........|........|
kSignatureMemBroadcastShift = 21, kSignatureMemBroadcastShift = 21,
kSignatureMemBroadcastMask = 0x7u << kSignatureMemBroadcastShift 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 { enum Broadcast : uint32_t {
//! Broadcast {1to1}.
kBroadcast1To1 = 0, kBroadcast1To1 = 0,
//! Broadcast {1to2}.
kBroadcast1To2 = 1, kBroadcast1To2 = 1,
//! Broadcast {1to4}.
kBroadcast1To4 = 2, kBroadcast1To4 = 2,
//! Broadcast {1to8}.
kBroadcast1To8 = 3, kBroadcast1To8 = 3,
//! Broadcast {1to16}.
kBroadcast1To16 = 4, kBroadcast1To16 = 4,
//! Broadcast {1to32}.
kBroadcast1To32 = 5, kBroadcast1To32 = 5,
//! Broadcast {1to64}.
kBroadcast1To64 = 6 kBroadcast1To64 = 6
}; };
//! \cond
//! Shortcuts.
enum SignatureMem : uint32_t {
kSignatureMemAbs = kAddrTypeAbs << kSignatureMemAddrTypeShift,
kSignatureMemRel = kAddrTypeRel << kSignatureMemAddrTypeShift
};
//! \endcond
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// [Construction / Destruction] // [Construction / Destruction]
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@@ -859,19 +897,19 @@ public:
: BaseMem(Decomposed { Label::kLabelTag, base.id(), 0, 0, off, size, flags }) {} : 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 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 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 }) {} : 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 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 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 }) {} : 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 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 constexpr Mem(Globals::Init_, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) noexcept
: BaseMem(Globals::Init, u0, u1, u2, u3) {} : BaseMem(Globals::Init, u0, u1, u2, u3) {}
@@ -918,6 +956,25 @@ public:
setShift(shift); 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. //! Tests whether the memory operand has a segment override.
constexpr bool hasSegment() const noexcept { return _hasSignaturePart<kSignatureMemSegmentMask>(); } constexpr bool hasSegment() const noexcept { return _hasSignaturePart<kSignatureMemSegmentMask>(); }
//! Returns the associated segment override as `SReg` operand. //! Returns the associated segment override as `SReg` operand.
@@ -933,13 +990,13 @@ public:
inline void resetSegment() noexcept { _setSignaturePart<kSignatureMemSegmentMask>(0); } inline void resetSegment() noexcept { _setSignaturePart<kSignatureMemSegmentMask>(0); }
//! Tests whether the memory operand has shift (aka scale) value. //! 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. //! 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. //! 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. //! 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}. //! Tests whether the memory operand has broadcast {1tox}.
constexpr bool hasBroadcast() const noexcept { return _hasSignaturePart<kSignatureMemBroadcastMask>(); } 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). //! Creates `[base]` absolute memory operand (absolute).
static constexpr Mem ptr_abs(uint64_t base, uint32_t size = 0) noexcept { 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). //! 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 { 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). //! 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 { 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). //! Creates `[base]` relative memory operand (relative).
static constexpr Mem ptr_rel(uint64_t base, uint32_t size = 0) noexcept { 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). //! 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 { 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). //! 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 { 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. // 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(ymmword_ptr, 32)
ASMJIT_MEM_PTR(zmmword_ptr, 64) 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 ASMJIT_END_SUB_NAMESPACE

View File

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

View File

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

View File

@@ -0,0 +1,247 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#include <asmjit/core.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory>
#include <vector>
#include "./cmdline.h"
#include "./asmjit_test_compiler.h"
#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86
#include <asmjit/x86.h>
void compiler_add_x86_tests(TestApp& app);
#endif
#if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM == 64
#include <asmjit/a64.h>
void compiler_add_a64_tests(TestApp& app);
#endif
#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86
#define ASMJIT_HAVE_WORKING_JIT
#endif
#if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM == 64
#define ASMJIT_HAVE_WORKING_JIT
#endif
using namespace asmjit;
// ============================================================================
// [TestApp]
// ============================================================================
static const char* archAsString(uint32_t arch) {
switch (arch) {
case Environment::kArchX86: return "X86";
case Environment::kArchX64: return "X64";
case Environment::kArchARM: return "A32";
case Environment::kArchThumb: return "T32";
case Environment::kArchAArch64: return "A64";
default: return "Unknown";
}
}
int TestApp::handleArgs(int argc, const char* const* argv) {
CmdLine cmd(argc, argv);
if (cmd.hasArg("--verbose")) _verbose = true;
if (cmd.hasArg("--dump-asm")) _dumpAsm = true;
if (cmd.hasArg("--dump-hex")) _dumpHex = true;
return 0;
}
void TestApp::showInfo() {
printf("AsmJit Compiler Test-Suite v%u.%u.%u [Arch=%s]:\n",
unsigned((ASMJIT_LIBRARY_VERSION >> 16) ),
unsigned((ASMJIT_LIBRARY_VERSION >> 8) & 0xFF),
unsigned((ASMJIT_LIBRARY_VERSION ) & 0xFF),
archAsString(Environment::kArchHost));
printf(" [%s] Verbose (use --verbose to turn verbose output ON)\n", _verbose ? "x" : " ");
printf(" [%s] DumpAsm (use --dump-asm to turn assembler dumps ON)\n", _dumpAsm ? "x" : " ");
printf(" [%s] DumpHex (use --dump-hex to dump binary in hexadecimal)\n", _dumpHex ? "x" : " ");
printf("\n");
}
int TestApp::run() {
#ifndef ASMJIT_HAVE_WORKING_JIT
return 0;
#else
#ifndef ASMJIT_NO_LOGGING
uint32_t kFormatFlags = FormatOptions::kFlagMachineCode |
FormatOptions::kFlagExplainImms |
FormatOptions::kFlagRegCasts |
FormatOptions::kFlagAnnotations |
FormatOptions::kFlagDebugPasses |
FormatOptions::kFlagDebugRA ;
FileLogger fileLogger(stdout);
fileLogger.addFlags(kFormatFlags);
StringLogger stringLogger;
stringLogger.addFlags(kFormatFlags);
#endif
for (std::unique_ptr<TestCase>& test : _tests) {
JitRuntime runtime;
CodeHolder code;
SimpleErrorHandler errorHandler;
code.init(runtime.environment());
code.setErrorHandler(&errorHandler);
#ifndef ASMJIT_NO_LOGGING
if (_verbose) {
code.setLogger(&fileLogger);
}
else {
stringLogger.clear();
code.setLogger(&stringLogger);
}
#endif
printf("[Test] %s", test->name());
#ifndef ASMJIT_NO_LOGGING
if (_verbose) printf("\n");
#endif
#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86
x86::Compiler cc(&code);
#endif
#if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM == 64
arm::Compiler cc(&code);
#endif
test->compile(cc);
void* func = nullptr;
Error err = errorHandler._err;
if (!err)
err = cc.finalize();
#ifndef ASMJIT_NO_LOGGING
if (_dumpAsm) {
if (!_verbose) printf("\n");
String sb;
Formatter::formatNodeList(sb, kFormatFlags, &cc);
printf("%s", sb.data());
}
#endif
if (err == kErrorOk)
err = runtime.add(&func, &code);
if (err == kErrorOk && _dumpHex) {
String sb;
sb.appendHex((void*)func, code.codeSize());
printf("\n (HEX: %s)\n", sb.data());
}
if (_verbose)
fflush(stdout);
if (err == kErrorOk) {
_outputSize += code.codeSize();
StringTmp<128> result;
StringTmp<128> expect;
if (test->run(func, result, expect)) {
if (!_verbose) printf(" [OK]\n");
}
else {
if (!_verbose) printf(" [FAILED]\n");
#ifndef ASMJIT_NO_LOGGING
if (!_verbose) printf("%s", stringLogger.data());
#endif
printf("[Status]\n");
printf(" Returned: %s\n", result.data());
printf(" Expected: %s\n", expect.data());
_nFailed++;
}
if (_dumpAsm)
printf("\n");
runtime.release(func);
}
else {
if (!_verbose) printf(" [FAILED]\n");
#ifndef ASMJIT_NO_LOGGING
if (!_verbose) printf("%s", stringLogger.data());
#endif
printf("[Status]\n");
printf(" ERROR 0x%08X: %s\n", unsigned(err), errorHandler._message.data());
_nFailed++;
}
}
if (_nFailed == 0)
printf("\nSuccess:\n All %u tests passed\n", unsigned(_tests.size()));
else
printf("\nFailure:\n %u %s of %u failed\n", _nFailed, _nFailed == 1 ? "test" : "tests", unsigned(_tests.size()));
printf(" OutputSize=%zu\n", _outputSize);
printf("\n");
return _nFailed == 0 ? 0 : 1;
#endif
}
// ============================================================================
// [Main]
// ============================================================================
int main(int argc, char* argv[]) {
TestApp app;
app.handleArgs(argc, argv);
app.showInfo();
#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86
compiler_add_x86_tests(app);
#endif
#if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM == 64
compiler_add_a64_tests(app);
#endif
return app.run();
}

103
test/asmjit_test_compiler.h Normal file
View File

@@ -0,0 +1,103 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_TEST_COMPILER_H_INCLUDED
#define ASMJIT_TEST_COMPILER_H_INCLUDED
#include <asmjit/core.h>
#include <memory>
#include <vector>
// ============================================================================
// [SimpleErrorHandler]
// ============================================================================
class SimpleErrorHandler : public asmjit::ErrorHandler {
public:
SimpleErrorHandler()
: _err(asmjit::kErrorOk) {}
virtual void handleError(asmjit::Error err, const char* message, asmjit::BaseEmitter* origin) {
asmjit::DebugUtils::unused(origin);
_err = err;
_message.assign(message);
}
asmjit::Error _err;
asmjit::String _message;
};
// ============================================================================
// [TestCase]
// ============================================================================
//! A test case interface for testing AsmJit's Compiler.
class TestCase {
public:
TestCase(const char* name = nullptr) {
if (name)
_name.assign(name);
}
virtual ~TestCase() {}
inline const char* name() const { return _name.data(); }
virtual void compile(asmjit::BaseCompiler& cc) = 0;
virtual bool run(void* func, asmjit::String& result, asmjit::String& expect) = 0;
asmjit::String _name;
};
// ============================================================================
// [TestApp]
// ============================================================================
class TestApp {
public:
std::vector<std::unique_ptr<TestCase>> _tests;
unsigned _nFailed = 0;
size_t _outputSize = 0;
bool _verbose = false;
bool _dumpAsm = false;
bool _dumpHex = false;
TestApp() noexcept {}
~TestApp() noexcept {}
void add(TestCase* test) noexcept {
_tests.push_back(std::unique_ptr<TestCase>(test));
}
template<class T>
inline void addT() { T::add(*this); }
int handleArgs(int argc, const char* const* argv);
void showInfo();
int run();
};
#endif // ASMJIT_TEST_COMPILER_H_INCLUDED

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -79,6 +79,41 @@ static void printInfo(uint32_t arch, const BaseInst& inst, const Operand_* opera
sb.append("\n"); 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 // CPU Features
// ------------ // ------------

83
test/cmdline.h Normal file
View File

@@ -0,0 +1,83 @@
// AsmJit - Machine code generation for C++
//
// * Official AsmJit Home Page: https://asmjit.com
// * Official Github Repository: https://github.com/asmjit/asmjit
//
// Copyright (c) 2008-2020 The AsmJit Authors
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef ASMJIT_TEST_CMDLINE_H_INCLUDED
#define ASMJIT_TEST_CMDLINE_H_INCLUDED
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
// ============================================================================
// [CmdLine]
// ============================================================================
class CmdLine {
public:
int _argc;
const char* const* _argv;
CmdLine(int argc, const char* const* argv)
: _argc(argc),
_argv(argv) {}
bool hasArg(const char* key) const {
for (int i = 1; i < _argc; i++)
if (strcmp(key, _argv[i]) == 0)
return true;
return false;
}
const char* valueOf(const char* key, const char* defaultValue) const {
size_t keySize = strlen(key);
for (int i = 1; i < _argc; i++) {
const char* val = _argv[i];
if (strlen(val) >= keySize + 1 && val[keySize] == '=' && memcmp(val, key, keySize) == 0)
return val + keySize + 1;
}
return defaultValue;
}
int valueAsInt(const char* key, int defaultValue) const {
const char* val = valueOf(key, nullptr);
if (val == nullptr || val[0] == '\0')
return defaultValue;
return atoi(val);
}
unsigned valueAsUInt(const char* key, unsigned defaultValue) const {
const char* val = valueOf(key, nullptr);
if (val == nullptr || val[0] == '\0')
return defaultValue;
int v = atoi(val);
if (v < 0)
return defaultValue;
else
return unsigned(v);
}
};
#endif // ASMJIT_TEST_CMDLINE_H_INCLUDED

View File

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