From cd44f41d9b34ed4bd5f2ea2da5ed460d52bd1134 Mon Sep 17 00:00:00 2001 From: kobalicek Date: Sat, 12 Sep 2020 16:25:35 +0200 Subject: [PATCH] Preparation for AArch64 support --- CMakeLists.txt | 42 +- src/asmjit/core.h | 3 +- src/asmjit/core/api-config.h | 19 +- src/asmjit/core/arch.cpp | 59 - src/asmjit/core/arch.h | 64 - src/asmjit/core/archcommons.h | 164 + src/asmjit/core/archtraits.cpp | 155 + src/asmjit/core/archtraits.h | 174 + src/asmjit/core/assembler.cpp | 31 +- src/asmjit/core/assembler.h | 8 +- src/asmjit/core/builder.cpp | 11 +- src/asmjit/core/builder.h | 20 +- src/asmjit/core/callconv.cpp | 59 - src/asmjit/core/callconv.h | 374 -- src/asmjit/core/codeholder.cpp | 47 +- src/asmjit/core/codeholder.h | 437 +- src/asmjit/core/codewriter.cpp | 151 + .../{codebufferwriter_p.h => codewriter_p.h} | 27 +- src/asmjit/core/compiler.cpp | 7 +- src/asmjit/core/compiler.h | 130 +- src/asmjit/core/compilerdefs.h | 170 + src/asmjit/core/cpuinfo.h | 2 +- src/asmjit/core/emithelper.cpp | 351 ++ src/asmjit/core/emithelper_p.h | 83 + src/asmjit/core/emitter.cpp | 62 +- src/asmjit/core/emitter.h | 37 +- src/asmjit/core/environment.h | 39 +- src/asmjit/core/func.cpp | 241 +- src/asmjit/core/func.h | 413 +- src/asmjit/core/funcargscontext.cpp | 315 ++ src/asmjit/core/funcargscontext_p.h | 224 + src/asmjit/core/globals.cpp | 7 +- src/asmjit/core/globals.h | 24 +- src/asmjit/core/inst.cpp | 24 +- src/asmjit/core/jitallocator.cpp | 2 +- src/asmjit/core/jitruntime.cpp | 12 +- src/asmjit/core/misc_p.h | 4 +- src/asmjit/core/operand.cpp | 9 +- src/asmjit/core/operand.h | 354 +- src/asmjit/core/raassignment_p.h | 51 +- src/asmjit/core/rabuilders_p.h | 42 +- src/asmjit/core/radefs_p.h | 168 +- src/asmjit/core/ralocal.cpp | 9 +- src/asmjit/core/ralocal_p.h | 16 +- src/asmjit/core/rapass.cpp | 170 +- src/asmjit/core/rapass_p.h | 179 +- src/asmjit/core/support.h | 178 +- src/asmjit/core/target.h | 2 +- src/asmjit/core/type.cpp | 4 +- src/asmjit/core/zonevector.h | 22 +- src/asmjit/x86.h | 3 +- src/asmjit/x86/x86archdata.cpp | 137 - src/asmjit/x86/x86archtraits_p.h | 150 + src/asmjit/x86/x86assembler.cpp | 112 +- src/asmjit/x86/x86builder.cpp | 5 +- src/asmjit/x86/x86callconv.cpp | 238 - src/asmjit/x86/x86compiler.cpp | 7 +- src/asmjit/x86/x86emithelper.cpp | 603 +++ .../{x86archdata_p.h => x86emithelper_p.h} | 45 +- src/asmjit/x86/x86emitter.h | 206 +- src/asmjit/x86/x86features.h | 1 + src/asmjit/x86/x86formatter.cpp | 110 +- src/asmjit/x86/x86func.cpp | 531 +++ .../x86/{x86callconv_p.h => x86func_p.h} | 19 +- src/asmjit/x86/x86globals.h | 12 +- src/asmjit/x86/x86instapi.cpp | 108 +- src/asmjit/x86/x86instdb.h | 16 +- src/asmjit/x86/x86internal.cpp | 1784 ------- src/asmjit/x86/x86internal_p.h | 87 - src/asmjit/x86/x86operand.cpp | 25 +- src/asmjit/x86/x86operand.h | 321 +- src/asmjit/x86/x86rapass.cpp | 101 +- src/asmjit/x86/x86rapass_p.h | 31 +- test/asmjit_test_compiler.cpp | 247 + test/asmjit_test_compiler.h | 103 + test/asmjit_test_compiler_x86.cpp | 4238 +++++++++++++++++ test/asmjit_test_opcode.cpp | 2 +- test/asmjit_test_unit.cpp | 2 +- test/asmjit_test_x86_instinfo.cpp | 35 + test/cmdline.h | 83 + tools/ci-run.sh | 4 +- 81 files changed, 10121 insertions(+), 4441 deletions(-) delete mode 100644 src/asmjit/core/arch.cpp delete mode 100644 src/asmjit/core/arch.h create mode 100644 src/asmjit/core/archcommons.h create mode 100644 src/asmjit/core/archtraits.cpp create mode 100644 src/asmjit/core/archtraits.h delete mode 100644 src/asmjit/core/callconv.cpp delete mode 100644 src/asmjit/core/callconv.h create mode 100644 src/asmjit/core/codewriter.cpp rename src/asmjit/core/{codebufferwriter_p.h => codewriter_p.h} (85%) create mode 100644 src/asmjit/core/compilerdefs.h create mode 100644 src/asmjit/core/emithelper.cpp create mode 100644 src/asmjit/core/emithelper_p.h create mode 100644 src/asmjit/core/funcargscontext.cpp create mode 100644 src/asmjit/core/funcargscontext_p.h delete mode 100644 src/asmjit/x86/x86archdata.cpp create mode 100644 src/asmjit/x86/x86archtraits_p.h delete mode 100644 src/asmjit/x86/x86callconv.cpp create mode 100644 src/asmjit/x86/x86emithelper.cpp rename src/asmjit/x86/{x86archdata_p.h => x86emithelper_p.h} (52%) create mode 100644 src/asmjit/x86/x86func.cpp rename src/asmjit/x86/{x86callconv_p.h => x86func_p.h} (76%) delete mode 100644 src/asmjit/x86/x86internal.cpp delete mode 100644 src/asmjit/x86/x86internal_p.h create mode 100644 test/asmjit_test_compiler.cpp create mode 100644 test/asmjit_test_compiler.h create mode 100644 test/asmjit_test_compiler_x86.cpp create mode 100644 test/cmdline.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f648b4b..7644171 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,10 +45,6 @@ if (NOT DEFINED ASMJIT_STATIC) set(ASMJIT_STATIC ${ASMJIT_EMBED}) endif() -if (NOT DEFINED ASMJIT_BUILD_ARM) - set(ASMJIT_BUILD_ARM FALSE) -endif() - if (NOT DEFINED ASMJIT_BUILD_X86) set(ASMJIT_BUILD_X86 FALSE) endif() @@ -72,7 +68,6 @@ set(ASMJIT_EMBED ${ASMJIT_EMBED} CACHE BOOL "Embed 'asmjit' lib set(ASMJIT_STATIC ${ASMJIT_STATIC} CACHE BOOL "Build 'asmjit' library as static") set(ASMJIT_SANITIZE ${ASMJIT_SANITIZE} CACHE STRING "Build with sanitizers: 'address', 'undefined', etc...") set(ASMJIT_BUILD_X86 ${ASMJIT_BUILD_X86} CACHE BOOL "Build X86 backends (X86 and X86_64)") -set(ASMJIT_BUILD_ARM ${ASMJIT_BUILD_ARM} CACHE BOOL "Build ARM backends (ARM/Trumb and AArch64") # ============================================================================= # [AsmJit - Project] @@ -140,6 +135,7 @@ function(asmjit_add_target target target_type) add_library(${target} ${target_type} ${X_SOURCES}) endif() + set_target_properties(${target} PROPERTIES DEFINE_SYMBOL "") target_link_libraries(${target} PRIVATE ${X_LIBRARIES}) # target_link_options was added in cmake v3.13, don't use it for now... @@ -255,8 +251,6 @@ endif() foreach(build_option ASMJIT_STATIC ASMJIT_BUILD_X86 - #ASMJIT_BUILD_ARM - ASMJIT_BUILD_A64 ASMJIT_NO_DEPRECATED ASMJIT_NO_JIT ASMJIT_NO_LOGGING @@ -291,25 +285,28 @@ set(ASMJIT_SRC_LIST asmjit/core.h asmjit/core/api-build_p.h asmjit/core/api-config.h - asmjit/core/arch.cpp - asmjit/core/arch.h + asmjit/core/archtraits.cpp + asmjit/core/archtraits.h + asmjit/core/archcommons.h asmjit/core/assembler.cpp asmjit/core/assembler.h asmjit/core/builder.cpp asmjit/core/builder.h - asmjit/core/callconv.cpp - asmjit/core/callconv.h asmjit/core/codebuffer.h - asmjit/core/codebufferwriter_p.h asmjit/core/codeholder.cpp asmjit/core/codeholder.h + asmjit/core/codewriter.cpp + asmjit/core/codewriter_p.h asmjit/core/compiler.cpp asmjit/core/compiler.h + asmjit/core/compilerdefs.h asmjit/core/constpool.cpp asmjit/core/constpool.h asmjit/core/cpuinfo.cpp asmjit/core/cpuinfo.h asmjit/core/datatypes.h + asmjit/core/emithelper.cpp + asmjit/core/emithelper_p.h asmjit/core/emitter.cpp asmjit/core/emitter.h asmjit/core/emitterutils.cpp @@ -323,6 +320,8 @@ set(ASMJIT_SRC_LIST asmjit/core/formatter.h asmjit/core/func.cpp asmjit/core/func.h + asmjit/core/funcargscontext.cpp + asmjit/core/funcargscontext_p.h asmjit/core/globals.cpp asmjit/core/globals.h asmjit/core/inst.cpp @@ -372,24 +371,23 @@ set(ASMJIT_SRC_LIST asmjit/core/zonevector.h asmjit/x86.h - asmjit/x86/x86archdata.cpp - asmjit/x86/x86archdata_p.h + asmjit/x86/x86archtraits_p.h asmjit/x86/x86assembler.cpp asmjit/x86/x86assembler.h asmjit/x86/x86builder.cpp asmjit/x86/x86builder.h - asmjit/x86/x86callconv.cpp - asmjit/x86/x86callconv_p.h asmjit/x86/x86compiler.cpp asmjit/x86/x86compiler.h + asmjit/x86/x86emithelper.cpp + asmjit/x86/x86emithelper_p.h asmjit/x86/x86emitter.h asmjit/x86/x86features.cpp asmjit/x86/x86features.h asmjit/x86/x86formatter.cpp asmjit/x86/x86formatter_p.h + asmjit/x86/x86func.cpp + asmjit/x86/x86func_p.h asmjit/x86/x86globals.h - asmjit/x86/x86internal.cpp - asmjit/x86/x86internal_p.h asmjit/x86/x86instdb.cpp asmjit/x86/x86instdb.h asmjit/x86/x86instdb_p.h @@ -518,15 +516,17 @@ if (NOT ASMJIT_EMBED) endif() if (NOT (ASMJIT_NO_BUILDER OR ASMJIT_NO_COMPILER)) - # Vectorcall tests and XMM tests require at least SSE2 (required in 32-bit mode). + # Vectorcall tests and XMM tests require at least SSE2 in 32-bit mode (in 64-bit mode it's implicit). set(sse2_flags "") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC") asmjit_detect_cflags(sse2_flags "-arch:SSE2") else() asmjit_detect_cflags(sse2_flags "-msse2") endif() - asmjit_add_target(asmjit_test_x86_cc TEST - SOURCES test/asmjit_test_x86_cc.cpp + asmjit_add_target(asmjit_test_compiler TEST + SOURCES test/asmjit_test_compiler.cpp + test/asmjit_test_compiler_x86.cpp + test/asmjit_test_compiler.h LIBRARIES asmjit::asmjit CFLAGS ${ASMJIT_PRIVATE_CFLAGS} ${sse2_flags} CFLAGS_DBG ${ASMJIT_PRIVATE_CFLAGS_DBG} diff --git a/src/asmjit/core.h b/src/asmjit/core.h index b83b152..216d015 100644 --- a/src/asmjit/core.h +++ b/src/asmjit/core.h @@ -2007,10 +2007,9 @@ namespace asmjit { #include "./core/globals.h" -#include "./core/arch.h" +#include "./core/archtraits.h" #include "./core/assembler.h" #include "./core/builder.h" -#include "./core/callconv.h" #include "./core/codeholder.h" #include "./core/compiler.h" #include "./core/constpool.h" diff --git a/src/asmjit/core/api-config.h b/src/asmjit/core/api-config.h index a133584..aab3473 100644 --- a/src/asmjit/core/api-config.h +++ b/src/asmjit/core/api-config.h @@ -68,9 +68,6 @@ namespace asmjit { //! Defined to build X86/X64 backend. #define ASMJIT_BUILD_X86 -//! Defined to build ARM/AArch64 backend. -#define ASMJIT_BUILD_ARM - //! Defined to build host backend autodetected at compile-time. #define ASMJIT_BUILD_HOST @@ -102,7 +99,6 @@ namespace asmjit { #define ASMJIT_NO_INTROSPECTION // Avoid doxygen preprocessor using feature-selection definitions. -#undef ASMJIT_NO_DEPRECATED #undef ASMJIT_NO_BUILDER #undef ASMJIT_NO_COMPILER #undef ASMJIT_NO_JIT @@ -116,6 +112,13 @@ namespace asmjit { } // {asmjit} #endif // _DOXYGEN +// Enable all features at IDE level, so it's properly highlighted and indexed. +#ifdef __INTELLISENSE__ + #ifndef ASMJIT_BUILD_X86 + #define ASMJIT_BUILD_X86 + #endif +#endif + // ============================================================================ // [asmjit::Dependencies] // ============================================================================ @@ -253,10 +256,6 @@ namespace asmjit { #if ASMJIT_ARCH_X86 && !defined(ASMJIT_BUILD_X86) #define ASMJIT_BUILD_X86 #endif - - #if ASMJIT_ARCH_ARM && !defined(ASMJIT_BUILD_ARM) - // #define ASMJIT_BUILD_ARM - #endif #endif // Define 'ASMJIT_BUILD_HOST' if we know that host architecture will be built. @@ -264,10 +263,6 @@ namespace asmjit { #define ASMJIT_BUILD_HOST #endif -#if !defined(ASMJIT_BUILD_HOST) && ASMJIT_ARCH_ARM && defined(ASMJIT_BUILD_ARM) - #define ASMJIT_BUILD_HOST -#endif - // ============================================================================ // [asmjit::Build - Globals - C++ Compiler and Features Detection] // ============================================================================ diff --git a/src/asmjit/core/arch.cpp b/src/asmjit/core/arch.cpp deleted file mode 100644 index 7200897..0000000 --- a/src/asmjit/core/arch.cpp +++ /dev/null @@ -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 diff --git a/src/asmjit/core/arch.h b/src/asmjit/core/arch.h deleted file mode 100644 index 4991a33..0000000 --- a/src/asmjit/core/arch.h +++ /dev/null @@ -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 diff --git a/src/asmjit/core/archcommons.h b/src/asmjit/core/archcommons.h new file mode 100644 index 0000000..fda2451 --- /dev/null +++ b/src/asmjit/core/archcommons.h @@ -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 diff --git a/src/asmjit/core/archtraits.cpp b/src/asmjit/core/archtraits.cpp new file mode 100644 index 0000000..f069354 --- /dev/null +++ b/src/asmjit/core/archtraits.cpp @@ -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 diff --git a/src/asmjit/core/archtraits.h b/src/asmjit/core/archtraits.h new file mode 100644 index 0000000..5af6c7e --- /dev/null +++ b/src/asmjit/core/archtraits.h @@ -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 diff --git a/src/asmjit/core/assembler.cpp b/src/asmjit/core/assembler.cpp index 08ca973..4cf706f 100644 --- a/src/asmjit/core/assembler.cpp +++ b/src/asmjit/core/assembler.cpp @@ -23,7 +23,7 @@ #include "../core/api-build_p.h" #include "../core/assembler.h" -#include "../core/codebufferwriter_p.h" +#include "../core/codewriter_p.h" #include "../core/constpool.h" #include "../core/emitterutils_p.h" #include "../core/formatter.h" @@ -37,11 +37,8 @@ ASMJIT_BEGIN_NAMESPACE // ============================================================================ BaseAssembler::BaseAssembler() noexcept - : BaseEmitter(kTypeAssembler), - _section(nullptr), - _bufferData(nullptr), - _bufferEnd(nullptr), - _bufferPtr(nullptr) {} + : BaseEmitter(kTypeAssembler) {} + BaseAssembler::~BaseAssembler() noexcept {} // ============================================================================ @@ -161,7 +158,7 @@ Error BaseAssembler::embed(const void* data, size_t dataSize) { if (dataSize == 0) return kErrorOk; - CodeBufferWriter writer(this); + CodeWriter writer(this); ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize)); writer.emitData(data, dataSize); @@ -194,7 +191,7 @@ Error BaseAssembler::embedDataArray(uint32_t typeId, const void* data, size_t it if (ASMJIT_UNLIKELY(of)) return reportError(DebugUtils::errored(kErrorOutOfMemory)); - CodeBufferWriter writer(this); + CodeWriter writer(this); ASMJIT_PROPAGATE(writer.ensureSpace(this, totalSize)); #ifndef ASMJIT_NO_LOGGING @@ -225,7 +222,7 @@ Error BaseAssembler::embedConstPool(const Label& label, const ConstPool& pool) { ASMJIT_PROPAGATE(bind(label)); size_t size = pool.size(); - CodeBufferWriter writer(this); + CodeWriter writer(this); ASMJIT_PROPAGATE(writer.ensureSpace(this, size)); pool.fill(writer.cursor()); @@ -258,7 +255,7 @@ Error BaseAssembler::embedLabel(const Label& label, size_t dataSize) { if (ASMJIT_UNLIKELY(!Support::isPowerOf2(dataSize) || dataSize > 8)) return reportError(DebugUtils::errored(kErrorInvalidOperandSize)); - CodeBufferWriter writer(this); + CodeWriter writer(this); ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize)); #ifndef ASMJIT_NO_LOGGING @@ -271,21 +268,26 @@ Error BaseAssembler::embedLabel(const Label& label, size_t dataSize) { } #endif - Error err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, uint32_t(dataSize)); + Error err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs); if (ASMJIT_UNLIKELY(err)) return reportError(err); re->_sourceSectionId = _section->id(); re->_sourceOffset = offset(); + re->_format.resetToDataValue(uint32_t(dataSize)); if (le->isBound()) { re->_targetSectionId = le->section()->id(); re->_payload = le->offset(); } else { - LabelLink* link = _code->newLabelLink(le, _section->id(), offset(), 0); + OffsetFormat of; + of.resetToDataValue(uint32_t(dataSize)); + + LabelLink* link = _code->newLabelLink(le, _section->id(), offset(), 0, of); if (ASMJIT_UNLIKELY(!link)) return reportError(DebugUtils::errored(kErrorOutOfMemory)); + link->relocId = re->id(); } @@ -312,7 +314,7 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, size if (ASMJIT_UNLIKELY(!Support::isPowerOf2(dataSize) || dataSize > 8)) return reportError(DebugUtils::errored(kErrorInvalidOperandSize)); - CodeBufferWriter writer(this); + CodeWriter writer(this); ASMJIT_PROPAGATE(writer.ensureSpace(this, dataSize)); #ifndef ASMJIT_NO_LOGGING @@ -334,7 +336,7 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, size } else { RelocEntry* re; - Error err = _code->newRelocEntry(&re, RelocEntry::kTypeExpression, uint32_t(dataSize)); + Error err = _code->newRelocEntry(&re, RelocEntry::kTypeExpression); if (ASMJIT_UNLIKELY(err)) return reportError(err); @@ -347,6 +349,7 @@ Error BaseAssembler::embedLabelDelta(const Label& label, const Label& base, size exp->setValueAsLabel(0, labelEntry); exp->setValueAsLabel(1, baseEntry); + re->_format.resetToDataValue(dataSize); re->_sourceSectionId = _section->id(); re->_sourceOffset = offset(); re->_payload = (uint64_t)(uintptr_t)exp; diff --git a/src/asmjit/core/assembler.h b/src/asmjit/core/assembler.h index fb84207..6e38bc5 100644 --- a/src/asmjit/core/assembler.h +++ b/src/asmjit/core/assembler.h @@ -54,13 +54,13 @@ public: typedef BaseEmitter Base; //! Current section where the assembling happens. - Section* _section; + Section* _section = nullptr; //! Start of the CodeBuffer of the current section. - uint8_t* _bufferData; + uint8_t* _bufferData = nullptr; //! End (first invalid byte) of the current section. - uint8_t* _bufferEnd; + uint8_t* _bufferEnd = nullptr; //! Pointer in the CodeBuffer of the current section. - uint8_t* _bufferPtr; + uint8_t* _bufferPtr = nullptr; //! \name Construction & Destruction //! \{ diff --git a/src/asmjit/core/builder.cpp b/src/asmjit/core/builder.cpp index a582e96..ad89f1d 100644 --- a/src/asmjit/core/builder.cpp +++ b/src/asmjit/core/builder.cpp @@ -69,13 +69,7 @@ BaseBuilder::BaseBuilder() noexcept _codeZone(32768 - Zone::kBlockOverhead), _dataZone(16384 - Zone::kBlockOverhead), _passZone(65536 - Zone::kBlockOverhead), - _allocator(&_codeZone), - _passes(), - _labelNodes(), - _cursor(nullptr), - _firstNode(nullptr), - _lastNode(nullptr), - _nodeFlags(0) {} + _allocator(&_codeZone) {} BaseBuilder::~BaseBuilder() noexcept { BaseBuilder_deletePasses(this); @@ -918,8 +912,7 @@ Error BaseBuilder::onDetach(CodeHolder* code) noexcept { // ============================================================================ Pass::Pass(const char* name) noexcept - : _cb(nullptr), - _name(name) {} + : _name(name) {} Pass::~Pass() noexcept {} ASMJIT_END_NAMESPACE diff --git a/src/asmjit/core/builder.h b/src/asmjit/core/builder.h index d6d7d22..317bda1 100644 --- a/src/asmjit/core/builder.h +++ b/src/asmjit/core/builder.h @@ -97,23 +97,23 @@ public: ZoneAllocator _allocator; //! Array of `Pass` objects. - ZoneVector _passes; + ZoneVector _passes {}; //! Maps section indexes to `LabelNode` nodes. - ZoneVector _sectionNodes; + ZoneVector _sectionNodes {}; //! Maps label indexes to `LabelNode` nodes. - ZoneVector _labelNodes; + ZoneVector _labelNodes {}; //! Current node (cursor). - BaseNode* _cursor; + BaseNode* _cursor = nullptr; //! First node of the current section. - BaseNode* _firstNode; + BaseNode* _firstNode = nullptr; //! Last node of the current section. - BaseNode* _lastNode; + BaseNode* _lastNode = nullptr; //! Flags assigned to each new node. - uint32_t _nodeFlags; + uint32_t _nodeFlags = 0; //! The sections links are dirty (used internally). - bool _dirtySectionLinks; + bool _dirtySectionLinks = false; //! \name Construction & Destruction //! \{ @@ -1393,9 +1393,9 @@ public: ASMJIT_NONCOPYABLE(Pass) //! BaseBuilder this pass is assigned to. - BaseBuilder* _cb; + BaseBuilder* _cb = nullptr; //! Name of the pass. - const char* _name; + const char* _name = nullptr; //! \name Construction & Destruction //! \{ diff --git a/src/asmjit/core/callconv.cpp b/src/asmjit/core/callconv.cpp deleted file mode 100644 index 722dbcd..0000000 --- a/src/asmjit/core/callconv.cpp +++ /dev/null @@ -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 diff --git a/src/asmjit/core/callconv.h b/src/asmjit/core/callconv.h deleted file mode 100644 index 6e75540..0000000 --- a/src/asmjit/core/callconv.h +++ /dev/null @@ -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 diff --git a/src/asmjit/core/codeholder.cpp b/src/asmjit/core/codeholder.cpp index 4a94d98..3c4154e 100644 --- a/src/asmjit/core/codeholder.cpp +++ b/src/asmjit/core/codeholder.cpp @@ -23,6 +23,7 @@ #include "../core/api-build_p.h" #include "../core/assembler.h" +#include "../core/codewriter_p.h" #include "../core/logger.h" #include "../core/support.h" @@ -507,20 +508,7 @@ static uint32_t CodeHolder_hashNameAndGetSize(const char* name, size_t& nameSize return hashCode; } -static bool CodeHolder_writeDisplacement(void* dst, int64_t displacement, uint32_t displacementSize) { - if (displacementSize == 4 && Support::isInt32(displacement)) { - Support::writeI32uLE(dst, int32_t(displacement)); - return true; - } - else if (displacementSize == 1 && Support::isInt8(displacement)) { - Support::writeI8(dst, int8_t(displacement)); - return true; - } - - return false; -} - -LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept { +LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel, const OffsetFormat& format) noexcept { LabelLink* link = _allocator.allocT(); if (ASMJIT_UNLIKELY(!link)) return nullptr; @@ -531,6 +519,7 @@ LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t o link->relocId = Globals::kInvalidId; link->offset = offset; link->rel = rel; + link->format = format; _unresolvedLinkCount++; return link; @@ -577,9 +566,9 @@ Error CodeHolder::newNamedLabelEntry(LabelEntry** entryOut, const char* name, si break; case Label::kTypeGlobal: + case Label::kTypeExternal: if (ASMJIT_UNLIKELY(parentId != Globals::kInvalidId)) return DebugUtils::errored(kErrorNonLocalLabelCannotHaveParent); - break; default: @@ -656,18 +645,16 @@ ASMJIT_API Error CodeHolder::resolveUnresolvedLinks() noexcept { ASMJIT_ASSERT(linkOffset < buf.size()); // Calculate the offset relative to the start of the virtual base. - uint64_t fromOffset = Support::addOverflow(fromSection->offset(), linkOffset, &of); + Support::FastUInt8 localOF = of; + uint64_t fromOffset = Support::addOverflow(fromSection->offset(), linkOffset, &localOF); int64_t displacement = int64_t(toOffset - fromOffset + uint64_t(int64_t(link->rel))); - if (!of) { + if (!localOF) { ASMJIT_ASSERT(size_t(linkOffset) < buf.size()); - - // Size of the value we are going to patch. Only BYTE/DWORD is allowed. - uint32_t displacementSize = buf._data[linkOffset]; - ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= displacementSize); + ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= link->format.valueSize()); // Overwrite a real displacement in the CodeBuffer. - if (CodeHolder_writeDisplacement(buf._data + linkOffset, displacement, displacementSize)) { + if (CodeWriterUtils::writeOffset(buf._data + linkOffset, displacement, link->format)) { link.resolveAndNext(this); continue; } @@ -730,11 +717,10 @@ ASMJIT_API Error CodeHolder::bindLabel(const Label& label, uint32_t toSectionId, int64_t displacement = int64_t(toOffset - uint64_t(linkOffset) + uint64_t(int64_t(link->rel))); // Size of the value we are going to patch. Only BYTE/DWORD is allowed. - uint32_t displacementSize = buf._data[linkOffset]; - ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= displacementSize); + ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= link->format.regionSize()); // Overwrite a real displacement in the CodeBuffer. - if (!CodeHolder_writeDisplacement(buf._data + linkOffset, displacement, displacementSize)) { + if (!CodeWriterUtils::writeOffset(buf._data + linkOffset, displacement, link->format)) { err = DebugUtils::errored(kErrorInvalidDisplacement); link.next(); continue; @@ -751,7 +737,7 @@ ASMJIT_API Error CodeHolder::bindLabel(const Label& label, uint32_t toSectionId, // [asmjit::BaseEmitter - Relocations] // ============================================================================ -Error CodeHolder::newRelocEntry(RelocEntry** dst, uint32_t relocType, uint32_t valueSize) noexcept { +Error CodeHolder::newRelocEntry(RelocEntry** dst, uint32_t relocType) noexcept { ASMJIT_PROPAGATE(_relocations.willGrow(&_allocator)); uint32_t relocId = _relocations.size(); @@ -764,7 +750,6 @@ Error CodeHolder::newRelocEntry(RelocEntry** dst, uint32_t relocType, uint32_t v re->_id = relocId; re->_relocType = uint8_t(relocType); - re->_valueSize = uint8_t(valueSize); re->_sourceSectionId = Globals::kInvalidId; re->_targetSectionId = Globals::kInvalidId; _relocations.appendUnsafe(re); @@ -946,13 +931,13 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept { uint64_t sourceOffset = re->sourceOffset(); // Make sure that the `RelocEntry` doesn't go out of bounds. - size_t regionSize = re->leadingSize() + re->valueSize() + re->trailingSize(); + size_t regionSize = re->format().regionSize(); if (ASMJIT_UNLIKELY(re->sourceOffset() >= sourceSection->bufferSize() || sourceSection->bufferSize() - size_t(re->sourceOffset()) < regionSize)) return DebugUtils::errored(kErrorInvalidRelocEntry); uint8_t* buffer = sourceSection->data(); - size_t valueOffset = size_t(re->sourceOffset()) + re->leadingSize(); + size_t valueOffset = size_t(re->sourceOffset()) + re->format().valueOffset(); switch (re->relocType()) { case RelocEntry::kTypeExpression: { @@ -984,7 +969,7 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept { } case RelocEntry::kTypeX64AddressEntry: { - if (re->valueSize() != 4 || re->leadingSize() < 2) + if (re->format().valueSize() != 4 || valueOffset < 2) return DebugUtils::errored(kErrorInvalidRelocEntry); // First try whether a relative 32-bit displacement would work. @@ -1038,7 +1023,7 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept { return DebugUtils::errored(kErrorInvalidRelocEntry); } - switch (re->valueSize()) { + switch (re->format().valueSize()) { case 1: Support::writeU8(buffer + valueOffset, uint32_t(value & 0xFFu)); break; diff --git a/src/asmjit/core/codeholder.h b/src/asmjit/core/codeholder.h index f1719ac..06bf3f9 100644 --- a/src/asmjit/core/codeholder.h +++ b/src/asmjit/core/codeholder.h @@ -24,7 +24,7 @@ #ifndef ASMJIT_CORE_CODEHOLDER_H_INCLUDED #define ASMJIT_CORE_CODEHOLDER_H_INCLUDED -#include "../core/arch.h" +#include "../core/archtraits.h" #include "../core/codebuffer.h" #include "../core/datatypes.h" #include "../core/errorhandler.h" @@ -68,6 +68,83 @@ enum AlignMode : uint32_t { kAlignCount = 3 }; +// ============================================================================ +// [asmjit::Expression] +// ============================================================================ + +//! Expression node that can reference constants, labels, and another expressions. +struct Expression { + //! Operation type. + enum OpType : uint8_t { + //! Addition. + kOpAdd = 0, + //! Subtraction. + kOpSub = 1, + //! Multiplication + kOpMul = 2, + //! Logical left shift. + kOpSll = 3, + //! Logical right shift. + kOpSrl = 4, + //! Arithmetic right shift. + kOpSra = 5 + }; + + //! Type of \ref Value. + enum ValueType : uint8_t { + //! No value or invalid. + kValueNone = 0, + //! Value is 64-bit unsigned integer (constant). + kValueConstant = 1, + //! Value is \ref LabelEntry, which references a \ref Label. + kValueLabel = 2, + //! Value is \ref Expression + kValueExpression = 3 + }; + + //! Expression value. + union Value { + //! Constant. + uint64_t constant; + //! Pointer to another expression. + Expression* expression; + //! Poitner to \ref LabelEntry. + LabelEntry* label; + }; + + //! Operation type. + uint8_t opType; + //! Value types of \ref value. + uint8_t valueType[2]; + //! Reserved for future use, should be initialized to zero. + uint8_t reserved[5]; + //! Expression left and right values. + Value value[2]; + + //! Resets the whole expression. + //! + //! Changes both values to \ref kValueNone. + inline void reset() noexcept { memset(this, 0, sizeof(*this)); } + + //! Sets the value type at `index` to \ref kValueConstant and its content to `constant`. + inline void setValueAsConstant(size_t index, uint64_t constant) noexcept { + valueType[index] = kValueConstant; + value[index].constant = constant; + } + + //! Sets the value type at `index` to \ref kValueLabel and its content to `labelEntry`. + inline void setValueAsLabel(size_t index, LabelEntry* labelEntry) noexcept { + valueType[index] = kValueLabel; + value[index].label = labelEntry; + } + + //! Sets the value type at `index` to \ref kValueExpression and its content to `expression`. + inline void setValueAsExpression(size_t index, Expression* expression) noexcept { + valueType[index] = kValueLabel; + value[index].expression = expression; + } +}; + // ============================================================================ // [asmjit::Section] // ============================================================================ @@ -165,6 +242,203 @@ public: //! \} }; +// ============================================================================ +// [asmjit::OffsetFormat] +// ============================================================================ + +//! Provides information about formatting offsets, absolute addresses, or their +//! parts. Offset format is used by both \ref RelocEntry and \ref LabelLink. +//! +//! The illustration above describes the relation of region size and offset size. +//! Region size is the size of the whole unit whereas offset size is the size of +//! the unit that will be patched. +//! +//! ``` +//! +-> Code buffer | The subject of the relocation (region) | +//! | | (Word-Offset) (Word-Size) | +//! |xxxxxxxxxxxxxxx|................|*PATCHED*|................|xxxxxxxxxxxx-> +//! | | +//! [Word Offset points here]----+ +--- [WordOffset + WordSize] +//! ``` +//! +//! Once the offset word has been located it can be patched like this: +//! +//! ``` +//! |ImmDiscardLSB (discard LSB bits). +//! |.. +//! [0000000000000iiiiiiiiiiiiiiiiiDD] - Offset value (32-bit) +//! [000000000000000iiiiiiiiiiiiiiiii] - Offset value after discard LSB. +//! [00000000000iiiiiiiiiiiiiiiii0000] - Offset value shifted by ImmBitShift. +//! [xxxxxxxxxxxiiiiiiiiiiiiiiiiixxxx] - Patched word (32-bit) +//! |...............| +//! (ImmBitCount) +- ImmBitShift +//! ``` +struct OffsetFormat { + //! Type of the displacement. + uint8_t _type; + //! Encoding flags. + uint8_t _flags; + //! Size of the region (in bytes) containing the offset value, if the offset + //! value is part of an instruction, otherwise it would be the same as + //! `_valueSize`. + uint8_t _regionSize; + //! Size of the offset value, in bytes (1, 2, 4, or 8). + uint8_t _valueSize; + //! Offset of the offset value, in bytes, relative to the start of the region + //! or data. Value offset would be zero if both region size and value size are + //! equal. + uint8_t _valueOffset; + //! Size of the displacement immediate value in bits. + uint8_t _immBitCount; + //! Shift of the displacement immediate value in bits in the target word. + uint8_t _immBitShift; + //! Number of least significant bits to discard before writing the immediate + //! to the destination. All discarded bits must be zero otherwise the value + //! is invalid. + uint8_t _immDiscardLsb; + + //! Type of the displacement. + enum Type : uint8_t { + //! A value having `_immBitCount` bits and shifted by `_immBitShift`. + //! + //! This displacement type is sufficient for both X86/X64 and many other + //! architectures that store displacement as continuous bits within a machine + //! word. + kTypeCommon = 0, + //! AARCH64 ADR format of `[.|immlo:2|.....|immhi:19|.....]`. + kTypeAArch64_ADR, + //! AARCH64 ADRP format of `[.|immlo:2|.....|immhi:19|.....]` (4kB pages). + kTypeAArch64_ADRP, + + //! Count of displacement types. + kTypeCount + }; + + //! Returns the type of the displacement. + inline uint32_t type() const noexcept { return _type; } + + //! Returns flags. + inline uint32_t flags() const noexcept { return _flags; } + + //! Returns the size of the region/instruction where the displacement is encoded. + inline uint32_t regionSize() const noexcept { return _regionSize; } + + //! Returns the the offset of the word relative to the start of the region + //! where the displacement is. + inline uint32_t valueOffset() const noexcept { return _valueOffset; } + + //! Returns the size of the data-type (word) that contains the displacement, in bytes. + inline uint32_t valueSize() const noexcept { return _valueSize; } + //! Returns the count of bits of the displacement value in the data it's stored in. + inline uint32_t immBitCount() const noexcept { return _immBitCount; } + //! Returns the bit-shift of the displacement value in the data it's stored in. + inline uint32_t immBitShift() const noexcept { return _immBitShift; } + //! Returns the number of least significant bits of the displacement value, + //! that must be zero and that are not part of the encoded data. + inline uint32_t immDiscardLsb() const noexcept { return _immDiscardLsb; } + + //! Resets this offset format to a simple data value of `dataSize` bytes. + //! + //! The region will be the same size as data and immediate bits would correspond + //! to `dataSize * 8`. There will be no immediate bit shift or discarded bits. + inline void resetToDataValue(size_t dataSize) noexcept { + ASMJIT_ASSERT(dataSize <= 8u); + + _type = uint8_t(kTypeCommon); + _flags = uint8_t(0); + _regionSize = uint8_t(dataSize); + _valueSize = uint8_t(dataSize); + _valueOffset = uint8_t(0); + _immBitCount = uint8_t(dataSize * 8u); + _immBitShift = uint8_t(0); + _immDiscardLsb = uint8_t(0); + } + + inline void resetToImmValue(uint32_t type, size_t valueSize, uint32_t immBitShift, uint32_t immBitCount, uint32_t immDiscardLsb) noexcept { + ASMJIT_ASSERT(valueSize <= 8u); + ASMJIT_ASSERT(immBitShift < valueSize * 8u); + ASMJIT_ASSERT(immBitCount <= 64u); + ASMJIT_ASSERT(immDiscardLsb <= 64u); + + _type = uint8_t(type); + _flags = uint8_t(0); + _regionSize = uint8_t(valueSize); + _valueSize = uint8_t(valueSize); + _valueOffset = uint8_t(0); + _immBitCount = uint8_t(immBitCount); + _immBitShift = uint8_t(immBitShift); + _immDiscardLsb = uint8_t(immDiscardLsb); + } + + inline void setRegion(size_t regionSize, size_t valueOffset) noexcept { + _regionSize = uint8_t(regionSize); + _valueOffset = uint8_t(valueOffset); + } + + inline void setLeadingAndTrailingSize(size_t leadingSize, size_t trailingSize) noexcept { + _regionSize = uint8_t(leadingSize + trailingSize + _valueSize); + _valueOffset = uint8_t(leadingSize); + } +}; + +// ============================================================================ +// [asmjit::RelocEntry] +// ============================================================================ + +//! Relocation entry. +struct RelocEntry { + //! Relocation id. + uint32_t _id; + //! Type of the relocation. + uint32_t _relocType; + //! Format of the relocated value. + OffsetFormat _format; + //! Source section id. + uint32_t _sourceSectionId; + //! Target section id. + uint32_t _targetSectionId; + //! Source offset (relative to start of the section). + uint64_t _sourceOffset; + //! Payload (target offset, target address, expression, etc). + uint64_t _payload; + + //! Relocation type. + enum RelocType : uint32_t { + //! None/deleted (no relocation). + kTypeNone = 0, + //! Expression evaluation, `_payload` is pointer to `Expression`. + kTypeExpression = 1, + //! Relocate absolute to absolute. + kTypeAbsToAbs = 2, + //! Relocate relative to absolute. + kTypeRelToAbs = 3, + //! Relocate absolute to relative. + kTypeAbsToRel = 4, + //! Relocate absolute to relative or use trampoline. + kTypeX64AddressEntry = 5 + }; + + //! \name Accessors + //! \{ + + inline uint32_t id() const noexcept { return _id; } + + inline uint32_t relocType() const noexcept { return _relocType; } + inline const OffsetFormat& format() const noexcept { return _format; } + + inline uint32_t sourceSectionId() const noexcept { return _sourceSectionId; } + inline uint32_t targetSectionId() const noexcept { return _targetSectionId; } + + inline uint64_t sourceOffset() const noexcept { return _sourceOffset; } + inline uint64_t payload() const noexcept { return _payload; } + + Expression* payloadAsExpression() const noexcept { + return reinterpret_cast(uintptr_t(_payload)); + } + + //! \} +}; + // ============================================================================ // [asmjit::LabelLink] // ============================================================================ @@ -181,83 +455,8 @@ struct LabelLink { size_t offset; //! Inlined rel8/rel32. intptr_t rel; -}; - -// ============================================================================ -// [asmjit::Expression] -// ============================================================================ - -//! Expression node that can reference constants, labels, and another expressions. -struct Expression { - //! Operation type. - enum OpType : uint8_t { - //! Addition. - kOpAdd = 0, - //! Subtraction. - kOpSub = 1, - //! Multiplication - kOpMul = 2, - //! Logical left shift. - kOpSll = 3, - //! Logical right shift. - kOpSrl = 4, - //! Arithmetic right shift. - kOpSra = 5 - }; - - //! Type of \ref Value. - enum ValueType : uint8_t { - //! No value or invalid. - kValueNone = 0, - //! Value is 64-bit unsigned integer (constant). - kValueConstant = 1, - //! Value is \ref LabelEntry, which references a \ref Label. - kValueLabel = 2, - //! Value is \ref Expression - kValueExpression = 3 - }; - - //! Expression value. - union Value { - //! Constant. - uint64_t constant; - //! Pointer to another expression. - Expression* expression; - //! Poitner to \ref LabelEntry. - LabelEntry* label; - }; - - //! Operation type. - uint8_t opType; - //! Value types of \ref value. - uint8_t valueType[2]; - //! Reserved for future use, should be initialized to zero. - uint8_t reserved[5]; - //! Expression left and right values. - Value value[2]; - - //! Resets the whole expression. - //! - //! Changes both values to \ref kValueNone. - inline void reset() noexcept { memset(this, 0, sizeof(*this)); } - - //! Sets the value type at `index` to \ref kValueConstant and its content to `constant`. - inline void setValueAsConstant(size_t index, uint64_t constant) noexcept { - valueType[index] = kValueConstant; - value[index].constant = constant; - } - - //! Sets the value type at `index` to \ref kValueLabel and its content to `labelEntry`. - inline void setValueAsLabel(size_t index, LabelEntry* labelEntry) noexcept { - valueType[index] = kValueLabel; - value[index].label = labelEntry; - } - - //! Sets the value type at `index` to \ref kValueExpression and its content to `expression`. - inline void setValueAsExpression(size_t index, Expression* expression) noexcept { - valueType[index] = kValueLabel; - value[index].expression = expression; - } + //! Offset format information. + OffsetFormat format; }; // ============================================================================ @@ -369,82 +568,6 @@ public: //! \} }; -// ============================================================================ -// [asmjit::RelocEntry] -// ============================================================================ - -//! Relocation entry. -//! -//! We describe relocation data in the following way: -//! -//! ``` -//! +- Start of the buffer +- End of the data -//! | |*PATCHED*| | or instruction -//! |xxxxxxxxxxxxxxxxxxxxxx|LeadSize|ValueSize|TrailSize|xxxxxxxxxxxxxxxxxxxx-> -//! | -//! +- Source offset -//! ``` -struct RelocEntry { - //! Relocation id. - uint32_t _id; - //! Type of the relocation. - uint8_t _relocType; - //! Size of the relocation data/value (1, 2, 4 or 8 bytes). - uint8_t _valueSize; - //! Number of bytes after `_sourceOffset` to reach the value to be patched. - uint8_t _leadingSize; - //! Number of bytes after `_sourceOffset + _valueSize` to reach end of the - //! instruction. - uint8_t _trailingSize; - //! Source section id. - uint32_t _sourceSectionId; - //! Target section id. - uint32_t _targetSectionId; - //! Source offset (relative to start of the section). - uint64_t _sourceOffset; - //! Payload (target offset, target address, expression, etc). - uint64_t _payload; - - //! Relocation type. - enum RelocType : uint32_t { - //! None/deleted (no relocation). - kTypeNone = 0, - //! Expression evaluation, `_payload` is pointer to `Expression`. - kTypeExpression = 1, - //! Relocate absolute to absolute. - kTypeAbsToAbs = 2, - //! Relocate relative to absolute. - kTypeRelToAbs = 3, - //! Relocate absolute to relative. - kTypeAbsToRel = 4, - //! Relocate absolute to relative or use trampoline. - kTypeX64AddressEntry = 5 - }; - - //! \name Accessors - //! \{ - - inline uint32_t id() const noexcept { return _id; } - - inline uint32_t relocType() const noexcept { return _relocType; } - inline uint32_t valueSize() const noexcept { return _valueSize; } - - inline uint32_t leadingSize() const noexcept { return _leadingSize; } - inline uint32_t trailingSize() const noexcept { return _trailingSize; } - - inline uint32_t sourceSectionId() const noexcept { return _sourceSectionId; } - inline uint32_t targetSectionId() const noexcept { return _targetSectionId; } - - inline uint64_t sourceOffset() const noexcept { return _sourceOffset; } - inline uint64_t payload() const noexcept { return _payload; } - - Expression* payloadAsExpression() const noexcept { - return reinterpret_cast(uintptr_t(_payload)); - } - - //! \} -}; - // ============================================================================ // [asmjit::AddressTableEntry] // ============================================================================ @@ -843,7 +966,7 @@ public: //! Creates a new label-link used to store information about yet unbound labels. //! //! Returns `null` if the allocation failed. - ASMJIT_API LabelLink* newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept; + ASMJIT_API LabelLink* newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel, const OffsetFormat& format) noexcept; //! Resolves cross-section links (`LabelLink`) associated with each label that //! was used as a destination in code of a different section. It's only useful @@ -869,10 +992,10 @@ public: //! Returns a RelocEntry of the given `id`. inline RelocEntry* relocEntry(uint32_t id) const noexcept { return _relocations[id]; } - //! Creates a new relocation entry of type `relocType` and size `valueSize`. + //! Creates a new relocation entry of type `relocType`. //! //! Additional fields can be set after the relocation entry was created. - ASMJIT_API Error newRelocEntry(RelocEntry** dst, uint32_t relocType, uint32_t valueSize) noexcept; + ASMJIT_API Error newRelocEntry(RelocEntry** dst, uint32_t relocType) noexcept; //! \} diff --git a/src/asmjit/core/codewriter.cpp b/src/asmjit/core/codewriter.cpp new file mode 100644 index 0000000..6097c0e --- /dev/null +++ b/src/asmjit/core/codewriter.cpp @@ -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(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(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(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(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(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(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 diff --git a/src/asmjit/core/codebufferwriter_p.h b/src/asmjit/core/codewriter_p.h similarity index 85% rename from src/asmjit/core/codebufferwriter_p.h rename to src/asmjit/core/codewriter_p.h index 75ee047..61c9101 100644 --- a/src/asmjit/core/codebufferwriter_p.h +++ b/src/asmjit/core/codewriter_p.h @@ -35,15 +35,21 @@ ASMJIT_BEGIN_NAMESPACE //! \{ // ============================================================================ -// [asmjit::CodeBufferWriter] +// [Forward Declarations] // ============================================================================ -//! Helper that is used to write into a `CodeBuffer` held by `BaseAssembler`. -class CodeBufferWriter { +struct OffsetFormat; + +// ============================================================================ +// [asmjit::CodeWriter] +// ============================================================================ + +//! Helper that is used to write into a \ref CodeBuffer held by \ref BaseAssembler. +class CodeWriter { public: uint8_t* _cursor; - ASMJIT_INLINE explicit CodeBufferWriter(BaseAssembler* a) noexcept + ASMJIT_INLINE explicit CodeWriter(BaseAssembler* a) noexcept : _cursor(a->_bufferPtr) {} ASMJIT_INLINE Error ensureSpace(BaseAssembler* a, size_t n) noexcept { @@ -181,6 +187,19 @@ public: } }; +// ============================================================================ +// [asmjit::CodeWriterUtils] +// ============================================================================ + +namespace CodeWriterUtils { + +bool encodeOffset32(uint32_t* dst, int64_t offset64, const OffsetFormat& format) noexcept; +bool encodeOffset64(uint64_t* dst, int64_t offset64, const OffsetFormat& format) noexcept; + +bool writeOffset(void* dst, int64_t offset64, const OffsetFormat& format) noexcept; + +} // {CodeWriterUtils} + //! \} //! \endcond diff --git a/src/asmjit/core/compiler.cpp b/src/asmjit/core/compiler.cpp index 12b0b15..3880986 100644 --- a/src/asmjit/core/compiler.cpp +++ b/src/asmjit/core/compiler.cpp @@ -282,11 +282,10 @@ Error BaseCompiler::newVirtReg(VirtReg** out, uint32_t typeId, uint32_t signatur } Error BaseCompiler::_newReg(BaseReg* out, uint32_t typeId, const char* name) { + RegInfo regInfo; out->reset(); - RegInfo regInfo; Error err = ArchUtils::typeIdToRegInfo(arch(), typeId, &typeId, ®Info); - if (ASMJIT_UNLIKELY(err)) return reportError(err); @@ -569,6 +568,10 @@ JumpAnnotation* BaseCompiler::newJumpAnnotation() { Error BaseCompiler::onAttach(CodeHolder* code) noexcept { ASMJIT_PROPAGATE(Base::onAttach(code)); + const ArchTraits& archTraits = ArchTraits::byArch(code->arch()); + uint32_t nativeRegType = Environment::is32Bit(code->arch()) ? BaseReg::kTypeGp32 : BaseReg::kTypeGp64; + _gpRegInfo.setSignature(archTraits.regTypeToSignature(nativeRegType)); + Error err = addPassT(); if (ASMJIT_UNLIKELY(err)) { onDetach(code); diff --git a/src/asmjit/core/compiler.h b/src/asmjit/core/compiler.h index f71a28f..eb2a5aa 100644 --- a/src/asmjit/core/compiler.h +++ b/src/asmjit/core/compiler.h @@ -30,6 +30,7 @@ #include "../core/assembler.h" #include "../core/builder.h" #include "../core/constpool.h" +#include "../core/compilerdefs.h" #include "../core/func.h" #include "../core/inst.h" #include "../core/operand.h" @@ -43,11 +44,7 @@ ASMJIT_BEGIN_NAMESPACE // [Forward Declarations] // ============================================================================ -struct RATiedReg; -class RAWorkReg; - class JumpAnnotation; - class JumpNode; class FuncNode; class FuncRetNode; @@ -56,131 +53,6 @@ class InvokeNode; //! \addtogroup asmjit_compiler //! \{ -// ============================================================================ -// [asmjit::VirtReg] -// ============================================================================ - -//! Virtual register data, managed by \ref BaseCompiler. -class VirtReg { -public: - ASMJIT_NONCOPYABLE(VirtReg) - - //! Virtual register id. - uint32_t _id; - //! Virtual register info (signature). - RegInfo _info; - //! Virtual register size (can be smaller than `regInfo._size`). - uint32_t _virtSize; - //! Virtual register alignment (for spilling). - uint8_t _alignment; - //! Type-id. - uint8_t _typeId; - //! Virtual register weight for alloc/spill decisions. - uint8_t _weight; - //! True if this is a fixed register, never reallocated. - uint8_t _isFixed : 1; - //! True if the virtual register is only used as a stack (never accessed as register). - uint8_t _isStack : 1; - uint8_t _reserved : 6; - - //! Virtual register name (user provided or automatically generated). - ZoneString<16> _name; - - // ------------------------------------------------------------------------- - // The following members are used exclusively by RAPass. They are initialized - // when the VirtReg is created to NULL pointers and then changed during RAPass - // execution. RAPass sets them back to NULL before it returns. - // ------------------------------------------------------------------------- - - //! Reference to `RAWorkReg`, used during register allocation. - RAWorkReg* _workReg; - - //! \name Construction & Destruction - //! \{ - - inline VirtReg(uint32_t id, uint32_t signature, uint32_t virtSize, uint32_t alignment, uint32_t typeId) noexcept - : _id(id), - _virtSize(virtSize), - _alignment(uint8_t(alignment)), - _typeId(uint8_t(typeId)), - _weight(1), - _isFixed(false), - _isStack(false), - _reserved(0), - _name(), - _workReg(nullptr) { _info._signature = signature; } - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the virtual register id. - inline uint32_t id() const noexcept { return _id; } - - //! Returns the virtual register name. - inline const char* name() const noexcept { return _name.data(); } - //! Returns the size of the virtual register name. - inline uint32_t nameSize() const noexcept { return _name.size(); } - - //! Returns a register information that wraps the register signature. - inline const RegInfo& info() const noexcept { return _info; } - //! Returns a virtual register type (maps to the physical register type as well). - inline uint32_t type() const noexcept { return _info.type(); } - //! Returns a virtual register group (maps to the physical register group as well). - inline uint32_t group() const noexcept { return _info.group(); } - - //! Returns a real size of the register this virtual register maps to. - //! - //! For example if this is a 128-bit SIMD register used for a scalar single - //! precision floating point value then its virtSize would be 4, however, the - //! `regSize` would still say 16 (128-bits), because it's the smallest size - //! of that register type. - inline uint32_t regSize() const noexcept { return _info.size(); } - - //! Returns a register signature of this virtual register. - inline uint32_t signature() const noexcept { return _info.signature(); } - - //! Returns the virtual register size. - //! - //! The virtual register size describes how many bytes the virtual register - //! needs to store its content. It can be smaller than the physical register - //! size, see `regSize()`. - inline uint32_t virtSize() const noexcept { return _virtSize; } - - //! Returns the virtual register alignment. - inline uint32_t alignment() const noexcept { return _alignment; } - - //! Returns the virtual register type id, see `Type::Id`. - inline uint32_t typeId() const noexcept { return _typeId; } - - //! Returns the virtual register weight - the register allocator can use it - //! as explicit hint for alloc/spill decisions. - inline uint32_t weight() const noexcept { return _weight; } - //! Sets the virtual register weight (0 to 255) - the register allocator can - //! use it as explicit hint for alloc/spill decisions and initial bin-packing. - inline void setWeight(uint32_t weight) noexcept { _weight = uint8_t(weight); } - - //! Returns whether the virtual register is always allocated to a fixed - //! physical register (and never reallocated). - //! - //! \note This is only used for special purposes and it's mostly internal. - inline bool isFixed() const noexcept { return bool(_isFixed); } - - //! Returns whether the virtual register is indeed a stack that only uses - //! the virtual register id for making it accessible. - //! - //! \note It's an error if a stack is accessed as a register. - inline bool isStack() const noexcept { return bool(_isStack); } - - inline bool hasWorkReg() const noexcept { return _workReg != nullptr; } - inline RAWorkReg* workReg() const noexcept { return _workReg; } - inline void setWorkReg(RAWorkReg* workReg) noexcept { _workReg = workReg; } - inline void resetWorkReg() noexcept { _workReg = nullptr; } - - //! \} -}; - // ============================================================================ // [asmjit::BaseCompiler] // ============================================================================ diff --git a/src/asmjit/core/compilerdefs.h b/src/asmjit/core/compilerdefs.h new file mode 100644 index 0000000..32f0757 --- /dev/null +++ b/src/asmjit/core/compilerdefs.h @@ -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 + diff --git a/src/asmjit/core/cpuinfo.h b/src/asmjit/core/cpuinfo.h index ee2c9e5..83bb8c1 100644 --- a/src/asmjit/core/cpuinfo.h +++ b/src/asmjit/core/cpuinfo.h @@ -24,7 +24,7 @@ #ifndef ASMJIT_CORE_CPUINFO_H_INCLUDED #define ASMJIT_CORE_CPUINFO_H_INCLUDED -#include "../core/arch.h" +#include "../core/archtraits.h" #include "../core/features.h" #include "../core/globals.h" #include "../core/string.h" diff --git a/src/asmjit/core/emithelper.cpp b/src/asmjit/core/emithelper.cpp new file mode 100644 index 0000000..a77211e --- /dev/null +++ b/src/asmjit/core/emithelper.cpp @@ -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(""); + + 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(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 diff --git a/src/asmjit/core/emithelper_p.h b/src/asmjit/core/emithelper_p.h new file mode 100644 index 0000000..cb8ddf0 --- /dev/null +++ b/src/asmjit/core/emithelper_p.h @@ -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 diff --git a/src/asmjit/core/emitter.cpp b/src/asmjit/core/emitter.cpp index b6ecc3d..23ac3b3 100644 --- a/src/asmjit/core/emitter.cpp +++ b/src/asmjit/core/emitter.cpp @@ -28,13 +28,13 @@ #include "../core/support.h" #ifdef ASMJIT_BUILD_X86 - #include "../x86/x86internal_p.h" + #include "../x86/x86emithelper_p.h" #include "../x86/x86instdb_p.h" #endif // ASMJIT_BUILD_X86 #ifdef ASMJIT_BUILD_ARM - #include "../arm/arminternal_p.h" - #include "../arm/arminstdb.h" + #include "../arm/a64emithelper_p.h" + #include "../arm/a64instdb.h" #endif // ASMJIT_BUILD_ARM ASMJIT_BEGIN_NAMESPACE @@ -44,21 +44,7 @@ ASMJIT_BEGIN_NAMESPACE // ============================================================================ BaseEmitter::BaseEmitter(uint32_t emitterType) noexcept - : _emitterType(uint8_t(emitterType)), - _emitterFlags(0), - _validationFlags(0), - _validationOptions(0), - _encodingOptions(0), - _forcedInstOptions(BaseInst::kOptionReserved), - _privateData(0), - _code(nullptr), - _logger(nullptr), - _errorHandler(nullptr), - _environment(), - _gpRegInfo(), - _instOptions(0), - _extraReg(), - _inlineComment(nullptr) {} + : _emitterType(uint8_t(emitterType)) {} BaseEmitter::~BaseEmitter() noexcept { if (_code) { @@ -257,13 +243,17 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitProlog(const FuncFrame& frame) { return DebugUtils::errored(kErrorNotInitialized); #ifdef ASMJIT_BUILD_X86 - if (environment().isFamilyX86()) - return x86::X86Internal::emitProlog(as(), frame); + if (environment().isFamilyX86()) { + x86::EmitHelper emitHelper(this, frame.isAvxEnabled()); + return emitHelper.emitProlog(frame); + } #endif #ifdef ASMJIT_BUILD_ARM - if (environment().isFamilyARM()) - return arm::ArmInternal::emitProlog(as(), frame); + if (environment().isArchAArch64()) { + a64::EmitHelper emitHelper(this); + return emitHelper.emitProlog(frame); + } #endif return DebugUtils::errored(kErrorInvalidArch); @@ -274,13 +264,17 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitEpilog(const FuncFrame& frame) { return DebugUtils::errored(kErrorNotInitialized); #ifdef ASMJIT_BUILD_X86 - if (environment().isFamilyX86()) - return x86::X86Internal::emitEpilog(as(), frame); + if (environment().isFamilyX86()) { + x86::EmitHelper emitHelper(this, frame.isAvxEnabled()); + return emitHelper.emitEpilog(frame); + } #endif #ifdef ASMJIT_BUILD_ARM - if (environment().isFamilyARM()) - return arm::ArmInternal::emitEpilog(as(), frame); + if (environment().isArchAArch64()) { + a64::EmitHelper emitHelper(this); + return emitHelper.emitEpilog(frame); + } #endif return DebugUtils::errored(kErrorInvalidArch); @@ -291,13 +285,17 @@ ASMJIT_FAVOR_SIZE Error BaseEmitter::emitArgsAssignment(const FuncFrame& frame, return DebugUtils::errored(kErrorNotInitialized); #ifdef ASMJIT_BUILD_X86 - if (environment().isFamilyX86()) - return x86::X86Internal::emitArgsAssignment(as(), frame, args); + if (environment().isFamilyX86()) { + x86::EmitHelper emitHelper(this, frame.isAvxEnabled()); + return emitHelper.emitArgsAssignment(frame, args); + } #endif #ifdef ASMJIT_BUILD_ARM - if (environment().isFamilyARM()) - return arm::ArmInternal::emitArgsAssignment(as(), frame, args); + if (environment().isArchAArch64()) { + a64::EmitHelper emitHelper(this); + return emitHelper.emitArgsAssignment(frame, args); + } #endif return DebugUtils::errored(kErrorInvalidArch); @@ -349,6 +347,10 @@ Error BaseEmitter::onAttach(CodeHolder* code) noexcept { _code = code; _environment = code->environment(); + const ArchTraits& archTraits = ArchTraits::byArch(code->arch()); + uint32_t nativeRegType = Environment::is32Bit(code->arch()) ? BaseReg::kTypeGp32 : BaseReg::kTypeGp64; + _gpRegInfo.setSignature(archTraits._regInfo[nativeRegType].signature()); + onSettingsUpdated(); return kErrorOk; } diff --git a/src/asmjit/core/emitter.h b/src/asmjit/core/emitter.h index 6da602e..51651fd 100644 --- a/src/asmjit/core/emitter.h +++ b/src/asmjit/core/emitter.h @@ -24,7 +24,7 @@ #ifndef ASMJIT_CORE_EMITTER_H_INCLUDED #define ASMJIT_CORE_EMITTER_H_INCLUDED -#include "../core/arch.h" +#include "../core/archtraits.h" #include "../core/codeholder.h" #include "../core/inst.h" #include "../core/operand.h" @@ -54,43 +54,43 @@ public: ASMJIT_BASE_CLASS(BaseEmitter) //! See \ref EmitterType. - uint8_t _emitterType; + uint8_t _emitterType = 0; //! See \ref BaseEmitter::EmitterFlags. - uint8_t _emitterFlags; + uint8_t _emitterFlags = 0; //! Validation flags in case validation is used, see \ref InstAPI::ValidationFlags. //! //! \note Validation flags are specific to the emitter and they are setup at //! construction time and then never changed. - uint8_t _validationFlags; + uint8_t _validationFlags = 0; //! Validation options, see \ref ValidationOptions. - uint8_t _validationOptions; + uint8_t _validationOptions = 0; //! Encoding options, see \ref EncodingOptions. - uint32_t _encodingOptions; + uint32_t _encodingOptions = 0; //! Forced instruction options, combined with \ref _instOptions by \ref emit(). - uint32_t _forcedInstOptions; + uint32_t _forcedInstOptions = BaseInst::kOptionReserved; //! Internal private data used freely by any emitter. - uint32_t _privateData; + uint32_t _privateData = 0; //! CodeHolder the emitter is attached to. - CodeHolder* _code; + CodeHolder* _code = nullptr; //! Attached \ref Logger. - Logger* _logger; + Logger* _logger = nullptr; //! Attached \ref ErrorHandler. - ErrorHandler* _errorHandler; + ErrorHandler* _errorHandler = nullptr; //! Describes the target environment, matches \ref CodeHolder::environment(). - Environment _environment; + Environment _environment {}; //! Native GP register signature and signature related information. - RegInfo _gpRegInfo; + RegInfo _gpRegInfo {}; //! Next instruction options (affects the next instruction). - uint32_t _instOptions; + uint32_t _instOptions = 0; //! Extra register (op-mask {k} on AVX-512) (affects the next instruction). - RegOnly _extraReg; + RegOnly _extraReg {}; //! Inline comment of the next instruction (affects the next instruction). - const char* _inlineComment; + const char* _inlineComment = nullptr; //! Emitter type. enum EmitterType : uint32_t { @@ -494,6 +494,11 @@ public: //! Creates a new named label. virtual Label newNamedLabel(const char* name, size_t nameSize = SIZE_MAX, uint32_t type = Label::kTypeGlobal, uint32_t parentId = Globals::kInvalidId) = 0; + //! Creates a new external label. + inline Label newExternalLabel(const char* name, size_t nameSize = SIZE_MAX) { + return newNamedLabel(name, nameSize, Label::kTypeExternal); + } + //! Returns `Label` by `name`. //! //! Returns invalid Label in case that the name is invalid or label was not found. diff --git a/src/asmjit/core/environment.h b/src/asmjit/core/environment.h index 99b34ec..79e6f7c 100644 --- a/src/asmjit/core/environment.h +++ b/src/asmjit/core/environment.h @@ -101,16 +101,16 @@ public: // 8 is not used, even numbers are 64-bit architectures. //! 32-bit MIPS architecture in (little endian). - kArchMIPS_LE = 9, + kArchMIPS32_LE = 9, //! 32-bit MIPS architecture in (big endian). - kArchMIPS_BE = kArchMIPS_LE | kArchBigEndianMask, + kArchMIPS32_BE = kArchMIPS32_LE | kArchBigEndianMask, //! 64-bit MIPS architecture in (little endian). kArchMIPS64_LE = 10, //! 64-bit MIPS architecture in (big endian). kArchMIPS64_BE = kArchMIPS64_LE | kArchBigEndianMask, //! Count of architectures. - kArchCount + kArchCount = 11 }; //! Sub-architecture. @@ -246,8 +246,8 @@ public: ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_LE ? kArchAArch64 : ASMJIT_ARCH_ARM == 64 && ASMJIT_ARCH_BE ? kArchAArch64_BE : - ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_LE ? kArchMIPS_LE : - ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_BE ? kArchMIPS_BE : + ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_LE ? kArchMIPS32_LE : + ASMJIT_ARCH_MIPS == 32 && ASMJIT_ARCH_BE ? kArchMIPS32_BE : ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_LE ? kArchMIPS64_LE : ASMJIT_ARCH_MIPS == 64 && ASMJIT_ARCH_BE ? kArchMIPS64_BE : @@ -410,6 +410,16 @@ public: _reserved = 0; } + inline bool isArchX86() const noexcept { return _arch == kArchX86; } + inline bool isArchX64() const noexcept { return _arch == kArchX64; } + inline bool isArchRISCV32() const noexcept { return _arch == kArchRISCV32; } + inline bool isArchRISCV64() const noexcept { return _arch == kArchRISCV64; } + inline bool isArchARM() const noexcept { return (_arch & ~kArchBigEndianMask) == kArchARM; } + inline bool isArchThumb() const noexcept { return (_arch & ~kArchBigEndianMask) == kArchThumb; } + inline bool isArchAArch64() const noexcept { return (_arch & ~kArchBigEndianMask) == kArchAArch64; } + inline bool isArchMIPS32() const noexcept { return (_arch & ~kArchBigEndianMask) == kArchMIPS32_LE; } + inline bool isArchMIPS64() const noexcept { return (_arch & ~kArchBigEndianMask) == kArchMIPS64_LE; } + //! Tests whether the architecture is 32-bit. inline bool is32Bit() const noexcept { return is32Bit(_arch); } //! Tests whether the architecture is 64-bit. @@ -422,11 +432,11 @@ public: //! Tests whether this architecture is of X86 family. inline bool isFamilyX86() const noexcept { return isFamilyX86(_arch); } - //! Tests whether this architecture is of ARM family. + //! Tests whether this architecture family is RISC-V (both 32-bit and 64-bit). inline bool isFamilyRISCV() const noexcept { return isFamilyRISCV(_arch); } - //! Tests whether this architecture is of ARM family. + //! Tests whether this architecture family is ARM, Thumb, or AArch64. inline bool isFamilyARM() const noexcept { return isFamilyARM(_arch); } - //! Tests whether this architecture is of ARM family. + //! Tests whether this architecture family is MISP or MIPS64. inline bool isFamilyMIPS() const noexcept { return isFamilyMIPS(_arch); } //! Tests whether the environment platform is Windows. @@ -486,6 +496,11 @@ public: //! \name Static Utilities //! \{ + static inline bool isValidArch(uint32_t arch) noexcept { + return (arch & ~kArchBigEndianMask) != 0 && + (arch & ~kArchBigEndianMask) < kArchCount; + } + //! Tests whether the given architecture `arch` is 32-bit. static inline bool is32Bit(uint32_t arch) noexcept { return (arch & kArch32BitMask) == kArch32BitMask; @@ -506,6 +521,12 @@ public: return (arch & kArchBigEndianMask) == kArchBigEndianMask; } + //! Tests whether the given architecture is AArch64. + static inline bool isArchAArch64(uint32_t arch) noexcept { + arch &= ~kArchBigEndianMask; + return arch == kArchAArch64; + } + //! Tests whether the given architecture family is X86 or X64. static inline bool isFamilyX86(uint32_t arch) noexcept { return arch == kArchX86 || @@ -529,7 +550,7 @@ public: //! Tests whether the given architecture family is MISP or MIPS64. static inline bool isFamilyMIPS(uint32_t arch) noexcept { arch &= ~kArchBigEndianMask; - return arch == kArchMIPS_LE || + return arch == kArchMIPS32_LE || arch == kArchMIPS64_LE; } diff --git a/src/asmjit/core/func.cpp b/src/asmjit/core/func.cpp index 5f0be48..bb131a0 100644 --- a/src/asmjit/core/func.cpp +++ b/src/asmjit/core/func.cpp @@ -22,22 +22,42 @@ // 3. This notice may not be removed or altered from any source distribution. #include "../core/api-build_p.h" -#include "../core/arch.h" +#include "../core/archtraits.h" #include "../core/func.h" +#include "../core/operand.h" #include "../core/type.h" +#include "../core/funcargscontext_p.h" #ifdef ASMJIT_BUILD_X86 - #include "../x86/x86internal_p.h" - #include "../x86/x86operand.h" + #include "../x86/x86func_p.h" #endif #ifdef ASMJIT_BUILD_ARM - #include "../arm/arminternal_p.h" - #include "../arm/armoperand.h" + #include "../arm/armfunc_p.h" #endif ASMJIT_BEGIN_NAMESPACE +// ============================================================================ +// [asmjit::CallConv - Init / Reset] +// ============================================================================ + +ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId, const Environment& environment) noexcept { + reset(); + +#ifdef ASMJIT_BUILD_X86 + if (environment.isFamilyX86()) + return x86::FuncInternal::initCallConv(*this, ccId, environment); +#endif + +#ifdef ASMJIT_BUILD_ARM + if (environment.isFamilyARM()) + return arm::FuncInternal::initCallConv(*this, ccId, environment); +#endif + + return DebugUtils::errored(kErrorInvalidArgument); +} + // ============================================================================ // [asmjit::FuncDetail - Init / Reset] // ============================================================================ @@ -69,12 +89,12 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const E #ifdef ASMJIT_BUILD_X86 if (environment.isFamilyX86()) - return x86::X86Internal::initFuncDetail(*this, signature, registerSize); + return x86::FuncInternal::initFuncDetail(*this, signature, registerSize); #endif #ifdef ASMJIT_BUILD_ARM if (environment.isFamilyARM()) - return arm::ArmInternal::initFuncDetail(*this, signature, registerSize); + return arm::FuncInternal::initFuncDetail(*this, signature, registerSize); #endif // We should never bubble here as if `cc.init()` succeeded then there has to @@ -83,35 +103,186 @@ ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const E } // ============================================================================ -// [asmjit::FuncFrame - Init / Reset / Finalize] +// [asmjit::FuncFrame - Init / Finalize] // ============================================================================ ASMJIT_FAVOR_SIZE Error FuncFrame::init(const FuncDetail& func) noexcept { -#ifdef ASMJIT_BUILD_X86 - if (Environment::isFamilyX86(func.callConv().arch())) - return x86::X86Internal::initFuncFrame(*this, func); -#endif + uint32_t arch = func.callConv().arch(); + if (!Environment::isValidArch(arch)) + return DebugUtils::errored(kErrorInvalidArch); -#ifdef ASMJIT_BUILD_ARM - if (Environment::isFamilyARM(func.callConv().arch())) - return arm::ArmInternal::initFuncFrame(*this, func); -#endif + const ArchTraits& archTraits = ArchTraits::byArch(arch); - return DebugUtils::errored(kErrorInvalidArgument); + // Initializing FuncFrame means making a copy of some properties of `func`. + // Properties like `_localStackSize` will be set by the user before the frame + // is finalized. + reset(); + + _arch = uint8_t(arch); + _spRegId = uint8_t(archTraits.spRegId()); + _saRegId = uint8_t(BaseReg::kIdBad); + + uint32_t naturalStackAlignment = func.callConv().naturalStackAlignment(); + uint32_t minDynamicAlignment = Support::max(naturalStackAlignment, 16); + + if (minDynamicAlignment == naturalStackAlignment) + minDynamicAlignment <<= 1; + + _naturalStackAlignment = uint8_t(naturalStackAlignment); + _minDynamicAlignment = uint8_t(minDynamicAlignment); + _redZoneSize = uint8_t(func.redZoneSize()); + _spillZoneSize = uint8_t(func.spillZoneSize()); + _finalStackAlignment = uint8_t(_naturalStackAlignment); + + if (func.hasFlag(CallConv::kFlagCalleePopsStack)) { + _calleeStackCleanup = uint16_t(func.argStackSize()); + } + + // Initial masks of dirty and preserved registers. + for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) { + _dirtyRegs[group] = func.usedRegs(group); + _preservedRegs[group] = func.preservedRegs(group); + } + + // Exclude stack pointer - this register is never included in saved GP regs. + _preservedRegs[BaseReg::kGroupGp] &= ~Support::bitMask(archTraits.spRegId()); + + // The size and alignment of save/restore area of registers for each significant register group. + memcpy(_saveRestoreRegSize, func.callConv()._saveRestoreRegSize, sizeof(_saveRestoreRegSize)); + memcpy(_saveRestoreAlignment, func.callConv()._saveRestoreAlignment, sizeof(_saveRestoreAlignment)); + + return kErrorOk; } ASMJIT_FAVOR_SIZE Error FuncFrame::finalize() noexcept { -#ifdef ASMJIT_BUILD_X86 - if (Environment::isFamilyX86(arch())) - return x86::X86Internal::finalizeFuncFrame(*this); -#endif + if (!Environment::isValidArch(arch())) + return DebugUtils::errored(kErrorInvalidArch); -#ifdef ASMJIT_BUILD_ARM - if (Environment::isFamilyARM(arch())) - return arm::ArmInternal::finalizeFuncFrame(*this); -#endif + const ArchTraits& archTraits = ArchTraits::byArch(arch()); - return DebugUtils::errored(kErrorInvalidArgument); + uint32_t registerSize = _saveRestoreRegSize[BaseReg::kGroupGp]; + uint32_t vectorSize = _saveRestoreRegSize[BaseReg::kGroupVec]; + uint32_t returnAddressSize = archTraits.hasLinkReg() ? 0u : registerSize; + + // The final stack alignment must be updated accordingly to call and local stack alignments. + uint32_t stackAlignment = _finalStackAlignment; + ASMJIT_ASSERT(stackAlignment == Support::max(_naturalStackAlignment, + _callStackAlignment, + _localStackAlignment)); + + bool hasFP = hasPreservedFP(); + bool hasDA = hasDynamicAlignment(); + + uint32_t kSp = archTraits.spRegId(); + uint32_t kFp = archTraits.fpRegId(); + uint32_t kLr = archTraits.linkRegId(); + + // Make frame pointer dirty if the function uses it. + if (hasFP) { + _dirtyRegs[BaseReg::kGroupGp] |= Support::bitMask(kFp); + + // Currently required by ARM, if this works differently across architectures + // we would have to generalize most likely in CallConv. + if (kLr != BaseReg::kIdBad) + _dirtyRegs[BaseReg::kGroupGp] |= Support::bitMask(kLr); + } + + // These two are identical if the function doesn't align its stack dynamically. + uint32_t saRegId = _saRegId; + if (saRegId == BaseReg::kIdBad) + saRegId = kSp; + + // Fix stack arguments base-register from SP to FP in case it was not picked + // before and the function performs dynamic stack alignment. + if (hasDA && saRegId == kSp) + saRegId = kFp; + + // Mark as dirty any register but SP if used as SA pointer. + if (saRegId != kSp) + _dirtyRegs[BaseReg::kGroupGp] |= Support::bitMask(saRegId); + + _spRegId = uint8_t(kSp); + _saRegId = uint8_t(saRegId); + + // Setup stack size used to save preserved registers. + uint32_t saveRestoreSizes[2] {}; + for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) + saveRestoreSizes[size_t(!archTraits.hasPushPop(group))] + += Support::alignUp(Support::popcnt(savedRegs(group)) * saveRestoreRegSize(group), saveRestoreAlignment(group)); + + _pushPopSaveSize = uint16_t(saveRestoreSizes[0]); + _extraRegSaveSize = uint16_t(saveRestoreSizes[1]); + + uint32_t v = 0; // The beginning of the stack frame relative to SP after prolog. + v += callStackSize(); // Count 'callStackSize' <- This is used to call functions. + v = Support::alignUp(v, stackAlignment); // Align to function's stack alignment. + + _localStackOffset = v; // Store 'localStackOffset' <- Function's local stack starts here. + v += localStackSize(); // Count 'localStackSize' <- Function's local stack ends here. + + // If the function's stack must be aligned, calculate the alignment necessary + // to store vector registers, and set `FuncFrame::kAttrAlignedVecSR` to inform + // PEI that it can use instructions that perform aligned stores/loads. + if (stackAlignment >= vectorSize && _extraRegSaveSize) { + addAttributes(FuncFrame::kAttrAlignedVecSR); + v = Support::alignUp(v, vectorSize); // Align 'extraRegSaveOffset'. + } + + _extraRegSaveOffset = v; // Store 'extraRegSaveOffset' <- Non-GP save/restore starts here. + v += _extraRegSaveSize; // Count 'extraRegSaveSize' <- Non-GP save/restore ends here. + + // Calculate if dynamic alignment (DA) slot (stored as offset relative to SP) is required and its offset. + if (hasDA && !hasFP) { + _daOffset = v; // Store 'daOffset' <- DA pointer would be stored here. + v += registerSize; // Count 'daOffset'. + } + else { + _daOffset = FuncFrame::kTagInvalidOffset; + } + + // Link Register + // ------------- + // + // The stack is aligned after the function call as the return address is + // stored in a link register. Some architectures may require to always + // have aligned stack after PUSH/POP operation, which is represented by + // ArchTraits::stackAlignmentConstraint(). + // + // No Link Register (X86/X64) + // -------------------------- + // + // The return address should be stored after GP save/restore regs. It has + // the same size as `registerSize` (basically the native register/pointer + // size). We don't adjust it now as `v` now contains the exact size that the + // function requires to adjust (call frame + stack frame, vec stack size). + // The stack (if we consider this size) is misaligned now, as it's always + // aligned before the function call - when `call()` is executed it pushes + // the current EIP|RIP onto the stack, and misaligns it by 12 or 8 bytes + // (depending on the architecture). So count number of bytes needed to align + // it up to the function's CallFrame (the beginning). + if (v || hasFuncCalls() || !returnAddressSize) + v += Support::alignUpDiff(v + pushPopSaveSize() + returnAddressSize, stackAlignment); + + _pushPopSaveOffset = v; // Store 'pushPopSaveOffset' <- Function's push/pop save/restore starts here. + _stackAdjustment = v; // Store 'stackAdjustment' <- SA used by 'add SP, SA' and 'sub SP, SA'. + v += _pushPopSaveSize; // Count 'pushPopSaveSize' <- Function's push/pop save/restore ends here. + _finalStackSize = v; // Store 'finalStackSize' <- Final stack used by the function. + + if (!archTraits.hasLinkReg()) + v += registerSize; // Count 'ReturnAddress' <- As CALL pushes onto stack. + + // If the function performs dynamic stack alignment then the stack-adjustment must be aligned. + if (hasDA) + _stackAdjustment = Support::alignUp(_stackAdjustment, stackAlignment); + + // Calculate where the function arguments start relative to SP. + _saOffsetFromSP = hasDA ? FuncFrame::kTagInvalidOffset : v; + + // Calculate where the function arguments start relative to FP or user-provided register. + _saOffsetFromSA = hasFP ? returnAddressSize + registerSize // Return address + frame pointer. + : returnAddressSize + _pushPopSaveSize; // Return address + all push/pop regs. + + return kErrorOk; } // ============================================================================ @@ -125,17 +296,15 @@ ASMJIT_FAVOR_SIZE Error FuncArgsAssignment::updateFuncFrame(FuncFrame& frame) co if (!func) return DebugUtils::errored(kErrorInvalidState); -#ifdef ASMJIT_BUILD_X86 - if (Environment::isFamilyX86(arch)) - return x86::X86Internal::argsToFuncFrame(*this, frame); -#endif + RAConstraints constraints; + ASMJIT_PROPAGATE(constraints.init(arch)); -#ifdef ASMJIT_BUILD_ARM - if (Environment::isFamilyARM(arch)) - return arm::ArmInternal::argsToFuncFrame(*this, frame); -#endif - - return DebugUtils::errored(kErrorInvalidArch); + FuncArgsContext ctx; + ASMJIT_PROPAGATE(ctx.initWorkData(frame, *this, &constraints)); + ASMJIT_PROPAGATE(ctx.markDstRegsDirty(frame)); + ASMJIT_PROPAGATE(ctx.markScratchRegs(frame)); + ASMJIT_PROPAGATE(ctx.markStackArgsReg(frame)); + return kErrorOk; } ASMJIT_END_NAMESPACE diff --git a/src/asmjit/core/func.h b/src/asmjit/core/func.h index 5636a5d..6cfd044 100644 --- a/src/asmjit/core/func.h +++ b/src/asmjit/core/func.h @@ -24,8 +24,7 @@ #ifndef ASMJIT_CORE_FUNC_H_INCLUDED #define ASMJIT_CORE_FUNC_H_INCLUDED -#include "../core/arch.h" -#include "../core/callconv.h" +#include "../core/archtraits.h" #include "../core/environment.h" #include "../core/operand.h" #include "../core/type.h" @@ -36,6 +35,365 @@ ASMJIT_BEGIN_NAMESPACE //! \addtogroup asmjit_function //! \{ +// ============================================================================ +// [asmjit::CallConv] +// ============================================================================ + +//! Function calling convention. +//! +//! Function calling convention is a scheme that defines how function parameters +//! are passed and how function returns its result. AsmJit defines a variety of +//! architecture and OS specific calling conventions and also provides a compile +//! time detection to make the code-generation easier. +struct CallConv { + //! Calling convention id, see \ref Id. + uint8_t _id; + //! Architecture identifier, see \ref Environment::Arch. + uint8_t _arch; + //! Register assignment strategy, see \ref Strategy. + uint8_t _strategy; + + //! Red zone size (AMD64 == 128 bytes). + uint8_t _redZoneSize; + //! Spill zone size (WIN-X64 == 32 bytes). + uint8_t _spillZoneSize; + //! Natural stack alignment as defined by OS/ABI. + uint8_t _naturalStackAlignment; + + //! Flags. + uint16_t _flags; + + //! Size to save/restore per register group. + uint8_t _saveRestoreRegSize[BaseReg::kGroupVirt]; + //! Alignment of save/restore groups. + uint8_t _saveRestoreAlignment[BaseReg::kGroupVirt]; + + //! Mask of all passed registers, per group. + uint32_t _passedRegs[BaseReg::kGroupVirt]; + //! Mask of all preserved registers, per group. + uint32_t _preservedRegs[BaseReg::kGroupVirt]; + + //! Internal limits of AsmJit's CallConv. + enum Limits : uint32_t { + //! Maximum number of register arguments per register group. + //! + //! \note This is not really AsmJit's limitatation, it's just the number + //! that makes sense considering all common calling conventions. Usually + //! even conventions that use registers to pass function arguments are + //! limited to 8 and less arguments passed via registers per group. + kMaxRegArgsPerGroup = 16 + }; + + //! Passed registers' order. + union RegOrder { + //! Passed registers, ordered. + uint8_t id[kMaxRegArgsPerGroup]; + //! Packed IDs in `uint32_t` array. + uint32_t packed[(kMaxRegArgsPerGroup + 3) / 4]; + }; + + //! Passed registers' order, per register group. + RegOrder _passedOrder[BaseReg::kGroupVirt]; + + //! Calling convention id. + //! + //! Calling conventions can be divided into the following groups: + //! + //! - Universal - calling conventions are applicable to any target. They + //! will be converted to a target dependent calling convention at runtime + //! by \ref init(). The purpose of these conventions is to make using + //! functions less target dependent and closer to how they are declared + //! in C and C++. + //! + //! - Target specific - calling conventions that are used by a particular + //! architecture and ABI. For example Windows 64-bit calling convention + //! and AMD64 SystemV calling convention. + enum Id : uint32_t { + //! None or invalid (can't be used). + kIdNone = 0, + + // ------------------------------------------------------------------------ + // [Universal Calling Conventions] + // ------------------------------------------------------------------------ + + //! Standard function call or explicit `__cdecl` where it can be specified. + //! + //! This is a universal calling convention, which is used to initialize + //! specific calling connventions based on architecture, platform, and its ABI. + kIdCDecl = 1, + + //! `__stdcall` on targets that support this calling convention (X86). + //! + //! \note This calling convention is only supported on 32-bit X86. If used + //! on environment that doesn't support this calling convention it will be + //! replaced by \ref kIdCDecl. + kIdStdCall = 2, + + //! `__fastcall` on targets that support this calling convention (X86). + //! + //! \note This calling convention is only supported on 32-bit X86. If used + //! on environment that doesn't support this calling convention it will be + //! replaced by \ref kIdCDecl. + kIdFastCall = 3, + + //! `__vectorcall` on targets that support this calling convention (X86/X64). + //! + //! \note This calling convention is only supported on 32-bit and 64-bit + //! X86 architecture on Windows platform. If used on environment that doesn't + //! support this calling it will be replaced by \ref kIdCDecl. + kIdVectorCall = 4, + + //! `__thiscall` on targets that support this calling convention (X86). + //! + //! \note This calling convention is only supported on 32-bit X86 Windows + //! platform. If used on environment that doesn't support this calling + //! convention it will be replaced by \ref kIdCDecl. + kIdThisCall = 5, + + //! `__attribute__((regparm(1)))` convention (GCC and Clang). + kIdRegParm1 = 6, + //! `__attribute__((regparm(2)))` convention (GCC and Clang). + kIdRegParm2 = 7, + //! `__attribute__((regparm(3)))` convention (GCC and Clang). + kIdRegParm3 = 8, + + //! Soft-float calling convention (ARM). + //! + //! Floating point arguments are passed via general purpose registers. + kIdSoftFloat = 9, + + //! Hard-float calling convention (ARM). + //! + //! Floating point arguments are passed via SIMD registers. + kIdHardFloat = 10, + + //! AsmJit specific calling convention designed for calling functions + //! inside a multimedia code that don't use many registers internally, + //! but are long enough to be called and not inlined. These functions are + //! usually used to calculate trigonometric functions, logarithms, etc... + kIdLightCall2 = 16, + kIdLightCall3 = 17, + kIdLightCall4 = 18, + + // ------------------------------------------------------------------------ + // [ABI-Specific Calling Conventions] + // ------------------------------------------------------------------------ + + //! X64 System-V calling convention. + kIdX64SystemV = 32, + //! X64 Windows calling convention. + kIdX64Windows = 33, + + // ------------------------------------------------------------------------ + // [Host] + // ------------------------------------------------------------------------ + + //! Host calling convention detected at compile-time. + kIdHost = +#if ASMJIT_ARCH_ARM == 32 && defined(__SOFTFP__) + kIdSoftFloat +#elif ASMJIT_ARCH_ARM == 32 && !defined(__SOFTFP__) + kIdHardFloat +#else + kIdCDecl +#endif + +#ifndef ASMJIT_NO_DEPRECATE + , kIdHostCDecl = kIdCDecl + , kIdHostStdCall = kIdStdCall + , kIdHostFastCall = kIdFastCall + , kIdHostLightCall2 = kIdLightCall2 + , kIdHostLightCall3 = kIdLightCall3 + , kIdHostLightCall4 = kIdLightCall4 +#endif // !ASMJIT_NO_DEPRECATE + }; + + //! Strategy used to assign registers to function arguments. + //! + //! This is AsmJit specific. It basically describes how AsmJit should convert + //! the function arguments defined by `FuncSignature` into register IDs and + //! stack offsets. The default strategy `kStrategyDefault` assigns registers + //! and then stack whereas `kStrategyWin64` strategy does register shadowing + //! as defined by WIN64 calling convention - it applies to 64-bit calling + //! conventions only. + enum Strategy : uint32_t { + //! Default register assignment strategy. + kStrategyDefault = 0, + //! Windows 64-bit ABI register assignment strategy. + kStrategyX64Windows = 1, + //! Windows 64-bit __vectorcall register assignment strategy. + kStrategyX64VectorCall = 2, + + //! Number of assignment strategies. + kStrategyCount = 3 + }; + + //! Calling convention flags. + enum Flags : uint32_t { + //! Callee is responsible for cleaning up the stack. + kFlagCalleePopsStack = 0x0001u, + //! Pass vector arguments indirectly (as a pointer). + kFlagIndirectVecArgs = 0x0002u, + //! Pass F32 and F64 arguments via VEC128 register. + kFlagPassFloatsByVec = 0x0004u, + //! Pass MMX and vector arguments via stack if the function has variable arguments. + kFlagPassVecByStackIfVA = 0x0008u, + //! MMX registers are passed and returned via GP registers. + kFlagPassMmxByGp = 0x0010u, + //! MMX registers are passed and returned via XMM registers. + kFlagPassMmxByXmm = 0x0020u, + //! Calling convention can be used with variable arguments. + kFlagVarArgCompatible = 0x0080u + }; + + //! \name Construction & Destruction + //! \{ + + //! Initializes this calling convention to the given `ccId` based on the + //! `environment`. + //! + //! See \ref Id and \ref Environment for more details. + ASMJIT_API Error init(uint32_t ccId, const Environment& environment) noexcept; + + //! Resets this CallConv struct into a defined state. + //! + //! It's recommended to reset the \ref CallConv struct in case you would + //! like create a custom calling convention as it prevents from using an + //! uninitialized data (CallConv doesn't have a constructor that would + //! initialize it, it's just a struct). + inline void reset() noexcept { + memset(this, 0, sizeof(*this)); + memset(_passedOrder, 0xFF, sizeof(_passedOrder)); + } + + //! \} + + //! \name Accessors + //! \{ + + //! Returns the calling convention id, see `Id`. + inline uint32_t id() const noexcept { return _id; } + //! Sets the calling convention id, see `Id`. + inline void setId(uint32_t id) noexcept { _id = uint8_t(id); } + + //! Returns the calling function architecture id. + inline uint32_t arch() const noexcept { return _arch; } + //! Sets the calling function architecture id. + inline void setArch(uint32_t arch) noexcept { _arch = uint8_t(arch); } + + //! Returns the strategy used to assign registers to arguments, see `Strategy`. + inline uint32_t strategy() const noexcept { return _strategy; } + //! Sets the strategy used to assign registers to arguments, see `Strategy`. + inline void setStrategy(uint32_t strategy) noexcept { _strategy = uint8_t(strategy); } + + //! Tests whether the calling convention has the given `flag` set. + inline bool hasFlag(uint32_t flag) const noexcept { return (uint32_t(_flags) & flag) != 0; } + //! Returns the calling convention flags, see `Flags`. + inline uint32_t flags() const noexcept { return _flags; } + //! Adds the calling convention flags, see `Flags`. + inline void setFlags(uint32_t flag) noexcept { _flags = uint16_t(flag); }; + //! Adds the calling convention flags, see `Flags`. + inline void addFlags(uint32_t flags) noexcept { _flags = uint16_t(_flags | flags); }; + + //! Tests whether this calling convention specifies 'RedZone'. + inline bool hasRedZone() const noexcept { return _redZoneSize != 0; } + //! Tests whether this calling convention specifies 'SpillZone'. + inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; } + + //! Returns size of 'RedZone'. + inline uint32_t redZoneSize() const noexcept { return _redZoneSize; } + //! Returns size of 'SpillZone'. + inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; } + + //! Sets size of 'RedZone'. + inline void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = uint8_t(size); } + //! Sets size of 'SpillZone'. + inline void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = uint8_t(size); } + + //! Returns a natural stack alignment. + inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; } + //! Sets a natural stack alignment. + //! + //! This function can be used to override the default stack alignment in case + //! that you know that it's alignment is different. For example it allows to + //! implement custom calling conventions that guarantee higher stack alignment. + inline void setNaturalStackAlignment(uint32_t value) noexcept { _naturalStackAlignment = uint8_t(value); } + + //! Returns the size of a register (or its part) to be saved and restored of the given `group`. + inline uint32_t saveRestoreRegSize(uint32_t group) const noexcept { return _saveRestoreRegSize[group]; } + //! Sets the size of a vector register (or its part) to be saved and restored. + inline void setSaveRestoreRegSize(uint32_t group, uint32_t size) noexcept { _saveRestoreRegSize[group] = uint8_t(size); } + + //! Returns the alignment of a save-restore area of the given `group`. + inline uint32_t saveRestoreAlignment(uint32_t group) const noexcept { return _saveRestoreAlignment[group]; } + //! Sets the alignment of a save-restore area of the given `group`. + inline void setSaveRestoreAlignment(uint32_t group, uint32_t alignment) noexcept { _saveRestoreAlignment[group] = uint8_t(alignment); } + + //! Returns the order of passed registers of the given `group`, see \ref BaseReg::RegGroup. + inline const uint8_t* passedOrder(uint32_t group) const noexcept { + ASMJIT_ASSERT(group < BaseReg::kGroupVirt); + return _passedOrder[group].id; + } + + //! Returns the mask of passed registers of the given `group`, see \ref BaseReg::RegGroup. + inline uint32_t passedRegs(uint32_t group) const noexcept { + ASMJIT_ASSERT(group < BaseReg::kGroupVirt); + return _passedRegs[group]; + } + + inline void _setPassedPacked(uint32_t group, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) noexcept { + ASMJIT_ASSERT(group < BaseReg::kGroupVirt); + + _passedOrder[group].packed[0] = p0; + _passedOrder[group].packed[1] = p1; + _passedOrder[group].packed[2] = p2; + _passedOrder[group].packed[3] = p3; + } + + //! Resets the order and mask of passed registers. + inline void setPassedToNone(uint32_t group) noexcept { + ASMJIT_ASSERT(group < BaseReg::kGroupVirt); + + _setPassedPacked(group, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu); + _passedRegs[group] = 0u; + } + + //! Sets the order and mask of passed registers. + inline void setPassedOrder(uint32_t group, uint32_t a0, uint32_t a1 = 0xFF, uint32_t a2 = 0xFF, uint32_t a3 = 0xFF, uint32_t a4 = 0xFF, uint32_t a5 = 0xFF, uint32_t a6 = 0xFF, uint32_t a7 = 0xFF) noexcept { + ASMJIT_ASSERT(group < BaseReg::kGroupVirt); + + // NOTE: This should always be called with all arguments known at compile time, + // so even if it looks scary it should be translated into few instructions. + _setPassedPacked(group, Support::bytepack32_4x8(a0, a1, a2, a3), + Support::bytepack32_4x8(a4, a5, a6, a7), + 0xFFFFFFFFu, + 0xFFFFFFFFu); + + _passedRegs[group] = (a0 != 0xFF ? 1u << a0 : 0u) | + (a1 != 0xFF ? 1u << a1 : 0u) | + (a2 != 0xFF ? 1u << a2 : 0u) | + (a3 != 0xFF ? 1u << a3 : 0u) | + (a4 != 0xFF ? 1u << a4 : 0u) | + (a5 != 0xFF ? 1u << a5 : 0u) | + (a6 != 0xFF ? 1u << a6 : 0u) | + (a7 != 0xFF ? 1u << a7 : 0u) ; + } + + //! Returns preserved register mask of the given `group`, see \ref BaseReg::RegGroup. + inline uint32_t preservedRegs(uint32_t group) const noexcept { + ASMJIT_ASSERT(group < BaseReg::kGroupVirt); + return _preservedRegs[group]; + } + + //! Sets preserved register mask of the given `group`, see \ref BaseReg::RegGroup. + inline void setPreservedRegs(uint32_t group, uint32_t regs) noexcept { + ASMJIT_ASSERT(group < BaseReg::kGroupVirt); + _preservedRegs[group] = regs; + } + + //! \} +}; + // ============================================================================ // [asmjit::FuncSignature] // ============================================================================ @@ -629,15 +987,19 @@ public: uint32_t _dirtyRegs[BaseReg::kGroupVirt]; //! Registers that must be preserved (copied from CallConv). uint32_t _preservedRegs[BaseReg::kGroupVirt]; + //! Size to save/restore per register group. + uint8_t _saveRestoreRegSize[BaseReg::kGroupVirt]; + //! Alignment of save/restore area per register group. + uint8_t _saveRestoreAlignment[BaseReg::kGroupVirt]; - //! Final stack size required to save GP regs. - uint16_t _gpSaveSize; - //! Final Stack size required to save other than GP regs. - uint16_t _nonGpSaveSize; - //! Final offset where saved GP regs are stored. - uint32_t _gpSaveOffset; - //! Final offset where saved other than GP regs are stored. - uint32_t _nonGpSaveOffset; + //! Stack size required to save registers with push/pop. + uint16_t _pushPopSaveSize; + //! Stack size required to save extra registers that cannot use push/pop. + uint16_t _extraRegSaveSize; + //! Offset where registers saved/restored via push/pop are stored + uint32_t _pushPopSaveOffset; + //! Offset where extra ragisters that cannot use push/pop are stored. + uint32_t _extraRegSaveOffset; //! \name Construction & Destruction //! \{ @@ -881,20 +1243,35 @@ public: return _preservedRegs[group]; } + inline uint32_t saveRestoreRegSize(uint32_t group) const noexcept { + ASMJIT_ASSERT(group < BaseReg::kGroupVirt); + return _saveRestoreRegSize[group]; + } + + inline uint32_t saveRestoreAlignment(uint32_t group) const noexcept { + ASMJIT_ASSERT(group < BaseReg::kGroupVirt); + return _saveRestoreAlignment[group]; + } + inline bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; } inline uint32_t saRegId() const noexcept { return _saRegId; } inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); } inline void resetSARegId() { setSARegId(BaseReg::kIdBad); } - //! Returns stack size required to save GP registers. - inline uint32_t gpSaveSize() const noexcept { return _gpSaveSize; } - //! Returns stack size required to save other than GP registers (MM, XMM|YMM|ZMM, K, VFP, etc...). - inline uint32_t nonGpSaveSize() const noexcept { return _nonGpSaveSize; } + //! Returns stack size required to save/restore registers via push/pop. + inline uint32_t pushPopSaveSize() const noexcept { return _pushPopSaveSize; } + //! Returns an offset to the stack where registers are saved via push/pop. + inline uint32_t pushPopSaveOffset() const noexcept { return _pushPopSaveOffset; } - //! Returns an offset to the stack where general purpose registers are saved. - inline uint32_t gpSaveOffset() const noexcept { return _gpSaveOffset; } - //! Returns an offset to the stack where other than GP registers are saved. - inline uint32_t nonGpSaveOffset() const noexcept { return _nonGpSaveOffset; } + //! Returns stack size required to save/restore extra registers that don't + //! use push/pop/ + //! + //! \note On X86 this covers all registers except GP registers, on other + //! architectures it can be always zero (for example AArch64 saves all + //! registers via push/pop like instructions, so this would be zero). + inline uint32_t extraRegSaveSize() const noexcept { return _extraRegSaveSize; } + //! Returns an offset to the stack where extra registers are saved. + inline uint32_t extraRegSaveOffset() const noexcept { return _extraRegSaveOffset; } //! Tests whether the functions contains stack adjustment. inline bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; } diff --git a/src/asmjit/core/funcargscontext.cpp b/src/asmjit/core/funcargscontext.cpp new file mode 100644 index 0000000..331e205 --- /dev/null +++ b/src/asmjit/core/funcargscontext.cpp @@ -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 diff --git a/src/asmjit/core/funcargscontext_p.h b/src/asmjit/core/funcargscontext_p.h new file mode 100644 index 0000000..6c4ea6a --- /dev/null +++ b/src/asmjit/core/funcargscontext_p.h @@ -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(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 diff --git a/src/asmjit/core/globals.cpp b/src/asmjit/core/globals.cpp index 7ec6628..c7d1f72 100644 --- a/src/asmjit/core/globals.cpp +++ b/src/asmjit/core/globals.cpp @@ -67,6 +67,7 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept { "InvalidRegGroup\0" "InvalidPhysId\0" "InvalidVirtId\0" + "InvalidElementIndex\0" "InvalidPrefixCombination\0" "InvalidLockPrefix\0" "InvalidXAcquirePrefix\0" @@ -108,9 +109,9 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept { static const uint16_t sErrorIndex[] = { 0, 3, 15, 31, 44, 56, 71, 90, 108, 123, 132, 148, 165, 178, 192, 210, 230, 247, 264, 283, 313, 328, 344, 363, 382, 400, 422, 440, 459, 474, 490, 504, - 518, 543, 561, 583, 605, 622, 639, 655, 671, 687, 704, 719, 734, 754, 774, - 794, 827, 847, 862, 879, 898, 919, 939, 953, 974, 988, 1006, 1022, 1038, - 1057, 1072, 1088, 1103, 1118, 1148, 1172, 1191 + 518, 538, 563, 581, 603, 625, 642, 659, 675, 691, 707, 724, 739, 754, 774, + 794, 814, 847, 867, 882, 899, 918, 939, 959, 973, 994, 1008, 1026, 1042, + 1058, 1077, 1092, 1108, 1123, 1138, 1168, 1192, 1211 }; // @EnumStringEnd@ diff --git a/src/asmjit/core/globals.h b/src/asmjit/core/globals.h index af4ddab..c1535e1 100644 --- a/src/asmjit/core/globals.h +++ b/src/asmjit/core/globals.h @@ -296,27 +296,29 @@ enum ErrorCode : uint32_t { kErrorInvalidPhysId, //! Invalid virtual register id. kErrorInvalidVirtId, - //! Invalid prefix combination. + //! Invalid element index (ARM). + kErrorInvalidElementIndex, + //! Invalid prefix combination (X86|X64). kErrorInvalidPrefixCombination, - //! Invalid LOCK prefix. + //! Invalid LOCK prefix (X86|X64). kErrorInvalidLockPrefix, - //! Invalid XACQUIRE prefix. + //! Invalid XACQUIRE prefix (X86|X64). kErrorInvalidXAcquirePrefix, - //! Invalid XRELEASE prefix. + //! Invalid XRELEASE prefix (X86|X64). kErrorInvalidXReleasePrefix, - //! Invalid REP prefix. + //! Invalid REP prefix (X86|X64). kErrorInvalidRepPrefix, - //! Invalid REX prefix. + //! Invalid REX prefix (X86|X64). kErrorInvalidRexPrefix, - //! Invalid {...} register. + //! Invalid {...} register (X86|X64). kErrorInvalidExtraReg, - //! Invalid {k} use (not supported by the instruction). + //! Invalid {k} use (not supported by the instruction) (X86|X64). kErrorInvalidKMaskUse, - //! Invalid {k}{z} use (not supported by the instruction). + //! Invalid {k}{z} use (not supported by the instruction) (X86|X64). kErrorInvalidKZeroUse, - //! Invalid broadcast - Currently only related to invalid use of AVX-512 {1tox}. + //! Invalid broadcast - Currently only related to invalid use of AVX-512 {1tox} (X86|X64). kErrorInvalidBroadcast, - //! Invalid 'embedded-rounding' {er} or 'suppress-all-exceptions' {sae} (AVX-512). + //! Invalid 'embedded-rounding' {er} or 'suppress-all-exceptions' {sae} (AVX-512) (X86|X64). kErrorInvalidEROrSAE, //! Invalid address used (not encodable). kErrorInvalidAddress, diff --git a/src/asmjit/core/inst.cpp b/src/asmjit/core/inst.cpp index a233b93..a79fe83 100644 --- a/src/asmjit/core/inst.cpp +++ b/src/asmjit/core/inst.cpp @@ -24,7 +24,7 @@ #include "../core/api-build_p.h" #ifdef ASMJIT_BUILD_X86 -#include "../core/arch.h" +#include "../core/archtraits.h" #include "../core/inst.h" #ifdef ASMJIT_BUILD_X86 @@ -32,7 +32,7 @@ #endif #ifdef ASMJIT_BUILD_ARM - #include "../arm/arminstapi_p.h" + #include "../arm/a64instapi_p.h" #endif ASMJIT_BEGIN_NAMESPACE @@ -49,8 +49,8 @@ Error InstAPI::instIdToString(uint32_t arch, uint32_t instId, String& output) no #endif #ifdef ASMJIT_BUILD_ARM - if (Environment::isFamilyARM(arch)) - return arm::InstInternal::instIdToString(arch, instId, output); + if (Environment::isArchAArch64(arch)) + return a64::InstInternal::instIdToString(arch, instId, output); #endif return DebugUtils::errored(kErrorInvalidArch); @@ -63,8 +63,8 @@ uint32_t InstAPI::stringToInstId(uint32_t arch, const char* s, size_t len) noexc #endif #ifdef ASMJIT_BUILD_ARM - if (Environment::isFamilyARM(arch)) - return arm::InstInternal::stringToInstId(arch, s, len); + if (Environment::isArchAArch64(arch)) + return a64::InstInternal::stringToInstId(arch, s, len); #endif return 0; @@ -83,8 +83,8 @@ Error InstAPI::validate(uint32_t arch, const BaseInst& inst, const Operand_* ope #endif #ifdef ASMJIT_BUILD_ARM - if (Environment::isFamilyARM(arch)) - return arm::InstInternal::validate(arch, inst, operands, opCount, validationFlags); + if (Environment::isArchAArch64(arch)) + return a64::InstInternal::validate(arch, inst, operands, opCount, validationFlags); #endif return DebugUtils::errored(kErrorInvalidArch); @@ -106,8 +106,8 @@ Error InstAPI::queryRWInfo(uint32_t arch, const BaseInst& inst, const Operand_* #endif #ifdef ASMJIT_BUILD_ARM - if (Environment::isFamilyARM(arch)) - return arm::InstInternal::queryRWInfo(arch, inst, operands, opCount, out); + if (Environment::isArchAArch64(arch)) + return a64::InstInternal::queryRWInfo(arch, inst, operands, opCount, out); #endif return DebugUtils::errored(kErrorInvalidArch); @@ -126,8 +126,8 @@ Error InstAPI::queryFeatures(uint32_t arch, const BaseInst& inst, const Operand_ #endif #ifdef ASMJIT_BUILD_ARM - if (Environment::isFamilyARM(arch)) - return arm::InstInternal::queryFeatures(arch, inst, operands, opCount, out); + if (Environment::isArchAArch64(arch)) + return a64::InstInternal::queryFeatures(arch, inst, operands, opCount, out); #endif return DebugUtils::errored(kErrorInvalidArch); diff --git a/src/asmjit/core/jitallocator.cpp b/src/asmjit/core/jitallocator.cpp index b228511..6a1f8fd 100644 --- a/src/asmjit/core/jitallocator.cpp +++ b/src/asmjit/core/jitallocator.cpp @@ -24,7 +24,7 @@ #include "../core/api-build_p.h" #ifndef ASMJIT_NO_JIT -#include "../core/arch.h" +#include "../core/archtraits.h" #include "../core/jitallocator.h" #include "../core/osutils_p.h" #include "../core/support.h" diff --git a/src/asmjit/core/jitruntime.cpp b/src/asmjit/core/jitruntime.cpp index 1127c86..381979e 100644 --- a/src/asmjit/core/jitruntime.cpp +++ b/src/asmjit/core/jitruntime.cpp @@ -35,11 +35,19 @@ ASMJIT_BEGIN_NAMESPACE // Only useful on non-x86 architectures. static inline void JitRuntime_flushInstructionCache(const void* p, size_t size) noexcept { -#if defined(_WIN32) && !ASMJIT_ARCH_X86 +#if ASMJIT_ARCH_X86 + DebugUtils::unused(p, size); +#else +# if defined(_WIN32) // Windows has a built-in support in `kernel32.dll`. ::FlushInstructionCache(::GetCurrentProcess(), p, size); -#else +# elif defined(__GNUC__) + void* start = const_cast(p); + void* end = static_cast(start) + size; + __builtin___clear_cache(start, end); +# else DebugUtils::unused(p, size); +# endif #endif } diff --git a/src/asmjit/core/misc_p.h b/src/asmjit/core/misc_p.h index b1056f4..225ba6a 100644 --- a/src/asmjit/core/misc_p.h +++ b/src/asmjit/core/misc_p.h @@ -32,9 +32,11 @@ ASMJIT_BEGIN_NAMESPACE //! \addtogroup asmjit_utilities //! \{ -#define ASMJIT_LOOKUP_TABLE_8(T, I) T((I)), T((I+1)), T((I+2)), T((I+3)), T((I+4)), T((I+5)), T((I+6)), T((I+7)) +#define ASMJIT_LOOKUP_TABLE_4(T, I) T((I)), T((I+1)), T((I+2)), T((I+3)) +#define ASMJIT_LOOKUP_TABLE_8(T, I) ASMJIT_LOOKUP_TABLE_4(T, I), ASMJIT_LOOKUP_TABLE_4(T, I + 4) #define ASMJIT_LOOKUP_TABLE_16(T, I) ASMJIT_LOOKUP_TABLE_8(T, I), ASMJIT_LOOKUP_TABLE_8(T, I + 8) #define ASMJIT_LOOKUP_TABLE_32(T, I) ASMJIT_LOOKUP_TABLE_16(T, I), ASMJIT_LOOKUP_TABLE_16(T, I + 16) +#define ASMJIT_LOOKUP_TABLE_40(T, I) ASMJIT_LOOKUP_TABLE_16(T, I), ASMJIT_LOOKUP_TABLE_16(T, I + 16), ASMJIT_LOOKUP_TABLE_8(T, I + 32) #define ASMJIT_LOOKUP_TABLE_64(T, I) ASMJIT_LOOKUP_TABLE_32(T, I), ASMJIT_LOOKUP_TABLE_32(T, I + 32) #define ASMJIT_LOOKUP_TABLE_128(T, I) ASMJIT_LOOKUP_TABLE_64(T, I), ASMJIT_LOOKUP_TABLE_64(T, I + 64) #define ASMJIT_LOOKUP_TABLE_256(T, I) ASMJIT_LOOKUP_TABLE_128(T, I), ASMJIT_LOOKUP_TABLE_128(T, I + 128) diff --git a/src/asmjit/core/operand.cpp b/src/asmjit/core/operand.cpp index 46bb23a..cd5931f 100644 --- a/src/asmjit/core/operand.cpp +++ b/src/asmjit/core/operand.cpp @@ -68,7 +68,7 @@ UNIT(operand) { uint32_t rSig = Operand::kOpReg | (1 << Operand::kSignatureRegTypeShift ) | (2 << Operand::kSignatureRegGroupShift) | (8 << Operand::kSignatureSizeShift ) ; - BaseReg r1(rSig, 5); + BaseReg r1 = BaseReg::fromSignatureAndId(rSig, 5); EXPECT(r1.isValid() == true); EXPECT(r1.isReg() == true); @@ -126,10 +126,17 @@ UNIT(operand) { INFO("Checking basic functionality of Imm"); Imm immValue(-42); + EXPECT(immValue.type() == Imm::kTypeInteger); EXPECT(Imm(-1).value() == -1); EXPECT(imm(-1).value() == -1); EXPECT(immValue.value() == -42); EXPECT(imm(0xFFFFFFFF).value() == int64_t(0xFFFFFFFF)); + + Imm immDouble(0.4); + EXPECT(immDouble.type() == Imm::kTypeDouble); + EXPECT(immDouble.valueAs() == 0.4); + EXPECT(immDouble == imm(0.4)); + } #endif diff --git a/src/asmjit/core/operand.h b/src/asmjit/core/operand.h index 1faaeca..05e4c0f 100644 --- a/src/asmjit/core/operand.h +++ b/src/asmjit/core/operand.h @@ -24,6 +24,7 @@ #ifndef ASMJIT_CORE_OPERAND_H_INCLUDED #define ASMJIT_CORE_OPERAND_H_INCLUDED +#include "../core/archcommons.h" #include "../core/support.h" ASMJIT_BEGIN_NAMESPACE @@ -47,7 +48,7 @@ struct RegTraits { \ static constexpr uint32_t kSize = SIZE; \ \ static constexpr uint32_t kSignature = \ - (Operand::kOpReg << Operand::kSignatureOpShift ) | \ + (Operand::kOpReg << Operand::kSignatureOpTypeShift ) | \ (kType << Operand::kSignatureRegTypeShift ) | \ (kGroup << Operand::kSignatureRegGroupShift) | \ (kSize << Operand::kSignatureSizeShift ) ; \ @@ -60,7 +61,7 @@ struct RegTraits { \ public: \ /*! Default constructor that only setups basics. */ \ constexpr REG() noexcept \ - : BASE(kSignature, kIdBad) {} \ + : BASE(SignatureAndId(kSignature, kIdBad)) {} \ \ /*! Makes a copy of the `other` register operand. */ \ constexpr REG(const REG& other) noexcept \ @@ -71,8 +72,8 @@ public: \ : BASE(other, rId) {} \ \ /*! Creates a register based on `signature` and `rId`. */ \ - constexpr REG(uint32_t signature, uint32_t rId) noexcept \ - : BASE(signature, rId) {} \ + constexpr explicit REG(const SignatureAndId& sid) noexcept \ + : BASE(sid) {} \ \ /*! Creates a completely uninitialized REG register operand (garbage). */ \ inline explicit REG(Globals::NoInit_) noexcept \ @@ -80,7 +81,12 @@ public: \ \ /*! Creates a new register from register type and id. */ \ static inline REG fromTypeAndId(uint32_t rType, uint32_t rId) noexcept { \ - return REG(signatureOf(rType), rId); \ + return REG(SignatureAndId(signatureOf(rType), rId)); \ + } \ + \ + /*! Creates a new register from register signature and id. */ \ + static inline REG fromSignatureAndId(uint32_t rSgn, uint32_t rId) noexcept {\ + return REG(SignatureAndId(rSgn, rId)); \ } \ \ /*! Clones the register operand. */ \ @@ -101,7 +107,7 @@ public: \ \ /*! Creates a register operand having its id set to `rId`. */ \ constexpr explicit REG(uint32_t rId) noexcept \ - : BASE(kSignature, rId) {} + : BASE(SignatureAndId(kSignature, rId)) {} //! \addtogroup asmjit_assembler //! \{ @@ -176,8 +182,8 @@ struct Operand_ { enum SignatureBits : uint32_t { // Operand type (3 least significant bits). // |........|........|........|.....XXX| - kSignatureOpShift = 0, - kSignatureOpMask = 0x07u << kSignatureOpShift, + kSignatureOpTypeShift = 0, + kSignatureOpTypeMask = 0x07u << kSignatureOpTypeShift, // Register type (5 bits). // |........|........|........|XXXXX...| @@ -204,16 +210,21 @@ struct Operand_ { kSignatureMemBaseIndexShift = 3, kSignatureMemBaseIndexMask = 0x3FFu << kSignatureMemBaseIndexShift, - // Memory address type (2 bits). - // |........|........|.XX.....|........| - kSignatureMemAddrTypeShift = 13, - kSignatureMemAddrTypeMask = 0x03u << kSignatureMemAddrTypeShift, - - // This memory operand represents a home-slot or stack (BaseCompiler). - // |........|........|X.......|........| - kSignatureMemRegHomeShift = 15, + // This memory operand represents a home-slot or stack (Compiler) (1 bit). + // |........|........|..X.....|........| + kSignatureMemRegHomeShift = 13, kSignatureMemRegHomeFlag = 0x01u << kSignatureMemRegHomeShift, + // Immediate type (1 bit). + // |........|........|........|....X...| + kSignatureImmTypeShift = 4, + kSignatureImmTypeMask = 0x01u << kSignatureImmTypeShift, + + // Predicate used by either registers or immediate values (4 bits). + // |........|XXXX....|........|........| + kSignaturePredicateShift = 20, + kSignaturePredicateMask = 0x0Fu << kSignaturePredicateShift, + // Operand size (8 most significant bits). // |XXXXXXXX|........|........|........| kSignatureSizeShift = 24, @@ -320,9 +331,9 @@ struct Operand_ { //! \name Accessors //! \{ - //! Tests whether the operand matches the given signature `sign`. + //! Tests whether the operand's signature matches the given signature `sign`. constexpr bool hasSignature(uint32_t signature) const noexcept { return _signature == signature; } - //! Tests whether the operand matches the signature of the `other` operand. + //! Tests whether the operand's signature matches the signature of the `other` operand. constexpr bool hasSignature(const Operand_& other) const noexcept { return _signature == other.signature(); } //! Returns operand signature as unsigned 32-bit integer. @@ -343,6 +354,11 @@ struct Operand_ { return (_signature & mask) != 0; } + template + constexpr bool _hasSignaturePart(uint32_t signature) const noexcept { + return (_signature & mask) == signature; + } + template constexpr uint32_t _getSignaturePart() const noexcept { return (_signature >> Support::constCtz(mask)) & (mask >> Support::constCtz(mask)); @@ -356,7 +372,7 @@ struct Operand_ { //! \endcond //! Returns the type of the operand, see `OpType`. - constexpr uint32_t opType() const noexcept { return _getSignaturePart(); } + constexpr uint32_t opType() const noexcept { return _getSignaturePart(); } //! Tests whether the operand is none (`kOpNone`). constexpr bool isNone() const noexcept { return _signature == 0; } //! Tests whether the operand is a register (`kOpReg`). @@ -422,8 +438,8 @@ struct Operand_ { //! Tests whether the operand is a register matching `rType`. constexpr bool isReg(uint32_t rType) const noexcept { - return (_signature & (kSignatureOpMask | kSignatureRegTypeMask)) == - ((kOpReg << kSignatureOpShift) | (rType << kSignatureRegTypeShift)); + return (_signature & (kSignatureOpTypeMask | kSignatureRegTypeMask)) == + ((kOpReg << kSignatureOpTypeShift) | (rType << kSignatureRegTypeShift)); } //! Tests whether the operand is register and of `rType` and `rId`. @@ -527,8 +543,10 @@ public: kTypeLocal = 1, //! Global label (never has parentId). kTypeGlobal = 2, + //! External label (references an external symbol). + kTypeExternal = 3, //! Number of label types. - kTypeCount = 3 + kTypeCount = 4 }; //! \name Construction & Destruction @@ -598,8 +616,8 @@ struct BaseRegTraits { //! No size by default. static constexpr uint32_t kSize = 0; - //! Empty signature by default. - static constexpr uint32_t kSignature = Operand::kOpReg; + //! Empty signature by default (not even having operand type set to register). + static constexpr uint32_t kSignature = 0; }; //! \endcond @@ -622,7 +640,7 @@ struct RegInfo { constexpr bool isValid() const noexcept { return _signature != 0; } constexpr uint32_t signature() const noexcept { return _signature; } - constexpr uint32_t opType() const noexcept { return _getSignaturePart(); } + constexpr uint32_t opType() const noexcept { return _getSignaturePart(); } constexpr uint32_t group() const noexcept { return _getSignaturePart(); } constexpr uint32_t type() const noexcept { return _getSignaturePart(); } constexpr uint32_t size() const noexcept { return _getSignaturePart(); } @@ -633,6 +651,12 @@ struct RegInfo { //! Physical or virtual register operand. class BaseReg : public Operand { public: + static constexpr uint32_t kBaseSignature = + kSignatureOpTypeMask | + kSignatureRegTypeMask | + kSignatureRegGroupMask | + kSignatureSizeMask ; + //! Architecture neutral register types. //! //! These must be reused by any platform that contains that types. All GP @@ -654,26 +678,30 @@ public: kTypeGp32 = 5, //! 64-bit general purpose register (X86|ARM). kTypeGp64 = 6, + //! 8-bit view of a vector register (ARM). + kTypeVec8 = 7, + //! 16-bit view of a vector register (ARM). + kTypeVec16 = 8, //! 32-bit view of a vector register (ARM). - kTypeVec32 = 7, + kTypeVec32 = 9, //! 64-bit view of a vector register (ARM). - kTypeVec64 = 8, + kTypeVec64 = 10, //! 128-bit view of a vector register (X86|ARM). - kTypeVec128 = 9, + kTypeVec128 = 11, //! 256-bit view of a vector register (X86). - kTypeVec256 = 10, + kTypeVec256 = 12, //! 512-bit view of a vector register (X86). - kTypeVec512 = 11, + kTypeVec512 = 13, //! 1024-bit view of a vector register (future). - kTypeVec1024 = 12, + kTypeVec1024 = 14, //! Other0 register, should match `kOther0` group. - kTypeOther0 = 13, + kTypeOther0 = 15, //! Other1 register, should match `kOther1` group. - kTypeOther1 = 14, + kTypeOther1 = 16, //! Universal id of IP/PC register (if separate). - kTypeIP = 15, - //! Start of platform dependent register types (must be honored). - kTypeCustom = 16, + kTypeIP = 17, + //! Start of platform dependent register types. + kTypeCustom = 18, //! Maximum possible register type value. kTypeMax = 31 }; @@ -688,9 +716,9 @@ public: kGroupOther0 = 2, //! Group that is architecture dependent. kGroupOther1 = 3, - //! Count of register groups used by virtual registers. + //! Count of register groups used by physical and virtual registers. kGroupVirt = 4, - //! Count of register groups used by physical registers. + //! Count of register groups used by physical registers only. kGroupCount = 16 }; @@ -699,6 +727,22 @@ public: kIdBad = 0xFFu }; + //! A helper used by constructors. + struct SignatureAndId { + uint32_t _signature; + uint32_t _id; + + inline SignatureAndId() noexcept = default; + constexpr SignatureAndId(const SignatureAndId& other) noexcept = default; + + constexpr explicit SignatureAndId(uint32_t signature, uint32_t id) noexcept + : _signature(signature), + _id(id) {} + + constexpr uint32_t signature() const noexcept { return _signature; } + constexpr uint32_t id() const noexcept { return _id; } + }; + static constexpr uint32_t kSignature = kOpReg; //! \name Construction & Destruction @@ -717,12 +761,17 @@ public: : Operand(Globals::Init, other._signature, rId, 0, 0) {} //! Creates a register initialized to `signature` and `rId`. - constexpr BaseReg(uint32_t signature, uint32_t rId) noexcept - : Operand(Globals::Init, signature, rId, 0, 0) {} + constexpr explicit BaseReg(const SignatureAndId& sid) noexcept + : Operand(Globals::Init, sid._signature, sid._id, 0, 0) {} inline explicit BaseReg(Globals::NoInit_) noexcept : Operand(Globals::NoInit) {} + /*! Creates a new register from register signature `rSgn` and id. */ + static inline BaseReg fromSignatureAndId(uint32_t rSgn, uint32_t rId) noexcept { + return BaseReg(SignatureAndId(rSgn, rId)); + } + //! \} //! \name Overloaded Operators @@ -735,6 +784,21 @@ public: //! \name Accessors //! \{ + //! Returns base signature of the register associated with each register type. + //! + //! Base signature only contains the operand type, register type, register + //! group, and register size. It doesn't contain element type, predicate, or + //! other architecture-specific data. Base signature is a signature that is + //! provided by architecture-specific `RegTraits`, like \ref x86::RegTraits. + constexpr uint32_t baseSignature() const noexcept { + return _signature & (kBaseSignature); + } + + //! Tests whether the operand's base signature matches the given signature `sign`. + constexpr bool hasBaseSignature(uint32_t signature) const noexcept { return baseSignature() == signature; } + //! Tests whether the operand's base signature matches the base signature of the `other` operand. + constexpr bool hasBaseSignature(const BaseReg& other) const noexcept { return baseSignature() == other.baseSignature(); } + //! Tests whether this register is the same as `other`. //! //! This is just an optimization. Registers by default only use the first @@ -778,6 +842,21 @@ public: //! Returns the register group. constexpr uint32_t group() const noexcept { return _getSignaturePart(); } + //! 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(); } + + //! 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(predicate); } + + //! Resets shift operation type of the register to the default value (ARM/AArch64). + inline void resetPredicate() noexcept { _setSignaturePart(0); } + //! Clones the register operand. constexpr BaseReg clone() const noexcept { return BaseReg(*this); } @@ -791,7 +870,7 @@ public: //! //! \note Improper use of `cloneAs()` can lead to hard-to-debug errors. template - constexpr RegT cloneAs(const RegT& other) const noexcept { return RegT(other.signature(), id()); } + constexpr RegT cloneAs(const RegT& other) const noexcept { return RegT(SignatureAndId(other.signature(), id())); } //! Sets the register id to `rId`. inline void setId(uint32_t rId) noexcept { _baseId = rId; } @@ -814,17 +893,17 @@ public: //! Tests whether the `op` operand is a general purpose register. static inline bool isGp(const Operand_& op) noexcept { // Check operand type and register group. Not interested in register type and size. - const uint32_t kSgn = (kOpReg << kSignatureOpShift ) | + const uint32_t kSgn = (kOpReg << kSignatureOpTypeShift ) | (kGroupGp << kSignatureRegGroupShift) ; - return (op.signature() & (kSignatureOpMask | kSignatureRegGroupMask)) == kSgn; + return (op.signature() & (kSignatureOpTypeMask | kSignatureRegGroupMask)) == kSgn; } //! Tests whether the `op` operand is a vector register. static inline bool isVec(const Operand_& op) noexcept { // Check operand type and register group. Not interested in register type and size. - const uint32_t kSgn = (kOpReg << kSignatureOpShift ) | + const uint32_t kSgn = (kOpReg << kSignatureOpTypeShift ) | (kGroupVec << kSignatureRegGroupShift) ; - return (op.signature() & (kSignatureOpMask | kSignatureRegGroupMask)) == kSgn; + return (op.signature() & (kSignatureOpTypeMask | kSignatureRegGroupMask)) == kSgn; } //! Tests whether the `op` is a general purpose register of the given `rId`. @@ -913,7 +992,7 @@ struct RegOnly { //! Converts this ExtraReg to a real `RegT` operand. template - constexpr RegT toReg() const noexcept { return RegT(_signature, _id); } + constexpr RegT toReg() const noexcept { return RegT(BaseReg::SignatureAndId(_signature, _id)); } //! \} }; @@ -927,46 +1006,28 @@ struct RegOnly { //! \note It's tricky to pack all possible cases that define a memory operand //! into just 16 bytes. The `BaseMem` splits data into the following parts: //! -//! BASE - Base register or label - requires 36 bits total. 4 bits are used to -//! encode the type of the BASE operand (label vs. register type) and -//! the remaining 32 bits define the BASE id, which can be a physical or -//! virtual register index. If BASE type is zero, which is never used as -//! a register-type and label doesn't use it as well then BASE field -//! contains a high DWORD of a possible 64-bit absolute address, which is -//! possible on X64. +//! - BASE - Base register or label - requires 36 bits total. 4 bits are used +//! to encode the type of the BASE operand (label vs. register type) and the +//! remaining 32 bits define the BASE id, which can be a physical or virtual +//! register index. If BASE type is zero, which is never used as a register +//! type and label doesn't use it as well then BASE field contains a high +//! DWORD of a possible 64-bit absolute address, which is possible on X64. //! -//! INDEX - Index register (or theoretically Label, which doesn't make sense). -//! Encoding is similar to BASE - it also requires 36 bits and splits -//! the encoding to INDEX type (4 bits defining the register type) and -//! id (32-bits). +//! - INDEX - Index register (or theoretically Label, which doesn't make sense). +//! Encoding is similar to BASE - it also requires 36 bits and splits the +//! encoding to INDEX type (4 bits defining the register type) and id (32-bits). //! -//! OFFSET - A relative offset of the address. Basically if BASE is specified -//! the relative displacement adjusts BASE and an optional INDEX. if -//! BASE is not specified then the OFFSET should be considered as ABSOLUTE -//! address (at least on X86). In that case its low 32 bits are stored in -//! DISPLACEMENT field and the remaining high 32 bits are stored in BASE. +//! - OFFSET - A relative offset of the address. Basically if BASE is specified +//! the relative displacement adjusts BASE and an optional INDEX. if BASE is +//! not specified then the OFFSET should be considered as ABSOLUTE address (at +//! least on X86). In that case its low 32 bits are stored in DISPLACEMENT +//! field and the remaining high 32 bits are stored in BASE. //! -//! OTHER - There is rest 8 bits that can be used for whatever purpose. The -//! x86::Mem operand uses these bits to store segment override prefix and -//! index shift (scale). +//! - OTHER - There is rest 8 bits that can be used for whatever purpose. For +//! example \ref x86::Mem operand uses these bits to store segment override +//! prefix and index shift (or scale). class BaseMem : public Operand { public: - //! Address type. - enum AddrType : uint32_t { - //! Default address type, Assembler will select the best type when necessary. - kAddrTypeDefault = 0, - //! Absolute address type. - kAddrTypeAbs = 1, - //! Relative address type. - kAddrTypeRel = 2 - }; - - // Shortcuts. - enum SignatureMem : uint32_t { - kSignatureMemAbs = kAddrTypeAbs << kSignatureMemAddrTypeShift, - kSignatureMemRel = kAddrTypeRel << kSignatureMemAddrTypeShift - }; - //! \cond INTERNAL //! Used internally to construct `BaseMem` operand from decomposed data. struct Decomposed { @@ -991,6 +1052,18 @@ public: constexpr BaseMem(const BaseMem& other) noexcept : Operand(other) {} + //! Creates a `BaseMem` operand from `baseReg` and `offset`. + //! + //! \note This is an architecture independent constructor that can be used to + //! create an architecture independent memory operand to be used in portable + //! code that can handle multiple architectures. + constexpr explicit BaseMem(const BaseReg& baseReg, int32_t offset = 0) noexcept + : Operand(Globals::Init, + kOpMem | (baseReg.type() << kSignatureMemBaseTypeShift), + baseReg.id(), + 0, + uint32_t(offset)) {} + //! \cond INTERNAL //! Creates a `BaseMem` operand from 4 integers as used by `Operand_` struct. @@ -1036,24 +1109,12 @@ public: //! Clones the memory operand. constexpr BaseMem clone() const noexcept { return BaseMem(*this); } - //! Returns the address type (see \ref AddrType) of the memory operand. - //! - //! By default, address type of newly created memory operands is always \ref kAddrTypeDefault. - constexpr uint32_t addrType() const noexcept { return _getSignaturePart(); } - //! Sets the address type to `addrType`, see \ref AddrType. - inline void setAddrType(uint32_t addrType) noexcept { _setSignaturePart(addrType); } - //! Resets the address type to \ref kAddrTypeDefault. - inline void resetAddrType() noexcept { _setSignaturePart(0); } - - //! Tests whether the address type is \ref kAddrTypeAbs. - constexpr bool isAbs() const noexcept { return addrType() == kAddrTypeAbs; } - //! Sets the address type to \ref kAddrTypeAbs. - inline void setAbs() noexcept { setAddrType(kAddrTypeAbs); } - - //! Tests whether the address type is \ref kAddrTypeRel. - constexpr bool isRel() const noexcept { return addrType() == kAddrTypeRel; } - //! Sets the address type to \ref kAddrTypeRel. - inline void setRel() noexcept { setAddrType(kAddrTypeRel); } + //! Creates a new copy of this memory operand adjusted by `off`. + inline BaseMem cloneAdjusted(int64_t off) const noexcept { + BaseMem result(*this); + result.addOffset(off); + return result; + } //! Tests whether this memory operand is a register home (only used by \ref asmjit_compiler) constexpr bool isRegHome() const noexcept { return _hasSignaturePart(); } @@ -1221,20 +1282,48 @@ public: //! with any type, not just the default 64-bit int. class Imm : public Operand { public: + //! Type of the immediate. + enum Type : uint32_t { + //! Immediate is integer. + kTypeInteger = 0, + //! Immediate is a floating point stored as double-precision. + kTypeDouble = 1 + }; + //! \name Construction & Destruction //! \{ //! Creates a new immediate value (initial value is 0). - constexpr Imm() noexcept + inline constexpr Imm() noexcept : Operand(Globals::Init, kOpImm, 0, 0, 0) {} //! Creates a new immediate value from `other`. - constexpr Imm(const Imm& other) noexcept + inline constexpr Imm(const Imm& other) noexcept : Operand(other) {} - //! Creates a new signed immediate value, assigning the value to `val`. - constexpr explicit Imm(int64_t val) noexcept - : Operand(Globals::Init, kOpImm, 0, Support::unpackU32At0(val), Support::unpackU32At1(val)) {} + //! Creates a new immediate value from ARM/AArch64 specific `shift`. + inline constexpr Imm(const arm::Shift& shift) noexcept + : Operand(Globals::Init, kOpImm | (shift.op() << kSignaturePredicateShift), + 0, + Support::unpackU32At0(shift.value()), + Support::unpackU32At1(shift.value())) {} + + //! Creates a new signed immediate value, assigning the value to `val` and + //! an architecture-specific predicate to `predicate`. + //! + //! \note Predicate is currently only used by ARM architectures. + template + inline constexpr Imm(const T& val, const uint32_t predicate = 0) noexcept + : Operand(Globals::Init, kOpImm | (predicate << kSignaturePredicateShift), + 0, + Support::unpackU32At0(int64_t(val)), + Support::unpackU32At1(int64_t(val))) {} + + inline Imm(const float& val, const uint32_t predicate = 0) noexcept + : Operand(Globals::Init, kOpImm | (predicate << kSignaturePredicateShift), 0, 0, 0) { setValue(val); } + + inline Imm(const double& val, const uint32_t predicate = 0) noexcept + : Operand(Globals::Init, kOpImm | (predicate << kSignaturePredicateShift), 0, 0, 0) { setValue(val); } inline explicit Imm(Globals::NoInit_) noexcept : Operand(Globals::NoInit) {} @@ -1252,23 +1341,50 @@ public: //! \name Accessors //! \{ + //! Returns immediate type, see \ref Type. + constexpr uint32_t type() const noexcept { return _getSignaturePart(); } + //! Sets the immediate type to `type`, see \ref Type. + inline void setType(uint32_t type) noexcept { _setSignaturePart(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(); } + + //! 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(predicate); } + + //! Resets the shift operation type of the immediate to the default value (no operation). + inline void resetPredicate() noexcept { _setSignaturePart(0); } + //! Returns the immediate value as `int64_t`, which is the internal format Imm uses. constexpr int64_t value() const noexcept { return int64_t((uint64_t(_data[kDataImmValueHi]) << 32) | _data[kDataImmValueLo]); } + //! Tests whether this immediate value is integer of any size. + constexpr uint32_t isInteger() const noexcept { return type() == kTypeInteger; } + //! Tests whether this immediate value is a double precision floating point value. + constexpr uint32_t isDouble() const noexcept { return type() == kTypeDouble; } + //! Tests whether the immediate can be casted to 8-bit signed integer. - constexpr bool isInt8() const noexcept { return Support::isInt8(value()); } + constexpr bool isInt8() const noexcept { return type() == kTypeInteger && Support::isInt8(value()); } //! Tests whether the immediate can be casted to 8-bit unsigned integer. - constexpr bool isUInt8() const noexcept { return Support::isUInt8(value()); } + constexpr bool isUInt8() const noexcept { return type() == kTypeInteger && Support::isUInt8(value()); } //! Tests whether the immediate can be casted to 16-bit signed integer. - constexpr bool isInt16() const noexcept { return Support::isInt16(value()); } + constexpr bool isInt16() const noexcept { return type() == kTypeInteger && Support::isInt16(value()); } //! Tests whether the immediate can be casted to 16-bit unsigned integer. - constexpr bool isUInt16() const noexcept { return Support::isUInt16(value()); } + constexpr bool isUInt16() const noexcept { return type() == kTypeInteger && Support::isUInt16(value()); } //! Tests whether the immediate can be casted to 32-bit signed integer. - constexpr bool isInt32() const noexcept { return Support::isInt32(value()); } + constexpr bool isInt32() const noexcept { return type() == kTypeInteger && Support::isInt32(value()); } //! Tests whether the immediate can be casted to 32-bit unsigned integer. - constexpr bool isUInt32() const noexcept { return _data[kDataImmValueHi] == 0; } + constexpr bool isUInt32() const noexcept { return type() == kTypeInteger && _data[kDataImmValueHi] == 0; } //! Returns the immediate value casted to `T`. //! @@ -1276,9 +1392,7 @@ public: //! simply the representation of `T` considering the original value's lowest //! bits. template - constexpr T valueAs() const noexcept { - return T(uint64_t(value()) & Support::allOnes::type>()); - } + inline T valueAs() const noexcept { return Support::immediateToT(value()); } //! Returns low 32-bit signed integer. constexpr int32_t int32Lo() const noexcept { return int32_t(_data[kDataImmValueLo]); } @@ -1292,13 +1406,13 @@ public: //! Sets immediate value to `val`, the value is casted to a signed 64-bit integer. template inline void setValue(const T& val) noexcept { - int64_t val64 = int64_t(Support::asNormalized(val)); - _data[kDataImmValueHi] = uint32_t(uint64_t(val64) >> 32); - _data[kDataImmValueLo] = uint32_t(uint64_t(val64) & 0xFFFFFFFFu); + _setValueInternal(Support::immediateFromT(val), std::is_floating_point::value ? kTypeDouble : kTypeInteger); } - inline void setDouble(double d) noexcept { - setValue(Support::bitCast(d)); + inline void _setValueInternal(int64_t val, uint32_t type) noexcept { + setType(type); + _data[kDataImmValueHi] = uint32_t(uint64_t(val) >> 32); + _data[kDataImmValueLo] = uint32_t(uint64_t(val) & 0xFFFFFFFFu); } //! \} @@ -1369,9 +1483,7 @@ public: //! Using `imm(x)` is much nicer than using `Imm(x)` as this is a template //! which can accept any integer including pointers and function pointers. template -static constexpr Imm imm(T val) noexcept { - return Imm(std::is_signed::value ? int64_t(val) : int64_t(uint64_t(val))); -} +static constexpr Imm imm(const T& val) noexcept { return Imm(val); } //! \} diff --git a/src/asmjit/core/raassignment_p.h b/src/asmjit/core/raassignment_p.h index 2618afd..bcdf1a9 100644 --- a/src/asmjit/core/raassignment_p.h +++ b/src/asmjit/core/raassignment_p.h @@ -54,6 +54,17 @@ public: }; struct Layout { + //! Index of architecture registers per group. + RARegIndex physIndex; + //! Count of architecture registers per group. + RARegCount physCount; + //! Count of physical registers of all groups. + uint32_t physTotal; + //! Count of work registers. + uint32_t workCount; + //! WorkRegs data (vector). + const RAWorkRegs* workRegs; + inline void reset() noexcept { physIndex.reset(); physCount.reset(); @@ -61,54 +72,52 @@ public: workCount = 0; workRegs = nullptr; } - - RARegIndex physIndex; //!< Index of architecture registers per group. - RARegCount physCount; //!< Count of architecture registers per group. - uint32_t physTotal; //!< Count of physical registers of all groups. - uint32_t workCount; //!< Count of work registers. - const RAWorkRegs* workRegs; //!< WorkRegs data (vector). }; struct PhysToWorkMap { - static inline size_t sizeOf(uint32_t count) noexcept { - return sizeof(PhysToWorkMap) - sizeof(uint32_t) + size_t(count) * sizeof(uint32_t); + //! Assigned registers (each bit represents one physical reg). + RARegMask assigned; + //! Dirty registers (spill slot out of sync or no spill slot). + RARegMask dirty; + //! PhysReg to WorkReg mapping. + uint32_t workIds[1 /* ... */]; + + static inline size_t sizeOf(size_t count) noexcept { + return sizeof(PhysToWorkMap) - sizeof(uint32_t) + count * sizeof(uint32_t); } - inline void reset(uint32_t count) noexcept { + inline void reset(size_t count) noexcept { assigned.reset(); dirty.reset(); - for (uint32_t i = 0; i < count; i++) + for (size_t i = 0; i < count; i++) workIds[i] = kWorkNone; } - inline void copyFrom(const PhysToWorkMap* other, uint32_t count) noexcept { + inline void copyFrom(const PhysToWorkMap* other, size_t count) noexcept { size_t size = sizeOf(count); memcpy(this, other, size); } - - RARegMask assigned; //!< Assigned registers (each bit represents one physical reg). - RARegMask dirty; //!< Dirty registers (spill slot out of sync or no spill slot). - uint32_t workIds[1 /* ... */]; //!< PhysReg to WorkReg mapping. }; struct WorkToPhysMap { - static inline size_t sizeOf(uint32_t count) noexcept { + //! WorkReg to PhysReg mapping + uint8_t physIds[1 /* ... */]; + + static inline size_t sizeOf(size_t count) noexcept { return size_t(count) * sizeof(uint8_t); } - inline void reset(uint32_t count) noexcept { - for (uint32_t i = 0; i < count; i++) + inline void reset(size_t count) noexcept { + for (size_t i = 0; i < count; i++) physIds[i] = kPhysNone; } - inline void copyFrom(const WorkToPhysMap* other, uint32_t count) noexcept { + inline void copyFrom(const WorkToPhysMap* other, size_t count) noexcept { size_t size = sizeOf(count); if (ASMJIT_LIKELY(size)) memcpy(this, other, size); } - - uint8_t physIds[1 /* ... */]; //!< WorkReg to PhysReg mapping }; //! Physical registers layout. diff --git a/src/asmjit/core/rabuilders_p.h b/src/asmjit/core/rabuilders_p.h index e14d47f..0360a78 100644 --- a/src/asmjit/core/rabuilders_p.h +++ b/src/asmjit/core/rabuilders_p.h @@ -37,29 +37,28 @@ ASMJIT_BEGIN_NAMESPACE //! \{ // ============================================================================ -// [asmjit::RACFGBuilder] +// [asmjit::RACFGBuilderT] // ============================================================================ template -class RACFGBuilder { +class RACFGBuilderT { public: - RAPass* _pass; - BaseCompiler* _cc; - - RABlock* _curBlock; - RABlock* _retBlock; - FuncNode* _funcNode; - RARegsStats _blockRegStats; - uint32_t _exitLabelId; - ZoneVector _sharedAssignmentsMap; + BaseRAPass* _pass = nullptr; + BaseCompiler* _cc = nullptr; + RABlock* _curBlock = nullptr; + RABlock* _retBlock = nullptr; + FuncNode* _funcNode = nullptr; + RARegsStats _blockRegStats {}; + uint32_t _exitLabelId = Globals::kInvalidId; + ZoneVector _sharedAssignmentsMap {}; // Only used by logging, it's fine to be here to prevent more #ifdefs... - bool _hasCode; - RABlock* _lastLoggedBlock; + bool _hasCode = false; + RABlock* _lastLoggedBlock = nullptr; #ifndef ASMJIT_NO_LOGGING - Logger* _logger; - uint32_t _logFlags; + Logger* _logger = nullptr; + uint32_t _logFlags = FormatOptions::kFlagPositions; StringTmp<512> _sb; #endif @@ -72,20 +71,11 @@ public: // position that is [at that time] unassigned. static constexpr uint32_t kNodePositionDidOnBefore = 0xFFFFFFFFu; - inline RACFGBuilder(RAPass* pass) noexcept + inline RACFGBuilderT(BaseRAPass* pass) noexcept : _pass(pass), - _cc(pass->cc()), - _curBlock(nullptr), - _retBlock(nullptr), - _funcNode(nullptr), - _blockRegStats{}, - _exitLabelId(Globals::kInvalidId), - _hasCode(false), - _lastLoggedBlock(nullptr) { + _cc(pass->cc()) { #ifndef ASMJIT_NO_LOGGING _logger = _pass->debugLogger(); - _logFlags = FormatOptions::kFlagPositions; - if (_logger) _logFlags |= _logger->flags(); #endif diff --git a/src/asmjit/core/radefs_p.h b/src/asmjit/core/radefs_p.h index 7b3113e..5395542 100644 --- a/src/asmjit/core/radefs_p.h +++ b/src/asmjit/core/radefs_p.h @@ -25,11 +25,12 @@ #define ASMJIT_CORE_RADEFS_P_H_INCLUDED #include "../core/api-config.h" -#ifndef ASMJIT_NO_COMPILER - -#include "../core/compiler.h" +#include "../core/archtraits.h" +#include "../core/compilerdefs.h" #include "../core/logger.h" +#include "../core/operand.h" #include "../core/support.h" +#include "../core/type.h" #include "../core/zone.h" #include "../core/zonevector.h" @@ -64,13 +65,52 @@ ASMJIT_BEGIN_NAMESPACE // [Forward Declarations] // ============================================================================ -class RAPass; +class BaseRAPass; class RABlock; +class BaseNode; struct RAStackSlot; typedef ZoneVector RABlocks; typedef ZoneVector 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(registerCount) & ~Support::bitMask(4u); + _availableRegs[BaseReg::kGroupVec] = Support::lsbMask(registerCount); + _availableRegs[BaseReg::kGroupOther0] = Support::lsbMask(8); + _availableRegs[BaseReg::kGroupOther1] = Support::lsbMask(8); + return kErrorOk; + } + + case Environment::kArchAArch64: { + _availableRegs[BaseReg::kGroupGp] = 0xFFFFFFFFu & ~Support::bitMask(18, 31u); + _availableRegs[BaseReg::kGroupVec] = 0xFFFFFFFFu; + _availableRegs[BaseReg::kGroupOther0] = 0; + _availableRegs[BaseReg::kGroupOther1] = 0; + return kErrorOk; + } + + default: + return DebugUtils::errored(kErrorInvalidArch); + } + } + + inline uint32_t availableRegs(uint32_t group) const noexcept { return _availableRegs[group]; } +}; + // ============================================================================ // [asmjit::RAStrategy] // ============================================================================ @@ -93,45 +133,6 @@ struct RAStrategy { inline bool isComplex() const noexcept { return _type >= kStrategyComplex; } }; -// ============================================================================ -// [asmjit::RAArchTraits] -// ============================================================================ - -//! Traits. -struct RAArchTraits { - enum Flags : uint32_t { - //! Registers can be swapped by a single instruction. - kHasSwap = 0x01u - }; - - uint8_t _flags[BaseReg::kGroupVirt]; - - //! \name Construction & Destruction - //! \{ - - inline RAArchTraits() noexcept { reset(); } - inline void reset() noexcept { memset(_flags, 0, sizeof(_flags)); } - - //! \} - - //! \name Accessors - //! \{ - - inline bool hasFlag(uint32_t group, uint32_t flag) const noexcept { return (_flags[group] & flag) != 0; } - inline bool hasSwap(uint32_t group) const noexcept { return hasFlag(group, kHasSwap); } - - inline uint8_t& operator[](uint32_t group) noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - return _flags[group]; - } - - inline const uint8_t& operator[](uint32_t group) const noexcept { - ASMJIT_ASSERT(group < BaseReg::kGroupVirt); - return _flags[group]; - } - - //! \} -}; // ============================================================================ // [asmjit::RARegCount] @@ -317,8 +318,9 @@ struct RARegMask { //! before the register allocator tries to do its job. For example to use fast //! register allocation inside a block or loop it cannot have clobbered and/or //! fixed registers, etc... -struct RARegsStats { - uint32_t _packed; +class RARegsStats { +public: + uint32_t _packed = 0; enum Index : uint32_t { kIndexUsed = 0, @@ -355,12 +357,12 @@ struct RARegsStats { //! Count of live registers, per group. class RALiveCount { public: - uint32_t n[BaseReg::kGroupVirt]; + uint32_t n[BaseReg::kGroupVirt] {}; //! \name Construction & Destruction //! \{ - inline RALiveCount() noexcept { reset(); } + inline RALiveCount() noexcept = default; inline RALiveCount(const RALiveCount& other) noexcept = default; inline void init(const RALiveCount& other) noexcept { @@ -674,19 +676,9 @@ public: //! Statistics about a register liveness. class RALiveStats { public: - uint32_t _width; - float _freq; - float _priority; - - //! \name Construction & Destruction - //! \{ - - inline RALiveStats() - : _width(0), - _freq(0.0f), - _priority(0.0f) {} - - //! \} + uint32_t _width = 0; + float _freq = 0.0f; + float _priority = 0.0f; //! \name Accessors //! \{ @@ -928,46 +920,46 @@ public: ASMJIT_NONCOPYABLE(RAWorkReg) //! RAPass specific ID used during analysis and allocation. - uint32_t _workId; - //! Copy of ID used by `VirtReg`. - uint32_t _virtId; + uint32_t _workId = 0; + //! Copy of ID used by \ref VirtReg. + uint32_t _virtId = 0; - //! Permanent association with `VirtReg`. - VirtReg* _virtReg; - //! Temporary association with `RATiedReg`. - RATiedReg* _tiedReg; + //! Permanent association with \ref VirtReg. + VirtReg* _virtReg = nullptr; + //! Temporary association with \ref RATiedReg. + RATiedReg* _tiedReg = nullptr; //! Stack slot associated with the register. - RAStackSlot* _stackSlot; + RAStackSlot* _stackSlot = nullptr; - //! Copy of a signature used by `VirtReg`. - RegInfo _info; + //! Copy of a signature used by \ref VirtReg. + RegInfo _info {}; //! RAPass specific flags used during analysis and allocation. - uint32_t _flags; + uint32_t _flags = 0; //! IDs of all physical registers this WorkReg has been allocated to. - uint32_t _allocatedMask; + uint32_t _allocatedMask = 0; //! IDs of all physical registers that are clobbered during the lifetime of //! this WorkReg. //! //! This mask should be updated by `RAPass::buildLiveness()`, because it's //! global and should be updated after unreachable code has been removed. - uint32_t _clobberSurvivalMask; + uint32_t _clobberSurvivalMask = 0; //! A byte-mask where each bit represents one valid byte of the register. - uint64_t _regByteMask; + uint64_t _regByteMask = 0; //! Argument index (or `kNoArgIndex` if none). - uint8_t _argIndex; + uint8_t _argIndex = kNoArgIndex; //! Argument value index in the pack (0 by default). - uint8_t _argValueIndex; + uint8_t _argValueIndex = 0; //! Global home register ID (if any, assigned by RA). - uint8_t _homeRegId; + uint8_t _homeRegId = BaseReg::kIdBad; //! Global hint register ID (provided by RA or user). - uint8_t _hintRegId; + uint8_t _hintRegId = BaseReg::kIdBad; //! Live spans of the `VirtReg`. - LiveRegSpans _liveSpans; + LiveRegSpans _liveSpans {}; //! Live statistics. - RALiveStats _liveStats; + RALiveStats _liveStats {}; //! All nodes that read/write this VirtReg/WorkReg. ZoneVector _refs; @@ -1000,20 +992,7 @@ public: : _workId(workId), _virtId(vReg->id()), _virtReg(vReg), - _tiedReg(nullptr), - _stackSlot(nullptr), - _info(vReg->info()), - _flags(0), - _allocatedMask(0), - _clobberSurvivalMask(0), - _regByteMask(0), - _argIndex(kNoArgIndex), - _argValueIndex(0), - _homeRegId(BaseReg::kIdBad), - _hintRegId(BaseReg::kIdBad), - _liveSpans(), - _liveStats(), - _refs() {} + _info(vReg->info()) {} //! \} @@ -1095,5 +1074,4 @@ public: ASMJIT_END_NAMESPACE -#endif // !ASMJIT_NO_COMPILER #endif // ASMJIT_CORE_RADEFS_P_H_INCLUDED diff --git a/src/asmjit/core/ralocal.cpp b/src/asmjit/core/ralocal.cpp index 5ae4880..35f6560 100644 --- a/src/asmjit/core/ralocal.cpp +++ b/src/asmjit/core/ralocal.cpp @@ -243,7 +243,7 @@ Error RALocalAllocator::switchToAssignment( // Reset as we will do some changes to the current assignment. runId = -1; - if (_archTraits.hasSwap(group)) { + if (_archTraits->hasSwap(group)) { ASMJIT_PROPAGATE(onSwapReg(group, curWorkId, physId, dstWorkId, altPhysId)); } else { @@ -653,7 +653,7 @@ Error RALocalAllocator::allocInst(InstNode* node) noexcept { // just a single instruction. However, swap is only available on few // architectures and it's definitely not available for each register // group. Calling `onSwapReg()` before checking these would be fatal. - if (_archTraits.hasSwap(group) && thisPhysId != RAAssignment::kPhysNone) { + if (_archTraits->hasSwap(group) && thisPhysId != RAAssignment::kPhysNone) { ASMJIT_PROPAGATE(onSwapReg(group, thisWorkId, thisPhysId, targetWorkId, targetPhysId)); thisTiedReg->markUseDone(); @@ -767,7 +767,7 @@ Error RALocalAllocator::allocInst(InstNode* node) noexcept { uint32_t dstId = it.next(); if (dstId == srcId) continue; - _pass->onEmitMove(workId, dstId, srcId); + _pass->emitMove(workId, dstId, srcId); } } @@ -916,7 +916,7 @@ Error RALocalAllocator::allocBranch(InstNode* node, RABlock* target, RABlock* co node->clearInstOptions(BaseInst::kOptionShortForm); // Finalize the switch assignment sequence. - ASMJIT_PROPAGATE(_pass->onEmitJump(savedTarget)); + ASMJIT_PROPAGATE(_pass->emitJump(savedTarget)); _cc->_setCursor(injectionPoint); _cc->bind(trampoline); } @@ -932,6 +932,7 @@ Error RALocalAllocator::allocBranch(InstNode* node, RABlock* target, RABlock* co } Error RALocalAllocator::allocJumpTable(InstNode* node, const RABlocks& targets, RABlock* cont) noexcept { + // TODO: Do we really need to use `cont`? DebugUtils::unused(cont); if (targets.empty()) diff --git a/src/asmjit/core/ralocal_p.h b/src/asmjit/core/ralocal_p.h index 911acca..eebecc9 100644 --- a/src/asmjit/core/ralocal_p.h +++ b/src/asmjit/core/ralocal_p.h @@ -50,13 +50,13 @@ public: typedef RAAssignment::PhysToWorkMap PhysToWorkMap; typedef RAAssignment::WorkToPhysMap WorkToPhysMap; - //! Link to `RAPass`. - RAPass* _pass; + //! Link to `BaseRAPass`. + BaseRAPass* _pass; //! Link to `BaseCompiler`. BaseCompiler* _cc; //! Architecture traits. - RAArchTraits _archTraits; + const ArchTraits* _archTraits; //! Registers available to the allocator. RARegMask _availableRegs; //! Registers clobbered by the allocator. @@ -82,7 +82,7 @@ public: //! \name Construction & Destruction //! \{ - inline RALocalAllocator(RAPass* pass) noexcept + inline RALocalAllocator(BaseRAPass* pass) noexcept : _pass(pass), _cc(pass->cc()), _archTraits(pass->_archTraits), @@ -219,7 +219,7 @@ public: inline Error onMoveReg(uint32_t group, uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept { if (dstPhysId == srcPhysId) return kErrorOk; _curAssignment.reassign(group, workId, dstPhysId, srcPhysId); - return _pass->onEmitMove(workId, dstPhysId, srcPhysId); + return _pass->emitMove(workId, dstPhysId, srcPhysId); } //! Emits a swap between two physical registers and fixes their assignment. @@ -227,14 +227,14 @@ public: //! \note Target must support this operation otherwise this would ASSERT. inline Error onSwapReg(uint32_t group, uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept { _curAssignment.swap(group, aWorkId, aPhysId, bWorkId, bPhysId); - return _pass->onEmitSwap(aWorkId, aPhysId, bWorkId, bPhysId); + return _pass->emitSwap(aWorkId, aPhysId, bWorkId, bPhysId); } //! Emits a load from [VirtReg/WorkReg]'s spill slot to a physical register //! and makes it assigned and clean. inline Error onLoadReg(uint32_t group, uint32_t workId, uint32_t physId) noexcept { _curAssignment.assign(group, workId, physId, RAAssignment::kClean); - return _pass->onEmitLoad(workId, physId); + return _pass->emitLoad(workId, physId); } //! Emits a save a physical register to a [VirtReg/WorkReg]'s spill slot, @@ -244,7 +244,7 @@ public: ASMJIT_ASSERT(_curAssignment.physToWorkId(group, physId) == workId); _curAssignment.makeClean(group, workId, physId); - return _pass->onEmitSave(workId, physId); + return _pass->emitSave(workId, physId); } //! Assigns a register, the content of it is undefined at this point. diff --git a/src/asmjit/core/rapass.cpp b/src/asmjit/core/rapass.cpp index 53f6511..270027a 100644 --- a/src/asmjit/core/rapass.cpp +++ b/src/asmjit/core/rapass.cpp @@ -70,50 +70,17 @@ Error RABlock::prependSuccessor(RABlock* successor) noexcept { } // ============================================================================ -// [asmjit::RAPass - Construction / Destruction] +// [asmjit::BaseRAPass - Construction / Destruction] // ============================================================================ -RAPass::RAPass() noexcept - : FuncPass("RAPass"), - _allocator(), - _logger(nullptr), - _debugLogger(nullptr), - _loggerFlags(0), - _func(nullptr), - _stop(nullptr), - _extraBlock(nullptr), - _blocks(), - _exits(), - _pov(), - _instructionCount(0), - _createdBlockCount(0), - _sharedAssignments(), - _lastTimestamp(0), - _archRegsInfo(nullptr), - _archTraits(), - _physRegIndex(), - _physRegCount(), - _physRegTotal(0), - _scratchRegIndexes{}, - _availableRegs(), - _availableRegCount(), - _clobberedRegs(), - _globalMaxLiveCount(), - _globalLiveSpans {}, - _temporaryMem(), - _sp(), - _fp(), - _stackAllocator(), - _argsAssignment(), - _numStackArgsToStackSlots(0), - _maxWorkRegNameSize(0) {} -RAPass::~RAPass() noexcept {} +BaseRAPass::BaseRAPass() noexcept : FuncPass("BaseRAPass") {} +BaseRAPass::~BaseRAPass() noexcept {} // ============================================================================ -// [asmjit::RAPass - RunOnFunction] +// [asmjit::BaseRAPass - RunOnFunction] // ============================================================================ -static void RAPass_reset(RAPass* self, FuncDetail* funcDetail) noexcept { +static void RAPass_reset(BaseRAPass* self, FuncDetail* funcDetail) noexcept { ZoneAllocator* allocator = self->allocator(); self->_blocks.reset(); @@ -126,8 +93,7 @@ static void RAPass_reset(RAPass* self, FuncDetail* funcDetail) noexcept { self->_sharedAssignments.reset(); self->_lastTimestamp = 0; - self->_archRegsInfo = nullptr; - self->_archTraits.reset(); + self->_archTraits = nullptr; self->_physRegIndex.reset(); self->_physRegCount.reset(); self->_physRegTotal = 0; @@ -154,7 +120,7 @@ static void RAPass_reset(RAPass* self, FuncDetail* funcDetail) noexcept { self->_maxWorkRegNameSize = 0; } -static void RAPass_resetVirtRegData(RAPass* self) noexcept { +static void RAPass_resetVirtRegData(BaseRAPass* self) noexcept { // Zero everything so it cannot be used by accident. for (RAWorkReg* wReg : self->_workRegs) { VirtReg* vReg = wReg->virtReg(); @@ -162,7 +128,7 @@ static void RAPass_resetVirtRegData(RAPass* self) noexcept { } } -Error RAPass::runOnFunction(Zone* zone, Logger* logger, FuncNode* func) { +Error BaseRAPass::runOnFunction(Zone* zone, Logger* logger, FuncNode* func) { _allocator.reset(zone); #ifndef ASMJIT_NO_LOGGING @@ -223,7 +189,7 @@ Error RAPass::runOnFunction(Zone* zone, Logger* logger, FuncNode* func) { return err; } -Error RAPass::onPerformAllSteps() noexcept { +Error BaseRAPass::onPerformAllSteps() noexcept { ASMJIT_PROPAGATE(buildCFG()); ASMJIT_PROPAGATE(buildViews()); ASMJIT_PROPAGATE(removeUnreachableBlocks()); @@ -249,10 +215,10 @@ Error RAPass::onPerformAllSteps() noexcept { } // ============================================================================ -// [asmjit::RAPass - CFG - Basic Block Management] +// [asmjit::BaseRAPass - CFG - Basic Block Management] // ============================================================================ -RABlock* RAPass::newBlock(BaseNode* initialNode) noexcept { +RABlock* BaseRAPass::newBlock(BaseNode* initialNode) noexcept { RABlock* block = zone()->newT(this); if (ASMJIT_UNLIKELY(!block)) return nullptr; @@ -264,7 +230,7 @@ RABlock* RAPass::newBlock(BaseNode* initialNode) noexcept { return block; } -RABlock* RAPass::newBlockOrExistingAt(LabelNode* cbLabel, BaseNode** stoppedAt) noexcept { +RABlock* BaseRAPass::newBlockOrExistingAt(LabelNode* cbLabel, BaseNode** stoppedAt) noexcept { if (cbLabel->hasPassData()) return cbLabel->passData(); @@ -351,7 +317,7 @@ RABlock* RAPass::newBlockOrExistingAt(LabelNode* cbLabel, BaseNode** stoppedAt) return block; } -Error RAPass::addBlock(RABlock* block) noexcept { +Error BaseRAPass::addBlock(RABlock* block) noexcept { ASMJIT_PROPAGATE(_blocks.willGrow(allocator())); block->_blockId = blockCount(); @@ -360,10 +326,10 @@ Error RAPass::addBlock(RABlock* block) noexcept { } // ============================================================================ -// [asmjit::RAPass - CFG - Build] +// [asmjit::BaseRAPass - CFG - Build] // ============================================================================ -Error RAPass::initSharedAssignments(const ZoneVector& sharedAssignmentsMap) noexcept { +Error BaseRAPass::initSharedAssignments(const ZoneVector& sharedAssignmentsMap) noexcept { if (sharedAssignmentsMap.empty()) return kErrorOk; @@ -403,7 +369,7 @@ Error RAPass::initSharedAssignments(const ZoneVector& sharedAssignment } // ============================================================================ -// [asmjit::RAPass - CFG - Views Order] +// [asmjit::BaseRAPass - CFG - Views Order] // ============================================================================ class RABlockVisitItem { @@ -425,7 +391,7 @@ public: uint32_t _index; }; -Error RAPass::buildViews() noexcept { +Error BaseRAPass::buildViews() noexcept { #ifndef ASMJIT_NO_LOGGING Logger* logger = debugLogger(); ASMJIT_RA_LOG_FORMAT("[RAPass::BuildViews]\n"); @@ -497,7 +463,7 @@ Error RAPass::buildViews() noexcept { } // ============================================================================ -// [asmjit::RAPass - CFG - Dominators] +// [asmjit::BaseRAPass - CFG - Dominators] // ============================================================================ static ASMJIT_INLINE RABlock* intersectBlocks(RABlock* b1, RABlock* b2) noexcept { @@ -509,7 +475,7 @@ static ASMJIT_INLINE RABlock* intersectBlocks(RABlock* b1, RABlock* b2) noexcept } // Based on "A Simple, Fast Dominance Algorithm". -Error RAPass::buildDominators() noexcept { +Error BaseRAPass::buildDominators() noexcept { #ifndef ASMJIT_NO_LOGGING Logger* logger = debugLogger(); ASMJIT_RA_LOG_FORMAT("[RAPass::BuildDominators]\n"); @@ -556,7 +522,7 @@ Error RAPass::buildDominators() noexcept { return kErrorOk; } -bool RAPass::_strictlyDominates(const RABlock* a, const RABlock* b) const noexcept { +bool BaseRAPass::_strictlyDominates(const RABlock* a, const RABlock* b) const noexcept { ASMJIT_ASSERT(a != nullptr); // There must be at least one block if this function is ASMJIT_ASSERT(b != nullptr); // called, as both `a` and `b` must be valid blocks. ASMJIT_ASSERT(a != b); // Checked by `dominates()` and `strictlyDominates()`. @@ -573,7 +539,7 @@ bool RAPass::_strictlyDominates(const RABlock* a, const RABlock* b) const noexce return iDom != entryBlock; } -const RABlock* RAPass::_nearestCommonDominator(const RABlock* a, const RABlock* b) const noexcept { +const RABlock* BaseRAPass::_nearestCommonDominator(const RABlock* a, const RABlock* b) const noexcept { ASMJIT_ASSERT(a != nullptr); // There must be at least one block if this function is ASMJIT_ASSERT(b != nullptr); // called, as both `a` and `b` must be valid blocks. ASMJIT_ASSERT(a != b); // Checked by `dominates()` and `properlyDominates()`. @@ -611,10 +577,10 @@ const RABlock* RAPass::_nearestCommonDominator(const RABlock* a, const RABlock* } // ============================================================================ -// [asmjit::RAPass - CFG - Utilities] +// [asmjit::BaseRAPass - CFG - Utilities] // ============================================================================ -Error RAPass::removeUnreachableBlocks() noexcept { +Error BaseRAPass::removeUnreachableBlocks() noexcept { uint32_t numAllBlocks = blockCount(); uint32_t numReachableBlocks = reachableBlockCount(); @@ -661,13 +627,13 @@ Error RAPass::removeUnreachableBlocks() noexcept { return kErrorOk; } -BaseNode* RAPass::findSuccessorStartingAt(BaseNode* node) noexcept { +BaseNode* BaseRAPass::findSuccessorStartingAt(BaseNode* node) noexcept { while (node && (node->isInformative() || node->hasNoEffect())) node = node->next(); return node; } -bool RAPass::isNextTo(BaseNode* node, BaseNode* target) noexcept { +bool BaseRAPass::isNextTo(BaseNode* node, BaseNode* target) noexcept { for (;;) { node = node->next(); if (node == target) @@ -682,10 +648,10 @@ bool RAPass::isNextTo(BaseNode* node, BaseNode* target) noexcept { } // ============================================================================ -// [asmjit::RAPass - ?] +// [asmjit::BaseRAPass - ?] // ============================================================================ -Error RAPass::_asWorkReg(VirtReg* vReg, RAWorkReg** out) noexcept { +Error BaseRAPass::_asWorkReg(VirtReg* vReg, RAWorkReg** out) noexcept { // Checked by `asWorkReg()` - must be true. ASMJIT_ASSERT(vReg->_workReg == nullptr); @@ -715,7 +681,7 @@ Error RAPass::_asWorkReg(VirtReg* vReg, RAWorkReg** out) noexcept { return kErrorOk; } -RAAssignment::WorkToPhysMap* RAPass::newWorkToPhysMap() noexcept { +RAAssignment::WorkToPhysMap* BaseRAPass::newWorkToPhysMap() noexcept { uint32_t count = workRegCount(); size_t size = WorkToPhysMap::sizeOf(count); @@ -734,7 +700,7 @@ RAAssignment::WorkToPhysMap* RAPass::newWorkToPhysMap() noexcept { return map; } -RAAssignment::PhysToWorkMap* RAPass::newPhysToWorkMap() noexcept { +RAAssignment::PhysToWorkMap* BaseRAPass::newPhysToWorkMap() noexcept { uint32_t count = physRegTotal(); size_t size = PhysToWorkMap::sizeOf(count); @@ -747,7 +713,7 @@ RAAssignment::PhysToWorkMap* RAPass::newPhysToWorkMap() noexcept { } // ============================================================================ -// [asmjit::RAPass - Registers - Liveness Analysis and Statistics] +// [asmjit::BaseRAPass - Registers - Liveness Analysis and Statistics] // ============================================================================ namespace LiveOps { @@ -823,7 +789,7 @@ namespace LiveOps { } } -ASMJIT_FAVOR_SPEED Error RAPass::buildLiveness() noexcept { +ASMJIT_FAVOR_SPEED Error BaseRAPass::buildLiveness() noexcept { #ifndef ASMJIT_NO_LOGGING Logger* logger = debugLogger(); StringTmp<512> sb; @@ -1106,7 +1072,7 @@ ASMJIT_FAVOR_SPEED Error RAPass::buildLiveness() noexcept { return kErrorOk; } -Error RAPass::assignArgIndexToWorkRegs() noexcept { +Error BaseRAPass::assignArgIndexToWorkRegs() noexcept { ZoneBitVector& liveIn = entryBlock()->liveIn(); uint32_t argCount = func()->argCount(); @@ -1130,7 +1096,7 @@ Error RAPass::assignArgIndexToWorkRegs() noexcept { workReg->setArgIndex(argIndex, valueIndex); const FuncValue& arg = func()->detail().arg(argIndex, valueIndex); - if (arg.isReg() && _archRegsInfo->regInfo[arg.regType()].group() == workReg->group()) { + if (arg.isReg() && _archTraits->regTypeToGroup(arg.regType()) == workReg->group()) { workReg->setHintRegId(arg.regId()); } } @@ -1138,8 +1104,9 @@ Error RAPass::assignArgIndexToWorkRegs() noexcept { return kErrorOk; } + // ============================================================================ -// [asmjit::RAPass - Allocation - Global] +// [asmjit::BaseRAPass - Allocation - Global] // ============================================================================ #ifndef ASMJIT_NO_LOGGING @@ -1156,7 +1123,7 @@ static void RAPass_dumpSpans(String& sb, uint32_t index, const LiveRegSpans& liv } #endif -Error RAPass::runGlobalAllocator() noexcept { +Error BaseRAPass::runGlobalAllocator() noexcept { ASMJIT_PROPAGATE(initGlobalLiveSpans()); for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) { @@ -1166,16 +1133,19 @@ Error RAPass::runGlobalAllocator() noexcept { return kErrorOk; } -ASMJIT_FAVOR_SPEED Error RAPass::initGlobalLiveSpans() noexcept { +ASMJIT_FAVOR_SPEED Error BaseRAPass::initGlobalLiveSpans() noexcept { for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) { size_t physCount = _physRegCount[group]; - LiveRegSpans* liveSpans = allocator()->allocT(physCount * sizeof(LiveRegSpans)); + LiveRegSpans* liveSpans = nullptr; - if (ASMJIT_UNLIKELY(!liveSpans)) - return DebugUtils::errored(kErrorOutOfMemory); + if (physCount) { + liveSpans = allocator()->allocT(physCount * sizeof(LiveRegSpans)); + if (ASMJIT_UNLIKELY(!liveSpans)) + return DebugUtils::errored(kErrorOutOfMemory); - for (size_t physId = 0; physId < physCount; physId++) - new(&liveSpans[physId]) LiveRegSpans(); + for (size_t physId = 0; physId < physCount; physId++) + new(&liveSpans[physId]) LiveRegSpans(); + } _globalLiveSpans[group] = liveSpans; } @@ -1183,7 +1153,7 @@ ASMJIT_FAVOR_SPEED Error RAPass::initGlobalLiveSpans() noexcept { return kErrorOk; } -ASMJIT_FAVOR_SPEED Error RAPass::binPack(uint32_t group) noexcept { +ASMJIT_FAVOR_SPEED Error BaseRAPass::binPack(uint32_t group) noexcept { if (workRegCount(group) == 0) return kErrorOk; @@ -1323,10 +1293,10 @@ ASMJIT_FAVOR_SPEED Error RAPass::binPack(uint32_t group) noexcept { } // ============================================================================ -// [asmjit::RAPass - Allocation - Local] +// [asmjit::BaseRAPass - Allocation - Local] // ============================================================================ -Error RAPass::runLocalAllocator() noexcept { +Error BaseRAPass::runLocalAllocator() noexcept { RALocalAllocator lra(this); ASMJIT_PROPAGATE(lra.init()); @@ -1396,7 +1366,7 @@ Error RAPass::runLocalAllocator() noexcept { ASMJIT_PROPAGATE(lra.allocInst(inst)); if (inst->type() == BaseNode::kNodeInvoke) - ASMJIT_PROPAGATE(onEmitPreCall(inst->as())); + ASMJIT_PROPAGATE(emitPreCall(inst->as())); else ASMJIT_PROPAGATE(lra.spillAfterAllocation(inst)); } @@ -1458,7 +1428,7 @@ Error RAPass::runLocalAllocator() noexcept { return kErrorOk; } -Error RAPass::setBlockEntryAssignment(RABlock* block, const RABlock* fromBlock, const RAAssignment& fromAssignment) noexcept { +Error BaseRAPass::setBlockEntryAssignment(RABlock* block, const RABlock* fromBlock, const RAAssignment& fromAssignment) noexcept { if (block->hasSharedAssignmentId()) { uint32_t sharedAssignmentId = block->sharedAssignmentId(); @@ -1514,7 +1484,7 @@ Error RAPass::setBlockEntryAssignment(RABlock* block, const RABlock* fromBlock, return blockEntryAssigned(as); } -Error RAPass::setSharedAssignment(uint32_t sharedAssignmentId, const RAAssignment& fromAssignment) noexcept { +Error BaseRAPass::setSharedAssignment(uint32_t sharedAssignmentId, const RAAssignment& fromAssignment) noexcept { ASMJIT_ASSERT(_sharedAssignments[sharedAssignmentId].empty()); PhysToWorkMap* physToWorkMap = clonePhysToWorkMap(fromAssignment.physToWorkMap()); @@ -1582,7 +1552,7 @@ Error RAPass::setSharedAssignment(uint32_t sharedAssignmentId, const RAAssignmen return blockEntryAssigned(as); } -Error RAPass::blockEntryAssigned(const RAAssignment& as) noexcept { +Error BaseRAPass::blockEntryAssigned(const RAAssignment& as) noexcept { // Complex allocation strategy requires to record register assignments upon // block entry (or per shared state). for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) { @@ -1603,10 +1573,10 @@ Error RAPass::blockEntryAssigned(const RAAssignment& as) noexcept { } // ============================================================================ -// [asmjit::RAPass - Allocation - Utilities] +// [asmjit::BaseRAPass - Allocation - Utilities] // ============================================================================ -Error RAPass::useTemporaryMem(BaseMem& out, uint32_t size, uint32_t alignment) noexcept { +Error BaseRAPass::useTemporaryMem(BaseMem& out, uint32_t size, uint32_t alignment) noexcept { ASMJIT_ASSERT(alignment <= 64); if (_temporaryMem.isNone()) { @@ -1627,10 +1597,10 @@ Error RAPass::useTemporaryMem(BaseMem& out, uint32_t size, uint32_t alignment) n } // ============================================================================ -// [asmjit::RAPass - Allocation - Prolog / Epilog] +// [asmjit::BaseRAPass - Allocation - Prolog / Epilog] // ============================================================================ -Error RAPass::updateStackFrame() noexcept { +Error BaseRAPass::updateStackFrame() noexcept { // Update some StackFrame information that we updated during allocation. The // only information we don't have at the moment is final local stack size, // which is calculated last. @@ -1668,7 +1638,7 @@ Error RAPass::updateStackFrame() noexcept { return kErrorOk; } -Error RAPass::_markStackArgsToKeep() noexcept { +Error BaseRAPass::_markStackArgsToKeep() noexcept { FuncFrame& frame = func()->frame(); bool hasSAReg = frame.hasPreservedFP() || !frame.hasDynamicAlignment(); @@ -1707,7 +1677,7 @@ Error RAPass::_markStackArgsToKeep() noexcept { return kErrorOk; } -Error RAPass::_updateStackArgs() noexcept { +Error BaseRAPass::_updateStackArgs() noexcept { FuncFrame& frame = func()->frame(); RAWorkRegs& workRegs = _workRegs; uint32_t numWorkRegs = workRegCount(); @@ -1741,12 +1711,12 @@ Error RAPass::_updateStackArgs() noexcept { return kErrorOk; } -Error RAPass::insertPrologEpilog() noexcept { +Error BaseRAPass::insertPrologEpilog() noexcept { FuncFrame& frame = _func->frame(); cc()->_setCursor(func()); ASMJIT_PROPAGATE(cc()->emitProlog(frame)); - ASMJIT_PROPAGATE(cc()->emitArgsAssignment(frame, _argsAssignment)); + ASMJIT_PROPAGATE(_iEmitHelper->emitArgsAssignment(frame, _argsAssignment)); cc()->_setCursor(func()->exitNode()); ASMJIT_PROPAGATE(cc()->emitEpilog(frame)); @@ -1755,10 +1725,10 @@ Error RAPass::insertPrologEpilog() noexcept { } // ============================================================================ -// [asmjit::RAPass - Rewriter] +// [asmjit::BaseRAPass - Rewriter] // ============================================================================ -Error RAPass::rewrite() noexcept { +Error BaseRAPass::rewrite() noexcept { #ifndef ASMJIT_NO_LOGGING Logger* logger = debugLogger(); ASMJIT_RA_LOG_FORMAT("[RAPass::Rewrite]\n"); @@ -1767,7 +1737,7 @@ Error RAPass::rewrite() noexcept { return _rewrite(_func, _stop); } -ASMJIT_FAVOR_SPEED Error RAPass::_rewrite(BaseNode* first, BaseNode* stop) noexcept { +ASMJIT_FAVOR_SPEED Error BaseRAPass::_rewrite(BaseNode* first, BaseNode* stop) noexcept { uint32_t virtCount = cc()->_vRegArray.size(); BaseNode* node = first; @@ -1814,7 +1784,7 @@ ASMJIT_FAVOR_SPEED Error RAPass::_rewrite(BaseNode* first, BaseNode* stop) noexc RABlock* block = raInst->block(); if (!isNextTo(node, _func->exitNode())) { cc()->_setCursor(node->prev()); - ASMJIT_PROPAGATE(onEmitJump(_func->exitNode()->label())); + ASMJIT_PROPAGATE(emitJump(_func->exitNode()->label())); } BaseNode* prev = node->prev(); @@ -1856,11 +1826,11 @@ ASMJIT_FAVOR_SPEED Error RAPass::_rewrite(BaseNode* first, BaseNode* stop) noexc } // ============================================================================ -// [asmjit::RAPass - Logging] +// [asmjit::BaseRAPass - Logging] // ============================================================================ #ifndef ASMJIT_NO_LOGGING -static void RAPass_dumpRAInst(RAPass* pass, String& sb, const RAInst* raInst) noexcept { +static void RAPass_dumpRAInst(BaseRAPass* pass, String& sb, const RAInst* raInst) noexcept { const RATiedReg* tiedRegs = raInst->tiedRegs(); uint32_t tiedCount = raInst->tiedCount(); @@ -1895,7 +1865,7 @@ static void RAPass_dumpRAInst(RAPass* pass, String& sb, const RAInst* raInst) no } } -ASMJIT_FAVOR_SIZE Error RAPass::annotateCode() noexcept { +ASMJIT_FAVOR_SIZE Error BaseRAPass::annotateCode() noexcept { uint32_t loggerFlags = _loggerFlags; StringTmp<1024> sb; @@ -1930,7 +1900,7 @@ ASMJIT_FAVOR_SIZE Error RAPass::annotateCode() noexcept { return kErrorOk; } -ASMJIT_FAVOR_SIZE Error RAPass::_dumpBlockIds(String& sb, const RABlocks& blocks) noexcept { +ASMJIT_FAVOR_SIZE Error BaseRAPass::_dumpBlockIds(String& sb, const RABlocks& blocks) noexcept { for (uint32_t i = 0, size = blocks.size(); i < size; i++) { const RABlock* block = blocks[i]; if (i != 0) @@ -1941,7 +1911,7 @@ ASMJIT_FAVOR_SIZE Error RAPass::_dumpBlockIds(String& sb, const RABlocks& blocks return kErrorOk; } -ASMJIT_FAVOR_SIZE Error RAPass::_dumpBlockLiveness(String& sb, const RABlock* block) noexcept { +ASMJIT_FAVOR_SIZE Error BaseRAPass::_dumpBlockLiveness(String& sb, const RABlock* block) noexcept { for (uint32_t liveType = 0; liveType < RABlock::kLiveCount; liveType++) { const char* bitsName = liveType == RABlock::kLiveIn ? "IN " : liveType == RABlock::kLiveOut ? "OUT " : @@ -1973,7 +1943,7 @@ ASMJIT_FAVOR_SIZE Error RAPass::_dumpBlockLiveness(String& sb, const RABlock* bl return kErrorOk; } -ASMJIT_FAVOR_SIZE Error RAPass::_dumpLiveSpans(String& sb) noexcept { +ASMJIT_FAVOR_SIZE Error BaseRAPass::_dumpLiveSpans(String& sb) noexcept { uint32_t numWorkRegs = _workRegs.size(); uint32_t maxSize = _maxWorkRegNameSize; diff --git a/src/asmjit/core/rapass_p.h b/src/asmjit/core/rapass_p.h index bedce96..d2fe505 100644 --- a/src/asmjit/core/rapass_p.h +++ b/src/asmjit/core/rapass_p.h @@ -27,6 +27,8 @@ #include "../core/api-config.h" #ifndef ASMJIT_NO_COMPILER +#include "../core/compiler.h" +#include "../core/emithelper_p.h" #include "../core/raassignment_p.h" #include "../core/radefs_p.h" #include "../core/rastack_p.h" @@ -80,42 +82,42 @@ public: }; //! Register allocator pass. - RAPass* _ra; + BaseRAPass* _ra; //! Block id (indexed from zero). - uint32_t _blockId; + uint32_t _blockId = kUnassignedId; //! Block flags, see `Flags`. - uint32_t _flags; + uint32_t _flags = 0; //! First `BaseNode` of this block (inclusive). - BaseNode* _first; + BaseNode* _first = nullptr; //! Last `BaseNode` of this block (inclusive). - BaseNode* _last; + BaseNode* _last = nullptr; //! Initial position of this block (inclusive). - uint32_t _firstPosition; + uint32_t _firstPosition = 0; //! End position of this block (exclusive). - uint32_t _endPosition; + uint32_t _endPosition = 0; //! Weight of this block (default 0, each loop adds one). - uint32_t _weight; + uint32_t _weight = 0; //! Post-order view order, used during POV construction. - uint32_t _povOrder; + uint32_t _povOrder = 0; //! Basic statistics about registers. - RARegsStats _regsStats; + RARegsStats _regsStats = RARegsStats(); //! Maximum live-count per register group. - RALiveCount _maxLiveCount; + RALiveCount _maxLiveCount = RALiveCount(); //! Timestamp (used by block visitors). - mutable uint64_t _timestamp; + mutable uint64_t _timestamp = 0; //! Immediate dominator of this block. - RABlock* _idom; + RABlock* _idom = nullptr; //! Block predecessors. - RABlocks _predecessors; + RABlocks _predecessors {}; //! Block successors. - RABlocks _successors; + RABlocks _successors {}; enum LiveType : uint32_t { kLiveIn = 0, @@ -126,52 +128,33 @@ public: }; //! Liveness in/out/use/kill. - ZoneBitVector _liveBits[kLiveCount]; + ZoneBitVector _liveBits[kLiveCount] {}; //! Shared assignment it or `Globals::kInvalidId` if this block doesn't //! have shared assignment. See `RASharedAssignment` for more details. - uint32_t _sharedAssignmentId; + uint32_t _sharedAssignmentId = Globals::kInvalidId; //! Scratch registers that cannot be allocated upon block entry. - uint32_t _entryScratchGpRegs; + uint32_t _entryScratchGpRegs = 0; //! Scratch registers used at exit, by a terminator instruction. - uint32_t _exitScratchGpRegs; + uint32_t _exitScratchGpRegs = 0; //! Register assignment (PhysToWork) on entry. - PhysToWorkMap* _entryPhysToWorkMap; + PhysToWorkMap* _entryPhysToWorkMap = nullptr; //! Register assignment (WorkToPhys) on entry. - WorkToPhysMap* _entryWorkToPhysMap; + WorkToPhysMap* _entryWorkToPhysMap = nullptr; //! \name Construction & Destruction //! \{ - inline RABlock(RAPass* ra) noexcept - : _ra(ra), - _blockId(kUnassignedId), - _flags(0), - _first(nullptr), - _last(nullptr), - _firstPosition(0), - _endPosition(0), - _weight(0), - _povOrder(kUnassignedId), - _regsStats(), - _maxLiveCount(), - _timestamp(0), - _idom(nullptr), - _predecessors(), - _successors(), - _sharedAssignmentId(Globals::kInvalidId), - _entryScratchGpRegs(0), - _exitScratchGpRegs(0), - _entryPhysToWorkMap(nullptr), - _entryWorkToPhysMap(nullptr) {} + inline RABlock(BaseRAPass* ra) noexcept + : _ra(ra) {} //! \} //! \name Accessors //! \{ - inline RAPass* pass() const noexcept { return _ra; } + inline BaseRAPass* pass() const noexcept { return _ra; } inline ZoneAllocator* allocator() const noexcept; inline uint32_t blockId() const noexcept { return _blockId; } @@ -620,21 +603,16 @@ public: //! ISA limits (like jecx/loop instructions on x86) or because the registers //! are used by jump/branch instruction that uses registers to perform an //! indirect jump. - uint32_t _entryScratchGpRegs; + uint32_t _entryScratchGpRegs = 0; //! Union of all live-in registers. - ZoneBitVector _liveIn; + ZoneBitVector _liveIn {}; //! Register assignment (PhysToWork). - PhysToWorkMap* _physToWorkMap; + PhysToWorkMap* _physToWorkMap = nullptr; //! Register assignment (WorkToPhys). - WorkToPhysMap* _workToPhysMap; + WorkToPhysMap* _workToPhysMap = nullptr; - //! Provided for clarity, most likely never called as we initialize a vector - //! of shared assignments to zero. - inline RASharedAssignment() noexcept - : _entryScratchGpRegs(0), - _liveIn(), - _physToWorkMap(nullptr), - _workToPhysMap(nullptr) {} + //! Most likely never called as we initialize a vector of shared assignments to zero. + inline RASharedAssignment() noexcept {} inline uint32_t entryScratchGpRegs() const noexcept { return _entryScratchGpRegs; } inline void addEntryScratchGpRegs(uint32_t mask) noexcept { _entryScratchGpRegs |= mask; } @@ -655,13 +633,13 @@ public: }; // ============================================================================ -// [asmjit::RAPass] +// [asmjit::BaseRAPass] // ============================================================================ //! Register allocation pass used by `BaseCompiler`. -class RAPass : public FuncPass { +class BaseRAPass : public FuncPass { public: - ASMJIT_NONCOPYABLE(RAPass) + ASMJIT_NONCOPYABLE(BaseRAPass) typedef FuncPass Base; enum Weights : uint32_t { @@ -672,58 +650,59 @@ public: typedef RAAssignment::WorkToPhysMap WorkToPhysMap; //! Allocator that uses zone passed to `runOnFunction()`. - ZoneAllocator _allocator; + ZoneAllocator _allocator {}; + //! Emit helper. + BaseEmitHelper* _iEmitHelper = nullptr; + //! Logger, disabled if null. - Logger* _logger; + Logger* _logger = nullptr; //! Debug logger, non-null only if `kOptionDebugPasses` option is set. - Logger* _debugLogger; + Logger* _debugLogger = nullptr; //! Logger flags. - uint32_t _loggerFlags; + uint32_t _loggerFlags = 0; //! Function being processed. - FuncNode* _func; + FuncNode* _func = nullptr; //! Stop node. - BaseNode* _stop; + BaseNode* _stop = nullptr; //! Node that is used to insert extra code after the function body. - BaseNode* _extraBlock; + BaseNode* _extraBlock = nullptr; //! Blocks (first block is the entry, always exists). - RABlocks _blocks; + RABlocks _blocks {}; //! Function exit blocks (usually one, but can contain more). - RABlocks _exits; + RABlocks _exits {}; //! Post order view (POV). - RABlocks _pov; + RABlocks _pov {}; //! Number of instruction nodes. - uint32_t _instructionCount; + uint32_t _instructionCount = 0; //! Number of created blocks (internal). - uint32_t _createdBlockCount; + uint32_t _createdBlockCount = 0; //! SharedState blocks. - ZoneVector _sharedAssignments; + ZoneVector _sharedAssignments {}; //! Timestamp generator (incremental). - mutable uint64_t _lastTimestamp; + mutable uint64_t _lastTimestamp = 0; - //!< Architecture registers information. - const ArchRegs* _archRegsInfo; //! Architecture traits. - RAArchTraits _archTraits; + const ArchTraits* _archTraits = nullptr; //! Index to physical registers in `RAAssignment::PhysToWorkMap`. - RARegIndex _physRegIndex; + RARegIndex _physRegIndex = RARegIndex(); //! Count of physical registers in `RAAssignment::PhysToWorkMap`. - RARegCount _physRegCount; + RARegCount _physRegCount = RARegCount(); //! Total number of physical registers. - uint32_t _physRegTotal; + uint32_t _physRegTotal = 0; //! Indexes of a possible scratch registers that can be selected if necessary. - uint8_t _scratchRegIndexes[2]; + uint8_t _scratchRegIndexes[2] {}; //! Registers available for allocation. - RARegMask _availableRegs; + RARegMask _availableRegs = RARegMask(); //! Count of physical registers per group. - RARegCount _availableRegCount; + RARegCount _availableRegCount = RARegCount(); //! Registers clobbered by the function. - RARegMask _clobberedRegs; + RARegMask _clobberedRegs = RARegMask(); //! Work registers (registers used by the function). RAWorkRegs _workRegs; @@ -733,33 +712,33 @@ public: //! Register allocation strategy per register group. RAStrategy _strategy[BaseReg::kGroupVirt]; //! Global max live-count (from all blocks) per register group. - RALiveCount _globalMaxLiveCount; + RALiveCount _globalMaxLiveCount = RALiveCount(); //! Global live spans per register group. - LiveRegSpans* _globalLiveSpans[BaseReg::kGroupVirt]; + LiveRegSpans* _globalLiveSpans[BaseReg::kGroupVirt] {}; //! Temporary stack slot. - Operand _temporaryMem; + Operand _temporaryMem = Operand(); //! Stack pointer. - BaseReg _sp; + BaseReg _sp = BaseReg(); //! Frame pointer. - BaseReg _fp; + BaseReg _fp = BaseReg(); //! Stack manager. - RAStackAllocator _stackAllocator; + RAStackAllocator _stackAllocator {}; //! Function arguments assignment. - FuncArgsAssignment _argsAssignment; + FuncArgsAssignment _argsAssignment {}; //! Some StackArgs have to be assigned to StackSlots. - uint32_t _numStackArgsToStackSlots; + uint32_t _numStackArgsToStackSlots = 0; //! Maximum name-size computed from all WorkRegs. - uint32_t _maxWorkRegNameSize; + uint32_t _maxWorkRegNameSize = 0; //! Temporary string builder used to format comments. StringTmp<80> _tmpString; //! \name Construction & Reset //! \{ - RAPass() noexcept; - virtual ~RAPass() noexcept; + BaseRAPass() noexcept; + virtual ~BaseRAPass() noexcept; //! \} @@ -949,7 +928,7 @@ public: //! information that is essential for further analysis and register //! allocation. //! - //! Use `RACFGBuilder` template that provides the necessary boilerplate. + //! Use `RACFGBuilderT` template that provides the necessary boilerplate. virtual Error buildCFG() noexcept = 0; //! Called after the CFG is built. @@ -1135,7 +1114,7 @@ public: //! \name Function Prolog & Epilog //! \{ - Error updateStackFrame() noexcept; + virtual Error updateStackFrame() noexcept; Error _markStackArgsToKeep() noexcept; Error _updateStackArgs() noexcept; Error insertPrologEpilog() noexcept; @@ -1166,14 +1145,14 @@ public: //! \name Emit //! \{ - virtual Error onEmitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept = 0; - virtual Error onEmitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept = 0; + virtual Error emitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept = 0; + virtual Error emitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept = 0; - virtual Error onEmitLoad(uint32_t workId, uint32_t dstPhysId) noexcept = 0; - virtual Error onEmitSave(uint32_t workId, uint32_t srcPhysId) noexcept = 0; + virtual Error emitLoad(uint32_t workId, uint32_t dstPhysId) noexcept = 0; + virtual Error emitSave(uint32_t workId, uint32_t srcPhysId) noexcept = 0; - virtual Error onEmitJump(const Label& label) noexcept = 0; - virtual Error onEmitPreCall(InvokeNode* invokeNode) noexcept = 0; + virtual Error emitJump(const Label& label) noexcept = 0; + virtual Error emitPreCall(InvokeNode* invokeNode) noexcept = 0; //! \} }; diff --git a/src/asmjit/core/support.h b/src/asmjit/core/support.h index c5df00a..22a0fa0 100644 --- a/src/asmjit/core/support.h +++ b/src/asmjit/core/support.h @@ -271,63 +271,96 @@ static constexpr T fillTrailingBits(const T& x) noexcept { //! \cond namespace Internal { - static constexpr uint32_t constCtzImpl(uint32_t xAndNegX) noexcept { - return 31 - ((xAndNegX & 0x0000FFFFu) ? 16 : 0) - - ((xAndNegX & 0x00FF00FFu) ? 8 : 0) - - ((xAndNegX & 0x0F0F0F0Fu) ? 4 : 0) - - ((xAndNegX & 0x33333333u) ? 2 : 0) - - ((xAndNegX & 0x55555555u) ? 1 : 0); +namespace { + +template +struct BitScanData { T x; uint32_t n; }; + +template +struct BitScanCalc { + static constexpr BitScanData advanceLeft(const BitScanData& data, uint32_t n) noexcept { + return BitScanData { data.x << n, data.n + n }; } - static constexpr uint32_t constCtzImpl(uint64_t xAndNegX) noexcept { - return 63 - ((xAndNegX & 0x00000000FFFFFFFFu) ? 32 : 0) - - ((xAndNegX & 0x0000FFFF0000FFFFu) ? 16 : 0) - - ((xAndNegX & 0x00FF00FF00FF00FFu) ? 8 : 0) - - ((xAndNegX & 0x0F0F0F0F0F0F0F0Fu) ? 4 : 0) - - ((xAndNegX & 0x3333333333333333u) ? 2 : 0) - - ((xAndNegX & 0x5555555555555555u) ? 1 : 0); + static constexpr BitScanData advanceRight(const BitScanData& data, uint32_t n) noexcept { + return BitScanData { data.x >> n, data.n + n }; } - template - static constexpr uint32_t constCtz(T x) noexcept { - return constCtzImpl(x & neg(x)); + static constexpr BitScanData clz(const BitScanData& data) noexcept { + return BitScanCalc::clz(advanceLeft(data, data.x & (allOnes() << (bitSizeOf() - N)) ? uint32_t(0) : N)); } - static ASMJIT_INLINE uint32_t ctz(uint32_t x) noexcept { - #if defined(__GNUC__) - return uint32_t(__builtin_ctz(x)); - #elif defined(_MSC_VER) && (ASMJIT_ARCH_X86 || ASMJIT_ARCH_ARM) - unsigned long i; - _BitScanForward(&i, x); - return uint32_t(i); - #else - return constCtz(x); - #endif + static constexpr BitScanData ctz(const BitScanData& data) noexcept { + return BitScanCalc::ctz(advanceRight(data, data.x & (allOnes() >> (bitSizeOf() - N)) ? uint32_t(0) : N)); + } +}; + +template +struct BitScanCalc { + static constexpr BitScanData clz(const BitScanData& ctx) noexcept { + return BitScanData { 0, ctx.n - uint32_t(ctx.x >> (bitSizeOf() - 1)) }; } - static ASMJIT_INLINE uint32_t ctz(uint64_t x) noexcept { - #if defined(__GNUC__) - return uint32_t(__builtin_ctzll(x)); - #elif defined(_MSC_VER) && (ASMJIT_ARCH_X86 == 64 || ASMJIT_ARCH_ARM == 64) - unsigned long i; - _BitScanForward64(&i, x); - return uint32_t(i); - #else - return constCtz(x); - #endif + static constexpr BitScanData ctz(const BitScanData& ctx) noexcept { + return BitScanData { 0, ctx.n - uint32_t(ctx.x & 0x1) }; } +}; + +template +constexpr uint32_t clzFallback(const T& x) noexcept { + return BitScanCalc() / 2u>::clz(BitScanData{x, 1}).n; } + +template +constexpr uint32_t ctzFallback(const T& x) noexcept { + return BitScanCalc() / 2u>::ctz(BitScanData{x, 1}).n; +} + +template constexpr uint32_t constClz(const T& x) noexcept { return clzFallback(asUInt(x)); } +template constexpr uint32_t constCtz(const T& x) noexcept { return ctzFallback(asUInt(x)); } + +template inline uint32_t clzImpl(const T& x) noexcept { return constClz(x); } +template inline uint32_t ctzImpl(const T& x) noexcept { return constCtz(x); } + +#if !defined(ASMJIT_NO_INTRINSICS) +# if defined(__GNUC__) +template<> inline uint32_t clzImpl(const uint32_t& x) noexcept { return uint32_t(__builtin_clz(x)); } +template<> inline uint32_t clzImpl(const uint64_t& x) noexcept { return uint32_t(__builtin_clzll(x)); } +template<> inline uint32_t ctzImpl(const uint32_t& x) noexcept { return uint32_t(__builtin_ctz(x)); } +template<> inline uint32_t ctzImpl(const uint64_t& x) noexcept { return uint32_t(__builtin_ctzll(x)); } +# elif defined(_MSC_VER) +template<> inline uint32_t clzImpl(const uint32_t& x) noexcept { unsigned long i; _BitScanReverse(&i, x); return uint32_t(i ^ 31); } +template<> inline uint32_t ctzImpl(const uint32_t& x) noexcept { unsigned long i; _BitScanForward(&i, x); return uint32_t(i); } +# if ASMJIT_ARCH_X86 == 64 || ASMJIT_ARCH_ARM == 64 +template<> inline uint32_t clzImpl(const uint64_t& x) noexcept { unsigned long i; _BitScanReverse64(&i, x); return uint32_t(i ^ 63); } +template<> inline uint32_t ctzImpl(const uint64_t& x) noexcept { unsigned long i; _BitScanForward64(&i, x); return uint32_t(i); } +# endif +# endif +#endif + +} // {anonymous} +} // {Internal} //! \endcond +//! Count leading zeros in `x` (returns a position of a first bit set in `x`). +//! +//! \note The input MUST NOT be zero, otherwise the result is undefined. +template +static inline uint32_t clz(T x) noexcept { return Internal::clzImpl(asUInt(x)); } + +//! Count leading zeros in `x` (constant expression). +template +static constexpr inline uint32_t constClz(T x) noexcept { return Internal::constClz(asUInt(x)); } + //! Count trailing zeros in `x` (returns a position of a first bit set in `x`). //! //! \note The input MUST NOT be zero, otherwise the result is undefined. template -static inline uint32_t ctz(T x) noexcept { return Internal::ctz(asUInt(x)); } +static inline uint32_t ctz(T x) noexcept { return Internal::ctzImpl(asUInt(x)); } //! Count trailing zeros in `x` (constant expression). template -static constexpr uint32_t constCtz(T x) noexcept { return Internal::constCtz(asUInt(x)); } +static constexpr inline uint32_t constCtz(T x) noexcept { return Internal::constCtz(asUInt(x)); } // ============================================================================ // [asmjit::Support - PopCnt] @@ -410,6 +443,30 @@ static constexpr T max(const T& a, const T& b) noexcept { return a < b ? b : a; template static constexpr T max(const T& a, const T& b, Args&&... args) noexcept { return max(max(a, b), std::forward(args)...); } +// ============================================================================ +// [asmjit::Support - Immediate Helpers] +// ============================================================================ + +namespace Internal { + template + 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::type>()); } + }; + + template + struct ImmConv { + static inline int64_t fromT(const T& x) noexcept { return int64_t(bitCast(double(x))); } + static inline T toT(int64_t x) noexcept { return T(bitCast(x)); } + }; +} + +template +static inline int64_t immediateFromT(const T& x) noexcept { return Internal::ImmConv::value>::fromT(x); } + +template +static inline T immediateToT(int64_t x) noexcept { return Internal::ImmConv::value>::toT(x); } + // ============================================================================ // [asmjit::Support - Overflow Arithmetic] // ============================================================================ @@ -601,8 +658,16 @@ static constexpr bool isInt4(T x) noexcept { typedef typename std::make_signed::type S; typedef typename std::make_unsigned::type U; - return std::is_signed::value ? isBetween(S(x), -8, 7) - : U(x) <= U(7u); + return std::is_signed::value ? isBetween(S(x), -8, 7) : U(x) <= U(7u); +} + +//! Checks whether the given integer `x` can be casted to a 7-bit signed integer. +template +static constexpr bool isInt7(T x) noexcept { + typedef typename std::make_signed::type S; + typedef typename std::make_unsigned::type U; + + return std::is_signed::value ? isBetween(S(x), -64, 63) : U(x) <= U(63u); } //! Checks whether the given integer `x` can be casted to an 8-bit signed integer. @@ -611,8 +676,27 @@ static constexpr bool isInt8(T x) noexcept { typedef typename std::make_signed::type S; typedef typename std::make_unsigned::type U; - return std::is_signed::value ? sizeof(T) <= 1 || isBetween(S(x), -128, 127) - : U(x) <= U(127u); + return std::is_signed::value ? sizeof(T) <= 1 || isBetween(S(x), -128, 127) : U(x) <= U(127u); +} + +//! Checks whether the given integer `x` can be casted to a 9-bit signed integer. +template +static constexpr bool isInt9(T x) noexcept { + typedef typename std::make_signed::type S; + typedef typename std::make_unsigned::type U; + + return std::is_signed::value ? sizeof(T) <= 1 || isBetween(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 +static constexpr bool isInt10(T x) noexcept { + typedef typename std::make_signed::type S; + typedef typename std::make_unsigned::type U; + + return std::is_signed::value ? sizeof(T) <= 1 || isBetween(S(x), -512, 511) + : sizeof(T) <= 1 || U(x) <= U(511u); } //! Checks whether the given integer `x` can be casted to a 16-bit signed integer. @@ -686,6 +770,16 @@ static constexpr bool isIntOrUInt32(T x) noexcept { return sizeof(T) <= 4 ? true : (uint32_t(uint64_t(x) >> 32) + 1u) <= 1u; } +static bool inline isEncodableOffset32(int32_t offset, uint32_t nBits) noexcept { + uint32_t nRev = 32 - nBits; + return Support::sar(Support::shl(offset, nRev), nRev) == offset; +} + +static bool inline isEncodableOffset64(int64_t offset, uint32_t nBits) noexcept { + uint32_t nRev = 64 - nBits; + return Support::sar(Support::shl(offset, nRev), nRev) == offset; +} + // ============================================================================ // [asmjit::Support - ByteSwap] // ============================================================================ diff --git a/src/asmjit/core/target.h b/src/asmjit/core/target.h index 4d144c6..f2045c0 100644 --- a/src/asmjit/core/target.h +++ b/src/asmjit/core/target.h @@ -24,7 +24,7 @@ #ifndef ASMJIT_CORE_TARGET_H_INCLUDED #define ASMJIT_CORE_TARGET_H_INCLUDED -#include "../core/arch.h" +#include "../core/archtraits.h" #include "../core/func.h" ASMJIT_BEGIN_NAMESPACE diff --git a/src/asmjit/core/type.cpp b/src/asmjit/core/type.cpp index 97892d9..a2bebf4 100644 --- a/src/asmjit/core/type.cpp +++ b/src/asmjit/core/type.cpp @@ -78,11 +78,11 @@ struct SizeOfTypeId { }; const TypeData _typeData = { - #define VALUE(X) BaseOfTypeId::kTypeId + #define VALUE(x) BaseOfTypeId::kTypeId { ASMJIT_LOOKUP_TABLE_256(VALUE, 0) }, #undef VALUE - #define VALUE(X) SizeOfTypeId::kTypeSize + #define VALUE(x) SizeOfTypeId::kTypeSize { ASMJIT_LOOKUP_TABLE_256(VALUE, 0) } #undef VALUE }; diff --git a/src/asmjit/core/zonevector.h b/src/asmjit/core/zonevector.h index 2245db2..e95422a 100644 --- a/src/asmjit/core/zonevector.h +++ b/src/asmjit/core/zonevector.h @@ -46,21 +46,18 @@ public: typedef ptrdiff_t difference_type; //! Vector data (untyped). - void* _data; + void* _data = nullptr; //! Size of the vector. - size_type _size; + size_type _size = 0; //! Capacity of the vector. - size_type _capacity; + size_type _capacity = 0; protected: //! \name Construction & Destruction //! \{ //! Creates a new instance of `ZoneVectorBase`. - inline ZoneVectorBase() noexcept - : _data(nullptr), - _size(0), - _capacity(0) {} + inline ZoneVectorBase() noexcept {} inline ZoneVectorBase(ZoneVectorBase&& other) noexcept : _data(other._data), @@ -436,11 +433,11 @@ public: static constexpr uint32_t kBitWordSizeInBits = Support::kBitWordSizeInBits; //! Bits. - BitWord* _data; + BitWord* _data = nullptr; //! Size of the bit-vector (in bits). - uint32_t _size; + uint32_t _size = 0; //! Capacity of the bit-vector (in bits). - uint32_t _capacity; + uint32_t _capacity = 0; ASMJIT_NONCOPYABLE(ZoneBitVector) @@ -473,10 +470,7 @@ public: //! \name Construction & Destruction //! \{ - inline ZoneBitVector() noexcept - : _data(nullptr), - _size(0), - _capacity(0) {} + inline ZoneBitVector() noexcept {} inline ZoneBitVector(ZoneBitVector&& other) noexcept : _data(other._data), diff --git a/src/asmjit/x86.h b/src/asmjit/x86.h index 3c2224c..69d47b4 100644 --- a/src/asmjit/x86.h +++ b/src/asmjit/x86.h @@ -45,6 +45,7 @@ //! functions provided are part of all X86 emitters. //! - \ref x86::EmitterImplicitT - Provides all instructions that use //! implicit operands, these cannot be used with \ref x86::Compiler. +//! //! - Instruction representation: //! - \ref x86::Inst::Id - instruction identifiers. //! - \ref x86::Inst::Options - instruction options. @@ -74,7 +75,7 @@ //! ### Memory Operands //! //! - \ref x86::Mem - X86/X64 memory operand that provides support for all -//! X86 and X64 addressing features, including absolute addresses, index +//! X86 and X64 addressing features including absolute addresses, index //! scales, and segment override prefixes. //! //! ### Other diff --git a/src/asmjit/x86/x86archdata.cpp b/src/asmjit/x86/x86archdata.cpp deleted file mode 100644 index cd928b2..0000000 --- a/src/asmjit/x86/x86archdata.cpp +++ /dev/null @@ -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 diff --git a/src/asmjit/x86/x86archtraits_p.h b/src/asmjit/x86/x86archtraits_p.h new file mode 100644 index 0000000..095919c --- /dev/null +++ b/src/asmjit/x86/x86archtraits_p.h @@ -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::kSignature } + { ASMJIT_LOOKUP_TABLE_32(V, 0) }, + #undef V + + // RegTypeToTypeId. + #define V(index) x86::RegTraits::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::kSignature } + { ASMJIT_LOOKUP_TABLE_32(V, 0) }, + #undef V + + // RegTypeToTypeId. + #define V(index) x86::RegTraits::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 diff --git a/src/asmjit/x86/x86assembler.cpp b/src/asmjit/x86/x86assembler.cpp index 7972283..78bc748 100644 --- a/src/asmjit/x86/x86assembler.cpp +++ b/src/asmjit/x86/x86assembler.cpp @@ -25,7 +25,7 @@ #ifdef ASMJIT_BUILD_X86 #include "../core/assembler.h" -#include "../core/codebufferwriter_p.h" +#include "../core/codewriter_p.h" #include "../core/cpuinfo.h" #include "../core/emitterutils_p.h" #include "../core/formatter.h" @@ -227,7 +227,7 @@ struct X86MemInfo_T { // - REX - A possible combination of REX.[B|X|R|W] bits in REX prefix where // REX.B and REX.X are possibly masked out, but REX.R and REX.W are // kept as is. -#define VALUE(X) X86MemInfo_T::kValue +#define VALUE(x) X86MemInfo_T::kValue static const uint8_t x86MemInfo[] = { ASMJIT_LOOKUP_TABLE_1024(VALUE, 0) }; #undef VALUE @@ -240,23 +240,23 @@ static const uint8_t x86MemInfo[] = { ASMJIT_LOOKUP_TABLE_1024(VALUE, 0) }; // decide between VEX3 vs XOP. // ____ ___ // [_OPCODE_|WvvvvLpp|RXBmmmmm|VEX3_XOP] -#define VALUE(X) ((X & 0x08) ? kX86ByteXop3 : kX86ByteVex3) | (0xF << 19) | (0x7 << 13) +#define VALUE(x) ((x & 0x08) ? kX86ByteXop3 : kX86ByteVex3) | (0xF << 19) | (0x7 << 13) static const uint32_t x86VEXPrefix[] = { ASMJIT_LOOKUP_TABLE_16(VALUE, 0) }; #undef VALUE // Table that contains LL opcode field addressed by a register size / 16. It's // used to propagate L.256 or L.512 when YMM or ZMM registers are used, // respectively. -#define VALUE(X) (X & (64 >> 4)) ? Opcode::kLL_2 : \ - (X & (32 >> 4)) ? Opcode::kLL_1 : Opcode::kLL_0 +#define VALUE(x) (x & (64 >> 4)) ? Opcode::kLL_2 : \ + (x & (32 >> 4)) ? Opcode::kLL_1 : Opcode::kLL_0 static const uint32_t x86LLBySizeDiv16[] = { ASMJIT_LOOKUP_TABLE_16(VALUE, 0) }; #undef VALUE // Table that contains LL opcode field addressed by a register size / 16. It's // used to propagate L.256 or L.512 when YMM or ZMM registers are used, // respectively. -#define VALUE(X) X == Reg::kTypeZmm ? Opcode::kLL_2 : \ - X == Reg::kTypeYmm ? Opcode::kLL_1 : Opcode::kLL_0 +#define VALUE(x) x == Reg::kTypeZmm ? Opcode::kLL_2 : \ + x == Reg::kTypeYmm ? Opcode::kLL_1 : Opcode::kLL_0 static const uint32_t x86LLByRegType[] = { ASMJIT_LOOKUP_TABLE_16(VALUE, 0) }; #undef VALUE @@ -278,7 +278,7 @@ struct X86CDisp8SHL_T { }; }; -#define VALUE(X) X86CDisp8SHL_T::kValue +#define VALUE(x) X86CDisp8SHL_T::kValue static const uint32_t x86CDisp8SHL[] = { ASMJIT_LOOKUP_TABLE_32(VALUE, 0) }; #undef VALUE @@ -310,7 +310,7 @@ struct X86Mod16BaseIndexTable_T { }; }; -#define VALUE(X) X86Mod16BaseIndexTable_T::kValue +#define VALUE(x) X86Mod16BaseIndexTable_T::kValue static const uint8_t x86Mod16BaseIndexTable[] = { ASMJIT_LOOKUP_TABLE_64(VALUE, 0) }; #undef VALUE @@ -375,10 +375,10 @@ static ASMJIT_INLINE uint32_t x86AltOpcodeOf(const InstDB::InstInfo* info) noexc // [asmjit::X86BufferWriter] // ============================================================================ -class X86BufferWriter : public CodeBufferWriter { +class X86BufferWriter : public CodeWriter { public: ASMJIT_INLINE explicit X86BufferWriter(Assembler* a) noexcept - : CodeBufferWriter(a) {} + : CodeWriter(a) {} ASMJIT_INLINE void emitPP(uint32_t opcode) noexcept { uint32_t ppIndex = (opcode >> Opcode::kPP_Shift) & @@ -509,7 +509,7 @@ static ASMJIT_INLINE uint32_t x86GetMovAbsAddrType(Assembler* self, X86BufferWri uint32_t addrType = rmRel.addrType(); int64_t addrValue = rmRel.offset(); - if (addrType == BaseMem::kAddrTypeDefault && !(options & Inst::kOptionModMR)) { + if (addrType == Mem::kAddrTypeDefault && !(options & Inst::kOptionModMR)) { if (self->is64Bit()) { uint64_t baseAddress = self->code()->baseAddress(); if (baseAddress != Globals::kNoBaseAddress && !rmRel.hasSegment()) { @@ -519,15 +519,15 @@ static ASMJIT_INLINE uint32_t x86GetMovAbsAddrType(Assembler* self, X86BufferWri uint64_t rel64 = uint64_t(addrValue) - rip64; if (!Support::isInt32(int64_t(rel64))) - addrType = BaseMem::kAddrTypeAbs; + addrType = Mem::kAddrTypeAbs; } else { if (!Support::isInt32(addrValue)) - addrType = BaseMem::kAddrTypeAbs; + addrType = Mem::kAddrTypeAbs; } } else { - addrType = BaseMem::kAddrTypeAbs; + addrType = Mem::kAddrTypeAbs; } } @@ -1637,7 +1637,7 @@ CaseX86M_GPB_MulDiv: // Handle a special form of `mov al|ax|eax|rax, [ptr64]` that doesn't use MOD. if (opReg == Gp::kIdAx && !rmRel->as().hasBaseOrIndex()) { immValue = rmRel->as().offset(); - if (x86GetMovAbsAddrType(this, writer, o0.size(), options, rmRel->as()) == BaseMem::kAddrTypeAbs) { + if (x86GetMovAbsAddrType(this, writer, o0.size(), options, rmRel->as()) == Mem::kAddrTypeAbs) { opcode += 0xA0; goto EmitX86OpMovAbs; } @@ -1670,7 +1670,7 @@ CaseX86M_GPB_MulDiv: // Handle a special form of `mov [ptr64], al|ax|eax|rax` that doesn't use MOD. if (opReg == Gp::kIdAx && !rmRel->as().hasBaseOrIndex()) { immValue = rmRel->as().offset(); - if (x86GetMovAbsAddrType(this, writer, o1.size(), options, rmRel->as()) == BaseMem::kAddrTypeAbs) { + if (x86GetMovAbsAddrType(this, writer, o1.size(), options, rmRel->as()) == Mem::kAddrTypeAbs) { opcode += 0xA2; goto EmitX86OpMovAbs; } @@ -3991,7 +3991,7 @@ EmitModSib: if (is32Bit()) { // Explicit relative addressing doesn't work in 32-bit mode. - if (ASMJIT_UNLIKELY(addrType == BaseMem::kAddrTypeRel)) + if (ASMJIT_UNLIKELY(addrType == Mem::kAddrTypeRel)) goto InvalidAddress; writer.emit8(x86EncodeMod(0, opReg, 5)); @@ -4005,11 +4005,11 @@ EmitModSib: // If relative addressing was not explicitly set then we can try to guess. // By guessing we check some properties of the memory operand and try to // base the decision on the segment prefix and the address type. - if (addrType == BaseMem::kAddrTypeDefault) { + if (addrType == Mem::kAddrTypeDefault) { if (baseAddress == Globals::kNoBaseAddress) { // Prefer absolute addressing mode if the offset is 32-bit. - addrType = isOffsetI32 || isOffsetU32 ? BaseMem::kAddrTypeAbs - : BaseMem::kAddrTypeRel; + addrType = isOffsetI32 || isOffsetU32 ? Mem::kAddrTypeAbs + : Mem::kAddrTypeRel; } else { // Prefer absolute addressing mode if FS|GS segment override is present. @@ -4017,30 +4017,30 @@ EmitModSib: // Prefer absolute addressing mode if this is LEA with 32-bit immediate. bool isLea32 = (instId == Inst::kIdLea) && (isOffsetI32 || isOffsetU32); - addrType = hasFsGs || isLea32 ? BaseMem::kAddrTypeAbs - : BaseMem::kAddrTypeRel; + addrType = hasFsGs || isLea32 ? Mem::kAddrTypeAbs + : Mem::kAddrTypeRel; } } - if (addrType == BaseMem::kAddrTypeRel) { + if (addrType == Mem::kAddrTypeRel) { uint32_t kModRel32Size = 5; uint64_t virtualOffset = uint64_t(writer.offsetFrom(_bufferData)) + immSize + kModRel32Size; - if (baseAddress == Globals::kNoBaseAddress) { + if (baseAddress == Globals::kNoBaseAddress || _section->id() != 0) { // Create a new RelocEntry as we cannot calculate the offset right now. - err = _code->newRelocEntry(&re, RelocEntry::kTypeAbsToRel, 4); + err = _code->newRelocEntry(&re, RelocEntry::kTypeAbsToRel); if (ASMJIT_UNLIKELY(err)) goto Failed; writer.emit8(x86EncodeMod(0, opReg, 5)); - writer.emit32uLE(0); re->_sourceSectionId = _section->id(); re->_sourceOffset = offset(); - re->_leadingSize = uint8_t(writer.offsetFrom(_bufferPtr) - 4); - re->_trailingSize = uint8_t(immSize); + re->_format.resetToDataValue(4); + re->_format.setLeadingAndTrailingSize(writer.offsetFrom(_bufferPtr), immSize); re->_payload = uint64_t(rmRel->as().offset()); + writer.emit32uLE(0); writer.emitImmediate(uint64_t(immValue), immSize); goto EmitDone; } @@ -4131,14 +4131,14 @@ EmitModSib_LabelRip_X86: if (ASMJIT_UNLIKELY(!label)) goto InvalidLabel; - err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, 4); + err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs); if (ASMJIT_UNLIKELY(err)) goto Failed; re->_sourceSectionId = _section->id(); re->_sourceOffset = offset(); - re->_leadingSize = uint8_t(writer.offsetFrom(_bufferPtr)); - re->_trailingSize = uint8_t(immSize); + re->_format.resetToDataValue(4); + re->_format.setLeadingAndTrailingSize(writer.offsetFrom(_bufferPtr), immSize); re->_payload = uint64_t(int64_t(relOffset)); if (label->isBound()) { @@ -4156,16 +4156,16 @@ EmitModSib_LabelRip_X86: } else { // [RIP->ABS]. - err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs, 4); + err = _code->newRelocEntry(&re, RelocEntry::kTypeRelToAbs); if (ASMJIT_UNLIKELY(err)) goto Failed; re->_sourceSectionId = _section->id(); re->_targetSectionId = _section->id(); + re->_format.resetToDataValue(4); + re->_format.setLeadingAndTrailingSize(writer.offsetFrom(_bufferPtr), immSize); re->_sourceOffset = offset(); - re->_leadingSize = uint8_t(writer.offsetFrom(_bufferPtr)); - re->_trailingSize = uint8_t(immSize); - re->_payload = re->_sourceOffset + re->_leadingSize + 4 + re->_trailingSize + uint64_t(int64_t(relOffset)); + re->_payload = re->_sourceOffset + re->_format.regionSize() + uint64_t(int64_t(relOffset)); writer.emit32uLE(0); } @@ -4682,7 +4682,7 @@ EmitJmpCall: } } - err = _code->newRelocEntry(&re, RelocEntry::kTypeAbsToRel, 0); + err = _code->newRelocEntry(&re, RelocEntry::kTypeAbsToRel); if (ASMJIT_UNLIKELY(err)) goto Failed; @@ -4709,19 +4709,15 @@ EmitJmpCall: writer.emit8If(0x0F, (opcode & Opcode::kMM_Mask) != 0); // Emit 0F prefix. writer.emit8(opcode.v); // Emit opcode. writer.emit8If(x86EncodeMod(3, opReg, 0), opReg != 0); // Emit MOD. + re->_format.resetToDataValue(4); + re->_format.setLeadingAndTrailingSize(writer.offsetFrom(_bufferPtr), immSize); writer.emit32uLE(0); // Emit DISP32. - - re->_valueSize = 4; - re->_leadingSize = uint8_t(writer.offsetFrom(_bufferPtr) - 4); - re->_trailingSize = uint8_t(immSize); } else { writer.emit8(opCode8); // Emit opcode. + re->_format.resetToDataValue(4); + re->_format.setLeadingAndTrailingSize(writer.offsetFrom(_bufferPtr), immSize); writer.emit8(0); // Emit DISP8 (zero). - - re->_valueSize = 1; - re->_leadingSize = uint8_t(writer.offsetFrom(_bufferPtr) - 1); - re->_trailingSize = uint8_t(immSize); } goto EmitDone; } @@ -4762,19 +4758,18 @@ EmitRel: // Chain with label. size_t offset = size_t(writer.offsetFrom(_bufferData)); - LabelLink* link = _code->newLabelLink(label, _section->id(), offset, relOffset); + OffsetFormat of; + of.resetToDataValue(relSize); + LabelLink* link = _code->newLabelLink(label, _section->id(), offset, relOffset, of); if (ASMJIT_UNLIKELY(!link)) goto OutOfMemory; if (re) link->relocId = re->id(); - // Emit label size as dummy data. - if (relSize == 1) - writer.emit8(0x01); - else // if (relSize == 4) - writer.emit32uLE(0x04040404); + // Emit dummy zeros, must be patched later when the reference becomes known. + writer.emitZeros(relSize); } writer.emitImmediate(uint64_t(immValue), immSize); @@ -4798,14 +4793,10 @@ EmitDone: return kErrorOk; // -------------------------------------------------------------------------- - // [Error Cases] + // [Error Handler] // -------------------------------------------------------------------------- - #define ERROR_HANDLER(ERROR) \ - ERROR: \ - err = DebugUtils::errored(kError##ERROR); \ - goto Failed; - +#define ERROR_HANDLER(ERR) ERR: err = DebugUtils::errored(kError##ERR); goto Failed; ERROR_HANDLER(OutOfMemory) ERROR_HANDLER(InvalidLabel) ERROR_HANDLER(InvalidInstruction) @@ -4825,8 +4816,7 @@ EmitDone: ERROR_HANDLER(OperandSizeMismatch) ERROR_HANDLER(AmbiguousOperandSize) ERROR_HANDLER(NotConsecutiveRegs) - - #undef ERROR_HANDLER +#undef ERROR_HANDLER Failed: #ifndef ASMJIT_NO_LOGGING @@ -4858,7 +4848,7 @@ Error Assembler::align(uint32_t alignMode, uint32_t alignment) { uint32_t i = uint32_t(Support::alignUpDiff(offset(), alignment)); if (i > 0) { - CodeBufferWriter writer(this); + CodeWriter writer(this); ASMJIT_PROPAGATE(writer.ensureSpace(this, i)); uint8_t pattern = 0x00; @@ -4937,13 +4927,11 @@ Error Assembler::onAttach(CodeHolder* code) noexcept { if (Environment::is32Bit(arch)) { // 32 bit architecture - X86. - _gpRegInfo.setSignature(Gpd::kSignature); _forcedInstOptions |= Inst::_kOptionInvalidRex; _setAddressOverrideMask(kX86MemInfo_67H_X86); } else { // 64 bit architecture - X64. - _gpRegInfo.setSignature(Gpq::kSignature); _forcedInstOptions &= ~Inst::_kOptionInvalidRex; _setAddressOverrideMask(kX86MemInfo_67H_X64); } diff --git a/src/asmjit/x86/x86builder.cpp b/src/asmjit/x86/x86builder.cpp index 8f9c63c..2227fa2 100644 --- a/src/asmjit/x86/x86builder.cpp +++ b/src/asmjit/x86/x86builder.cpp @@ -60,10 +60,7 @@ Error Builder::onAttach(CodeHolder* code) noexcept { if (!Environment::isFamilyX86(arch)) return DebugUtils::errored(kErrorInvalidArch); - ASMJIT_PROPAGATE(Base::onAttach(code)); - - _gpRegInfo.setSignature(Environment::is32Bit(arch) ? uint32_t(Gpd::kSignature) : uint32_t(Gpq::kSignature)); - return kErrorOk; + return Base::onAttach(code); } ASMJIT_END_SUB_NAMESPACE diff --git a/src/asmjit/x86/x86callconv.cpp b/src/asmjit/x86/x86callconv.cpp deleted file mode 100644 index e0a3f21..0000000 --- a/src/asmjit/x86/x86callconv.cpp +++ /dev/null @@ -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(8)); - cc.setPreservedRegs(kGroupVec, Support::lsbMask(8) & ~Support::lsbMask(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(16)); - cc.setPreservedRegs(kGroupVec, ~Support::lsbMask(n)); - break; - } - - default: - return DebugUtils::errored(kErrorInvalidArgument); - } - } - - cc.setId(ccId); - return kErrorOk; -} - -} // {CallConvInternal} - -ASMJIT_END_SUB_NAMESPACE - -#endif // ASMJIT_BUILD_X86 diff --git a/src/asmjit/x86/x86compiler.cpp b/src/asmjit/x86/x86compiler.cpp index c087548..566437f 100644 --- a/src/asmjit/x86/x86compiler.cpp +++ b/src/asmjit/x86/x86compiler.cpp @@ -51,6 +51,7 @@ Error Compiler::finalize() { a.addValidationOptions(validationOptions()); return serializeTo(&a); } + // ============================================================================ // [asmjit::x86::Compiler - Events] // ============================================================================ @@ -61,12 +62,8 @@ Error Compiler::onAttach(CodeHolder* code) noexcept { return DebugUtils::errored(kErrorInvalidArch); ASMJIT_PROPAGATE(Base::onAttach(code)); - - bool is32Bit = Environment::is32Bit(arch); - _gpRegInfo.setSignature(is32Bit ? uint32_t(Gpd::kSignature) - : uint32_t(Gpq::kSignature)); - Error err = addPassT(); + if (ASMJIT_UNLIKELY(err)) { onDetach(code); return err; diff --git a/src/asmjit/x86/x86emithelper.cpp b/src/asmjit/x86/x86emithelper.cpp new file mode 100644 index 0000000..1b21402 --- /dev/null +++ b/src/asmjit/x86/x86emithelper.cpp @@ -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().setSize(src.size()); } + if (src.isMem()) { memFlags |= kSrcMem; src.as().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()); + } + else if (!memFlags) { + // Change both destination and source registers to GPD (safer, no dependencies). + dst.setSignature(Reg::signatureOfT()); + src.setSignature(Reg::signatureOfT()); + } + 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().setSize(overrideMemSize); + if (src.isMem()) src.as().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()); + 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()); + } + 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::signatureOfT()); + 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()); + break; + } + + if (Type::isMask(srcTypeId)) { + instId = kmovInstFromSize(srcSize); + dst.setSignature(srcSize <= 4 ? Reg::signatureOfT() + : Reg::signatureOfT()); + 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()); + 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()); + 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()); + break; + } + } + + if (Type::isVec(dstTypeId)) { + // By default set destination to XMM, will be set to YMM|ZMM if needed. + dst.setSignature(Reg::signatureOfT()); + + // 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()); + 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()); + 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()); + 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().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(); + 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 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 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(); + + 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 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 diff --git a/src/asmjit/x86/x86archdata_p.h b/src/asmjit/x86/x86emithelper_p.h similarity index 52% rename from src/asmjit/x86/x86archdata_p.h rename to src/asmjit/x86/x86emithelper_p.h index e765cb9..0fb8abc 100644 --- a/src/asmjit/x86/x86archdata_p.h +++ b/src/asmjit/x86/x86emithelper_p.h @@ -21,10 +21,15 @@ // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. -#ifndef ASMJIT_X86_X86ARCHDATA_P_H_INCLUDED -#define ASMJIT_X86_X86ARCHDATA_P_H_INCLUDED +#ifndef ASMJIT_X86_X86EMITHELPER_P_H_INCLUDED +#define ASMJIT_X86_X86EMITHELPER_P_H_INCLUDED -#include "../core/arch.h" +#include "../core/api-config.h" + +#include "../core/emithelper_p.h" +#include "../core/func.h" +#include "../x86/x86emitter.h" +#include "../x86/x86operand.h" ASMJIT_BEGIN_SUB_NAMESPACE(x86) @@ -33,19 +38,41 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86) //! \{ // ============================================================================ -// [asmjit::x86::ArchInternal] +// [asmjit::x86::EmitHelper] // ============================================================================ -//! X86-specific function API (calling conventions and other utilities). -namespace ArchInternal { +static ASMJIT_INLINE uint32_t vecTypeIdToRegType(uint32_t typeId) noexcept { + return typeId <= Type::_kIdVec128End ? Reg::kTypeXmm : + typeId <= Type::_kIdVec256End ? Reg::kTypeYmm : Reg::kTypeZmm; +} -Error typeIdToRegInfo(uint32_t arch, uint32_t typeId, uint32_t* typeIdOut, RegInfo* regInfoOut) noexcept; +class EmitHelper : public BaseEmitHelper { +public: + bool _avxEnabled; -} // {ArchInternal} + inline explicit EmitHelper(BaseEmitter* emitter = nullptr, bool avxEnabled = false) noexcept + : BaseEmitHelper(emitter), + _avxEnabled(avxEnabled) {} + + Error emitRegMove( + const Operand_& dst_, + const Operand_& src_, uint32_t typeId, const char* comment = nullptr) override; + + Error emitArgMove( + const BaseReg& dst_, uint32_t dstTypeId, + const Operand_& src_, uint32_t srcTypeId, const char* comment = nullptr) override; + + Error emitRegSwap( + const BaseReg& a, + const BaseReg& b, const char* comment = nullptr) override; + + Error emitProlog(const FuncFrame& frame); + Error emitEpilog(const FuncFrame& frame); +}; //! \} //! \endcond ASMJIT_END_SUB_NAMESPACE -#endif // ASMJIT_X86_X86ARCHDATA_P_H_INCLUDED +#endif // ASMJIT_X86_X86EMITHELPER_P_H_INCLUDED diff --git a/src/asmjit/x86/x86emitter.h b/src/asmjit/x86/x86emitter.h index 8e37dc5..0334573 100644 --- a/src/asmjit/x86/x86emitter.h +++ b/src/asmjit/x86/x86emitter.h @@ -32,144 +32,112 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86) #define ASMJIT_INST_0x(NAME, ID) \ - inline Error NAME() { return _emitter()->emit(Inst::kId##ID); } + inline Error NAME() { return _emitter()->_emitI(Inst::kId##ID); } #define ASMJIT_INST_1x(NAME, ID, T0) \ - inline Error NAME(const T0& o0) { return _emitter()->emit(Inst::kId##ID, o0); } + inline Error NAME(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID, o0); } #define ASMJIT_INST_1i(NAME, ID, T0) \ - inline Error NAME(const T0& o0) { return _emitter()->emit(Inst::kId##ID, o0); } \ - /** \cond */ \ - inline Error NAME(int o0) { return _emitter()->emit(Inst::kId##ID, Support::asInt(o0)); } \ - inline Error NAME(unsigned int o0) { return _emitter()->emit(Inst::kId##ID, Support::asInt(o0)); } \ - inline Error NAME(int64_t o0) { return _emitter()->emit(Inst::kId##ID, Support::asInt(o0)); } \ - inline Error NAME(uint64_t o0) { return _emitter()->emit(Inst::kId##ID, Support::asInt(o0)); } \ - /** \endcond */ + inline Error NAME(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID, o0); } #define ASMJIT_INST_1c(NAME, ID, CONV, T0) \ - inline Error NAME(uint32_t cc, const T0& o0) { return _emitter()->emit(CONV(cc), o0); } \ - inline Error NAME##a(const T0& o0) { return _emitter()->emit(Inst::kId##ID##a, o0); } \ - inline Error NAME##ae(const T0& o0) { return _emitter()->emit(Inst::kId##ID##ae, o0); } \ - inline Error NAME##b(const T0& o0) { return _emitter()->emit(Inst::kId##ID##b, o0); } \ - inline Error NAME##be(const T0& o0) { return _emitter()->emit(Inst::kId##ID##be, o0); } \ - inline Error NAME##c(const T0& o0) { return _emitter()->emit(Inst::kId##ID##c, o0); } \ - inline Error NAME##e(const T0& o0) { return _emitter()->emit(Inst::kId##ID##e, o0); } \ - inline Error NAME##g(const T0& o0) { return _emitter()->emit(Inst::kId##ID##g, o0); } \ - inline Error NAME##ge(const T0& o0) { return _emitter()->emit(Inst::kId##ID##ge, o0); } \ - inline Error NAME##l(const T0& o0) { return _emitter()->emit(Inst::kId##ID##l, o0); } \ - inline Error NAME##le(const T0& o0) { return _emitter()->emit(Inst::kId##ID##le, o0); } \ - inline Error NAME##na(const T0& o0) { return _emitter()->emit(Inst::kId##ID##na, o0); } \ - inline Error NAME##nae(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nae, o0); } \ - inline Error NAME##nb(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nb, o0); } \ - inline Error NAME##nbe(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nbe, o0); } \ - inline Error NAME##nc(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nc, o0); } \ - inline Error NAME##ne(const T0& o0) { return _emitter()->emit(Inst::kId##ID##ne, o0); } \ - inline Error NAME##ng(const T0& o0) { return _emitter()->emit(Inst::kId##ID##ng, o0); } \ - inline Error NAME##nge(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nge, o0); } \ - inline Error NAME##nl(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nl, o0); } \ - inline Error NAME##nle(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nle, o0); } \ - inline Error NAME##no(const T0& o0) { return _emitter()->emit(Inst::kId##ID##no, o0); } \ - inline Error NAME##np(const T0& o0) { return _emitter()->emit(Inst::kId##ID##np, o0); } \ - inline Error NAME##ns(const T0& o0) { return _emitter()->emit(Inst::kId##ID##ns, o0); } \ - inline Error NAME##nz(const T0& o0) { return _emitter()->emit(Inst::kId##ID##nz, o0); } \ - inline Error NAME##o(const T0& o0) { return _emitter()->emit(Inst::kId##ID##o, o0); } \ - inline Error NAME##p(const T0& o0) { return _emitter()->emit(Inst::kId##ID##p, o0); } \ - inline Error NAME##pe(const T0& o0) { return _emitter()->emit(Inst::kId##ID##pe, o0); } \ - inline Error NAME##po(const T0& o0) { return _emitter()->emit(Inst::kId##ID##po, o0); } \ - inline Error NAME##s(const T0& o0) { return _emitter()->emit(Inst::kId##ID##s, o0); } \ - inline Error NAME##z(const T0& o0) { return _emitter()->emit(Inst::kId##ID##z, o0); } + inline Error NAME(uint32_t cc, const T0& o0) { return _emitter()->_emitI(CONV(cc), o0); } \ + inline Error NAME##a(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##a, o0); } \ + inline Error NAME##ae(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##ae, o0); } \ + inline Error NAME##b(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##b, o0); } \ + inline Error NAME##be(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##be, o0); } \ + inline Error NAME##c(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##c, o0); } \ + inline Error NAME##e(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##e, o0); } \ + inline Error NAME##g(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##g, o0); } \ + inline Error NAME##ge(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##ge, o0); } \ + inline Error NAME##l(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##l, o0); } \ + inline Error NAME##le(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##le, o0); } \ + inline Error NAME##na(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##na, o0); } \ + inline Error NAME##nae(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nae, o0); } \ + inline Error NAME##nb(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nb, o0); } \ + inline Error NAME##nbe(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nbe, o0); } \ + inline Error NAME##nc(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nc, o0); } \ + inline Error NAME##ne(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##ne, o0); } \ + inline Error NAME##ng(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##ng, o0); } \ + inline Error NAME##nge(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nge, o0); } \ + inline Error NAME##nl(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nl, o0); } \ + inline Error NAME##nle(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nle, o0); } \ + inline Error NAME##no(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##no, o0); } \ + inline Error NAME##np(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##np, o0); } \ + inline Error NAME##ns(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##ns, o0); } \ + inline Error NAME##nz(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##nz, o0); } \ + inline Error NAME##o(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##o, o0); } \ + inline Error NAME##p(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##p, o0); } \ + inline Error NAME##pe(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##pe, o0); } \ + inline Error NAME##po(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##po, o0); } \ + inline Error NAME##s(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##s, o0); } \ + inline Error NAME##z(const T0& o0) { return _emitter()->_emitI(Inst::kId##ID##z, o0); } #define ASMJIT_INST_2x(NAME, ID, T0, T1) \ - inline Error NAME(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID, o0, o1); } + inline Error NAME(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID, o0, o1); } #define ASMJIT_INST_2i(NAME, ID, T0, T1) \ - inline Error NAME(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID, o0, o1); } \ - /** \cond */ \ - inline Error NAME(const T0& o0, int o1) { return _emitter()->emit(Inst::kId##ID, o0, Support::asInt(o1)); } \ - inline Error NAME(const T0& o0, unsigned int o1) { return _emitter()->emit(Inst::kId##ID, o0, Support::asInt(o1)); } \ - inline Error NAME(const T0& o0, int64_t o1) { return _emitter()->emit(Inst::kId##ID, o0, Support::asInt(o1)); } \ - inline Error NAME(const T0& o0, uint64_t o1) { return _emitter()->emit(Inst::kId##ID, o0, Support::asInt(o1)); } \ - /** \endcond */ + inline Error NAME(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID, o0, o1); } #define ASMJIT_INST_2c(NAME, ID, CONV, T0, T1) \ - inline Error NAME(uint32_t cc, const T0& o0, const T1& o1) { return _emitter()->emit(CONV(cc), o0, o1); } \ - inline Error NAME##a(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##a, o0, o1); } \ - inline Error NAME##ae(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##ae, o0, o1); } \ - inline Error NAME##b(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##b, o0, o1); } \ - inline Error NAME##be(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##be, o0, o1); } \ - inline Error NAME##c(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##c, o0, o1); } \ - inline Error NAME##e(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##e, o0, o1); } \ - inline Error NAME##g(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##g, o0, o1); } \ - inline Error NAME##ge(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##ge, o0, o1); } \ - inline Error NAME##l(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##l, o0, o1); } \ - inline Error NAME##le(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##le, o0, o1); } \ - inline Error NAME##na(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##na, o0, o1); } \ - inline Error NAME##nae(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nae, o0, o1); } \ - inline Error NAME##nb(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nb, o0, o1); } \ - inline Error NAME##nbe(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nbe, o0, o1); } \ - inline Error NAME##nc(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nc, o0, o1); } \ - inline Error NAME##ne(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##ne, o0, o1); } \ - inline Error NAME##ng(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##ng, o0, o1); } \ - inline Error NAME##nge(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nge, o0, o1); } \ - inline Error NAME##nl(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nl, o0, o1); } \ - inline Error NAME##nle(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nle, o0, o1); } \ - inline Error NAME##no(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##no, o0, o1); } \ - inline Error NAME##np(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##np, o0, o1); } \ - inline Error NAME##ns(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##ns, o0, o1); } \ - inline Error NAME##nz(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##nz, o0, o1); } \ - inline Error NAME##o(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##o, o0, o1); } \ - inline Error NAME##p(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##p, o0, o1); } \ - inline Error NAME##pe(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##pe, o0, o1); } \ - inline Error NAME##po(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##po, o0, o1); } \ - inline Error NAME##s(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##s, o0, o1); } \ - inline Error NAME##z(const T0& o0, const T1& o1) { return _emitter()->emit(Inst::kId##ID##z, o0, o1); } + inline Error NAME(uint32_t cc, const T0& o0, const T1& o1) { return _emitter()->_emitI(CONV(cc), o0, o1); } \ + inline Error NAME##a(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##a, o0, o1); } \ + inline Error NAME##ae(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##ae, o0, o1); } \ + inline Error NAME##b(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##b, o0, o1); } \ + inline Error NAME##be(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##be, o0, o1); } \ + inline Error NAME##c(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##c, o0, o1); } \ + inline Error NAME##e(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##e, o0, o1); } \ + inline Error NAME##g(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##g, o0, o1); } \ + inline Error NAME##ge(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##ge, o0, o1); } \ + inline Error NAME##l(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##l, o0, o1); } \ + inline Error NAME##le(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##le, o0, o1); } \ + inline Error NAME##na(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##na, o0, o1); } \ + inline Error NAME##nae(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nae, o0, o1); } \ + inline Error NAME##nb(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nb, o0, o1); } \ + inline Error NAME##nbe(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nbe, o0, o1); } \ + inline Error NAME##nc(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nc, o0, o1); } \ + inline Error NAME##ne(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##ne, o0, o1); } \ + inline Error NAME##ng(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##ng, o0, o1); } \ + inline Error NAME##nge(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nge, o0, o1); } \ + inline Error NAME##nl(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nl, o0, o1); } \ + inline Error NAME##nle(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nle, o0, o1); } \ + inline Error NAME##no(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##no, o0, o1); } \ + inline Error NAME##np(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##np, o0, o1); } \ + inline Error NAME##ns(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##ns, o0, o1); } \ + inline Error NAME##nz(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##nz, o0, o1); } \ + inline Error NAME##o(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##o, o0, o1); } \ + inline Error NAME##p(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##p, o0, o1); } \ + inline Error NAME##pe(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##pe, o0, o1); } \ + inline Error NAME##po(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##po, o0, o1); } \ + inline Error NAME##s(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##s, o0, o1); } \ + inline Error NAME##z(const T0& o0, const T1& o1) { return _emitter()->_emitI(Inst::kId##ID##z, o0, o1); } #define ASMJIT_INST_3x(NAME, ID, T0, T1, T2) \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2); } + inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2); } #define ASMJIT_INST_3i(NAME, ID, T0, T1, T2) \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2); } \ - /** \cond */ \ - inline Error NAME(const T0& o0, const T1& o1, int o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, Support::asInt(o2)); } \ - inline Error NAME(const T0& o0, const T1& o1, unsigned int o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, Support::asInt(o2)); } \ - inline Error NAME(const T0& o0, const T1& o1, int64_t o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, Support::asInt(o2)); } \ - inline Error NAME(const T0& o0, const T1& o1, uint64_t o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, Support::asInt(o2)); } \ - /** \endcond */ + inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2); } #define ASMJIT_INST_3ii(NAME, ID, T0, T1, T2) \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2); } \ - inline Error NAME(const T0& o0, int o1, int o2) { return _emitter()->emit(Inst::kId##ID, o0, Imm(o1), Support::asInt(o2)); } + inline Error NAME(const T0& o0, const T1& o1, const T2& o2) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2); } #define ASMJIT_INST_4x(NAME, ID, T0, T1, T2, T3) \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3); } + inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3); } #define ASMJIT_INST_4i(NAME, ID, T0, T1, T2, T3) \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3); } \ - /** \cond */ \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, int o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, Support::asInt(o3)); } \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, unsigned int o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, Support::asInt(o3)); } \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, int64_t o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, Support::asInt(o3)); } \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, uint64_t o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, Support::asInt(o3)); } \ - /** \endcond */ + inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3); } #define ASMJIT_INST_4ii(NAME, ID, T0, T1, T2, T3) \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3); } \ - inline Error NAME(const T0& o0, const T1& o1, int o2, int o3) { return _emitter()->emit(Inst::kId##ID, o0, o1, Imm(o2), Support::asInt(o3)); } + inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3); } #define ASMJIT_INST_5x(NAME, ID, T0, T1, T2, T3, T4) \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, o4); } + inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3, o4); } #define ASMJIT_INST_5i(NAME, ID, T0, T1, T2, T3, T4) \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, o4); } \ - /** \cond */ \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, int o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, Support::asInt(o4)); } \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, unsigned int o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, Support::asInt(o4)); } \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, int64_t o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, Support::asInt(o4)); } \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, uint64_t o4) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, Support::asInt(o4)); } \ - /** \endcond */ + inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3, o4); } #define ASMJIT_INST_6x(NAME, ID, T0, T1, T2, T3, T4, T5) \ - inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4, const T5& o5) { return _emitter()->emit(Inst::kId##ID, o0, o1, o2, o3, o4, o5); } + inline Error NAME(const T0& o0, const T1& o1, const T2& o2, const T3& o3, const T4& o4, const T5& o5) { return _emitter()->_emitI(Inst::kId##ID, o0, o1, o2, o3, o4, o5); } //! \addtogroup asmjit_x86 //! \{ @@ -224,16 +192,16 @@ struct EmitterExplicitT { //! \{ //! Returns either GPD or GPQ register of the given `id` depending on the emitter's architecture. - inline Gp gpz(uint32_t id) const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), id); } + inline Gp gpz(uint32_t id) const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), id); } - inline Gp zax() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdAx); } - inline Gp zcx() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdCx); } - inline Gp zdx() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdDx); } - inline Gp zbx() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdBx); } - inline Gp zsp() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdSp); } - inline Gp zbp() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdBp); } - inline Gp zsi() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdSi); } - inline Gp zdi() const noexcept { return Gp(_emitter()->_gpRegInfo.signature(), Gp::kIdDi); } + inline Gp zax() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdAx); } + inline Gp zcx() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdCx); } + inline Gp zdx() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdDx); } + inline Gp zbx() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdBx); } + inline Gp zsp() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdSp); } + inline Gp zbp() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdBp); } + inline Gp zsi() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdSi); } + inline Gp zdi() const noexcept { return Gp::fromSignatureAndId(_emitter()->_gpRegInfo.signature(), Gp::kIdDi); } //! \} @@ -302,12 +270,12 @@ struct EmitterExplicitT { //! \overload inline Mem intptr_ptr_abs(uint64_t base) const noexcept { uint32_t nativeGpSize = _emitter()->registerSize(); - return Mem(base, nativeGpSize, BaseMem::kSignatureMemAbs); + return Mem(base, nativeGpSize, Mem::kSignatureMemAbs); } //! \overload inline Mem intptr_ptr_abs(uint64_t base, const Gp& index, uint32_t shift = 0) const noexcept { uint32_t nativeGpSize = _emitter()->registerSize(); - return Mem(base, index, shift, nativeGpSize, BaseMem::kSignatureMemAbs); + return Mem(base, index, shift, nativeGpSize, Mem::kSignatureMemAbs); } //! \} diff --git a/src/asmjit/x86/x86features.h b/src/asmjit/x86/x86features.h index 24a73f5..527a765 100644 --- a/src/asmjit/x86/x86features.h +++ b/src/asmjit/x86/x86features.h @@ -168,6 +168,7 @@ public: inline Features() noexcept : BaseFeatures() {} + inline Features(const Features& other) noexcept : BaseFeatures(other) {} diff --git a/src/asmjit/x86/x86formatter.cpp b/src/asmjit/x86/x86formatter.cpp index 398a30c..d7b065c 100644 --- a/src/asmjit/x86/x86formatter.cpp +++ b/src/asmjit/x86/x86formatter.cpp @@ -338,6 +338,59 @@ Error FormatterInternal::formatFeature(String& sb, uint32_t featureId) noexcept return sb.append(sFeatureString + sFeatureIndex[Support::min(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(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("?%u", rType, rId); +} + // ============================================================================ // [asmjit::x86::FormatterInternal - Format Operand] // ============================================================================ @@ -363,8 +416,8 @@ ASMJIT_FAVOR_SIZE Error FormatterInternal::formatOperand( ASMJIT_PROPAGATE(sb.append('[')); switch (m.addrType()) { - case BaseMem::kAddrTypeAbs: ASMJIT_PROPAGATE(sb.append("abs ")); break; - case BaseMem::kAddrTypeRel: ASMJIT_PROPAGATE(sb.append("rel ")); break; + case Mem::kAddrTypeAbs: ASMJIT_PROPAGATE(sb.append("abs ")); break; + case Mem::kAddrTypeRel: ASMJIT_PROPAGATE(sb.append("rel ")); break; } char opSign = '\0'; @@ -763,59 +816,6 @@ ASMJIT_FAVOR_SIZE static Error FormatterInternal_explainConst( } } -// ============================================================================ -// [asmjit::x86::FormatterInternal - Format Register] -// ============================================================================ - -ASMJIT_FAVOR_SIZE Error FormatterInternal::formatRegister(String& sb, uint32_t flags, const BaseEmitter* emitter, uint32_t arch, uint32_t rType, uint32_t rId) noexcept { - DebugUtils::unused(arch); - const RegFormatInfo& info = x86RegFormatInfo; - -#ifndef ASMJIT_NO_COMPILER - if (Operand::isVirtId(rId)) { - if (emitter && emitter->emitterType() == BaseEmitter::kTypeCompiler) { - const BaseCompiler* cc = static_cast(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] // ============================================================================ diff --git a/src/asmjit/x86/x86func.cpp b/src/asmjit/x86/x86func.cpp new file mode 100644 index 0000000..ef5c4d9 --- /dev/null +++ b/src/asmjit/x86/x86func.cpp @@ -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(8)); + cc.setPreservedRegs(kGroupVec, Support::lsbMask(8) & ~Support::lsbMask(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(16)); + cc.setPreservedRegs(kGroupVec, ~Support::lsbMask(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(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 diff --git a/src/asmjit/x86/x86callconv_p.h b/src/asmjit/x86/x86func_p.h similarity index 76% rename from src/asmjit/x86/x86callconv_p.h rename to src/asmjit/x86/x86func_p.h index 1607ea2..94745ca 100644 --- a/src/asmjit/x86/x86callconv_p.h +++ b/src/asmjit/x86/x86func_p.h @@ -21,10 +21,10 @@ // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. -#ifndef ASMJIT_X86_X86CALLCONV_P_H_INCLUDED -#define ASMJIT_X86_X86CALLCONV_P_H_INCLUDED +#ifndef ASMJIT_X86_X86FUNC_P_H_INCLUDED +#define ASMJIT_X86_X86FUNC_P_H_INCLUDED -#include "../core/callconv.h" +#include "../core/func.h" ASMJIT_BEGIN_SUB_NAMESPACE(x86) @@ -33,20 +33,23 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86) //! \{ // ============================================================================ -// [asmjit::x86::CallConvInternal] +// [asmjit::x86::FuncInternal] // ============================================================================ //! X86-specific function API (calling conventions and other utilities). -namespace CallConvInternal { +namespace FuncInternal { //! Initialize `CallConv` structure (X86 specific). -Error init(CallConv& cc, uint32_t ccId, const Environment& environment) noexcept; +Error initCallConv(CallConv& cc, uint32_t ccId, const Environment& environment) noexcept; -} // {CallConvInternal} +//! Initialize `FuncDetail` (X86 specific). +Error initFuncDetail(FuncDetail& func, const FuncSignature& signature, uint32_t registerSize) noexcept; + +} // {FuncInternal} //! \} //! \endcond ASMJIT_END_SUB_NAMESPACE -#endif // ASMJIT_X86_X86CALLCONV_P_H_INCLUDED +#endif // ASMJIT_X86_X86FUNC_P_H_INCLUDED diff --git a/src/asmjit/x86/x86globals.h b/src/asmjit/x86/x86globals.h index 4e11565..95838fc 100644 --- a/src/asmjit/x86/x86globals.h +++ b/src/asmjit/x86/x86globals.h @@ -24,7 +24,7 @@ #ifndef ASMJIT_X86_X86GLOBALS_H_INCLUDED #define ASMJIT_X86_X86GLOBALS_H_INCLUDED -#include "../core/arch.h" +#include "../core/archtraits.h" #include "../core/inst.h" ASMJIT_BEGIN_SUB_NAMESPACE(x86) @@ -1723,16 +1723,16 @@ namespace Condition { static constexpr uint16_t cmovccTable[] = { ASMJIT_INST_FROM_COND(Inst::kIdCmov) }; #undef ASMJIT_INST_FROM_COND - //! Reverse a condition code (reverses the corresponding operands of a comparison). + //! Reverses a condition code (reverses the corresponding operands of a comparison). static constexpr uint32_t reverse(uint32_t cond) noexcept { return reverseTable[cond]; } - //! Negate a condition code. + //! Negates a condition code. static constexpr uint32_t negate(uint32_t cond) noexcept { return cond ^ 1u; } - //! Translate a condition code `cond` to a `jcc` instruction id. + //! Translates a condition code `cond` to a `jcc` instruction id. static constexpr uint32_t toJcc(uint32_t cond) noexcept { return jccTable[cond]; } - //! Translate a condition code `cond` to a `setcc` instruction id. + //! Translates a condition code `cond` to a `setcc` instruction id. static constexpr uint32_t toSetcc(uint32_t cond) noexcept { return setccTable[cond]; } - //! Translate a condition code `cond` to a `cmovcc` instruction id. + //! Translates a condition code `cond` to a `cmovcc` instruction id. static constexpr uint32_t toCmovcc(uint32_t cond) noexcept { return cmovccTable[cond]; } } diff --git a/src/asmjit/x86/x86instapi.cpp b/src/asmjit/x86/x86instapi.cpp index 9ad8f93..fec69b2 100644 --- a/src/asmjit/x86/x86instapi.cpp +++ b/src/asmjit/x86/x86instapi.cpp @@ -126,64 +126,64 @@ struct X86ValidationData { uint32_t allowedMemIndexRegs; }; -#define VALUE(X) \ - (X == Reg::kTypeGpbLo) ? InstDB::kOpGpbLo : \ - (X == Reg::kTypeGpbHi) ? InstDB::kOpGpbHi : \ - (X == Reg::kTypeGpw ) ? InstDB::kOpGpw : \ - (X == Reg::kTypeGpd ) ? InstDB::kOpGpd : \ - (X == Reg::kTypeGpq ) ? InstDB::kOpGpq : \ - (X == Reg::kTypeXmm ) ? InstDB::kOpXmm : \ - (X == Reg::kTypeYmm ) ? InstDB::kOpYmm : \ - (X == Reg::kTypeZmm ) ? InstDB::kOpZmm : \ - (X == Reg::kTypeMm ) ? InstDB::kOpMm : \ - (X == Reg::kTypeKReg ) ? InstDB::kOpKReg : \ - (X == Reg::kTypeSReg ) ? InstDB::kOpSReg : \ - (X == Reg::kTypeCReg ) ? InstDB::kOpCReg : \ - (X == Reg::kTypeDReg ) ? InstDB::kOpDReg : \ - (X == Reg::kTypeSt ) ? InstDB::kOpSt : \ - (X == Reg::kTypeBnd ) ? InstDB::kOpBnd : \ - (X == Reg::kTypeTmm ) ? InstDB::kOpTmm : \ - (X == Reg::kTypeRip ) ? InstDB::kOpNone : InstDB::kOpNone +#define VALUE(x) \ + (x == Reg::kTypeGpbLo) ? InstDB::kOpGpbLo : \ + (x == Reg::kTypeGpbHi) ? InstDB::kOpGpbHi : \ + (x == Reg::kTypeGpw ) ? InstDB::kOpGpw : \ + (x == Reg::kTypeGpd ) ? InstDB::kOpGpd : \ + (x == Reg::kTypeGpq ) ? InstDB::kOpGpq : \ + (x == Reg::kTypeXmm ) ? InstDB::kOpXmm : \ + (x == Reg::kTypeYmm ) ? InstDB::kOpYmm : \ + (x == Reg::kTypeZmm ) ? InstDB::kOpZmm : \ + (x == Reg::kTypeMm ) ? InstDB::kOpMm : \ + (x == Reg::kTypeKReg ) ? InstDB::kOpKReg : \ + (x == Reg::kTypeSReg ) ? InstDB::kOpSReg : \ + (x == Reg::kTypeCReg ) ? InstDB::kOpCReg : \ + (x == Reg::kTypeDReg ) ? InstDB::kOpDReg : \ + (x == Reg::kTypeSt ) ? InstDB::kOpSt : \ + (x == Reg::kTypeBnd ) ? InstDB::kOpBnd : \ + (x == Reg::kTypeTmm ) ? InstDB::kOpTmm : \ + (x == Reg::kTypeRip ) ? InstDB::kOpNone : InstDB::kOpNone static const uint32_t _x86OpFlagFromRegType[Reg::kTypeMax + 1] = { ASMJIT_LOOKUP_TABLE_32(VALUE, 0) }; #undef VALUE -#define REG_MASK_FROM_REG_TYPE_X86(X) \ - (X == Reg::kTypeGpbLo) ? 0x0000000Fu : \ - (X == Reg::kTypeGpbHi) ? 0x0000000Fu : \ - (X == Reg::kTypeGpw ) ? 0x000000FFu : \ - (X == Reg::kTypeGpd ) ? 0x000000FFu : \ - (X == Reg::kTypeGpq ) ? 0x000000FFu : \ - (X == Reg::kTypeXmm ) ? 0x000000FFu : \ - (X == Reg::kTypeYmm ) ? 0x000000FFu : \ - (X == Reg::kTypeZmm ) ? 0x000000FFu : \ - (X == Reg::kTypeMm ) ? 0x000000FFu : \ - (X == Reg::kTypeKReg ) ? 0x000000FFu : \ - (X == Reg::kTypeSReg ) ? 0x0000007Eu : \ - (X == Reg::kTypeCReg ) ? 0x0000FFFFu : \ - (X == Reg::kTypeDReg ) ? 0x000000FFu : \ - (X == Reg::kTypeSt ) ? 0x000000FFu : \ - (X == Reg::kTypeBnd ) ? 0x0000000Fu : \ - (X == Reg::kTypeTmm ) ? 0x000000FFu : \ - (X == Reg::kTypeRip ) ? 0x00000001u : 0u +#define REG_MASK_FROM_REG_TYPE_X86(x) \ + (x == Reg::kTypeGpbLo) ? 0x0000000Fu : \ + (x == Reg::kTypeGpbHi) ? 0x0000000Fu : \ + (x == Reg::kTypeGpw ) ? 0x000000FFu : \ + (x == Reg::kTypeGpd ) ? 0x000000FFu : \ + (x == Reg::kTypeGpq ) ? 0x000000FFu : \ + (x == Reg::kTypeXmm ) ? 0x000000FFu : \ + (x == Reg::kTypeYmm ) ? 0x000000FFu : \ + (x == Reg::kTypeZmm ) ? 0x000000FFu : \ + (x == Reg::kTypeMm ) ? 0x000000FFu : \ + (x == Reg::kTypeKReg ) ? 0x000000FFu : \ + (x == Reg::kTypeSReg ) ? 0x0000007Eu : \ + (x == Reg::kTypeCReg ) ? 0x0000FFFFu : \ + (x == Reg::kTypeDReg ) ? 0x000000FFu : \ + (x == Reg::kTypeSt ) ? 0x000000FFu : \ + (x == Reg::kTypeBnd ) ? 0x0000000Fu : \ + (x == Reg::kTypeTmm ) ? 0x000000FFu : \ + (x == Reg::kTypeRip ) ? 0x00000001u : 0u -#define REG_MASK_FROM_REG_TYPE_X64(X) \ - (X == Reg::kTypeGpbLo) ? 0x0000FFFFu : \ - (X == Reg::kTypeGpbHi) ? 0x0000000Fu : \ - (X == Reg::kTypeGpw ) ? 0x0000FFFFu : \ - (X == Reg::kTypeGpd ) ? 0x0000FFFFu : \ - (X == Reg::kTypeGpq ) ? 0x0000FFFFu : \ - (X == Reg::kTypeXmm ) ? 0xFFFFFFFFu : \ - (X == Reg::kTypeYmm ) ? 0xFFFFFFFFu : \ - (X == Reg::kTypeZmm ) ? 0xFFFFFFFFu : \ - (X == Reg::kTypeMm ) ? 0x000000FFu : \ - (X == Reg::kTypeKReg ) ? 0x000000FFu : \ - (X == Reg::kTypeSReg ) ? 0x0000007Eu : \ - (X == Reg::kTypeCReg ) ? 0x0000FFFFu : \ - (X == Reg::kTypeDReg ) ? 0x0000FFFFu : \ - (X == Reg::kTypeSt ) ? 0x000000FFu : \ - (X == Reg::kTypeBnd ) ? 0x0000000Fu : \ - (X == Reg::kTypeTmm ) ? 0x000000FFu : \ - (X == Reg::kTypeRip ) ? 0x00000001u : 0u +#define REG_MASK_FROM_REG_TYPE_X64(x) \ + (x == Reg::kTypeGpbLo) ? 0x0000FFFFu : \ + (x == Reg::kTypeGpbHi) ? 0x0000000Fu : \ + (x == Reg::kTypeGpw ) ? 0x0000FFFFu : \ + (x == Reg::kTypeGpd ) ? 0x0000FFFFu : \ + (x == Reg::kTypeGpq ) ? 0x0000FFFFu : \ + (x == Reg::kTypeXmm ) ? 0xFFFFFFFFu : \ + (x == Reg::kTypeYmm ) ? 0xFFFFFFFFu : \ + (x == Reg::kTypeZmm ) ? 0xFFFFFFFFu : \ + (x == Reg::kTypeMm ) ? 0x000000FFu : \ + (x == Reg::kTypeKReg ) ? 0x000000FFu : \ + (x == Reg::kTypeSReg ) ? 0x0000007Eu : \ + (x == Reg::kTypeCReg ) ? 0x0000FFFFu : \ + (x == Reg::kTypeDReg ) ? 0x0000FFFFu : \ + (x == Reg::kTypeSt ) ? 0x000000FFu : \ + (x == Reg::kTypeBnd ) ? 0x0000000Fu : \ + (x == Reg::kTypeTmm ) ? 0x000000FFu : \ + (x == Reg::kTypeRip ) ? 0x00000001u : 0u static const X86ValidationData _x86ValidationData = { { ASMJIT_LOOKUP_TABLE_32(REG_MASK_FROM_REG_TYPE_X86, 0) }, diff --git a/src/asmjit/x86/x86instdb.h b/src/asmjit/x86/x86instdb.h index f02c3e8..6c40f6a 100644 --- a/src/asmjit/x86/x86instdb.h +++ b/src/asmjit/x86/x86instdb.h @@ -366,21 +366,21 @@ ASMJIT_VARAPI const CommonInfo _commonInfoTable[]; //! Instruction information (X86). struct InstInfo { - //! Index to `_nameData`. + //! Index to \ref _nameData. uint32_t _nameDataIndex : 14; - //! Index to `_commonInfoTable`. + //! Index to \ref _commonInfoTable. uint32_t _commonInfoIndex : 10; - //! Index to `InstDB::_commonInfoTableB`. + //! Index to \ref _commonInfoTableB. uint32_t _commonInfoIndexB : 8; - //! Instruction encoding, see `InstDB::EncodingId`. + //! Instruction encoding (internal encoding identifier used by \ref Assembler). uint8_t _encoding; - //! Main opcode value (0.255). + //! Main opcode value (0..255). uint8_t _mainOpcodeValue; - //! Index to `InstDB::_mainOpcodeTable` that is combined with `_mainOpcodeValue` + //! Index to \ref _mainOpcodeTable` that is combined with \ref _mainOpcodeValue //! to form the final opcode. uint8_t _mainOpcodeIndex; - //! Index to `InstDB::_altOpcodeTable` that contains a full alternative opcode. + //! Index to \ref _altOpcodeTable that contains a full alternative opcode. uint8_t _altOpcodeIndex; // -------------------------------------------------------------------------- @@ -456,7 +456,7 @@ struct InstInfo { ASMJIT_VARAPI const InstInfo _instInfoTable[]; -inline const InstInfo& infoById(uint32_t instId) noexcept { +static inline const InstInfo& infoById(uint32_t instId) noexcept { ASMJIT_ASSERT(Inst::isDefinedId(instId)); return _instInfoTable[instId]; } diff --git a/src/asmjit/x86/x86internal.cpp b/src/asmjit/x86/x86internal.cpp deleted file mode 100644 index 93a0937..0000000 --- a/src/asmjit/x86/x86internal.cpp +++ /dev/null @@ -1,1784 +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/formatter.h" -#include "../core/string.h" -#include "../core/support.h" -#include "../core/type.h" -#include "../x86/x86internal_p.h" - -// Can be used for debugging... -// #define ASMJIT_DUMP_ARGS_ASSIGNMENT - -ASMJIT_BEGIN_SUB_NAMESPACE(x86) - -// ============================================================================ -// [asmjit::X86Internal - Helpers] -// ============================================================================ - -static ASMJIT_INLINE uint32_t x86GetXmmMovInst(const FuncFrame& frame) { - bool avx = frame.isAvxEnabled(); - bool aligned = frame.hasAlignedVecSR(); - - return aligned ? (avx ? Inst::kIdVmovaps : Inst::kIdMovaps) - : (avx ? Inst::kIdVmovups : Inst::kIdMovups); -} - -static ASMJIT_INLINE uint32_t x86VecTypeIdToRegType(uint32_t typeId) noexcept { - return typeId <= Type::_kIdVec128End ? Reg::kTypeXmm : - typeId <= Type::_kIdVec256End ? Reg::kTypeYmm : Reg::kTypeZmm; -} - -//! Converts `size` to a 'kmov?' instructio. -static inline uint32_t x86KmovFromSize(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 - FuncDetail] -// ============================================================================ - -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 X86Internal::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(x86VecTypeIdToRegType(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(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(x86VecTypeIdToRegType(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 = x86VecTypeIdToRegType(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; -} - -// ============================================================================ -// [asmjit::X86FuncArgsContext] -// ============================================================================ - -static RegInfo x86GetRegForMemToMemMove(uint32_t arch, uint32_t dstTypeId, uint32_t srcTypeId) noexcept { - uint32_t dstSize = Type::sizeOf(dstTypeId); - uint32_t srcSize = Type::sizeOf(srcTypeId); - uint32_t maxSize = Support::max(dstSize, srcSize); - uint32_t regSize = Environment::registerSizeFromArch(arch); - - uint32_t signature = 0; - if (maxSize <= regSize || (Type::isInt(dstTypeId) && Type::isInt(srcTypeId))) - signature = maxSize <= 4 ? Gpd::kSignature : Gpq::kSignature; - else if (maxSize <= 16) - signature = Xmm::kSignature; - else if (maxSize <= 32) - signature = Ymm::kSignature; - else if (maxSize <= 64) - signature = Zmm::kSignature; - - return RegInfo { signature }; -} - -// Used by both `argsToFuncFrame()` and `emitArgsAssignment()`. -class X86FuncArgsContext { -public: - enum VarId : uint32_t { - kVarIdNone = 0xFF - }; - - //! Contains information about a single argument or SA register that may need shuffling. - struct Var { - 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); } - - FuncValue cur; - FuncValue out; - }; - - struct WorkData { - 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; } - - uint32_t _archRegs; //!< All allocable registers provided by the architecture. - uint32_t _workRegs; //!< All registers that can be used by the shuffler. - uint32_t _usedRegs; //!< Registers used by the shuffler (all). - uint32_t _assignedRegs; //!< Assigned registers. - uint32_t _dstRegs; //!< Destination registers assigned to arguments or SA. - uint32_t _dstShuf; //!< Destination registers that require shuffling. - uint8_t _numSwaps; //!< Number of register swaps. - uint8_t _numStackArgs; //!< Number of stack loads. - uint8_t _reserved[6]; //!< Reserved (only used as padding). - uint8_t _physToVarId[32]; //!< Physical ID to variable ID mapping. - }; - - uint8_t _arch; - bool _hasStackSrc; //!< Has arguments passed via stack (SRC). - bool _hasPreservedFP; //!< Has preserved frame-pointer (FP). - uint8_t _stackDstMask; //!< Has arguments assigned to stack (DST). - uint8_t _regSwapsMask; //!< Register swap groups (bit-mask). - uint8_t _saVarId; - uint32_t _varCount; - WorkData _workData[BaseReg::kGroupVirt]; - Var _vars[Globals::kMaxFuncArgs + 1]; - - X86FuncArgsContext() noexcept; - - inline uint32_t arch() const noexcept { return _arch; } - inline uint32_t varCount() const noexcept { return _varCount; } - - inline Var& var(size_t varId) noexcept { return _vars[varId]; } - inline const Var& var(size_t varId) const noexcept { return _vars[varId]; } - inline size_t indexOf(const Var* var) const noexcept { return (size_t)(var - _vars); } - - Error initWorkData(const FuncFrame& frame, const FuncArgsAssignment& args) noexcept; - Error markScratchRegs(FuncFrame& frame) noexcept; - Error markDstRegsDirty(FuncFrame& frame) noexcept; - Error markStackArgsReg(FuncFrame& frame) noexcept; -}; - -X86FuncArgsContext::X86FuncArgsContext() noexcept { - _arch = Environment::kArchUnknown; - _varCount = 0; - _hasStackSrc = false; - _hasPreservedFP = false; - _stackDstMask = 0; - _regSwapsMask = 0; - _saVarId = kVarIdNone; - - for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) - _workData[group].reset(); -} - -ASMJIT_FAVOR_SIZE Error X86FuncArgsContext::initWorkData(const FuncFrame& frame, const FuncArgsAssignment& args) noexcept { - // The code has to be updated if this changes. - ASMJIT_ASSERT(BaseReg::kGroupVirt == 4); - - const FuncDetail& func = *args.funcDetail(); - - // Initialize Architecture. - uint32_t arch = func.callConv().arch(); - uint32_t archRegCount = Environment::is32Bit(arch) ? 8 : 16; - - _arch = uint8_t(arch); - - // Initialize `_archRegs`. - _workData[Reg::kGroupGp ]._archRegs = Support::lsbMask(archRegCount) & ~Support::bitMask(Gp::kIdSp); - _workData[Reg::kGroupVec ]._archRegs = Support::lsbMask(archRegCount); - _workData[Reg::kGroupMm ]._archRegs = Support::lsbMask(8); - _workData[Reg::kGroupKReg]._archRegs = Support::lsbMask(8); - - if (frame.hasPreservedFP()) - _workData[Reg::kGroupGp]._archRegs &= ~Support::bitMask(Gp::kIdBp); - - // 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(dstType >= Reg::kTypeCount)) - 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(Reg::typeIdOf(dst.regType())); - - dstGroup = Reg::groupOf(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 = x86GetRegForMemToMemMove(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 = Reg::groupOf(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. - uint32_t i; - 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 = Reg::groupOf(var.cur.regType()); - if (group != Reg::groupOf(var.out.regType())) - continue; - - WorkData& wd = _workData[group]; - if (wd.isAssigned(dstId)) { - Var& other = _vars[wd._physToVarId[dstId]]; - if (Reg::groupOf(other.out.regType()) == group && other.out.regId() == srcId) { - wd._numSwaps++; - _regSwapsMask = uint8_t(_regSwapsMask | Support::bitMask(group)); - } - } - } - } - - return kErrorOk; -} - -ASMJIT_FAVOR_SIZE Error X86FuncArgsContext::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 X86FuncArgsContext::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 X86FuncArgsContext::markStackArgsReg(FuncFrame& frame) noexcept { - if (_saVarId != kVarIdNone) { - const Var& var = _vars[_saVarId]; - frame.setSARegId(var.cur.regId()); - } - else if (frame.hasPreservedFP()) { - // Always EBP|RBP if the frame-pointer isn't omitted. - frame.setSARegId(Gp::kIdBp); - } - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::X86Internal - FrameLayout] -// ============================================================================ - -ASMJIT_FAVOR_SIZE Error X86Internal::initFuncFrame(FuncFrame& frame, const FuncDetail& func) noexcept { - uint32_t arch = func.callConv().arch(); - - // 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. - frame.reset(); - - frame._arch = uint8_t(arch); - frame._spRegId = Gp::kIdSp; - frame._saRegId = Gp::kIdBad; - - uint32_t naturalStackAlignment = func.callConv().naturalStackAlignment(); - uint32_t minDynamicAlignment = Support::max(naturalStackAlignment, 16); - - if (minDynamicAlignment == naturalStackAlignment) - minDynamicAlignment <<= 1; - - frame._naturalStackAlignment = uint8_t(naturalStackAlignment); - frame._minDynamicAlignment = uint8_t(minDynamicAlignment); - frame._redZoneSize = uint8_t(func.redZoneSize()); - frame._spillZoneSize = uint8_t(func.spillZoneSize()); - frame._finalStackAlignment = uint8_t(frame._naturalStackAlignment); - - if (func.hasFlag(CallConv::kFlagCalleePopsStack)) { - frame._calleeStackCleanup = uint16_t(func.argStackSize()); - } - - // Initial masks of dirty and preserved registers. - for (uint32_t group = 0; group < BaseReg::kGroupVirt; group++) { - frame._dirtyRegs[group] = func.usedRegs(group); - frame._preservedRegs[group] = func.preservedRegs(group); - } - - // Exclude ESP/RSP - this register is never included in saved GP regs. - frame._preservedRegs[BaseReg::kGroupGp] &= ~Support::bitMask(Gp::kIdSp); - - return kErrorOk; -} - -ASMJIT_FAVOR_SIZE Error X86Internal::finalizeFuncFrame(FuncFrame& frame) noexcept { - uint32_t registerSize = Environment::registerSizeFromArch(frame.arch()); - - // The final stack alignment must be updated accordingly to call and local stack alignments. - uint32_t stackAlignment = frame._finalStackAlignment; - ASMJIT_ASSERT(stackAlignment == Support::max(frame._naturalStackAlignment, - frame._callStackAlignment, - frame._localStackAlignment)); - - // TODO: Must be configurable. - uint32_t vecSize = 16; - - bool hasFP = frame.hasPreservedFP(); - bool hasDA = frame.hasDynamicAlignment(); - - // Include EBP|RBP if the function preserves the frame-pointer. - if (hasFP) - frame._dirtyRegs[Reg::kGroupGp] |= Support::bitMask(Gp::kIdBp); - - // These two are identical if the function doesn't align its stack dynamically. - uint32_t saRegId = frame.saRegId(); - if (saRegId == BaseReg::kIdBad) - saRegId = Gp::kIdSp; - - // Fix stack arguments base-register from ESP|RSP to EBP|RBP in case it was - // not picked before and the function performs dynamic stack alignment. - if (hasDA && saRegId == Gp::kIdSp) - saRegId = Gp::kIdBp; - - // Mark as dirty any register but ESP|RSP if used as SA pointer. - if (saRegId != Gp::kIdSp) - frame._dirtyRegs[Reg::kGroupGp] |= Support::bitMask(saRegId); - - frame._spRegId = uint8_t(Gp::kIdSp); - frame._saRegId = uint8_t(saRegId); - - // Setup stack size used to save preserved registers. - frame._gpSaveSize = uint16_t(Support::popcnt(frame.savedRegs(Reg::kGroupGp )) * registerSize); - frame._nonGpSaveSize = uint16_t(Support::popcnt(frame.savedRegs(Reg::kGroupVec )) * vecSize + - Support::popcnt(frame.savedRegs(Reg::kGroupMm )) * 8 + - Support::popcnt(frame.savedRegs(Reg::kGroupKReg)) * 8); - - uint32_t v = 0; // The beginning of the stack frame relative to SP after prolog. - v += frame.callStackSize(); // Count 'callStackSize' <- This is used to call functions. - v = Support::alignUp(v, stackAlignment); // Align to function's stack alignment. - - frame._localStackOffset = v; // Store 'localStackOffset' <- Function's local stack starts here. - v += frame.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 >= vecSize && frame._nonGpSaveSize) { - frame.addAttributes(FuncFrame::kAttrAlignedVecSR); - v = Support::alignUp(v, vecSize); // Align '_nonGpSaveOffset'. - } - - frame._nonGpSaveOffset = v; // Store '_nonGpSaveOffset' <- Non-GP Save/Restore starts here. - v += frame._nonGpSaveSize; // Count '_nonGpSaveSize' <- 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) { - frame._daOffset = v; // Store 'daOffset' <- DA pointer would be stored here. - v += registerSize; // Count 'daOffset'. - } - else { - frame._daOffset = FuncFrame::kTagInvalidOffset; - } - - // 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 || frame.hasFuncCalls()) - v += Support::alignUpDiff(v + frame.gpSaveSize() + registerSize, stackAlignment); - - frame._gpSaveOffset = v; // Store 'gpSaveOffset' <- Function's GP Save/Restore starts here. - frame._stackAdjustment = v; // Store 'stackAdjustment' <- SA used by 'add zsp, SA' and 'sub zsp, SA'. - - v += frame._gpSaveSize; // Count 'gpSaveSize' <- Function's GP Save/Restore ends here. - 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) - frame._stackAdjustment = Support::alignUp(frame._stackAdjustment, stackAlignment); - - uint32_t saInvOff = FuncFrame::kTagInvalidOffset; - uint32_t saTmpOff = registerSize + frame._gpSaveSize; - - // Calculate where the function arguments start relative to SP. - frame._saOffsetFromSP = hasDA ? saInvOff : v; - - // Calculate where the function arguments start relative to FP or user-provided register. - frame._saOffsetFromSA = hasFP ? registerSize * 2 // Return address + frame pointer. - : saTmpOff; // Return address + all saved GP regs. - - return kErrorOk; -} - -// ============================================================================ -// [asmjit::X86Internal - ArgsToFrameInfo] -// ============================================================================ - -ASMJIT_FAVOR_SIZE Error X86Internal::argsToFuncFrame(const FuncArgsAssignment& args, FuncFrame& frame) noexcept { - X86FuncArgsContext ctx; - ASMJIT_PROPAGATE(ctx.initWorkData(frame, args)); - ASMJIT_PROPAGATE(ctx.markDstRegsDirty(frame)); - ASMJIT_PROPAGATE(ctx.markScratchRegs(frame)); - ASMJIT_PROPAGATE(ctx.markStackArgsReg(frame)); - return kErrorOk; -} - -// ============================================================================ -// [asmjit::X86Internal - Emit Helpers] -// ============================================================================ - -ASMJIT_FAVOR_SIZE Error X86Internal::emitRegMove(Emitter* emitter, - const Operand_& dst_, - const Operand_& src_, uint32_t typeId, bool avxEnabled, 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().setSize(src.size()); } - if (src.isMem()) { memFlags |= kSrcMem; src.as().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()); - } - else if (!memFlags) { - // Change both destination and source registers to GPD (safer, no dependencies). - dst.setSignature(Reg::signatureOfT()); - src.setSignature(Reg::signatureOfT()); - } - 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().setSize(overrideMemSize); - if (src.isMem()) src.as().setSize(overrideMemSize); - } - - emitter->setInlineComment(comment); - return emitter->emit(instId, dst, src); -} - -ASMJIT_FAVOR_SIZE Error X86Internal::emitArgMove(Emitter* emitter, - const Reg& dst_, uint32_t dstTypeId, - const Operand_& src_, uint32_t srcTypeId, bool avxEnabled, const char* comment) { - - // Deduce optional `dstTypeId`, which may be `Type::kIdVoid` in some cases. - if (!dstTypeId) - dstTypeId = opData.archRegs.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_); - 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()); - } - 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::signatureOfT()); - 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()); - break; - } - - if (Type::isMask(srcTypeId)) { - instId = x86KmovFromSize(srcSize); - dst.setSignature(srcSize <= 4 ? Reg::signatureOfT() - : Reg::signatureOfT()); - 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()); - 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()); - break; - } - - if (Type::isMmx(srcTypeId)) - break; - - // This will hurt if `avxEnabled`. - 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 = x86KmovFromSize(srcSize); - if (Reg::isGp(src) && srcSize <= 4) - src.setSignature(Reg::signatureOfT()); - break; - } - } - - if (Type::isVec(dstTypeId)) { - // By default set destination to XMM, will be set to YMM|ZMM if needed. - dst.setSignature(Reg::signatureOfT()); - - // This will hurt if `avxEnabled`. - 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()); - 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()); - 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()); - 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().setSize(srcSize); - - emitter->setInlineComment(comment); - return emitter->emit(instId, dst, src); -} - -// ============================================================================ -// [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 = x86GetXmmMovInst(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 X86Internal::emitProlog(Emitter* emitter, const FuncFrame& frame) { - 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 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.nonGpSaveOffset())); - - uint32_t xInst; - uint32_t xSize; - - for (uint32_t group = 1; group < BaseReg::kGroupVirt; group++) { - Support::BitWordIterator 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 X86Internal::emitEpilog(Emitter* emitter, const FuncFrame& frame) { - 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.nonGpSaveOffset())); - - uint32_t xInst; - uint32_t xSize; - - for (uint32_t group = 1; group < BaseReg::kGroupVirt; group++) { - Support::BitWordIterator 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.gpSaveSize() - 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::X86Internal - Emit Arguments Assignment] -// ============================================================================ - -#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(""); - - if (value.isIndirect()) - sb.append(']'); -} - -static void dumpAssignment(String& sb, const X86FuncArgsContext& ctx) noexcept { - typedef X86FuncArgsContext::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_FAVOR_SIZE Error X86Internal::emitArgsAssignment(Emitter* emitter, const FuncFrame& frame, const FuncArgsAssignment& args) { - typedef X86FuncArgsContext::Var Var; - typedef X86FuncArgsContext::WorkData WorkData; - - enum WorkFlags : uint32_t { - kWorkNone = 0x00, - kWorkDidSome = 0x01, - kWorkPending = 0x02, - kWorkPostponed = 0x04 - }; - - X86FuncArgsContext ctx; - ASMJIT_PROPAGATE(ctx.initWorkData(frame, args)); - -#ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT - { - String sb; - dumpAssignment(sb, ctx); - printf("%s\n", sb.data()); - } -#endif - - uint32_t arch = ctx.arch(); - uint32_t varCount = ctx._varCount; - WorkData* workData = ctx._workData; - - // Use AVX if it's enabled. - bool avxEnabled = frame.isAvxEnabled(); - - uint32_t saVarId = ctx._saVarId; - uint32_t saRegId = Gp::kIdSp; - - if (frame.hasDynamicAlignment()) { - if (frame.hasPreservedFP()) - saRegId = Gp::kIdBp; - else - saRegId = saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId(); - } - - RegInfo gpRegInfo = emitter->_gpRegInfo; - - // -------------------------------------------------------------------------- - // 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. - Mem baseArgPtr = ptr(emitter->gpz(saRegId), int32_t(frame.saOffset(saRegId))); - Mem baseStackPtr = ptr(emitter->gpz(Gp::kIdSp), int32_t(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()); - Reg reg; - - Mem dstStackPtr = baseStackPtr.cloneAdjusted(out.stackOffset()); - Mem srcStackPtr = baseArgPtr.cloneAdjusted(cur.stackOffset()); - - if (cur.isIndirect()) { - if (cur.isStack()) { - // TODO: Indirect stack. - return DebugUtils::errored(kErrorInvalidAssignment); - } - else { - srcStackPtr = ptr(Gp(gpRegInfo.signature(), cur.regId())); - } - } - - if (cur.isReg() && !cur.isIndirect()) { - WorkData& wd = workData[Reg::groupOf(cur.regType())]; - uint32_t rId = cur.regId(); - - reg.setSignatureAndId(Reg::signatureOf(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 = x86GetRegForMemToMemMove(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(emitter, reg, out.typeId(), srcStackPtr, cur.typeId(), avxEnabled)); - } - - if (cur.isIndirect() && cur.isReg()) - workData[BaseReg::kGroupGp].unassign(varId, cur.regId()); - - // Register to stack move. - ASMJIT_PROPAGATE(emitRegMove(emitter, dstStackPtr, reg, cur.typeId(), avxEnabled)); - 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 = Reg::groupOf(cur.regType()); - uint32_t outGroup = Reg::groupOf(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(emitter, - Reg::fromTypeAndId(out.regType(), outId), out.typeId(), - Reg::fromTypeAndId(cur.regType(), curId), cur.typeId(), avxEnabled)); - - 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)) { - // Swap operation is possible only between two GP registers. - if (curGroup == Reg::kGroupGp) { - uint32_t highestType = Support::max(cur.regType(), altVar.cur.regType()); - uint32_t signature = highestType == Reg::kTypeGpq ? Reg::signatureOfT() - : Reg::signatureOfT(); - - ASMJIT_PROPAGATE(emitter->emit(Inst::kIdXchg, Reg(signature, outId), Reg(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()) - saRegId = saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId(); - - // Base address of all arguments passed by stack. - Mem baseArgPtr = ptr(emitter->gpz(saRegId), int32_t(frame.saOffset(saRegId))); - - 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 = Reg::groupOf(outType); - WorkData& wd = ctx._workData[group]; - - if (outId == saRegId && 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); - } - - Reg dstReg = Reg::fromTypeAndId(outType, outId); - Mem srcMem = baseArgPtr.cloneAdjusted(var.cur.stackOffset()); - - ASMJIT_PROPAGATE( - emitArgMove(emitter, - dstReg, var.out.typeId(), - srcMem, var.cur.typeId(), avxEnabled)); - - wd.assign(varId, outId); - var.cur.initReg(outType, outId, var.cur.typeId(), FuncValue::kFlagIsDone); - } - } - } - } - - return kErrorOk; -} - -ASMJIT_END_SUB_NAMESPACE - -#endif // ASMJIT_BUILD_X86 diff --git a/src/asmjit/x86/x86internal_p.h b/src/asmjit/x86/x86internal_p.h deleted file mode 100644 index d4ea8e1..0000000 --- a/src/asmjit/x86/x86internal_p.h +++ /dev/null @@ -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 diff --git a/src/asmjit/x86/x86operand.cpp b/src/asmjit/x86/x86operand.cpp index ca7ce5a..4c81d8e 100644 --- a/src/asmjit/x86/x86operand.cpp +++ b/src/asmjit/x86/x86operand.cpp @@ -29,29 +29,6 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86) -// ============================================================================ -// [asmjit::x86::OpData] -// ============================================================================ - -const OpData opData = { - { - // RegInfo[] - #define VALUE(X) { RegTraits::kSignature } - { ASMJIT_LOOKUP_TABLE_32(VALUE, 0) }, - #undef VALUE - - // RegCount[] - #define VALUE(X) RegTraits::kCount - { ASMJIT_LOOKUP_TABLE_32(VALUE, 0) }, - #undef VALUE - - // RegTypeToTypeId[] - #define VALUE(X) RegTraits::kTypeId - { ASMJIT_LOOKUP_TABLE_32(VALUE, 0) } - #undef VALUE - } -}; - // ============================================================================ // [asmjit::x86::Operand - Unit] // ============================================================================ @@ -167,7 +144,7 @@ UNIT(x86_operand) { EXPECT(zmm7.ymm() == ymm7); EXPECT(zmm7.zmm() == zmm7); - INFO("Checking x86::FpMm register properties"); + INFO("Checking x86::Mm register properties"); EXPECT(Mm().isReg() == true); EXPECT(mm2.isReg() == true); EXPECT(mm2.id() == 2); diff --git a/src/asmjit/x86/x86operand.h b/src/asmjit/x86/x86operand.h index da988ce..79e9ce1 100644 --- a/src/asmjit/x86/x86operand.h +++ b/src/asmjit/x86/x86operand.h @@ -24,7 +24,7 @@ #ifndef ASMJIT_X86_X86OPERAND_H_INCLUDED #define ASMJIT_X86_X86OPERAND_H_INCLUDED -#include "../core/arch.h" +#include "../core/archtraits.h" #include "../core/operand.h" #include "../core/type.h" #include "../x86/x86globals.h" @@ -61,23 +61,23 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86) } \ \ static constexpr Mem FUNC##_abs(uint64_t base) noexcept { \ - return Mem(base, SIZE, BaseMem::kSignatureMemAbs); \ + return Mem(base, SIZE, Mem::kSignatureMemAbs); \ } \ static constexpr Mem FUNC##_abs(uint64_t base, const Gp& index, uint32_t shift = 0) noexcept { \ - return Mem(base, index, shift, SIZE, BaseMem::kSignatureMemAbs); \ + return Mem(base, index, shift, SIZE, Mem::kSignatureMemAbs); \ } \ static constexpr Mem FUNC##_abs(uint64_t base, const Vec& index, uint32_t shift = 0) noexcept { \ - return Mem(base, index, shift, SIZE, BaseMem::kSignatureMemAbs); \ + return Mem(base, index, shift, SIZE, Mem::kSignatureMemAbs); \ } \ \ static constexpr Mem FUNC##_rel(uint64_t base) noexcept { \ - return Mem(base, SIZE, BaseMem::kSignatureMemRel); \ + return Mem(base, SIZE, Mem::kSignatureMemRel); \ } \ static constexpr Mem FUNC##_rel(uint64_t base, const Gp& index, uint32_t shift = 0) noexcept { \ - return Mem(base, index, shift, SIZE, BaseMem::kSignatureMemRel); \ + return Mem(base, index, shift, SIZE, Mem::kSignatureMemRel); \ } \ static constexpr Mem FUNC##_rel(uint64_t base, const Vec& index, uint32_t shift = 0) noexcept { \ - return Mem(base, index, shift, SIZE, BaseMem::kSignatureMemRel); \ + return Mem(base, index, shift, SIZE, Mem::kSignatureMemRel); \ } //! \addtogroup asmjit_x86 @@ -231,39 +231,39 @@ public: //! Tests whether the register is a GPB register (8-bit). constexpr bool isGpb() const noexcept { return size() == 1; } //! Tests whether the register is a low GPB register (8-bit). - constexpr bool isGpbLo() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isGpbLo() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a high GPB register (8-bit). - constexpr bool isGpbHi() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isGpbHi() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a GPW register (16-bit). - constexpr bool isGpw() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isGpw() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a GPD register (32-bit). - constexpr bool isGpd() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isGpd() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a GPQ register (64-bit). - constexpr bool isGpq() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isGpq() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is an XMM register (128-bit). - constexpr bool isXmm() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isXmm() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a YMM register (256-bit). - constexpr bool isYmm() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isYmm() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a ZMM register (512-bit). - constexpr bool isZmm() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isZmm() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is an MMX register (64-bit). - constexpr bool isMm() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isMm() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a K register (64-bit). - constexpr bool isKReg() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isKReg() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a segment register. - constexpr bool isSReg() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isSReg() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a control register. - constexpr bool isCReg() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isCReg() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a debug register. - constexpr bool isDReg() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isDReg() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is an FPU register (80-bit). - constexpr bool isSt() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isSt() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a bound register. - constexpr bool isBnd() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isBnd() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is a TMM register. - constexpr bool isTmm() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isTmm() const noexcept { return hasBaseSignature(RegTraits::kSignature); } //! Tests whether the register is RIP. - constexpr bool isRip() const noexcept { return hasSignature(RegTraits::kSignature); } + constexpr bool isRip() const noexcept { return hasBaseSignature(RegTraits::kSignature); } template inline void setRegT(uint32_t rId) noexcept { @@ -277,15 +277,16 @@ public: setId(rId); } - static inline uint32_t groupOf(uint32_t rType) noexcept; + static inline uint32_t groupOf(uint32_t rType) noexcept { return _archTraits[Environment::kArchX86].regTypeToGroup(rType); } + static inline uint32_t typeIdOf(uint32_t rType) noexcept { return _archTraits[Environment::kArchX86].regTypeToTypeId(rType); } + static inline uint32_t signatureOf(uint32_t rType) noexcept { return _archTraits[Environment::kArchX86].regTypeToSignature(rType); } + template static inline uint32_t groupOfT() noexcept { return RegTraits::kGroup; } - static inline uint32_t typeIdOf(uint32_t rType) noexcept; template static inline uint32_t typeIdOfT() noexcept { return RegTraits::kTypeId; } - static inline uint32_t signatureOf(uint32_t rType) noexcept; template static inline uint32_t signatureOfT() noexcept { return RegTraits::kSignature; } @@ -302,9 +303,9 @@ public: //! Tests whether the `op` operand is either a low or high 8-bit GPB register. static inline bool isGpb(const Operand_& op) noexcept { // Check operand type, register group, and size. Not interested in register type. - const uint32_t kSgn = (Operand::kOpReg << kSignatureOpShift ) | - (1 << kSignatureSizeShift) ; - return (op.signature() & (kSignatureOpMask | kSignatureSizeMask)) == kSgn; + const uint32_t kSgn = (Operand::kOpReg << kSignatureOpTypeShift) | + (1 << kSignatureSizeShift ) ; + return (op.signature() & (kSignatureOpTypeMask | kSignatureSizeMask)) == kSgn; } static inline bool isGpbLo(const Operand_& op) noexcept { return op.as().isGpbLo(); } @@ -401,7 +402,7 @@ class Vec : public Reg { //! Casts this register to a register that has half the size (or XMM if it's already XMM). inline Vec half() const noexcept { - return Vec(type() == kTypeZmm ? signatureOf(kTypeYmm) : signatureOf(kTypeXmm), id()); + return Vec::fromSignatureAndId(type() == kTypeZmm ? signatureOfT() : signatureOfT(), id()); } }; @@ -544,78 +545,78 @@ static constexpr Bnd bnd(uint32_t rId) noexcept { return Bnd(rId); } //! Creates a TMM register operand. static constexpr Tmm tmm(uint32_t rId) noexcept { return Tmm(rId); } -static constexpr Gp al = Gp(GpbLo::kSignature, Gp::kIdAx); -static constexpr Gp bl = Gp(GpbLo::kSignature, Gp::kIdBx); -static constexpr Gp cl = Gp(GpbLo::kSignature, Gp::kIdCx); -static constexpr Gp dl = Gp(GpbLo::kSignature, Gp::kIdDx); -static constexpr Gp spl = Gp(GpbLo::kSignature, Gp::kIdSp); -static constexpr Gp bpl = Gp(GpbLo::kSignature, Gp::kIdBp); -static constexpr Gp sil = Gp(GpbLo::kSignature, Gp::kIdSi); -static constexpr Gp dil = Gp(GpbLo::kSignature, Gp::kIdDi); -static constexpr Gp r8b = Gp(GpbLo::kSignature, Gp::kIdR8); -static constexpr Gp r9b = Gp(GpbLo::kSignature, Gp::kIdR9); -static constexpr Gp r10b = Gp(GpbLo::kSignature, Gp::kIdR10); -static constexpr Gp r11b = Gp(GpbLo::kSignature, Gp::kIdR11); -static constexpr Gp r12b = Gp(GpbLo::kSignature, Gp::kIdR12); -static constexpr Gp r13b = Gp(GpbLo::kSignature, Gp::kIdR13); -static constexpr Gp r14b = Gp(GpbLo::kSignature, Gp::kIdR14); -static constexpr Gp r15b = Gp(GpbLo::kSignature, Gp::kIdR15); +static constexpr Gp al = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdAx)); +static constexpr Gp bl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdBx)); +static constexpr Gp cl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdCx)); +static constexpr Gp dl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdDx)); +static constexpr Gp spl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdSp)); +static constexpr Gp bpl = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdBp)); +static constexpr Gp sil = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdSi)); +static constexpr Gp dil = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdDi)); +static constexpr Gp r8b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR8)); +static constexpr Gp r9b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR9)); +static constexpr Gp r10b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR10)); +static constexpr Gp r11b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR11)); +static constexpr Gp r12b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR12)); +static constexpr Gp r13b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR13)); +static constexpr Gp r14b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR14)); +static constexpr Gp r15b = Gp(Gp::SignatureAndId(GpbLo::kSignature, Gp::kIdR15)); -static constexpr Gp ah = Gp(GpbHi::kSignature, Gp::kIdAx); -static constexpr Gp bh = Gp(GpbHi::kSignature, Gp::kIdBx); -static constexpr Gp ch = Gp(GpbHi::kSignature, Gp::kIdCx); -static constexpr Gp dh = Gp(GpbHi::kSignature, Gp::kIdDx); +static constexpr Gp ah = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdAx)); +static constexpr Gp bh = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdBx)); +static constexpr Gp ch = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdCx)); +static constexpr Gp dh = Gp(Gp::SignatureAndId(GpbHi::kSignature, Gp::kIdDx)); -static constexpr Gp ax = Gp(Gpw::kSignature, Gp::kIdAx); -static constexpr Gp bx = Gp(Gpw::kSignature, Gp::kIdBx); -static constexpr Gp cx = Gp(Gpw::kSignature, Gp::kIdCx); -static constexpr Gp dx = Gp(Gpw::kSignature, Gp::kIdDx); -static constexpr Gp sp = Gp(Gpw::kSignature, Gp::kIdSp); -static constexpr Gp bp = Gp(Gpw::kSignature, Gp::kIdBp); -static constexpr Gp si = Gp(Gpw::kSignature, Gp::kIdSi); -static constexpr Gp di = Gp(Gpw::kSignature, Gp::kIdDi); -static constexpr Gp r8w = Gp(Gpw::kSignature, Gp::kIdR8); -static constexpr Gp r9w = Gp(Gpw::kSignature, Gp::kIdR9); -static constexpr Gp r10w = Gp(Gpw::kSignature, Gp::kIdR10); -static constexpr Gp r11w = Gp(Gpw::kSignature, Gp::kIdR11); -static constexpr Gp r12w = Gp(Gpw::kSignature, Gp::kIdR12); -static constexpr Gp r13w = Gp(Gpw::kSignature, Gp::kIdR13); -static constexpr Gp r14w = Gp(Gpw::kSignature, Gp::kIdR14); -static constexpr Gp r15w = Gp(Gpw::kSignature, Gp::kIdR15); +static constexpr Gp ax = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdAx)); +static constexpr Gp bx = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdBx)); +static constexpr Gp cx = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdCx)); +static constexpr Gp dx = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdDx)); +static constexpr Gp sp = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdSp)); +static constexpr Gp bp = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdBp)); +static constexpr Gp si = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdSi)); +static constexpr Gp di = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdDi)); +static constexpr Gp r8w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR8)); +static constexpr Gp r9w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR9)); +static constexpr Gp r10w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR10)); +static constexpr Gp r11w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR11)); +static constexpr Gp r12w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR12)); +static constexpr Gp r13w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR13)); +static constexpr Gp r14w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR14)); +static constexpr Gp r15w = Gp(Gp::SignatureAndId(Gpw::kSignature, Gp::kIdR15)); -static constexpr Gp eax = Gp(Gpd::kSignature, Gp::kIdAx); -static constexpr Gp ebx = Gp(Gpd::kSignature, Gp::kIdBx); -static constexpr Gp ecx = Gp(Gpd::kSignature, Gp::kIdCx); -static constexpr Gp edx = Gp(Gpd::kSignature, Gp::kIdDx); -static constexpr Gp esp = Gp(Gpd::kSignature, Gp::kIdSp); -static constexpr Gp ebp = Gp(Gpd::kSignature, Gp::kIdBp); -static constexpr Gp esi = Gp(Gpd::kSignature, Gp::kIdSi); -static constexpr Gp edi = Gp(Gpd::kSignature, Gp::kIdDi); -static constexpr Gp r8d = Gp(Gpd::kSignature, Gp::kIdR8); -static constexpr Gp r9d = Gp(Gpd::kSignature, Gp::kIdR9); -static constexpr Gp r10d = Gp(Gpd::kSignature, Gp::kIdR10); -static constexpr Gp r11d = Gp(Gpd::kSignature, Gp::kIdR11); -static constexpr Gp r12d = Gp(Gpd::kSignature, Gp::kIdR12); -static constexpr Gp r13d = Gp(Gpd::kSignature, Gp::kIdR13); -static constexpr Gp r14d = Gp(Gpd::kSignature, Gp::kIdR14); -static constexpr Gp r15d = Gp(Gpd::kSignature, Gp::kIdR15); +static constexpr Gp eax = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdAx)); +static constexpr Gp ebx = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdBx)); +static constexpr Gp ecx = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdCx)); +static constexpr Gp edx = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdDx)); +static constexpr Gp esp = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdSp)); +static constexpr Gp ebp = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdBp)); +static constexpr Gp esi = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdSi)); +static constexpr Gp edi = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdDi)); +static constexpr Gp r8d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR8)); +static constexpr Gp r9d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR9)); +static constexpr Gp r10d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR10)); +static constexpr Gp r11d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR11)); +static constexpr Gp r12d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR12)); +static constexpr Gp r13d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR13)); +static constexpr Gp r14d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR14)); +static constexpr Gp r15d = Gp(Gp::SignatureAndId(Gpd::kSignature, Gp::kIdR15)); -static constexpr Gp rax = Gp(Gpq::kSignature, Gp::kIdAx); -static constexpr Gp rbx = Gp(Gpq::kSignature, Gp::kIdBx); -static constexpr Gp rcx = Gp(Gpq::kSignature, Gp::kIdCx); -static constexpr Gp rdx = Gp(Gpq::kSignature, Gp::kIdDx); -static constexpr Gp rsp = Gp(Gpq::kSignature, Gp::kIdSp); -static constexpr Gp rbp = Gp(Gpq::kSignature, Gp::kIdBp); -static constexpr Gp rsi = Gp(Gpq::kSignature, Gp::kIdSi); -static constexpr Gp rdi = Gp(Gpq::kSignature, Gp::kIdDi); -static constexpr Gp r8 = Gp(Gpq::kSignature, Gp::kIdR8); -static constexpr Gp r9 = Gp(Gpq::kSignature, Gp::kIdR9); -static constexpr Gp r10 = Gp(Gpq::kSignature, Gp::kIdR10); -static constexpr Gp r11 = Gp(Gpq::kSignature, Gp::kIdR11); -static constexpr Gp r12 = Gp(Gpq::kSignature, Gp::kIdR12); -static constexpr Gp r13 = Gp(Gpq::kSignature, Gp::kIdR13); -static constexpr Gp r14 = Gp(Gpq::kSignature, Gp::kIdR14); -static constexpr Gp r15 = Gp(Gpq::kSignature, Gp::kIdR15); +static constexpr Gp rax = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdAx)); +static constexpr Gp rbx = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdBx)); +static constexpr Gp rcx = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdCx)); +static constexpr Gp rdx = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdDx)); +static constexpr Gp rsp = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdSp)); +static constexpr Gp rbp = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdBp)); +static constexpr Gp rsi = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdSi)); +static constexpr Gp rdi = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdDi)); +static constexpr Gp r8 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR8)); +static constexpr Gp r9 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR9)); +static constexpr Gp r10 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR10)); +static constexpr Gp r11 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR11)); +static constexpr Gp r12 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR12)); +static constexpr Gp r13 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR13)); +static constexpr Gp r14 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR14)); +static constexpr Gp r15 = Gp(Gp::SignatureAndId(Gpq::kSignature, Gp::kIdR15)); static constexpr Xmm xmm0 = Xmm(0); static constexpr Xmm xmm1 = Xmm(1); @@ -815,28 +816,65 @@ using namespace regs; //! Memory operand. class Mem : public BaseMem { public: - //! Additional bits of operand's signature used by `Mem`. + //! Additional bits of operand's signature used by `x86::Mem`. enum AdditionalBits : uint32_t { - kSignatureMemSegmentShift = 16, - kSignatureMemSegmentMask = 0x07u << kSignatureMemSegmentShift, + // Memory address type (2 bits). + // |........|........|XX......|........| + kSignatureMemAddrTypeShift = 14, + kSignatureMemAddrTypeMask = 0x03u << kSignatureMemAddrTypeShift, - kSignatureMemShiftShift = 19, - kSignatureMemShiftMask = 0x03u << kSignatureMemShiftShift, + // Memory shift amount (2 bits). + // |........|......XX|........|........| + kSignatureMemShiftValueShift = 16, + kSignatureMemShiftValueMask = 0x03u << kSignatureMemShiftValueShift, + // Memory segment reg (3 bits). + // |........|...XXX..|........|........| + kSignatureMemSegmentShift = 18, + kSignatureMemSegmentMask = 0x07u << kSignatureMemSegmentShift, + + // Memory broadcast type (3 bits). + // |........|XXX.....|........|........| 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 { + //! Broadcast {1to1}. kBroadcast1To1 = 0, + //! Broadcast {1to2}. kBroadcast1To2 = 1, + //! Broadcast {1to4}. kBroadcast1To4 = 2, + //! Broadcast {1to8}. kBroadcast1To8 = 3, + //! Broadcast {1to16}. kBroadcast1To16 = 4, + //! Broadcast {1to32}. kBroadcast1To32 = 5, + //! Broadcast {1to64}. kBroadcast1To64 = 6 }; + //! \cond + //! Shortcuts. + enum SignatureMem : uint32_t { + kSignatureMemAbs = kAddrTypeAbs << kSignatureMemAddrTypeShift, + kSignatureMemRel = kAddrTypeRel << kSignatureMemAddrTypeShift + }; + //! \endcond + // -------------------------------------------------------------------------- // [Construction / Destruction] // -------------------------------------------------------------------------- @@ -859,19 +897,19 @@ public: : BaseMem(Decomposed { Label::kLabelTag, base.id(), 0, 0, off, size, flags }) {} constexpr Mem(const Label& base, const BaseReg& index, uint32_t shift, int32_t off, uint32_t size = 0, uint32_t flags = 0) noexcept - : BaseMem(Decomposed { Label::kLabelTag, base.id(), index.type(), index.id(), off, size, flags | (shift << kSignatureMemShiftShift) }) {} + : BaseMem(Decomposed { Label::kLabelTag, base.id(), index.type(), index.id(), off, size, flags | (shift << kSignatureMemShiftValueShift) }) {} constexpr Mem(const BaseReg& base, int32_t off, uint32_t size = 0, uint32_t flags = 0) noexcept : BaseMem(Decomposed { base.type(), base.id(), 0, 0, off, size, flags }) {} constexpr Mem(const BaseReg& base, const BaseReg& index, uint32_t shift, int32_t off, uint32_t size = 0, uint32_t flags = 0) noexcept - : BaseMem(Decomposed { base.type(), base.id(), index.type(), index.id(), off, size, flags | (shift << kSignatureMemShiftShift) }) {} + : BaseMem(Decomposed { base.type(), base.id(), index.type(), index.id(), off, size, flags | (shift << kSignatureMemShiftValueShift) }) {} constexpr explicit Mem(uint64_t base, uint32_t size = 0, uint32_t flags = 0) noexcept : BaseMem(Decomposed { 0, uint32_t(base >> 32), 0, 0, int32_t(uint32_t(base & 0xFFFFFFFFu)), size, flags }) {} constexpr Mem(uint64_t base, const BaseReg& index, uint32_t shift = 0, uint32_t size = 0, uint32_t flags = 0) noexcept - : BaseMem(Decomposed { 0, uint32_t(base >> 32), index.type(), index.id(), int32_t(uint32_t(base & 0xFFFFFFFFu)), size, flags | (shift << kSignatureMemShiftShift) }) {} + : BaseMem(Decomposed { 0, uint32_t(base >> 32), index.type(), index.id(), int32_t(uint32_t(base & 0xFFFFFFFFu)), size, flags | (shift << kSignatureMemShiftValueShift) }) {} constexpr Mem(Globals::Init_, uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) noexcept : BaseMem(Globals::Init, u0, u1, u2, u3) {} @@ -918,6 +956,25 @@ public: setShift(shift); } + //! Returns the address type (see \ref AddrType) of the memory operand. + //! + //! By default, address type of newly created memory operands is always \ref kAddrTypeDefault. + constexpr uint32_t addrType() const noexcept { return _getSignaturePart(); } + //! Sets the address type to `addrType`, see \ref AddrType. + inline void setAddrType(uint32_t addrType) noexcept { _setSignaturePart(addrType); } + //! Resets the address type to \ref kAddrTypeDefault. + inline void resetAddrType() noexcept { _setSignaturePart(0); } + + //! Tests whether the address type is \ref kAddrTypeAbs. + constexpr bool isAbs() const noexcept { return addrType() == kAddrTypeAbs; } + //! Sets the address type to \ref kAddrTypeAbs. + inline void setAbs() noexcept { setAddrType(kAddrTypeAbs); } + + //! Tests whether the address type is \ref kAddrTypeRel. + constexpr bool isRel() const noexcept { return addrType() == kAddrTypeRel; } + //! Sets the address type to \ref kAddrTypeRel. + inline void setRel() noexcept { setAddrType(kAddrTypeRel); } + //! Tests whether the memory operand has a segment override. constexpr bool hasSegment() const noexcept { return _hasSignaturePart(); } //! Returns the associated segment override as `SReg` operand. @@ -933,13 +990,13 @@ public: inline void resetSegment() noexcept { _setSignaturePart(0); } //! Tests whether the memory operand has shift (aka scale) value. - constexpr bool hasShift() const noexcept { return _hasSignaturePart(); } + constexpr bool hasShift() const noexcept { return _hasSignaturePart(); } //! Returns the memory operand's shift (aka scale) value. - constexpr uint32_t shift() const noexcept { return _getSignaturePart(); } + constexpr uint32_t shift() const noexcept { return _getSignaturePart(); } //! Sets the memory operand's shift (aka scale) value. - inline void setShift(uint32_t shift) noexcept { _setSignaturePart(shift); } + inline void setShift(uint32_t shift) noexcept { _setSignaturePart(shift); } //! Resets the memory operand's shift (aka scale) value to zero. - inline void resetShift() noexcept { _setSignaturePart(0); } + inline void resetShift() noexcept { _setSignaturePart(0); } //! Tests whether the memory operand has broadcast {1tox}. constexpr bool hasBroadcast() const noexcept { return _hasSignaturePart(); } @@ -1003,28 +1060,28 @@ static constexpr Mem ptr(uint64_t base, const Vec& index, uint32_t shift = 0, ui //! Creates `[base]` absolute memory operand (absolute). static constexpr Mem ptr_abs(uint64_t base, uint32_t size = 0) noexcept { - return Mem(base, size, BaseMem::kSignatureMemAbs); + return Mem(base, size, Mem::kSignatureMemAbs); } //! Creates `[base + (index.reg << shift)]` absolute memory operand (absolute). static constexpr Mem ptr_abs(uint64_t base, const Reg& index, uint32_t shift = 0, uint32_t size = 0) noexcept { - return Mem(base, index, shift, size, BaseMem::kSignatureMemAbs); + return Mem(base, index, shift, size, Mem::kSignatureMemAbs); } //! Creates `[base + (index.reg << shift)]` absolute memory operand (absolute). static constexpr Mem ptr_abs(uint64_t base, const Vec& index, uint32_t shift = 0, uint32_t size = 0) noexcept { - return Mem(base, index, shift, size, BaseMem::kSignatureMemAbs); + return Mem(base, index, shift, size, Mem::kSignatureMemAbs); } //! Creates `[base]` relative memory operand (relative). static constexpr Mem ptr_rel(uint64_t base, uint32_t size = 0) noexcept { - return Mem(base, size, BaseMem::kSignatureMemRel); + return Mem(base, size, Mem::kSignatureMemRel); } //! Creates `[base + (index.reg << shift)]` relative memory operand (relative). static constexpr Mem ptr_rel(uint64_t base, const Reg& index, uint32_t shift = 0, uint32_t size = 0) noexcept { - return Mem(base, index, shift, size, BaseMem::kSignatureMemRel); + return Mem(base, index, shift, size, Mem::kSignatureMemRel); } //! Creates `[base + (index.reg << shift)]` relative memory operand (relative). static constexpr Mem ptr_rel(uint64_t base, const Vec& index, uint32_t shift = 0, uint32_t size = 0) noexcept { - return Mem(base, index, shift, size, BaseMem::kSignatureMemRel); + return Mem(base, index, shift, size, Mem::kSignatureMemRel); } // Definition of memory operand constructors that use platform independent naming. @@ -1051,34 +1108,6 @@ ASMJIT_MEM_PTR(xmmword_ptr, 16) ASMJIT_MEM_PTR(ymmword_ptr, 32) ASMJIT_MEM_PTR(zmmword_ptr, 64) -// ============================================================================ -// [asmjit::x86::OpData] -// ============================================================================ - -struct OpData { - //! Information about all architecture registers. - ArchRegs archRegs; -}; -ASMJIT_VARAPI const OpData opData; - -//! \cond -// ... Reg methods that require `opData`. -inline uint32_t Reg::groupOf(uint32_t rType) noexcept { - ASMJIT_ASSERT(rType <= BaseReg::kTypeMax); - return opData.archRegs.regInfo[rType].group(); -} - -inline uint32_t Reg::typeIdOf(uint32_t rType) noexcept { - ASMJIT_ASSERT(rType <= BaseReg::kTypeMax); - return opData.archRegs.regTypeToTypeId[rType]; -} - -inline uint32_t Reg::signatureOf(uint32_t rType) noexcept { - ASMJIT_ASSERT(rType <= BaseReg::kTypeMax); - return opData.archRegs.regInfo[rType].signature(); -} -//! \endcond - //! \} ASMJIT_END_SUB_NAMESPACE diff --git a/src/asmjit/x86/x86rapass.cpp b/src/asmjit/x86/x86rapass.cpp index cf4cfcb..faf05dc 100644 --- a/src/asmjit/x86/x86rapass.cpp +++ b/src/asmjit/x86/x86rapass.cpp @@ -31,7 +31,7 @@ #include "../x86/x86compiler.h" #include "../x86/x86instapi_p.h" #include "../x86/x86instdb_p.h" -#include "../x86/x86internal_p.h" +#include "../x86/x86emithelper_p.h" #include "../x86/x86rapass_p.h" ASMJIT_BEGIN_SUB_NAMESPACE(x86) @@ -86,20 +86,20 @@ static ASMJIT_INLINE uint32_t raMemIndexRwFlags(uint32_t flags) noexcept { } // ============================================================================ -// [asmjit::x86::X86RACFGBuilder] +// [asmjit::x86::RACFGBuilder] // ============================================================================ -class X86RACFGBuilder : public RACFGBuilder { +class RACFGBuilder : public RACFGBuilderT { public: uint32_t _arch; bool _is64Bit; bool _avxEnabled; - inline X86RACFGBuilder(X86RAPass* pass) noexcept - : RACFGBuilder(pass), + inline RACFGBuilder(X86RAPass* pass) noexcept + : RACFGBuilderT(pass), _arch(pass->cc()->arch()), _is64Bit(pass->registerSize() == 8), - _avxEnabled(pass->_avxEnabled) { + _avxEnabled(pass->avxEnabled()) { } inline Compiler* cc() const noexcept { return static_cast(_cc); } @@ -123,10 +123,10 @@ public: }; // ============================================================================ -// [asmjit::x86::X86RACFGBuilder - OnInst] +// [asmjit::x86::RACFGBuilder - OnInst] // ============================================================================ -Error X86RACFGBuilder::onInst(InstNode* inst, uint32_t& controlType, RAInstBuilder& ib) noexcept { +Error RACFGBuilder::onInst(InstNode* inst, uint32_t& controlType, RAInstBuilder& ib) noexcept { InstRWInfo rwInfo; uint32_t instId = inst->id(); @@ -400,10 +400,10 @@ Error X86RACFGBuilder::onInst(InstNode* inst, uint32_t& controlType, RAInstBuild } // ============================================================================ -// [asmjit::x86::X86RACFGBuilder - OnCall] +// [asmjit::x86::RACFGBuilder - OnInvoke] // ============================================================================ -Error X86RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept { +Error RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept { const FuncDetail& fd = invokeNode->detail(); uint32_t argCount = invokeNode->argCount(); @@ -503,7 +503,7 @@ Error X86RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept { if (workReg->group() != Reg::kGroupVec) return DebugUtils::errored(kErrorInvalidAssignment); - Reg dst = Reg(workReg->signature(), workReg->virtId()); + Reg dst = Reg::fromSignatureAndId(workReg->signature(), workReg->virtId()); Mem mem; uint32_t typeId = Type::baseOf(workReg->typeId()); @@ -543,7 +543,7 @@ Error X86RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept { } } - // This block has function invokeNode(s). + // This block has function call(s). _curBlock->addFlags(RABlock::kFlagHasFuncCalls); _pass->func()->frame().addAttributes(FuncFrame::kAttrHasFuncCalls); _pass->func()->frame().updateCallStackSize(fd.argStackSize()); @@ -551,7 +551,7 @@ Error X86RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept { return kErrorOk; } -Error X86RACFGBuilder::onInvoke(InvokeNode* invokeNode, RAInstBuilder& ib) noexcept { +Error RACFGBuilder::onInvoke(InvokeNode* invokeNode, RAInstBuilder& ib) noexcept { uint32_t argCount = invokeNode->argCount(); const FuncDetail& fd = invokeNode->detail(); @@ -629,7 +629,7 @@ Error X86RACFGBuilder::onInvoke(InvokeNode* invokeNode, RAInstBuilder& ib) noexc } // ============================================================================ -// [asmjit::x86::X86RACFGBuilder - MoveVecToPtr] +// [asmjit::x86::RACFGBuilder - MoveVecToPtr] // ============================================================================ static uint32_t x86VecRegSignatureBySize(uint32_t size) noexcept { @@ -641,7 +641,7 @@ static uint32_t x86VecRegSignatureBySize(uint32_t size) noexcept { return Xmm::kSignature; } -Error X86RACFGBuilder::moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg, const Vec& src, BaseReg* out) noexcept { +Error RACFGBuilder::moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg, const Vec& src, BaseReg* out) noexcept { DebugUtils::unused(invokeNode); ASMJIT_ASSERT(arg.isReg()); @@ -656,7 +656,7 @@ Error X86RACFGBuilder::moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg _funcNode->frame().updateCallStackAlignment(argSize); invokeNode->detail()._argStackSize = argStackOffset + argSize; - Vec vecReg(x86VecRegSignatureBySize(argSize), src.id()); + Vec vecReg = Vec::fromSignatureAndId(x86VecRegSignatureBySize(argSize), src.id()); Mem vecPtr = ptr(_pass->_sp.as(), int32_t(argStackOffset)); uint32_t vMovInstId = choose(Inst::kIdMovaps, Inst::kIdVmovaps); @@ -666,7 +666,7 @@ Error X86RACFGBuilder::moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg ASMJIT_PROPAGATE(cc()->_newReg(out, cc()->_gpRegInfo.type(), nullptr)); VirtReg* vReg = cc()->virtRegById(out->id()); - vReg->setWeight(RAPass::kCallArgWeight); + vReg->setWeight(BaseRAPass::kCallArgWeight); ASMJIT_PROPAGATE(cc()->lea(out->as(), vecPtr)); ASMJIT_PROPAGATE(cc()->emit(vMovInstId, ptr(out->as()), vecReg)); @@ -680,10 +680,10 @@ Error X86RACFGBuilder::moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg } // ============================================================================ -// [asmjit::x86::X86RACFGBuilder - MoveImmToRegArg] +// [asmjit::x86::RACFGBuilder - MoveImmToRegArg] // ============================================================================ -Error X86RACFGBuilder::moveImmToRegArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_, BaseReg* out) noexcept { +Error RACFGBuilder::moveImmToRegArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_, BaseReg* out) noexcept { DebugUtils::unused(invokeNode); ASMJIT_ASSERT(arg.isReg()); @@ -718,16 +718,16 @@ MovU32: } ASMJIT_PROPAGATE(cc()->_newReg(out, rTypeId, nullptr)); - cc()->virtRegById(out->id())->setWeight(RAPass::kCallArgWeight); + cc()->virtRegById(out->id())->setWeight(BaseRAPass::kCallArgWeight); return cc()->mov(out->as(), imm); } // ============================================================================ -// [asmjit::x86::X86RACFGBuilder - MoveImmToStackArg] +// [asmjit::x86::RACFGBuilder - MoveImmToStackArg] // ============================================================================ -Error X86RACFGBuilder::moveImmToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_) noexcept { +Error RACFGBuilder::moveImmToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_) noexcept { DebugUtils::unused(invokeNode); ASMJIT_ASSERT(arg.isStack()); @@ -786,10 +786,10 @@ MovU32: } // ============================================================================ -// [asmjit::x86::X86RACFGBuilder - MoveRegToStackArg] +// [asmjit::x86::RACFGBuilder - MoveRegToStackArg] // ============================================================================ -Error X86RACFGBuilder::moveRegToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const BaseReg& reg) noexcept { +Error RACFGBuilder::moveRegToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const BaseReg& reg) noexcept { DebugUtils::unused(invokeNode); ASMJIT_ASSERT(arg.isStack()); @@ -992,10 +992,10 @@ MovXmmQ: } // ============================================================================ -// [asmjit::x86::X86RACFGBuilder - OnReg] +// [asmjit::x86::RACFGBuilder - OnReg] // ============================================================================ -Error X86RACFGBuilder::onBeforeRet(FuncRetNode* funcRet) noexcept { +Error RACFGBuilder::onBeforeRet(FuncRetNode* funcRet) noexcept { const FuncDetail& funcDetail = _pass->func()->detail(); const Operand* opArray = funcRet->operands(); uint32_t opCount = funcRet->opCount(); @@ -1020,7 +1020,7 @@ Error X86RACFGBuilder::onBeforeRet(FuncRetNode* funcRet) noexcept { if (workReg->group() != Reg::kGroupVec) return DebugUtils::errored(kErrorInvalidAssignment); - Reg src = Reg(workReg->signature(), workReg->virtId()); + Reg src = Reg::fromSignatureAndId(workReg->signature(), workReg->virtId()); Mem mem; uint32_t typeId = Type::baseOf(workReg->typeId()); @@ -1052,7 +1052,7 @@ Error X86RACFGBuilder::onBeforeRet(FuncRetNode* funcRet) noexcept { return kErrorOk; } -Error X86RACFGBuilder::onRet(FuncRetNode* funcRet, RAInstBuilder& ib) noexcept { +Error RACFGBuilder::onRet(FuncRetNode* funcRet, RAInstBuilder& ib) noexcept { const FuncDetail& funcDetail = _pass->func()->detail(); const Operand* opArray = funcRet->operands(); uint32_t opCount = funcRet->opCount(); @@ -1096,8 +1096,7 @@ Error X86RACFGBuilder::onRet(FuncRetNode* funcRet, RAInstBuilder& ib) noexcept { // ============================================================================ X86RAPass::X86RAPass() noexcept - : RAPass(), - _avxEnabled(false) {} + : BaseRAPass() { _iEmitHelper = &_emitHelper; } X86RAPass::~X86RAPass() noexcept {} // ============================================================================ @@ -1108,9 +1107,10 @@ void X86RAPass::onInit() noexcept { uint32_t arch = cc()->arch(); uint32_t baseRegCount = Environment::is32Bit(arch) ? 8u : 16u; - _archRegsInfo = &opData.archRegs; - _archTraits[Reg::kGroupGp] |= RAArchTraits::kHasSwap; + _emitHelper._emitter = _cb; + _emitHelper._avxEnabled = _func->frame().isAvxEnabled(); + _archTraits = &ArchTraits::byArch(arch); _physRegCount.set(Reg::kGroupGp , baseRegCount); _physRegCount.set(Reg::kGroupVec , baseRegCount); _physRegCount.set(Reg::kGroupMm , 8); @@ -1135,7 +1135,6 @@ void X86RAPass::onInit() noexcept { _sp = cc()->zsp(); _fp = cc()->zbp(); - _avxEnabled = _func->frame().isAvxEnabled(); } void X86RAPass::onDone() noexcept {} @@ -1145,17 +1144,17 @@ void X86RAPass::onDone() noexcept {} // ============================================================================ Error X86RAPass::buildCFG() noexcept { - return X86RACFGBuilder(this).run(); + return RACFGBuilder(this).run(); } // ============================================================================ // [asmjit::x86::X86RAPass - OnEmit] // ============================================================================ -Error X86RAPass::onEmitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept { +Error X86RAPass::emitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept { RAWorkReg* wReg = workRegById(workId); - BaseReg dst(wReg->info().signature(), dstPhysId); - BaseReg src(wReg->info().signature(), srcPhysId); + BaseReg dst = BaseReg::fromSignatureAndId(wReg->info().signature(), dstPhysId); + BaseReg src = BaseReg::fromSignatureAndId(wReg->info().signature(), srcPhysId); const char* comment = nullptr; @@ -1166,10 +1165,10 @@ Error X86RAPass::onEmitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhy } #endif - return X86Internal::emitRegMove(cc()->as(), dst, src, wReg->typeId(), _avxEnabled, comment); + return _emitHelper.emitRegMove(dst, src, wReg->typeId(), comment); } -Error X86RAPass::onEmitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept { +Error X86RAPass::emitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept { RAWorkReg* waReg = workRegById(aWorkId); RAWorkReg* wbReg = workRegById(bWorkId); @@ -1184,13 +1183,15 @@ Error X86RAPass::onEmitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId } #endif - return cc()->emit(Inst::kIdXchg, Reg(sign, aPhysId), Reg(sign, bPhysId)); + return cc()->emit(Inst::kIdXchg, + Reg::fromSignatureAndId(sign, aPhysId), + Reg::fromSignatureAndId(sign, bPhysId)); } -Error X86RAPass::onEmitLoad(uint32_t workId, uint32_t dstPhysId) noexcept { +Error X86RAPass::emitLoad(uint32_t workId, uint32_t dstPhysId) noexcept { RAWorkReg* wReg = workRegById(workId); - BaseReg dstReg(wReg->info().signature(), dstPhysId); - BaseMem srcMem(workRegAsMem(wReg)); + BaseReg dstReg = BaseReg::fromSignatureAndId(wReg->info().signature(), dstPhysId); + BaseMem srcMem = BaseMem(workRegAsMem(wReg)); const char* comment = nullptr; @@ -1201,13 +1202,13 @@ Error X86RAPass::onEmitLoad(uint32_t workId, uint32_t dstPhysId) noexcept { } #endif - return X86Internal::emitRegMove(cc()->as(), dstReg, srcMem, wReg->typeId(), _avxEnabled, comment); + return _emitHelper.emitRegMove(dstReg, srcMem, wReg->typeId(), comment); } -Error X86RAPass::onEmitSave(uint32_t workId, uint32_t srcPhysId) noexcept { +Error X86RAPass::emitSave(uint32_t workId, uint32_t srcPhysId) noexcept { RAWorkReg* wReg = workRegById(workId); - BaseMem dstMem(workRegAsMem(wReg)); - BaseReg srcReg(wReg->info().signature(), srcPhysId); + BaseMem dstMem = BaseMem(workRegAsMem(wReg)); + BaseReg srcReg = BaseReg::fromSignatureAndId(wReg->info().signature(), srcPhysId); const char* comment = nullptr; @@ -1218,14 +1219,14 @@ Error X86RAPass::onEmitSave(uint32_t workId, uint32_t srcPhysId) noexcept { } #endif - return X86Internal::emitRegMove(cc()->as(), dstMem, srcReg, wReg->typeId(), _avxEnabled, comment); + return _emitHelper.emitRegMove(dstMem, srcReg, wReg->typeId(), comment); } -Error X86RAPass::onEmitJump(const Label& label) noexcept { +Error X86RAPass::emitJump(const Label& label) noexcept { return cc()->jmp(label); } -Error X86RAPass::onEmitPreCall(InvokeNode* invokeNode) noexcept { +Error X86RAPass::emitPreCall(InvokeNode* invokeNode) noexcept { if (invokeNode->detail().hasVarArgs() && cc()->is64Bit()) { const FuncDetail& fd = invokeNode->detail(); uint32_t argCount = invokeNode->argCount(); diff --git a/src/asmjit/x86/x86rapass_p.h b/src/asmjit/x86/x86rapass_p.h index 99093ab..efcfd3c 100644 --- a/src/asmjit/x86/x86rapass_p.h +++ b/src/asmjit/x86/x86rapass_p.h @@ -32,14 +32,12 @@ #include "../core/rapass_p.h" #include "../x86/x86assembler.h" #include "../x86/x86compiler.h" +#include "../x86/x86emithelper_p.h" ASMJIT_BEGIN_SUB_NAMESPACE(x86) //! \cond INTERNAL - -//! \brief X86/X64 register allocation. - -//! \addtogroup asmjit_ra +//! \addtogroup asmjit_x86 //! \{ // ============================================================================ @@ -50,12 +48,12 @@ ASMJIT_BEGIN_SUB_NAMESPACE(x86) //! //! Takes care of generating function prologs and epilogs, and also performs //! register allocation. -class X86RAPass : public RAPass { +class X86RAPass : public BaseRAPass { public: ASMJIT_NONCOPYABLE(X86RAPass) - typedef RAPass Base; + typedef BaseRAPass Base; - bool _avxEnabled; + EmitHelper _emitHelper; // -------------------------------------------------------------------------- // [Construction / Destruction] @@ -71,12 +69,17 @@ public: //! Returns the compiler casted to `x86::Compiler`. inline Compiler* cc() const noexcept { return static_cast(_cb); } + //! Returns emit helper. + inline EmitHelper* emitHelper() noexcept { return &_emitHelper; } + // -------------------------------------------------------------------------- // [Utilities] // -------------------------------------------------------------------------- + inline bool avxEnabled() const noexcept { return _emitHelper._avxEnabled; } + inline uint32_t choose(uint32_t sseInstId, uint32_t avxInstId) noexcept { - return _avxEnabled ? avxInstId : sseInstId; + return avxEnabled() ? avxInstId : sseInstId; } // -------------------------------------------------------------------------- @@ -96,14 +99,14 @@ public: // [Emit] // -------------------------------------------------------------------------- - Error onEmitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept override; - Error onEmitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept override; + Error emitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept override; + Error emitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept override; - Error onEmitLoad(uint32_t workId, uint32_t dstPhysId) noexcept override; - Error onEmitSave(uint32_t workId, uint32_t srcPhysId) noexcept override; + Error emitLoad(uint32_t workId, uint32_t dstPhysId) noexcept override; + Error emitSave(uint32_t workId, uint32_t srcPhysId) noexcept override; - Error onEmitJump(const Label& label) noexcept override; - Error onEmitPreCall(InvokeNode* invokeNode) noexcept override; + Error emitJump(const Label& label) noexcept override; + Error emitPreCall(InvokeNode* invokeNode) noexcept override; }; //! \} diff --git a/test/asmjit_test_compiler.cpp b/test/asmjit_test_compiler.cpp new file mode 100644 index 0000000..6a83e7c --- /dev/null +++ b/test/asmjit_test_compiler.cpp @@ -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 + +#include +#include +#include + +#include +#include + +#include "./cmdline.h" +#include "./asmjit_test_compiler.h" + +#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86 +#include +void compiler_add_x86_tests(TestApp& app); +#endif + +#if defined(ASMJIT_BUILD_ARM) && ASMJIT_ARCH_ARM == 64 +#include +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& 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(); +} diff --git a/test/asmjit_test_compiler.h b/test/asmjit_test_compiler.h new file mode 100644 index 0000000..5933e4a --- /dev/null +++ b/test/asmjit_test_compiler.h @@ -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 + +#include +#include + +// ============================================================================ +// [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> _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(test)); + } + + template + inline void addT() { T::add(*this); } + + int handleArgs(int argc, const char* const* argv); + void showInfo(); + int run(); +}; + +#endif // ASMJIT_TEST_COMPILER_H_INCLUDED diff --git a/test/asmjit_test_compiler_x86.cpp b/test/asmjit_test_compiler_x86.cpp new file mode 100644 index 0000000..4caa7d8 --- /dev/null +++ b/test/asmjit_test_compiler_x86.cpp @@ -0,0 +1,4238 @@ +// 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 +#if defined(ASMJIT_BUILD_X86) && ASMJIT_ARCH_X86 + +#include +#include +#include +#include +#include + +// Required for function tests that pass / return XMM registers. +#include + +#include "./asmjit_test_misc.h" +#include "./asmjit_test_compiler.h" + +#ifdef _MSC_VER +// Interaction between '_setjmp' and C++ object destruction is non-portable. +#pragma warning(disable: 4611) +#endif + +using namespace asmjit; + +// ============================================================================ +// [X86TestCase] +// ============================================================================ + +class X86TestCase : public TestCase { +public: + X86TestCase(const char* name = nullptr) + : TestCase(name) {} + + virtual void compile(BaseCompiler& cc) override { + compile(static_cast(cc)); + } + + virtual void compile(x86::Compiler& cc) = 0; +}; + +// ============================================================================ +// [X86Test_AlignBase] +// ============================================================================ + +class X86Test_AlignBase : public X86TestCase { +public: + X86Test_AlignBase(uint32_t argCount, uint32_t alignment, bool preserveFP) + : _argCount(argCount), + _alignment(alignment), + _preserveFP(preserveFP) { + _name.assignFormat("AlignBase {NumArgs=%u Alignment=%u PreserveFP=%c}", argCount, alignment, preserveFP ? 'Y' : 'N'); + } + + static void add(TestApp& app) { + for (uint32_t i = 0; i <= 16; i++) { + for (uint32_t a = 16; a <= 32; a += 16) { + app.add(new X86Test_AlignBase(i, a, true)); + app.add(new X86Test_AlignBase(i, a, false)); + } + } + } + + virtual void compile(x86::Compiler& cc) { + uint32_t i; + uint32_t argCount = _argCount; + + FuncSignatureBuilder signature(CallConv::kIdHost); + signature.setRetT(); + for (i = 0; i < argCount; i++) + signature.addArgT(); + + cc.addFunc(signature); + if (_preserveFP) + cc.func()->frame().setPreservedFP(); + + x86::Gp gpVar = cc.newIntPtr("gpVar"); + x86::Gp gpSum; + x86::Mem stack = cc.newStack(_alignment, _alignment); + + // Do a sum of arguments to verify a possible relocation when misaligned. + if (argCount) { + for (i = 0; i < argCount; i++) { + x86::Gp gpArg = cc.newInt32("gpArg%u", i); + cc.setArg(i, gpArg); + + if (i == 0) + gpSum = gpArg; + else + cc.add(gpSum, gpArg); + } + } + + // Check alignment of xmmVar (has to be 16). + cc.lea(gpVar, stack); + cc.and_(gpVar, _alignment - 1); + + // Add a sum of all arguments to check if they are correct. + if (argCount) + cc.or_(gpVar.r32(), gpSum); + + cc.ret(gpVar); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef unsigned int U; + + typedef U (*Func0)(); + typedef U (*Func1)(U); + typedef U (*Func2)(U, U); + typedef U (*Func3)(U, U, U); + typedef U (*Func4)(U, U, U, U); + typedef U (*Func5)(U, U, U, U, U); + typedef U (*Func6)(U, U, U, U, U, U); + typedef U (*Func7)(U, U, U, U, U, U, U); + typedef U (*Func8)(U, U, U, U, U, U, U, U); + typedef U (*Func9)(U, U, U, U, U, U, U, U, U); + typedef U (*Func10)(U, U, U, U, U, U, U, U, U, U); + typedef U (*Func11)(U, U, U, U, U, U, U, U, U, U, U); + typedef U (*Func12)(U, U, U, U, U, U, U, U, U, U, U, U); + typedef U (*Func13)(U, U, U, U, U, U, U, U, U, U, U, U, U); + typedef U (*Func14)(U, U, U, U, U, U, U, U, U, U, U, U, U, U); + typedef U (*Func15)(U, U, U, U, U, U, U, U, U, U, U, U, U, U, U); + typedef U (*Func16)(U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U); + + unsigned int resultRet = 0; + unsigned int expectRet = 0; + + switch (_argCount) { + case 0: + resultRet = ptr_as_func(_func)(); + expectRet = 0; + break; + case 1: + resultRet = ptr_as_func(_func)(1); + expectRet = 1; + break; + case 2: + resultRet = ptr_as_func(_func)(1, 2); + expectRet = 1 + 2; + break; + case 3: + resultRet = ptr_as_func(_func)(1, 2, 3); + expectRet = 1 + 2 + 3; + break; + case 4: + resultRet = ptr_as_func(_func)(1, 2, 3, 4); + expectRet = 1 + 2 + 3 + 4; + break; + case 5: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5); + expectRet = 1 + 2 + 3 + 4 + 5; + break; + case 6: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6); + expectRet = 1 + 2 + 3 + 4 + 5 + 6; + break; + case 7: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7); + expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7; + break; + case 8: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7, 8); + expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8; + break; + case 9: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9); + expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9; + break; + case 10: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10; + break; + case 11: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); + expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11; + break; + case 12: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); + expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12; + break; + case 13: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); + expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13; + break; + case 14: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); + expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14; + break; + case 15: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15; + break; + case 16: + resultRet = ptr_as_func(_func)(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + expectRet = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16; + break; + } + + result.assignFormat("ret={%u, %u}", resultRet >> 28, resultRet & 0x0FFFFFFFu); + expect.assignFormat("ret={%u, %u}", expectRet >> 28, expectRet & 0x0FFFFFFFu); + + return resultRet == expectRet; + } + + uint32_t _argCount; + uint32_t _alignment; + bool _preserveFP; +}; + +// ============================================================================ +// [X86Test_NoCode] +// ============================================================================ + +class X86Test_NoCode : public X86TestCase { +public: + X86Test_NoCode() : X86TestCase("NoCode") {} + + static void add(TestApp& app) { + app.add(new X86Test_NoCode()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + DebugUtils::unused(result, expect); + + typedef void(*Func)(void); + Func func = ptr_as_func(_func); + + func(); + return true; + } +}; + +// ============================================================================ +// [X86Test_AlignNone] +// ============================================================================ + +class X86Test_NoAlign : public X86TestCase { +public: + X86Test_NoAlign() : X86TestCase("NoAlign") {} + + static void add(TestApp& app) { + app.add(new X86Test_NoAlign()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + cc.align(kAlignCode, 0); + cc.align(kAlignCode, 1); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + DebugUtils::unused(result, expect); + + typedef void (*Func)(void); + Func func = ptr_as_func(_func); + + func(); + return true; + } +}; + +// ============================================================================ +// [X86Test_JumpMerge] +// ============================================================================ + +class X86Test_JumpMerge : public X86TestCase { +public: + X86Test_JumpMerge() : X86TestCase("JumpMerge") {} + + static void add(TestApp& app) { + app.add(new X86Test_JumpMerge()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + Label L0 = cc.newLabel(); + Label L1 = cc.newLabel(); + Label L2 = cc.newLabel(); + Label LEnd = cc.newLabel(); + + x86::Gp dst = cc.newIntPtr("dst"); + x86::Gp val = cc.newInt32("val"); + + cc.setArg(0, dst); + cc.setArg(1, val); + + cc.cmp(val, 0); + cc.je(L2); + + cc.cmp(val, 1); + cc.je(L1); + + cc.cmp(val, 2); + cc.je(L0); + + cc.mov(x86::dword_ptr(dst), val); + cc.jmp(LEnd); + + // On purpose. This tests whether the CFG constructs a single basic-block + // from multiple labels next to each other. + cc.bind(L0); + cc.bind(L1); + cc.bind(L2); + cc.mov(x86::dword_ptr(dst), 0); + + cc.bind(LEnd); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void(*Func)(int*, int); + Func func = ptr_as_func(_func); + + int arr[5] = { -1, -1, -1, -1, -1 }; + int exp[5] = { 0, 0, 0, 3, 4 }; + + for (int i = 0; i < 5; i++) + func(&arr[i], i); + + result.assignFormat("ret={%d, %d, %d, %d, %d}", arr[0], arr[1], arr[2], arr[3], arr[4]); + expect.assignFormat("ret={%d, %d, %d, %d, %d}", exp[0], exp[1], exp[2], exp[3], exp[4]); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_JumpCross] +// ============================================================================ + +class X86Test_JumpCross : public X86TestCase { +public: + X86Test_JumpCross() : X86TestCase("JumpCross") {} + + static void add(TestApp& app) { + app.add(new X86Test_JumpCross()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + Label L1 = cc.newLabel(); + Label L2 = cc.newLabel(); + Label L3 = cc.newLabel(); + + cc.jmp(L2); + + cc.bind(L1); + cc.jmp(L3); + + cc.bind(L2); + cc.jmp(L1); + + cc.bind(L3); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + DebugUtils::unused(result, expect); + + typedef void (*Func)(void); + Func func = ptr_as_func(_func); + + func(); + return true; + } +}; + +// ============================================================================ +// [X86Test_JumpMany] +// ============================================================================ + +class X86Test_JumpMany : public X86TestCase { +public: + X86Test_JumpMany() : X86TestCase("JumpMany") {} + + static void add(TestApp& app) { + app.add(new X86Test_JumpMany()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + for (uint32_t i = 0; i < 1000; i++) { + Label L = cc.newLabel(); + cc.jmp(L); + cc.bind(L); + } + + x86::Gp ret = cc.newInt32("ret"); + cc.xor_(ret, ret); + cc.ret(ret); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(void); + + Func func = ptr_as_func(_func); + + int resultRet = func(); + int expectRet = 0; + + result.assignFormat("ret={%d}", resultRet); + expect.assignFormat("ret={%d}", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_JumpUnreachable1] +// ============================================================================ + +class X86Test_JumpUnreachable1 : public X86TestCase { +public: + X86Test_JumpUnreachable1() : X86TestCase("JumpUnreachable1") {} + + static void add(TestApp& app) { + app.add(new X86Test_JumpUnreachable1()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + Label L_1 = cc.newLabel(); + Label L_2 = cc.newLabel(); + Label L_3 = cc.newLabel(); + Label L_4 = cc.newLabel(); + Label L_5 = cc.newLabel(); + Label L_6 = cc.newLabel(); + Label L_7 = cc.newLabel(); + + x86::Gp v0 = cc.newUInt32("v0"); + x86::Gp v1 = cc.newUInt32("v1"); + + cc.bind(L_2); + cc.bind(L_3); + + cc.jmp(L_1); + + cc.bind(L_5); + cc.mov(v0, 0); + + cc.bind(L_6); + cc.jmp(L_3); + cc.mov(v1, 1); + cc.jmp(L_1); + + cc.bind(L_4); + cc.jmp(L_2); + cc.bind(L_7); + cc.add(v0, v1); + + cc.align(kAlignCode, 16); + cc.bind(L_1); + cc.ret(); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(void); + Func func = ptr_as_func(_func); + + func(); + + result.append("ret={}"); + expect.append("ret={}"); + + return true; + } +}; + +// ============================================================================ +// [X86Test_JumpUnreachable2] +// ============================================================================ + +class X86Test_JumpUnreachable2 : public X86TestCase { +public: + X86Test_JumpUnreachable2() : X86TestCase("JumpUnreachable2") {} + + static void add(TestApp& app) { + app.add(new X86Test_JumpUnreachable2()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + Label L_1 = cc.newLabel(); + Label L_2 = cc.newLabel(); + + x86::Gp v0 = cc.newUInt32("v0"); + x86::Gp v1 = cc.newUInt32("v1"); + + cc.jmp(L_1); + cc.bind(L_2); + cc.mov(v0, 1); + cc.mov(v1, 2); + cc.cmp(v0, v1); + cc.jz(L_2); + cc.jmp(L_1); + + cc.bind(L_1); + cc.ret(); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(void); + Func func = ptr_as_func(_func); + + func(); + + result.append("ret={}"); + expect.append("ret={}"); + + return true; + } +}; + +// ============================================================================ +// [X86Test_JumpTable] +// ============================================================================ + +class X86Test_JumpTable : public X86TestCase { +public: + bool _annotated; + + X86Test_JumpTable(bool annotated) + : X86TestCase("X86Test_JumpTable"), + _annotated(annotated) { + _name.assignFormat("JumpTable {%s}", annotated ? "Annotated" : "Unknown Reg/Mem"); + } + + enum Operator { + kOperatorAdd = 0, + kOperatorSub = 1, + kOperatorMul = 2, + kOperatorDiv = 3 + }; + + static void add(TestApp& app) { + app.add(new X86Test_JumpTable(false)); + app.add(new X86Test_JumpTable(true)); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Xmm a = cc.newXmmSs("a"); + x86::Xmm b = cc.newXmmSs("b"); + x86::Gp op = cc.newUInt32("op"); + x86::Gp target = cc.newIntPtr("target"); + x86::Gp offset = cc.newIntPtr("offset"); + + Label L_End = cc.newLabel(); + + Label L_Table = cc.newLabel(); + Label L_Add = cc.newLabel(); + Label L_Sub = cc.newLabel(); + Label L_Mul = cc.newLabel(); + Label L_Div = cc.newLabel(); + + cc.setArg(0, a); + cc.setArg(1, b); + cc.setArg(2, op); + + cc.lea(offset, x86::ptr(L_Table)); + if (cc.is64Bit()) + cc.movsxd(target, x86::dword_ptr(offset, op.cloneAs(offset), 2)); + else + cc.mov(target, x86::dword_ptr(offset, op.cloneAs(offset), 2)); + cc.add(target, offset); + + // JumpAnnotation allows to annotate all possible jump targets of + // instructions where it cannot be deduced from operands. + if (_annotated) { + JumpAnnotation* annotation = cc.newJumpAnnotation(); + annotation->addLabel(L_Add); + annotation->addLabel(L_Sub); + annotation->addLabel(L_Mul); + annotation->addLabel(L_Div); + cc.jmp(target, annotation); + } + else { + cc.jmp(target); + } + + cc.bind(L_Add); + cc.addss(a, b); + cc.jmp(L_End); + + cc.bind(L_Sub); + cc.subss(a, b); + cc.jmp(L_End); + + cc.bind(L_Mul); + cc.mulss(a, b); + cc.jmp(L_End); + + cc.bind(L_Div); + cc.divss(a, b); + + cc.bind(L_End); + cc.ret(a); + + cc.endFunc(); + + cc.bind(L_Table); + cc.embedLabelDelta(L_Add, L_Table, 4); + cc.embedLabelDelta(L_Sub, L_Table, 4); + cc.embedLabelDelta(L_Mul, L_Table, 4); + cc.embedLabelDelta(L_Div, L_Table, 4); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef float (*Func)(float, float, uint32_t); + Func func = ptr_as_func(_func); + + float results[4]; + float expected[4]; + + results[0] = func(33.0f, 14.0f, kOperatorAdd); + results[1] = func(33.0f, 14.0f, kOperatorSub); + results[2] = func(10.0f, 6.0f, kOperatorMul); + results[3] = func(80.0f, 8.0f, kOperatorDiv); + + expected[0] = 47.0f; + expected[1] = 19.0f; + expected[2] = 60.0f; + expected[3] = 10.0f; + + result.assignFormat("ret={%f, %f, %f, %f}", results[0], results[1], results[2], results[3]); + expect.assignFormat("ret={%f, %f, %f, %f}", expected[0], expected[1], expected[2], expected[3]); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocBase] +// ============================================================================ + +class X86Test_AllocBase : public X86TestCase { +public: + X86Test_AllocBase() : X86TestCase("AllocBase") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocBase()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp v0 = cc.newInt32("v0"); + x86::Gp v1 = cc.newInt32("v1"); + x86::Gp v2 = cc.newInt32("v2"); + x86::Gp v3 = cc.newInt32("v3"); + x86::Gp v4 = cc.newInt32("v4"); + + cc.xor_(v0, v0); + + cc.mov(v1, 1); + cc.mov(v2, 2); + cc.mov(v3, 3); + cc.mov(v4, 4); + + cc.add(v0, v1); + cc.add(v0, v2); + cc.add(v0, v3); + cc.add(v0, v4); + + cc.ret(v0); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(void); + Func func = ptr_as_func(_func); + + int resultRet = func(); + int expectRet = 1 + 2 + 3 + 4; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_AllocMany1] +// ============================================================================ + +class X86Test_AllocMany1 : public X86TestCase { +public: + X86Test_AllocMany1() : X86TestCase("AllocMany1") {} + + enum { kCount = 8 }; + + static void add(TestApp& app) { + app.add(new X86Test_AllocMany1()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp a0 = cc.newIntPtr("a0"); + x86::Gp a1 = cc.newIntPtr("a1"); + + cc.setArg(0, a0); + cc.setArg(1, a1); + + // Create some variables. + x86::Gp t = cc.newInt32("t"); + x86::Gp x[kCount]; + + uint32_t i; + + // Setup variables (use mov with reg/imm to se if register allocator works). + for (i = 0; i < kCount; i++) x[i] = cc.newInt32("x%u", i); + for (i = 0; i < kCount; i++) cc.mov(x[i], int(i + 1)); + + // Make sum (addition). + cc.xor_(t, t); + for (i = 0; i < kCount; i++) cc.add(t, x[i]); + + // Store result to a given pointer in first argument. + cc.mov(x86::dword_ptr(a0), t); + + // Clear t. + cc.xor_(t, t); + + // Make sum (subtraction). + for (i = 0; i < kCount; i++) cc.sub(t, x[i]); + + // Store result to a given pointer in second argument. + cc.mov(x86::dword_ptr(a1), t); + + // End of function. + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(int*, int*); + Func func = ptr_as_func(_func); + + int resultX; + int resultY; + + int expectX = 36; + int expectY = -36; + + func(&resultX, &resultY); + + result.assignFormat("ret={x=%d, y=%d}", resultX, resultY); + expect.assignFormat("ret={x=%d, y=%d}", expectX, expectY); + + return resultX == expectX && resultY == expectY; + } +}; + +// ============================================================================ +// [X86Test_AllocMany2] +// ============================================================================ + +class X86Test_AllocMany2 : public X86TestCase { +public: + X86Test_AllocMany2() : X86TestCase("AllocMany2") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocMany2()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp a = cc.newIntPtr("a"); + x86::Gp v[32]; + + uint32_t i; + cc.setArg(0, a); + + for (i = 0; i < ASMJIT_ARRAY_SIZE(v); i++) v[i] = cc.newInt32("v%d", i); + for (i = 0; i < ASMJIT_ARRAY_SIZE(v); i++) cc.xor_(v[i], v[i]); + + x86::Gp x = cc.newInt32("x"); + Label L = cc.newLabel(); + + cc.mov(x, 32); + cc.bind(L); + for (i = 0; i < ASMJIT_ARRAY_SIZE(v); i++) cc.add(v[i], i); + + cc.dec(x); + cc.jnz(L); + for (i = 0; i < ASMJIT_ARRAY_SIZE(v); i++) cc.mov(x86::dword_ptr(a, int(i * 4)), v[i]); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(uint32_t*); + Func func = ptr_as_func(_func); + + uint32_t i; + uint32_t resultBuf[32]; + uint32_t expectBuf[32]; + + for (i = 0; i < ASMJIT_ARRAY_SIZE(resultBuf); i++) + expectBuf[i] = i * 32; + func(resultBuf); + + for (i = 0; i < ASMJIT_ARRAY_SIZE(resultBuf); i++) { + if (i != 0) { + result.append(','); + expect.append(','); + } + + result.appendFormat("%u", resultBuf[i]); + expect.appendFormat("%u", expectBuf[i]); + } + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocImul1] +// ============================================================================ + +class X86Test_AllocImul1 : public X86TestCase { +public: + X86Test_AllocImul1() : X86TestCase("AllocImul1") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocImul1()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp dstHi = cc.newIntPtr("dstHi"); + x86::Gp dstLo = cc.newIntPtr("dstLo"); + + x86::Gp vHi = cc.newInt32("vHi"); + x86::Gp vLo = cc.newInt32("vLo"); + x86::Gp src = cc.newInt32("src"); + + cc.setArg(0, dstHi); + cc.setArg(1, dstLo); + cc.setArg(2, vLo); + cc.setArg(3, src); + + cc.imul(vHi, vLo, src); + + cc.mov(x86::dword_ptr(dstHi), vHi); + cc.mov(x86::dword_ptr(dstLo), vLo); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(int*, int*, int, int); + Func func = ptr_as_func(_func); + + int v0 = 4; + int v1 = 4; + + int resultHi; + int resultLo; + + int expectHi = 0; + int expectLo = v0 * v1; + + func(&resultHi, &resultLo, v0, v1); + + result.assignFormat("hi=%d, lo=%d", resultHi, resultLo); + expect.assignFormat("hi=%d, lo=%d", expectHi, expectLo); + + return resultHi == expectHi && resultLo == expectLo; + } +}; + +// ============================================================================ +// [X86Test_AllocImul2] +// ============================================================================ + +class X86Test_AllocImul2 : public X86TestCase { +public: + X86Test_AllocImul2() : X86TestCase("AllocImul2") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocImul2()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp dst = cc.newIntPtr("dst"); + x86::Gp src = cc.newIntPtr("src"); + + cc.setArg(0, dst); + cc.setArg(1, src); + + for (unsigned int i = 0; i < 4; i++) { + x86::Gp x = cc.newInt32("x"); + x86::Gp y = cc.newInt32("y"); + x86::Gp hi = cc.newInt32("hi"); + + cc.mov(x, x86::dword_ptr(src, 0)); + cc.mov(y, x86::dword_ptr(src, 4)); + + cc.imul(hi, x, y); + cc.add(x86::dword_ptr(dst, 0), hi); + cc.add(x86::dword_ptr(dst, 4), x); + } + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(int*, const int*); + Func func = ptr_as_func(_func); + + int src[2] = { 4, 9 }; + int resultRet[2] = { 0, 0 }; + int expectRet[2] = { 0, (4 * 9) * 4 }; + + func(resultRet, src); + + result.assignFormat("ret={%d, %d}", resultRet[0], resultRet[1]); + expect.assignFormat("ret={%d, %d}", expectRet[0], expectRet[1]); + + return resultRet[0] == expectRet[0] && resultRet[1] == expectRet[1]; + } +}; + +// ============================================================================ +// [X86Test_AllocIdiv1] +// ============================================================================ + +class X86Test_AllocIdiv1 : public X86TestCase { +public: + X86Test_AllocIdiv1() : X86TestCase("AllocIdiv1") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocIdiv1()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp a = cc.newInt32("a"); + x86::Gp b = cc.newInt32("b"); + x86::Gp dummy = cc.newInt32("dummy"); + + cc.setArg(0, a); + cc.setArg(1, b); + + cc.xor_(dummy, dummy); + cc.idiv(dummy, a, b); + + cc.ret(a); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int); + Func func = ptr_as_func(_func); + + int v0 = 2999; + int v1 = 245; + + int resultRet = func(v0, v1); + int expectRet = 2999 / 245; + + result.assignFormat("result=%d", resultRet); + expect.assignFormat("result=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_AllocSetz] +// ============================================================================ + +class X86Test_AllocSetz : public X86TestCase { +public: + X86Test_AllocSetz() : X86TestCase("AllocSetz") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocSetz()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp src0 = cc.newInt32("src0"); + x86::Gp src1 = cc.newInt32("src1"); + x86::Gp dst0 = cc.newIntPtr("dst0"); + + cc.setArg(0, src0); + cc.setArg(1, src1); + cc.setArg(2, dst0); + + cc.cmp(src0, src1); + cc.setz(x86::byte_ptr(dst0)); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(int, int, char*); + Func func = ptr_as_func(_func); + + char resultBuf[4]; + char expectBuf[4] = { 1, 0, 0, 1 }; + + func(0, 0, &resultBuf[0]); // We are expecting 1 (0 == 0). + func(0, 1, &resultBuf[1]); // We are expecting 0 (0 != 1). + func(1, 0, &resultBuf[2]); // We are expecting 0 (1 != 0). + func(1, 1, &resultBuf[3]); // We are expecting 1 (1 == 1). + + result.assignFormat("out={%d, %d, %d, %d}", resultBuf[0], resultBuf[1], resultBuf[2], resultBuf[3]); + expect.assignFormat("out={%d, %d, %d, %d}", expectBuf[0], expectBuf[1], expectBuf[2], expectBuf[3]); + + return resultBuf[0] == expectBuf[0] && + resultBuf[1] == expectBuf[1] && + resultBuf[2] == expectBuf[2] && + resultBuf[3] == expectBuf[3] ; + } +}; + +// ============================================================================ +// [X86Test_AllocShlRor] +// ============================================================================ + +class X86Test_AllocShlRor : public X86TestCase { +public: + X86Test_AllocShlRor() : X86TestCase("AllocShlRor") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocShlRor()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp dst = cc.newIntPtr("dst"); + x86::Gp var = cc.newInt32("var"); + x86::Gp vShlParam = cc.newInt32("vShlParam"); + x86::Gp vRorParam = cc.newInt32("vRorParam"); + + cc.setArg(0, dst); + cc.setArg(1, var); + cc.setArg(2, vShlParam); + cc.setArg(3, vRorParam); + + cc.shl(var, vShlParam); + cc.ror(var, vRorParam); + + cc.mov(x86::dword_ptr(dst), var); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(int*, int, int, int); + Func func = ptr_as_func(_func); + + int v0 = 0x000000FF; + + int resultRet; + int expectRet = 0x0000FF00; + + func(&resultRet, v0, 16, 8); + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_AllocGpbLo] +// ============================================================================ + +class X86Test_AllocGpbLo1 : public X86TestCase { +public: + X86Test_AllocGpbLo1() : X86TestCase("AllocGpbLo1") {} + + enum { kCount = 32 }; + + static void add(TestApp& app) { + app.add(new X86Test_AllocGpbLo1()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp rPtr = cc.newUIntPtr("rPtr"); + x86::Gp rSum = cc.newUInt32("rSum"); + + cc.setArg(0, rPtr); + + x86::Gp x[kCount]; + uint32_t i; + + for (i = 0; i < kCount; i++) { + x[i] = cc.newUInt32("x%u", i); + } + + // Init pseudo-regs with values from our array. + for (i = 0; i < kCount; i++) { + cc.mov(x[i], x86::dword_ptr(rPtr, int(i * 4))); + } + + for (i = 2; i < kCount; i++) { + // Add and truncate to 8 bit; no purpose, just mess with jit. + cc.add (x[i ], x[i-1]); + cc.movzx(x[i ], x[i ].r8()); + cc.movzx(x[i-2], x[i-1].r8()); + cc.movzx(x[i-1], x[i-2].r8()); + } + + // Sum up all computed values. + cc.mov(rSum, 0); + for (i = 0; i < kCount; i++) { + cc.add(rSum, x[i]); + } + + // Return the sum. + cc.ret(rSum); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef uint32_t (*Func)(uint32_t*); + Func func = ptr_as_func(_func); + + uint32_t i; + uint32_t buf[kCount]; + uint32_t resultRet; + uint32_t expectRet; + + expectRet = 0; + for (i = 0; i < kCount; i++) { + buf[i] = 1; + } + + for (i = 2; i < kCount; i++) { + buf[i ]+= buf[i-1]; + buf[i ] = buf[i ] & 0xFF; + buf[i-2] = buf[i-1] & 0xFF; + buf[i-1] = buf[i-2] & 0xFF; + } + + for (i = 0; i < kCount; i++) { + expectRet += buf[i]; + } + + for (i = 0; i < kCount; i++) { + buf[i] = 1; + } + resultRet = func(buf); + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_AllocGpbLo2] +// ============================================================================ + +class X86Test_AllocGpbLo2 : public X86TestCase { +public: + X86Test_AllocGpbLo2() : X86TestCase("AllocGpbLo2") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocGpbLo2()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp v = cc.newUInt32("v"); + cc.setArg(0, v); + cc.mov(v.r8(), 0xFF); + cc.ret(v); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef uint32_t (*Func)(uint32_t); + Func func = ptr_as_func(_func); + + uint32_t resultRet = func(0x12345678u); + uint32_t expectRet = 0x123456FFu; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_AllocRepMovsb] +// ============================================================================ + +class X86Test_AllocRepMovsb : public X86TestCase { +public: + X86Test_AllocRepMovsb() : X86TestCase("AllocRepMovsb") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocRepMovsb()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp dst = cc.newIntPtr("dst"); + x86::Gp src = cc.newIntPtr("src"); + x86::Gp cnt = cc.newIntPtr("cnt"); + + cc.setArg(0, dst); + cc.setArg(1, src); + cc.setArg(2, cnt); + + cc.rep(cnt).movs(x86::byte_ptr(dst), x86::byte_ptr(src)); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(void*, void*, size_t); + Func func = ptr_as_func(_func); + + char dst[20] = { 0 }; + char src[20] = "Hello AsmJit!"; + func(dst, src, strlen(src) + 1); + + result.assignFormat("ret=\"%s\"", dst); + expect.assignFormat("ret=\"%s\"", src); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocIfElse1] +// ============================================================================ + +class X86Test_AllocIfElse1 : public X86TestCase { +public: + X86Test_AllocIfElse1() : X86TestCase("AllocIfElse1") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocIfElse1()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp v1 = cc.newInt32("v1"); + x86::Gp v2 = cc.newInt32("v2"); + + Label L_1 = cc.newLabel(); + Label L_2 = cc.newLabel(); + + cc.setArg(0, v1); + cc.setArg(1, v2); + + cc.cmp(v1, v2); + cc.jg(L_1); + + cc.mov(v1, 1); + cc.jmp(L_2); + + cc.bind(L_1); + cc.mov(v1, 2); + + cc.bind(L_2); + cc.ret(v1); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int); + Func func = ptr_as_func(_func); + + int a = func(0, 1); + int b = func(1, 0); + + result.appendFormat("ret={%d, %d}", a, b); + expect.appendFormat("ret={%d, %d}", 1, 2); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocIfElse2] +// ============================================================================ + +class X86Test_AllocIfElse2 : public X86TestCase { +public: + X86Test_AllocIfElse2() : X86TestCase("AllocIfElse2") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocIfElse2()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp v1 = cc.newInt32("v1"); + x86::Gp v2 = cc.newInt32("v2"); + + Label L_1 = cc.newLabel(); + Label L_2 = cc.newLabel(); + Label L_3 = cc.newLabel(); + Label L_4 = cc.newLabel(); + + cc.setArg(0, v1); + cc.setArg(1, v2); + + cc.jmp(L_1); + cc.bind(L_2); + cc.jmp(L_4); + cc.bind(L_1); + + cc.cmp(v1, v2); + cc.jg(L_3); + + cc.mov(v1, 1); + cc.jmp(L_2); + + cc.bind(L_3); + cc.mov(v1, 2); + cc.jmp(L_2); + + cc.bind(L_4); + + cc.ret(v1); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int); + Func func = ptr_as_func(_func); + + int a = func(0, 1); + int b = func(1, 0); + + result.appendFormat("ret={%d, %d}", a, b); + expect.appendFormat("ret={%d, %d}", 1, 2); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocIfElse3] +// ============================================================================ + +class X86Test_AllocIfElse3 : public X86TestCase { +public: + X86Test_AllocIfElse3() : X86TestCase("AllocIfElse3") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocIfElse3()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp v1 = cc.newInt32("v1"); + x86::Gp v2 = cc.newInt32("v2"); + x86::Gp counter = cc.newInt32("counter"); + + Label L_1 = cc.newLabel(); + Label L_Loop = cc.newLabel(); + Label L_Exit = cc.newLabel(); + + cc.setArg(0, v1); + cc.setArg(1, v2); + + cc.cmp(v1, v2); + cc.jg(L_1); + + cc.mov(counter, 0); + + cc.bind(L_Loop); + cc.mov(v1, counter); + + cc.inc(counter); + cc.cmp(counter, 1); + cc.jle(L_Loop); + cc.jmp(L_Exit); + + cc.bind(L_1); + cc.mov(v1, 2); + + cc.bind(L_Exit); + cc.ret(v1); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int); + Func func = ptr_as_func(_func); + + int a = func(0, 1); + int b = func(1, 0); + + result.appendFormat("ret={%d, %d}", a, b); + expect.appendFormat("ret={%d, %d}", 1, 2); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocIfElse4] +// ============================================================================ + +class X86Test_AllocIfElse4 : public X86TestCase { +public: + X86Test_AllocIfElse4() : X86TestCase("AllocIfElse4") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocIfElse4()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp v1 = cc.newInt32("v1"); + x86::Gp v2 = cc.newInt32("v2"); + x86::Gp counter = cc.newInt32("counter"); + + Label L_1 = cc.newLabel(); + Label L_Loop1 = cc.newLabel(); + Label L_Loop2 = cc.newLabel(); + Label L_Exit = cc.newLabel(); + + cc.mov(counter, 0); + + cc.setArg(0, v1); + cc.setArg(1, v2); + + cc.cmp(v1, v2); + cc.jg(L_1); + + cc.bind(L_Loop1); + cc.mov(v1, counter); + + cc.inc(counter); + cc.cmp(counter, 1); + cc.jle(L_Loop1); + cc.jmp(L_Exit); + + cc.bind(L_1); + cc.bind(L_Loop2); + cc.mov(v1, counter); + cc.inc(counter); + cc.cmp(counter, 2); + cc.jle(L_Loop2); + + cc.bind(L_Exit); + cc.ret(v1); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int); + Func func = ptr_as_func(_func); + + int a = func(0, 1); + int b = func(1, 0); + + result.appendFormat("ret={%d, %d}", a, b); + expect.appendFormat("ret={%d, %d}", 1, 2); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocInt8] +// ============================================================================ + +class X86Test_AllocInt8 : public X86TestCase { +public: + X86Test_AllocInt8() : X86TestCase("AllocInt8") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocInt8()); + } + + virtual void compile(x86::Compiler& cc) { + x86::Gp x = cc.newInt8("x"); + x86::Gp y = cc.newInt32("y"); + + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + cc.setArg(0, x); + + cc.movsx(y, x); + + cc.ret(y); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(char); + Func func = ptr_as_func(_func); + + int resultRet = func(-13); + int expectRet = -13; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocUnhandledArg] +// ============================================================================ + +class X86Test_AllocUnhandledArg : public X86TestCase { +public: + X86Test_AllocUnhandledArg() : X86TestCase("AllocUnhandledArg") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocUnhandledArg()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp x = cc.newInt32("x"); + cc.setArg(2, x); + cc.ret(x); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int, int); + Func func = ptr_as_func(_func); + + int resultRet = func(42, 155, 199); + int expectRet = 199; + + result.assignFormat("ret={%d}", resultRet); + expect.assignFormat("ret={%d}", expectRet); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocArgsIntPtr] +// ============================================================================ + +class X86Test_AllocArgsIntPtr : public X86TestCase { +public: + X86Test_AllocArgsIntPtr() : X86TestCase("AllocArgsIntPtr") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocArgsIntPtr()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + uint32_t i; + x86::Gp var[8]; + + for (i = 0; i < 8; i++) { + var[i] = cc.newIntPtr("var%u", i); + cc.setArg(i, var[i]); + } + + for (i = 0; i < 8; i++) { + cc.add(var[i], int(i + 1)); + } + + // Move some data into buffer provided by arguments so we can verify if it + // really works without looking into assembler output. + for (i = 0; i < 8; i++) { + cc.add(x86::byte_ptr(var[i]), int(i + 1)); + } + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(void*, void*, void*, void*, void*, void*, void*, void*); + Func func = ptr_as_func(_func); + + uint8_t resultBuf[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + uint8_t expectBuf[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + + func(resultBuf, resultBuf, resultBuf, resultBuf, + resultBuf, resultBuf, resultBuf, resultBuf); + + result.assignFormat("buf={%d, %d, %d, %d, %d, %d, %d, %d, %d}", + resultBuf[0], resultBuf[1], resultBuf[2], resultBuf[3], + resultBuf[4], resultBuf[5], resultBuf[6], resultBuf[7], + resultBuf[8]); + expect.assignFormat("buf={%d, %d, %d, %d, %d, %d, %d, %d, %d}", + expectBuf[0], expectBuf[1], expectBuf[2], expectBuf[3], + expectBuf[4], expectBuf[5], expectBuf[6], expectBuf[7], + expectBuf[8]); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocArgsFloat] +// ============================================================================ + +class X86Test_AllocArgsFloat : public X86TestCase { +public: + X86Test_AllocArgsFloat() : X86TestCase("AllocArgsFloat") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocArgsFloat()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + uint32_t i; + + x86::Gp p = cc.newIntPtr("p"); + x86::Xmm xv[7]; + + for (i = 0; i < 7; i++) { + xv[i] = cc.newXmmSs("xv%u", i); + cc.setArg(i, xv[i]); + } + + cc.setArg(7, p); + + cc.addss(xv[0], xv[1]); + cc.addss(xv[0], xv[2]); + cc.addss(xv[0], xv[3]); + cc.addss(xv[0], xv[4]); + cc.addss(xv[0], xv[5]); + cc.addss(xv[0], xv[6]); + + cc.movss(x86::ptr(p), xv[0]); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(float, float, float, float, float, float, float, float*); + Func func = ptr_as_func(_func); + + float resultRet; + float expectRet = 1.0f + 2.0f + 3.0f + 4.0f + 5.0f + 6.0f + 7.0f; + + func(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, &resultRet); + + result.assignFormat("ret={%g}", resultRet); + expect.assignFormat("ret={%g}", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_AllocArgsDouble] +// ============================================================================ + +class X86Test_AllocArgsDouble : public X86TestCase { +public: + X86Test_AllocArgsDouble() : X86TestCase("AllocArgsDouble") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocArgsDouble()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + uint32_t i; + + x86::Gp p = cc.newIntPtr("p"); + x86::Xmm xv[7]; + + for (i = 0; i < 7; i++) { + xv[i] = cc.newXmmSd("xv%u", i); + cc.setArg(i, xv[i]); + } + + cc.setArg(7, p); + + cc.addsd(xv[0], xv[1]); + cc.addsd(xv[0], xv[2]); + cc.addsd(xv[0], xv[3]); + cc.addsd(xv[0], xv[4]); + cc.addsd(xv[0], xv[5]); + cc.addsd(xv[0], xv[6]); + + cc.movsd(x86::ptr(p), xv[0]); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(double, double, double, double, double, double, double, double*); + Func func = ptr_as_func(_func); + + double resultRet; + double expectRet = 1.0 + 2.0 + 3.0 + 4.0 + 5.0 + 6.0 + 7.0; + + func(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, &resultRet); + + result.assignFormat("ret={%g}", resultRet); + expect.assignFormat("ret={%g}", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_AllocArgsVec] +// ============================================================================ + +class X86Test_AllocArgsVec : public X86TestCase { +public: + X86Test_AllocArgsVec() : X86TestCase("AllocArgsVec") {} + + static void add(TestApp& app) { + // Not supported on Windows. +#ifndef _WIN32 + app.add(new X86Test_AllocArgsVec()); +#endif + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Xmm a = cc.newXmm("aXmm"); + x86::Xmm b = cc.newXmm("bXmm"); + + cc.setArg(0, a); + cc.setArg(1, b); + + cc.paddb(a, b); + cc.ret(a); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef __m128i (*Func)(__m128i, __m128i); + Func func = ptr_as_func(_func); + + uint8_t aData[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + uint8_t bData[16] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + + uint8_t rData[16]; + uint8_t eData[16] = { 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 }; + + __m128i aVec = _mm_loadu_si128(reinterpret_cast(aData)); + __m128i bVec = _mm_loadu_si128(reinterpret_cast(bData)); + + __m128i rVec = func(aVec, bVec); + _mm_storeu_si128(reinterpret_cast<__m128i*>(rData), rVec); + + result.appendHex(rData, 16); + expect.appendHex(eData, 16); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocRetFloat1] +// ============================================================================ + +class X86Test_AllocRetFloat1 : public X86TestCase { +public: + X86Test_AllocRetFloat1() : X86TestCase("AllocRetFloat1") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocRetFloat1()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Xmm x = cc.newXmmSs("x"); + cc.setArg(0, x); + cc.ret(x); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef float (*Func)(float); + Func func = ptr_as_func(_func); + + float resultRet = func(42.0f); + float expectRet = 42.0f; + + result.assignFormat("ret={%g}", resultRet); + expect.assignFormat("ret={%g}", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_AllocRetFloat2] +// ============================================================================ + +class X86Test_AllocRetFloat2 : public X86TestCase { +public: + X86Test_AllocRetFloat2() : X86TestCase("AllocRetFloat2") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocRetFloat2()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Xmm x = cc.newXmmSs("x"); + x86::Xmm y = cc.newXmmSs("y"); + + cc.setArg(0, x); + cc.setArg(1, y); + + cc.addss(x, y); + cc.ret(x); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef float (*Func)(float, float); + Func func = ptr_as_func(_func); + + float resultRet = func(1.0f, 2.0f); + float expectRet = 1.0f + 2.0f; + + result.assignFormat("ret={%g}", resultRet); + expect.assignFormat("ret={%g}", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_AllocRetDouble1] +// ============================================================================ + +class X86Test_AllocRetDouble1 : public X86TestCase { +public: + X86Test_AllocRetDouble1() : X86TestCase("AllocRetDouble1") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocRetDouble1()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Xmm x = cc.newXmmSd("x"); + cc.setArg(0, x); + cc.ret(x); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef double (*Func)(double); + Func func = ptr_as_func(_func); + + double resultRet = func(42.0); + double expectRet = 42.0; + + result.assignFormat("ret={%g}", resultRet); + expect.assignFormat("ret={%g}", expectRet); + + return resultRet == expectRet; + } +}; +// ============================================================================ +// [X86Test_AllocRetDouble2] +// ============================================================================ + +class X86Test_AllocRetDouble2 : public X86TestCase { +public: + X86Test_AllocRetDouble2() : X86TestCase("AllocRetDouble2") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocRetDouble2()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Xmm x = cc.newXmmSd("x"); + x86::Xmm y = cc.newXmmSd("y"); + + cc.setArg(0, x); + cc.setArg(1, y); + + cc.addsd(x, y); + cc.ret(x); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef double (*Func)(double, double); + Func func = ptr_as_func(_func); + + double resultRet = func(1.0, 2.0); + double expectRet = 1.0 + 2.0; + + result.assignFormat("ret={%g}", resultRet); + expect.assignFormat("ret={%g}", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_AllocStack] +// ============================================================================ + +class X86Test_AllocStack : public X86TestCase { +public: + X86Test_AllocStack() : X86TestCase("AllocStack") {} + + enum { kSize = 256 }; + + static void add(TestApp& app) { + app.add(new X86Test_AllocStack()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Mem stack = cc.newStack(kSize, 1); + stack.setSize(1); + + x86::Gp i = cc.newIntPtr("i"); + x86::Gp a = cc.newInt32("a"); + x86::Gp b = cc.newInt32("b"); + + Label L_1 = cc.newLabel(); + Label L_2 = cc.newLabel(); + + // Fill stack by sequence [0, 1, 2, 3 ... 255]. + cc.xor_(i, i); + + x86::Mem stackWithIndex = stack.clone(); + stackWithIndex.setIndex(i, 0); + + cc.bind(L_1); + cc.mov(stackWithIndex, i.r8()); + cc.inc(i); + cc.cmp(i, 255); + cc.jle(L_1); + + // Sum sequence in stack. + cc.xor_(i, i); + cc.xor_(a, a); + + cc.bind(L_2); + cc.movzx(b, stackWithIndex); + cc.add(a, b); + cc.inc(i); + cc.cmp(i, 255); + cc.jle(L_2); + + cc.ret(a); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(void); + Func func = ptr_as_func(_func); + + int resultRet = func(); + int expectRet = 32640; + + result.assignInt(resultRet); + expect.assignInt(expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_AllocMemcpy] +// ============================================================================ + +class X86Test_AllocMemcpy : public X86TestCase { +public: + X86Test_AllocMemcpy() : X86TestCase("AllocMemcpy") {} + + enum { kCount = 32 }; + + static void add(TestApp& app) { + app.add(new X86Test_AllocMemcpy()); + } + + virtual void compile(x86::Compiler& cc) { + x86::Gp dst = cc.newIntPtr("dst"); + x86::Gp src = cc.newIntPtr("src"); + x86::Gp cnt = cc.newUIntPtr("cnt"); + + Label L_Loop = cc.newLabel(); // Create base labels we use + Label L_Exit = cc.newLabel(); // in our function. + + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + cc.setArg(0, dst); + cc.setArg(1, src); + cc.setArg(2, cnt); + + cc.test(cnt, cnt); // Exit if the size is zero. + cc.jz(L_Exit); + + cc.bind(L_Loop); // Bind the loop label here. + + x86::Gp tmp = cc.newInt32("tmp"); // Copy a single dword (4 bytes). + cc.mov(tmp, x86::dword_ptr(src)); + cc.mov(x86::dword_ptr(dst), tmp); + + cc.add(src, 4); // Increment dst/src pointers. + cc.add(dst, 4); + + cc.dec(cnt); // Loop until cnt isn't zero. + cc.jnz(L_Loop); + + cc.bind(L_Exit); // Bind the exit label here. + cc.endFunc(); // End of function. + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(uint32_t*, const uint32_t*, size_t); + Func func = ptr_as_func(_func); + + uint32_t i; + + uint32_t dstBuffer[kCount]; + uint32_t srcBuffer[kCount]; + + for (i = 0; i < kCount; i++) { + dstBuffer[i] = 0; + srcBuffer[i] = i; + } + + func(dstBuffer, srcBuffer, kCount); + + result.assign("buf={"); + expect.assign("buf={"); + + for (i = 0; i < kCount; i++) { + if (i != 0) { + result.append(", "); + expect.append(", "); + } + + result.appendFormat("%u", unsigned(dstBuffer[i])); + expect.appendFormat("%u", unsigned(srcBuffer[i])); + } + + result.append("}"); + expect.append("}"); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocExtraBlock] +// ============================================================================ + +class X86Test_AllocExtraBlock : public X86TestCase { +public: + X86Test_AllocExtraBlock() : X86TestCase("AllocExtraBlock") {} + + static void add(TestApp& app) { + app.add(new X86Test_AllocExtraBlock()); + } + + virtual void compile(x86::Compiler& cc) { + x86::Gp cond = cc.newInt32("cond"); + x86::Gp ret = cc.newInt32("ret"); + x86::Gp a = cc.newInt32("a"); + x86::Gp b = cc.newInt32("b"); + + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + cc.setArg(0, cond); + cc.setArg(1, a); + cc.setArg(2, b); + + Label L_Ret = cc.newLabel(); + Label L_Extra = cc.newLabel(); + + cc.test(cond, cond); + cc.jnz(L_Extra); + + cc.mov(ret, a); + cc.add(ret, b); + + cc.bind(L_Ret); + cc.ret(ret); + + // Emit code sequence at the end of the function. + BaseNode* prevCursor = cc.setCursor(cc.func()->endNode()->prev()); + cc.bind(L_Extra); + cc.mov(ret, a); + cc.sub(ret, b); + cc.jmp(L_Ret); + cc.setCursor(prevCursor); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int, int); + Func func = ptr_as_func(_func); + + int ret1 = func(0, 4, 5); + int ret2 = func(1, 4, 5); + + int exp1 = 4 + 5; + int exp2 = 4 - 5; + + result.assignFormat("ret={%d, %d}", ret1, ret2); + expect.assignFormat("ret={%d, %d}", exp1, exp2); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_AllocAlphaBlend] +// ============================================================================ + +class X86Test_AllocAlphaBlend : public X86TestCase { +public: + X86Test_AllocAlphaBlend() : X86TestCase("AllocAlphaBlend") {} + + enum { kCount = 17 }; + + static void add(TestApp& app) { + app.add(new X86Test_AllocAlphaBlend()); + } + + static uint32_t blendSrcOver(uint32_t d, uint32_t s) { + uint32_t saInv = ~s >> 24; + + uint32_t d_20 = (d ) & 0x00FF00FF; + uint32_t d_31 = (d >> 8) & 0x00FF00FF; + + d_20 *= saInv; + d_31 *= saInv; + + d_20 = ((d_20 + ((d_20 >> 8) & 0x00FF00FFu) + 0x00800080u) & 0xFF00FF00u) >> 8; + d_31 = ((d_31 + ((d_31 >> 8) & 0x00FF00FFu) + 0x00800080u) & 0xFF00FF00u); + + return d_20 + d_31 + s; + } + + virtual void compile(x86::Compiler& cc) { + asmtest::generateAlphaBlend(cc); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(void*, const void*, size_t); + Func func = ptr_as_func(_func); + + static const uint32_t dstConstData[] = { 0x00000000, 0x10101010, 0x20100804, 0x30200003, 0x40204040, 0x5000004D, 0x60302E2C, 0x706F6E6D, 0x807F4F2F, 0x90349001, 0xA0010203, 0xB03204AB, 0xC023AFBD, 0xD0D0D0C0, 0xE0AABBCC, 0xFFFFFFFF, 0xF8F4F2F1 }; + static const uint32_t srcConstData[] = { 0xE0E0E0E0, 0xA0008080, 0x341F1E1A, 0xFEFEFEFE, 0x80302010, 0x49490A0B, 0x998F7798, 0x00000000, 0x01010101, 0xA0264733, 0xBAB0B1B9, 0xFF000000, 0xDAB0A0C1, 0xE0BACFDA, 0x99887766, 0xFFFFFF80, 0xEE0A5FEC }; + + uint32_t _dstBuffer[kCount + 3]; + uint32_t _srcBuffer[kCount + 3]; + + // Has to be aligned. + uint32_t* dstBuffer = (uint32_t*)Support::alignUp((intptr_t)_dstBuffer, 16); + uint32_t* srcBuffer = (uint32_t*)Support::alignUp((intptr_t)_srcBuffer, 16); + + memcpy(dstBuffer, dstConstData, sizeof(dstConstData)); + memcpy(srcBuffer, srcConstData, sizeof(srcConstData)); + + uint32_t i; + uint32_t expBuffer[kCount]; + + for (i = 0; i < kCount; i++) { + expBuffer[i] = blendSrcOver(dstBuffer[i], srcBuffer[i]); + } + + func(dstBuffer, srcBuffer, kCount); + + result.assign("buf={"); + expect.assign("buf={"); + + for (i = 0; i < kCount; i++) { + if (i != 0) { + result.append(", "); + expect.append(", "); + } + + result.appendFormat("%08X", unsigned(dstBuffer[i])); + expect.appendFormat("%08X", unsigned(expBuffer[i])); + } + + result.append("}"); + expect.append("}"); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_FuncCallBase1] +// ============================================================================ + +class X86Test_FuncCallBase1 : public X86TestCase { +public: + X86Test_FuncCallBase1() : X86TestCase("FuncCallBase1") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallBase1()); + } + + virtual void compile(x86::Compiler& cc) { + x86::Gp v0 = cc.newInt32("v0"); + x86::Gp v1 = cc.newInt32("v1"); + x86::Gp v2 = cc.newInt32("v2"); + + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + cc.setArg(0, v0); + cc.setArg(1, v1); + cc.setArg(2, v2); + + // Just do something. + cc.shl(v0, 1); + cc.shl(v1, 1); + cc.shl(v2, 1); + + // Call a function. + InvokeNode* invokeNode; + cc.invoke(&invokeNode, imm((void*)calledFunc), FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, v2); + invokeNode->setArg(1, v1); + invokeNode->setArg(2, v0); + invokeNode->setRet(0, v0); + + cc.ret(v0); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int, int); + Func func = ptr_as_func(_func); + + int resultRet = func(3, 2, 1); + int expectRet = 36; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } + + static int calledFunc(int a, int b, int c) { return (a + b) * c; } +}; + +// ============================================================================ +// [X86Test_FuncCallBase2] +// ============================================================================ + +class X86Test_FuncCallBase2 : public X86TestCase { +public: + X86Test_FuncCallBase2() : X86TestCase("FuncCallBase2") {} + + enum { kSize = 256 }; + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallBase2()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + const int kTokenSize = 32; + + x86::Mem s1 = cc.newStack(kTokenSize, 32); + x86::Mem s2 = cc.newStack(kTokenSize, 32); + + x86::Gp p1 = cc.newIntPtr("p1"); + x86::Gp p2 = cc.newIntPtr("p2"); + + x86::Gp ret = cc.newInt32("ret"); + Label L_Exit = cc.newLabel(); + + static const char token[kTokenSize] = "-+:|abcdefghijklmnopqrstuvwxyz|"; + InvokeNode* invokeNode; + + cc.lea(p1, s1); + cc.lea(p2, s2); + + // Try to corrupt the stack if wrongly allocated. + cc.invoke(&invokeNode, imm((void*)memcpy), FuncSignatureT(CallConv::kIdCDecl)); + invokeNode->setArg(0, p1); + invokeNode->setArg(1, imm(token)); + invokeNode->setArg(2, imm(kTokenSize)); + invokeNode->setRet(0, p1); + + cc.invoke(&invokeNode, imm((void*)memcpy), FuncSignatureT(CallConv::kIdCDecl)); + invokeNode->setArg(0, p2); + invokeNode->setArg(1, imm(token)); + invokeNode->setArg(2, imm(kTokenSize)); + invokeNode->setRet(0, p2); + + cc.invoke(&invokeNode, imm((void*)memcmp), FuncSignatureT(CallConv::kIdCDecl)); + invokeNode->setArg(0, p1); + invokeNode->setArg(1, p2); + invokeNode->setArg(2, imm(kTokenSize)); + invokeNode->setRet(0, ret); + + // This should be 0 on success, however, if both `p1` and `p2` were + // allocated in the same address this check will still pass. + cc.cmp(ret, 0); + cc.jnz(L_Exit); + + // Checks whether `p1` and `p2` are different (must be). + cc.xor_(ret, ret); + cc.cmp(p1, p2); + cc.setz(ret.r8()); + + cc.bind(L_Exit); + cc.ret(ret); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(void); + Func func = ptr_as_func(_func); + + int resultRet = func(); + int expectRet = 0; // Must be zero, stack addresses must be different. + + result.assignInt(resultRet); + expect.assignInt(expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_FuncCallStd] +// ============================================================================ + +class X86Test_FuncCallStd : public X86TestCase { +public: + X86Test_FuncCallStd() : X86TestCase("FuncCallStd") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallStd()); + } + + virtual void compile(x86::Compiler& cc) { + x86::Gp x = cc.newInt32("x"); + x86::Gp y = cc.newInt32("y"); + x86::Gp z = cc.newInt32("z"); + + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + cc.setArg(0, x); + cc.setArg(1, y); + cc.setArg(2, z); + + InvokeNode* invokeNode; + cc.invoke(&invokeNode, + imm((void*)calledFunc), + FuncSignatureT(CallConv::kIdStdCall)); + invokeNode->setArg(0, x); + invokeNode->setArg(1, y); + invokeNode->setArg(2, z); + invokeNode->setRet(0, x); + + cc.ret(x); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int, int); + Func func = ptr_as_func(_func); + + int resultRet = func(1, 42, 3); + int expectRet = calledFunc(1, 42, 3); + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } + + // STDCALL function that is called inside the generated one. + static int ASMJIT_STDCALL calledFunc(int a, int b, int c) noexcept { + return (a + b) * c; + } +}; + +// ============================================================================ +// [X86Test_FuncCallFast] +// ============================================================================ + +class X86Test_FuncCallFast : public X86TestCase { +public: + X86Test_FuncCallFast() : X86TestCase("FuncCallFast") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallFast()); + } + + virtual void compile(x86::Compiler& cc) { + x86::Gp var = cc.newInt32("var"); + + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + cc.setArg(0, var); + + InvokeNode* invokeNode; + + cc.invoke(&invokeNode, imm((void*)calledFunc), FuncSignatureT(CallConv::kIdFastCall)); + invokeNode->setArg(0, var); + invokeNode->setRet(0, var); + + cc.invoke(&invokeNode, imm((void*)calledFunc), FuncSignatureT(CallConv::kIdFastCall)); + invokeNode->setArg(0, var); + invokeNode->setRet(0, var); + + cc.ret(var); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int); + Func func = ptr_as_func(_func); + + int resultRet = func(9); + int expectRet = (9 * 9) * (9 * 9); + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } + + // FASTCALL function that is called inside the generated one. + static int ASMJIT_FASTCALL calledFunc(int a) noexcept { + return a * a; + } +}; + +// ============================================================================ +// [X86Test_FuncCallSIMD] +// ============================================================================ + +class X86Test_FuncCallSIMD : public X86TestCase { +public: + bool _useVectorCall; + + X86Test_FuncCallSIMD(bool useVectorCall) + : X86TestCase(), + _useVectorCall(useVectorCall) { + _name.assignFormat("FuncCallSIMD {%s}", _useVectorCall ? "__vectorcall" : "__cdecl"); + } + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallSIMD(false)); +#ifdef _MSC_VER + app.add(new X86Test_FuncCallSIMD(true)); +#endif + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp resultPtr = cc.newIntPtr("resultPtr"); + x86::Gp aPtr = cc.newIntPtr("aPtr"); + x86::Gp bPtr = cc.newIntPtr("bPtr"); + x86::Gp pFn = cc.newIntPtr("pFn"); + + x86::Xmm aXmm = cc.newXmm("aXmm"); + x86::Xmm bXmm = cc.newXmm("bXmm"); + + cc.setArg(0, resultPtr); + cc.setArg(1, aPtr); + cc.setArg(2, bPtr); + + uint32_t ccId = CallConv::kIdCDecl; + Imm pFnImm = imm((void*)calledFunc_cdecl); + +#ifdef _MSC_VER + if (_useVectorCall) { + ccId = CallConv::kIdVectorCall; + pFnImm = imm((void*)calledFunc_vcall); + } +#endif + + cc.mov(pFn, pFnImm); + cc.movdqu(aXmm, x86::ptr(aPtr)); + cc.movdqu(bXmm, x86::ptr(bPtr)); + + InvokeNode* invokeNode; + cc.invoke(&invokeNode, pFn, FuncSignatureT(ccId)); + + invokeNode->setArg(0, aXmm); + invokeNode->setArg(1, bXmm); + invokeNode->setRet(0, aXmm); + + cc.movdqu(x86::ptr(resultPtr), aXmm); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(void*, const void*, const void*); + Func func = ptr_as_func(_func); + + uint8_t aData[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + uint8_t bData[16] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; + + uint8_t rData[16]; + uint8_t eData[16] = { 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 }; + + func(rData, aData, bData); + + result.appendHex(rData, 16); + expect.appendHex(eData, 16); + + return result == expect; + } + + static __m128i calledFunc_cdecl(__m128i a, __m128i b) { + return _mm_add_epi8(a, b); + } + +#ifdef _MSC_VER + static __m128i __vectorcall calledFunc_vcall(__m128i a, __m128i b) { + return _mm_add_epi8(a, b); + } +#endif +}; + +// ============================================================================ +// [X86Test_FuncCallLight] +// ============================================================================ + +class X86Test_FuncCallLight : public X86TestCase { +public: + X86Test_FuncCallLight() : X86TestCase("FuncCallLight") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallLight()); + } + + virtual void compile(x86::Compiler& cc) { + FuncSignatureT funcSig(CallConv::kIdCDecl); + FuncSignatureT fastSig(CallConv::kIdLightCall2); + + FuncNode* func = cc.newFunc(funcSig); + FuncNode* fast = cc.newFunc(fastSig); + + { + x86::Gp aPtr = cc.newIntPtr("aPtr"); + x86::Gp bPtr = cc.newIntPtr("bPtr"); + x86::Gp cPtr = cc.newIntPtr("cPtr"); + x86::Gp dPtr = cc.newIntPtr("dPtr"); + x86::Gp pOut = cc.newIntPtr("pOut"); + + x86::Xmm aXmm = cc.newXmm("aXmm"); + x86::Xmm bXmm = cc.newXmm("bXmm"); + x86::Xmm cXmm = cc.newXmm("cXmm"); + x86::Xmm dXmm = cc.newXmm("dXmm"); + + cc.addFunc(func); + + cc.setArg(0, aPtr); + cc.setArg(1, bPtr); + cc.setArg(2, cPtr); + cc.setArg(3, dPtr); + cc.setArg(4, pOut); + + cc.movups(aXmm, x86::ptr(aPtr)); + cc.movups(bXmm, x86::ptr(bPtr)); + cc.movups(cXmm, x86::ptr(cPtr)); + cc.movups(dXmm, x86::ptr(dPtr)); + + x86::Xmm xXmm = cc.newXmm("xXmm"); + x86::Xmm yXmm = cc.newXmm("yXmm"); + + InvokeNode* invokeNode; + + cc.invoke(&invokeNode, fast->label(), fastSig); + invokeNode->setArg(0, aXmm); + invokeNode->setArg(1, bXmm); + invokeNode->setRet(0, xXmm); + + cc.invoke(&invokeNode, fast->label(), fastSig); + invokeNode->setArg(0, cXmm); + invokeNode->setArg(1, dXmm); + invokeNode->setRet(0, yXmm); + + cc.pmullw(xXmm, yXmm); + cc.movups(x86::ptr(pOut), xXmm); + + cc.endFunc(); + } + + { + x86::Xmm aXmm = cc.newXmm("aXmm"); + x86::Xmm bXmm = cc.newXmm("bXmm"); + + cc.addFunc(fast); + cc.setArg(0, aXmm); + cc.setArg(1, bXmm); + cc.paddw(aXmm, bXmm); + cc.ret(aXmm); + cc.endFunc(); + } + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef void (*Func)(const void*, const void*, const void*, const void*, void*); + + Func func = ptr_as_func(_func); + + int16_t a[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + int16_t b[8] = { 7, 6, 5, 4, 3, 2, 1, 0 }; + int16_t c[8] = { 1, 3, 9, 7, 5, 4, 2, 1 }; + int16_t d[8] = { 2, 0,-6,-4,-2,-1, 1, 2 }; + + int16_t o[8]; + int oExp = 7 * 3; + + func(a, b, c, d, o); + + result.assignFormat("ret={%02X %02X %02X %02X %02X %02X %02X %02X}", o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7]); + expect.assignFormat("ret={%02X %02X %02X %02X %02X %02X %02X %02X}", oExp, oExp, oExp, oExp, oExp, oExp, oExp, oExp); + + return result == expect; + } +}; + +// ============================================================================ +// [X86Test_FuncCallManyArgs] +// ============================================================================ + +class X86Test_FuncCallManyArgs : public X86TestCase { +public: + X86Test_FuncCallManyArgs() : X86TestCase("FuncCallManyArgs") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallManyArgs()); + } + + static int calledFunc(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { + return (a * b * c * d * e) + (f * g * h * i * j); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + // Prepare. + x86::Gp va = cc.newInt32("va"); + x86::Gp vb = cc.newInt32("vb"); + x86::Gp vc = cc.newInt32("vc"); + x86::Gp vd = cc.newInt32("vd"); + x86::Gp ve = cc.newInt32("ve"); + x86::Gp vf = cc.newInt32("vf"); + x86::Gp vg = cc.newInt32("vg"); + x86::Gp vh = cc.newInt32("vh"); + x86::Gp vi = cc.newInt32("vi"); + x86::Gp vj = cc.newInt32("vj"); + + cc.mov(va, 0x03); + cc.mov(vb, 0x12); + cc.mov(vc, 0xA0); + cc.mov(vd, 0x0B); + cc.mov(ve, 0x2F); + cc.mov(vf, 0x02); + cc.mov(vg, 0x0C); + cc.mov(vh, 0x12); + cc.mov(vi, 0x18); + cc.mov(vj, 0x1E); + + // Function call. + InvokeNode* invokeNode; + cc.invoke(&invokeNode, + imm((void*)calledFunc), + FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, va); + invokeNode->setArg(1, vb); + invokeNode->setArg(2, vc); + invokeNode->setArg(3, vd); + invokeNode->setArg(4, ve); + invokeNode->setArg(5, vf); + invokeNode->setArg(6, vg); + invokeNode->setArg(7, vh); + invokeNode->setArg(8, vi); + invokeNode->setArg(9, vj); + invokeNode->setRet(0, va); + + cc.ret(va); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(void); + Func func = ptr_as_func(_func); + + int resultRet = func(); + int expectRet = calledFunc(0x03, 0x12, 0xA0, 0x0B, 0x2F, 0x02, 0x0C, 0x12, 0x18, 0x1E); + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_FuncCallDuplicateArgs] +// ============================================================================ + +class X86Test_FuncCallDuplicateArgs : public X86TestCase { +public: + X86Test_FuncCallDuplicateArgs() : X86TestCase("FuncCallDuplicateArgs") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallDuplicateArgs()); + } + + static int calledFunc(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { + return (a * b * c * d * e) + (f * g * h * i * j); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + // Prepare. + x86::Gp a = cc.newInt32("a"); + cc.mov(a, 3); + + // Call function. + InvokeNode* invokeNode; + cc.invoke(&invokeNode, + imm((void*)calledFunc), + FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, a); + invokeNode->setArg(1, a); + invokeNode->setArg(2, a); + invokeNode->setArg(3, a); + invokeNode->setArg(4, a); + invokeNode->setArg(5, a); + invokeNode->setArg(6, a); + invokeNode->setArg(7, a); + invokeNode->setArg(8, a); + invokeNode->setArg(9, a); + invokeNode->setRet(0, a); + + cc.ret(a); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(void); + Func func = ptr_as_func(_func); + + int resultRet = func(); + int expectRet = calledFunc(3, 3, 3, 3, 3, 3, 3, 3, 3, 3); + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_FuncCallImmArgs] +// ============================================================================ + +class X86Test_FuncCallImmArgs : public X86TestCase { +public: + X86Test_FuncCallImmArgs() : X86TestCase("FuncCallImmArgs") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallImmArgs()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + // Prepare. + x86::Gp rv = cc.newInt32("rv"); + + // Call function. + InvokeNode* invokeNode; + cc.invoke(&invokeNode, + imm((void*)X86Test_FuncCallManyArgs::calledFunc), + FuncSignatureT(CallConv::kIdHost)); + + invokeNode->setArg(0, imm(0x03)); + invokeNode->setArg(1, imm(0x12)); + invokeNode->setArg(2, imm(0xA0)); + invokeNode->setArg(3, imm(0x0B)); + invokeNode->setArg(4, imm(0x2F)); + invokeNode->setArg(5, imm(0x02)); + invokeNode->setArg(6, imm(0x0C)); + invokeNode->setArg(7, imm(0x12)); + invokeNode->setArg(8, imm(0x18)); + invokeNode->setArg(9, imm(0x1E)); + invokeNode->setRet(0, rv); + + cc.ret(rv); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(void); + Func func = ptr_as_func(_func); + + int resultRet = func(); + int expectRet = X86Test_FuncCallManyArgs::calledFunc(0x03, 0x12, 0xA0, 0x0B, 0x2F, 0x02, 0x0C, 0x12, 0x18, 0x1E); + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_FuncCallPtrArgs] +// ============================================================================ + +class X86Test_FuncCallPtrArgs : public X86TestCase { +public: + X86Test_FuncCallPtrArgs() : X86TestCase("FuncCallPtrArgs") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallPtrArgs()); + } + + static int calledFunc(void* a, void* b, void* c, void* d, void* e, void* f, void* g, void* h, void* i, void* j) { + return int((intptr_t)a) + + int((intptr_t)b) + + int((intptr_t)c) + + int((intptr_t)d) + + int((intptr_t)e) + + int((intptr_t)f) + + int((intptr_t)g) + + int((intptr_t)h) + + int((intptr_t)i) + + int((intptr_t)j) ; + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + // Prepare. + x86::Gp rv = cc.newInt32("rv"); + + // Call function. + InvokeNode* invokeNode; + cc.invoke(&invokeNode, + imm((void*)calledFunc), + FuncSignatureT(CallConv::kIdHost)); + + invokeNode->setArg(0, imm(0x01)); + invokeNode->setArg(1, imm(0x02)); + invokeNode->setArg(2, imm(0x03)); + invokeNode->setArg(3, imm(0x04)); + invokeNode->setArg(4, imm(0x05)); + invokeNode->setArg(5, imm(0x06)); + invokeNode->setArg(6, imm(0x07)); + invokeNode->setArg(7, imm(0x08)); + invokeNode->setArg(8, imm(0x09)); + invokeNode->setArg(9, imm(0x0A)); + invokeNode->setRet(0, rv); + + cc.ret(rv); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(void); + Func func = ptr_as_func(_func); + + int resultRet = func(); + int expectRet = 55; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_FuncCallRefArgs] +// ============================================================================ + +class X86Test_FuncCallRefArgs : public X86TestCase { +public: + X86Test_FuncCallRefArgs() : X86TestCase("FuncCallRefArgs") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallRefArgs()); + } + + static int calledFunc(int& a, int& b, int& c, int& d) { + a += a; + b += b; + c += c; + d += d; + return a + b + c + d; + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + // Prepare. + x86::Gp arg1 = cc.newInt32(); + x86::Gp arg2 = cc.newInt32(); + x86::Gp arg3 = cc.newInt32(); + x86::Gp arg4 = cc.newInt32(); + x86::Gp rv = cc.newInt32("rv"); + + cc.setArg(0, arg1); + cc.setArg(1, arg2); + cc.setArg(2, arg3); + cc.setArg(3, arg4); + + // Call function. + InvokeNode* invokeNode; + cc.invoke(&invokeNode, + imm((void*)calledFunc), + FuncSignatureT(CallConv::kIdHost)); + + invokeNode->setArg(0, arg1); + invokeNode->setArg(1, arg2); + invokeNode->setArg(2, arg3); + invokeNode->setArg(3, arg4); + invokeNode->setRet(0, rv); + + cc.ret(rv); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int&, int&, int&, int&); + Func func = ptr_as_func(_func); + + int inputs[4] = { 1, 2, 3, 4 }; + int outputs[4] = { 2, 4, 6, 8 }; + int resultRet = func(inputs[0], inputs[1], inputs[2], inputs[3]); + int expectRet = 20; + + result.assignFormat("ret={%08X %08X %08X %08X %08X}", resultRet, inputs[0], inputs[1], inputs[2], inputs[3]); + expect.assignFormat("ret={%08X %08X %08X %08X %08X}", expectRet, outputs[0], outputs[1], outputs[2], outputs[3]); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_FuncCallFloatAsXmmRet] +// ============================================================================ + +class X86Test_FuncCallFloatAsXmmRet : public X86TestCase { +public: + X86Test_FuncCallFloatAsXmmRet() : X86TestCase("FuncCallFloatAsXmmRet") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallFloatAsXmmRet()); + } + + static float calledFunc(float a, float b) { + return a * b; + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Xmm a = cc.newXmmSs("a"); + x86::Xmm b = cc.newXmmSs("b"); + x86::Xmm ret = cc.newXmmSs("ret"); + + cc.setArg(0, a); + cc.setArg(1, b); + + // Call function. + InvokeNode* invokeNode; + cc.invoke(&invokeNode, imm((void*)calledFunc), FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, a); + invokeNode->setArg(1, b); + invokeNode->setRet(0, ret); + + cc.ret(ret); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef float (*Func)(float, float); + Func func = ptr_as_func(_func); + + float resultRet = func(15.5f, 2.0f); + float expectRet = calledFunc(15.5f, 2.0f); + + result.assignFormat("ret=%g", resultRet); + expect.assignFormat("ret=%g", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_FuncCallDoubleAsXmmRet] +// ============================================================================ + +class X86Test_FuncCallDoubleAsXmmRet : public X86TestCase { +public: + X86Test_FuncCallDoubleAsXmmRet() : X86TestCase("FuncCallDoubleAsXmmRet") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallDoubleAsXmmRet()); + } + + static double calledFunc(double a, double b) { + return a * b; + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Xmm a = cc.newXmmSd("a"); + x86::Xmm b = cc.newXmmSd("b"); + x86::Xmm ret = cc.newXmmSd("ret"); + + cc.setArg(0, a); + cc.setArg(1, b); + + InvokeNode* invokeNode; + cc.invoke(&invokeNode, imm((void*)calledFunc), FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, a); + invokeNode->setArg(1, b); + invokeNode->setRet(0, ret); + + cc.ret(ret); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef double (*Func)(double, double); + Func func = ptr_as_func(_func); + + double resultRet = func(15.5, 2.0); + double expectRet = calledFunc(15.5, 2.0); + + result.assignFormat("ret=%g", resultRet); + expect.assignFormat("ret=%g", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_FuncCallConditional] +// ============================================================================ + +class X86Test_FuncCallConditional : public X86TestCase { +public: + X86Test_FuncCallConditional() : X86TestCase("FuncCallConditional") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallConditional()); + } + + virtual void compile(x86::Compiler& cc) { + x86::Gp x = cc.newInt32("x"); + x86::Gp y = cc.newInt32("y"); + x86::Gp op = cc.newInt32("op"); + + InvokeNode* invokeNode; + x86::Gp result; + + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + cc.setArg(0, x); + cc.setArg(1, y); + cc.setArg(2, op); + + Label opAdd = cc.newLabel(); + Label opMul = cc.newLabel(); + + cc.cmp(op, 0); + cc.jz(opAdd); + cc.cmp(op, 1); + cc.jz(opMul); + + result = cc.newInt32("result_0"); + cc.mov(result, 0); + cc.ret(result); + + cc.bind(opAdd); + result = cc.newInt32("result_1"); + + cc.invoke(&invokeNode, (uint64_t)calledFuncAdd, FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, x); + invokeNode->setArg(1, y); + invokeNode->setRet(0, result); + cc.ret(result); + + cc.bind(opMul); + result = cc.newInt32("result_2"); + + cc.invoke(&invokeNode, (uint64_t)calledFuncMul, FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, x); + invokeNode->setArg(1, y); + invokeNode->setRet(0, result); + + cc.ret(result); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int, int); + Func func = ptr_as_func(_func); + + int arg1 = 4; + int arg2 = 8; + + int resultAdd = func(arg1, arg2, 0); + int expectAdd = calledFuncAdd(arg1, arg2); + + int resultMul = func(arg1, arg2, 1); + int expectMul = calledFuncMul(arg1, arg2); + + result.assignFormat("ret={add=%d, mul=%d}", resultAdd, resultMul); + expect.assignFormat("ret={add=%d, mul=%d}", expectAdd, expectMul); + + return (resultAdd == expectAdd) && (resultMul == expectMul); + } + + static int calledFuncAdd(int x, int y) { return x + y; } + static int calledFuncMul(int x, int y) { return x * y; } +}; + +// ============================================================================ +// [X86Test_FuncCallMultiple] +// ============================================================================ + +class X86Test_FuncCallMultiple : public X86TestCase { +public: + X86Test_FuncCallMultiple() : X86TestCase("FuncCallMultiple") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallMultiple()); + } + + static int ASMJIT_FASTCALL calledFunc(int* pInt, int index) { + return pInt[index]; + } + + virtual void compile(x86::Compiler& cc) { + unsigned int i; + + x86::Gp buf = cc.newIntPtr("buf"); + x86::Gp acc0 = cc.newInt32("acc0"); + x86::Gp acc1 = cc.newInt32("acc1"); + + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + cc.setArg(0, buf); + + cc.mov(acc0, 0); + cc.mov(acc1, 0); + + for (i = 0; i < 4; i++) { + x86::Gp ret = cc.newInt32("ret"); + x86::Gp ptr = cc.newIntPtr("ptr"); + x86::Gp idx = cc.newInt32("idx"); + InvokeNode* invokeNode; + + cc.mov(ptr, buf); + cc.mov(idx, int(i)); + + cc.invoke(&invokeNode, (uint64_t)calledFunc, FuncSignatureT(CallConv::kIdFastCall)); + invokeNode->setArg(0, ptr); + invokeNode->setArg(1, idx); + invokeNode->setRet(0, ret); + + cc.add(acc0, ret); + + cc.mov(ptr, buf); + cc.mov(idx, int(i)); + + cc.invoke(&invokeNode, (uint64_t)calledFunc, FuncSignatureT(CallConv::kIdFastCall)); + invokeNode->setArg(0, ptr); + invokeNode->setArg(1, idx); + invokeNode->setRet(0, ret); + + cc.sub(acc1, ret); + } + + cc.add(acc0, acc1); + cc.ret(acc0); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int*); + Func func = ptr_as_func(_func); + + int buffer[4] = { 127, 87, 23, 17 }; + + int resultRet = func(buffer); + int expectRet = 0; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_FuncCallRecursive] +// ============================================================================ + +class X86Test_FuncCallRecursive : public X86TestCase { +public: + X86Test_FuncCallRecursive() : X86TestCase("FuncCallRecursive") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallRecursive()); + } + + virtual void compile(x86::Compiler& cc) { + x86::Gp val = cc.newInt32("val"); + Label skip = cc.newLabel(); + + FuncNode* func = cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + cc.setArg(0, val); + + cc.cmp(val, 1); + cc.jle(skip); + + x86::Gp tmp = cc.newInt32("tmp"); + cc.mov(tmp, val); + cc.dec(tmp); + + InvokeNode* invokeNode; + + cc.invoke(&invokeNode, func->label(), FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, tmp); + invokeNode->setRet(0, tmp); + cc.mul(cc.newInt32(), val, tmp); + + cc.bind(skip); + cc.ret(val); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int); + Func func = ptr_as_func(_func); + + int resultRet = func(5); + int expectRet = 1 * 2 * 3 * 4 * 5; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_FuncCallVarArg1] +// ============================================================================ + +class X86Test_FuncCallVarArg1 : public X86TestCase { +public: + X86Test_FuncCallVarArg1() : X86TestCase("FuncCallVarArg1") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallVarArg1()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp a0 = cc.newInt32("a0"); + x86::Gp a1 = cc.newInt32("a1"); + x86::Gp a2 = cc.newInt32("a2"); + x86::Gp a3 = cc.newInt32("a3"); + + cc.setArg(0, a0); + cc.setArg(1, a1); + cc.setArg(2, a2); + cc.setArg(3, a3); + + // We call `int func(size_t, ...)` + // - The `vaIndex` must be 1 (first argument after size_t). + // - The full signature of varargs (int, int, int, int) must follow. + InvokeNode* invokeNode; + cc.invoke(&invokeNode, + imm((void*)calledFunc), + FuncSignatureT(CallConv::kIdHost, 1)); + invokeNode->setArg(0, imm(4)); + invokeNode->setArg(1, a0); + invokeNode->setArg(2, a1); + invokeNode->setArg(3, a2); + invokeNode->setArg(4, a3); + invokeNode->setRet(0, a0); + + cc.ret(a0); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int, int, int); + Func func = ptr_as_func(_func); + + int resultRet = func(1, 2, 3, 4); + int expectRet = 1 + 2 + 3 + 4; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } + + static int calledFunc(size_t n, ...) { + int sum = 0; + va_list ap; + va_start(ap, n); + for (size_t i = 0; i < n; i++) { + int arg = va_arg(ap, int); + sum += arg; + } + va_end(ap); + return sum; + } +}; + +// ============================================================================ +// [X86Test_FuncCallVarArg2] +// ============================================================================ + +class X86Test_FuncCallVarArg2 : public X86TestCase { +public: + X86Test_FuncCallVarArg2() : X86TestCase("FuncCallVarArg2") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallVarArg2()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Xmm a0 = cc.newXmmSd("a0"); + x86::Xmm a1 = cc.newXmmSd("a1"); + x86::Xmm a2 = cc.newXmmSd("a2"); + x86::Xmm a3 = cc.newXmmSd("a3"); + + cc.setArg(0, a0); + cc.setArg(1, a1); + cc.setArg(2, a2); + cc.setArg(3, a3); + + // We call `double func(size_t, ...)` + // - The `vaIndex` must be 1 (first argument after size_t). + // - The full signature of varargs (double, double, double, double) must follow. + InvokeNode* invokeNode; + cc.invoke(&invokeNode, + imm((void*)calledFunc), + FuncSignatureT(CallConv::kIdHost, 1)); + invokeNode->setArg(0, imm(4)); + invokeNode->setArg(1, a0); + invokeNode->setArg(2, a1); + invokeNode->setArg(3, a2); + invokeNode->setArg(4, a3); + invokeNode->setRet(0, a0); + + cc.ret(a0); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef double (*Func)(double, double, double, double); + Func func = ptr_as_func(_func); + + double resultRet = func(1.0, 2.0, 3.0, 4.0); + double expectRet = 1.0 + 2.0 + 3.0 + 4.0; + + result.assignFormat("ret=%f", resultRet); + expect.assignFormat("ret=%f", expectRet); + + return resultRet == expectRet; + } + + static double calledFunc(size_t n, ...) { + double sum = 0; + va_list ap; + va_start(ap, n); + for (size_t i = 0; i < n; i++) { + double arg = va_arg(ap, double); + sum += arg; + } + va_end(ap); + return sum; + } +}; + +// ============================================================================ +// [X86Test_FuncCallInt64Arg] +// ============================================================================ + +class X86Test_FuncCallInt64Arg : public X86TestCase { +public: + X86Test_FuncCallInt64Arg() : X86TestCase("FuncCallInt64Arg") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallInt64Arg()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + if (cc.is64Bit()) { + x86::Gp reg = cc.newUInt64(); + cc.setArg(0, reg); + cc.add(reg, 1); + cc.ret(reg); + } + else { + x86::Gp hi = cc.newUInt32("hi"); + x86::Gp lo = cc.newUInt32("lo"); + + cc.setArg(0, 0, lo); + cc.setArg(0, 1, hi); + + cc.add(lo, 1); + cc.adc(hi, 0); + cc.ret(lo, hi); + } + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef uint64_t (*Func)(uint64_t); + Func func = ptr_as_func(_func); + + uint64_t resultRet = func(uint64_t(0xFFFFFFFF)); + uint64_t expectRet = 0x100000000; + + result.assignFormat("ret=%llu", (unsigned long long)resultRet); + expect.assignFormat("ret=%llu", (unsigned long long)expectRet); + + return resultRet == expectRet; + } + + static double calledFunc(size_t n, ...) { + double sum = 0; + va_list ap; + va_start(ap, n); + for (size_t i = 0; i < n; i++) { + double arg = va_arg(ap, double); + sum += arg; + } + va_end(ap); + return sum; + } +}; + +// ============================================================================ +// [X86Test_FuncCallMisc1] +// ============================================================================ + +class X86Test_FuncCallMisc1 : public X86TestCase { +public: + X86Test_FuncCallMisc1() : X86TestCase("FuncCallMisc1") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallMisc1()); + } + + static void dummy(int, int) {} + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp a = cc.newInt32("a"); + x86::Gp b = cc.newInt32("b"); + x86::Gp r = cc.newInt32("r"); + + cc.setArg(0, a); + cc.setArg(1, b); + + InvokeNode* invokeNode; + cc.invoke(&invokeNode, + imm((void*)dummy), + FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, a); + invokeNode->setArg(1, b); + + cc.lea(r, x86::ptr(a, b)); + cc.ret(r); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int); + Func func = ptr_as_func(_func); + + int resultRet = func(44, 199); + int expectRet = 243; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_FuncCallMisc2] +// ============================================================================ + +class X86Test_FuncCallMisc2 : public X86TestCase { +public: + X86Test_FuncCallMisc2() : X86TestCase("FuncCallMisc2") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallMisc2()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp p = cc.newIntPtr("p"); + x86::Xmm arg = cc.newXmmSd("arg"); + x86::Xmm ret = cc.newXmmSd("ret"); + + cc.setArg(0, p); + cc.movsd(arg, x86::ptr(p)); + + InvokeNode* invokeNode; + cc.invoke(&invokeNode, + imm((void*)op), + FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, arg); + invokeNode->setRet(0, ret); + + cc.ret(ret); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef double (*Func)(const double*); + Func func = ptr_as_func(_func); + + double arg = 2; + + double resultRet = func(&arg); + double expectRet = op(arg); + + result.assignFormat("ret=%g", resultRet); + expect.assignFormat("ret=%g", expectRet); + + return resultRet == expectRet; + } + + static double op(double a) { return a * a; } +}; + +// ============================================================================ +// [X86Test_FuncCallMisc3] +// ============================================================================ + +class X86Test_FuncCallMisc3 : public X86TestCase { +public: + X86Test_FuncCallMisc3() : X86TestCase("FuncCallMisc3") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallMisc3()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp p = cc.newIntPtr("p"); + x86::Xmm arg = cc.newXmmSd("arg"); + x86::Xmm ret = cc.newXmmSd("ret"); + + cc.setArg(0, p); + cc.movsd(arg, x86::ptr(p)); + + InvokeNode* invokeNode; + cc.invoke(&invokeNode, + imm((void*)op), + FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, arg); + invokeNode->setRet(0, ret); + + cc.xorps(arg, arg); + cc.subsd(arg, ret); + + cc.ret(arg); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef double (*Func)(const double*); + Func func = ptr_as_func(_func); + + double arg = 2; + + double resultRet = func(&arg); + double expectRet = -op(arg); + + result.assignFormat("ret=%g", resultRet); + expect.assignFormat("ret=%g", expectRet); + + return resultRet == expectRet; + } + + static double op(double a) { return a * a; } +}; + +// ============================================================================ +// [X86Test_FuncCallMisc4] +// ============================================================================ + +class X86Test_FuncCallMisc4 : public X86TestCase { +public: + X86Test_FuncCallMisc4() : X86TestCase("FuncCallMisc4") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallMisc4()); + } + + virtual void compile(x86::Compiler& cc) { + InvokeNode* invokeNode; + + FuncSignatureBuilder funcSignature; + funcSignature.setCallConv(CallConv::kIdHost); + funcSignature.setRet(Type::kIdF64); + cc.addFunc(funcSignature); + + FuncSignatureBuilder invokeSignature; + invokeSignature.setCallConv(CallConv::kIdHost); + invokeSignature.setRet(Type::kIdF64); + + cc.invoke(&invokeNode, imm((void*)calledFunc), invokeSignature); + x86::Xmm ret = cc.newXmmSd("ret"); + invokeNode->setRet(0, ret); + cc.ret(ret); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef double (*Func)(void); + Func func = ptr_as_func(_func); + + double resultRet = func(); + double expectRet = 3.14; + + result.assignFormat("ret=%g", resultRet); + expect.assignFormat("ret=%g", expectRet); + + return resultRet == expectRet; + } + + static double calledFunc() { return 3.14; } +}; + +// ============================================================================ +// [X86Test_FuncCallMisc5] +// ============================================================================ + +// The register allocator should clobber the register used by the `call` itself. +class X86Test_FuncCallMisc5 : public X86TestCase { +public: + X86Test_FuncCallMisc5() : X86TestCase("FuncCallMisc5") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallMisc5()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp pFn = cc.newIntPtr("pFn"); + x86::Gp vars[16]; + + uint32_t i, regCount = cc.arch() == Environment::kArchX86 ? 8 : 16; + ASMJIT_ASSERT(regCount <= ASMJIT_ARRAY_SIZE(vars)); + + cc.mov(pFn, imm((void*)calledFunc)); + + for (i = 0; i < regCount; i++) { + if (i == x86::Gp::kIdBp || i == x86::Gp::kIdSp) + continue; + + vars[i] = cc.newInt32("%%%u", unsigned(i)); + cc.mov(vars[i], 1); + } + + InvokeNode* invokeNode; + cc.invoke(&invokeNode, pFn, FuncSignatureT(CallConv::kIdHost)); + + for (i = 1; i < regCount; i++) + if (vars[i].isValid()) + cc.add(vars[0], vars[i]); + cc.ret(vars[0]); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(void); + Func func = ptr_as_func(_func); + + int resultRet = func(); + int expectRet = sizeof(void*) == 4 ? 6 : 14; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } + + static void calledFunc() {} +}; + +// ============================================================================ +// [X86Test_FuncCallMisc6] +// ============================================================================ + +class X86Test_FuncCallMisc6 : public X86TestCase { +public: + X86Test_FuncCallMisc6() : X86TestCase("FuncCallMisc6") {} + + static void add(TestApp& app) { + app.add(new X86Test_FuncCallMisc6()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + constexpr uint32_t kCount = 16; + + x86::Gp v[kCount]; + x86::Gp argVal = cc.newUInt32("argVal"); + x86::Gp retVal = cc.newUInt32("retVal"); + uint32_t i; + + cc.setArg(0, argVal); + cc.add(argVal, 1); + + for (i = 0; i < kCount; i++) + v[i] = cc.newUInt32("v%u", i); + + InvokeNode* invokeNode; + cc.invoke(&invokeNode, imm((void*)calledFunc), FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, argVal); + invokeNode->setRet(0, retVal); + + for (i = 0; i < kCount; i++) + cc.mov(v[i], i + 1); + + for (i = 0; i < kCount; i++) + cc.add(argVal, v[i]); + + cc.add(retVal, argVal); + cc.ret(retVal); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef uint32_t (*Func)(uint32_t x); + Func func = ptr_as_func(_func); + + uint32_t resultRet = func(111); + uint32_t expectRet = 111 + 112 + 2 + (1 + 16) * 8; + + result.assignFormat("ret=%u", resultRet); + expect.assignFormat("ret=%u", expectRet); + + return resultRet == expectRet; + } + + static uint32_t calledFunc(uint32_t x) { return x + 1; } +}; + +// ============================================================================ +// [X86Test_MiscLocalConstPool] +// ============================================================================ + +class X86Test_MiscLocalConstPool : public X86TestCase { +public: + X86Test_MiscLocalConstPool() : X86TestCase("MiscLocalConstPool") {} + + static void add(TestApp& app) { + app.add(new X86Test_MiscLocalConstPool()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp v0 = cc.newInt32("v0"); + x86::Gp v1 = cc.newInt32("v1"); + + x86::Mem c0 = cc.newInt32Const(ConstPool::kScopeLocal, 200); + x86::Mem c1 = cc.newInt32Const(ConstPool::kScopeLocal, 33); + + cc.mov(v0, c0); + cc.mov(v1, c1); + cc.add(v0, v1); + + cc.ret(v0); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(void); + Func func = ptr_as_func(_func); + + int resultRet = func(); + int expectRet = 233; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_MiscGlobalConstPool] +// ============================================================================ + +class X86Test_MiscGlobalConstPool : public X86TestCase { +public: + X86Test_MiscGlobalConstPool() : X86TestCase("MiscGlobalConstPool") {} + + static void add(TestApp& app) { + app.add(new X86Test_MiscGlobalConstPool()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp v0 = cc.newInt32("v0"); + x86::Gp v1 = cc.newInt32("v1"); + + x86::Mem c0 = cc.newInt32Const(ConstPool::kScopeGlobal, 200); + x86::Mem c1 = cc.newInt32Const(ConstPool::kScopeGlobal, 33); + + cc.mov(v0, c0); + cc.mov(v1, c1); + cc.add(v0, v1); + + cc.ret(v0); + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(void); + Func func = ptr_as_func(_func); + + int resultRet = func(); + int expectRet = 233; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return resultRet == expectRet; + } +}; + +// ============================================================================ +// [X86Test_MiscMultiRet] +// ============================================================================ + +struct X86Test_MiscMultiRet : public X86TestCase { + X86Test_MiscMultiRet() : X86TestCase("MiscMultiRet") {} + + static void add(TestApp& app) { + app.add(new X86Test_MiscMultiRet()); + } + + virtual void compile(x86::Compiler& cc) { + cc.addFunc(FuncSignatureT(CallConv::kIdHost)); + + x86::Gp op = cc.newInt32("op"); + x86::Gp a = cc.newInt32("a"); + x86::Gp b = cc.newInt32("b"); + + Label L_Zero = cc.newLabel(); + Label L_Add = cc.newLabel(); + Label L_Sub = cc.newLabel(); + Label L_Mul = cc.newLabel(); + Label L_Div = cc.newLabel(); + + cc.setArg(0, op); + cc.setArg(1, a); + cc.setArg(2, b); + + cc.cmp(op, 0); + cc.jz(L_Add); + + cc.cmp(op, 1); + cc.jz(L_Sub); + + cc.cmp(op, 2); + cc.jz(L_Mul); + + cc.cmp(op, 3); + cc.jz(L_Div); + + cc.bind(L_Zero); + cc.xor_(a, a); + cc.ret(a); + + cc.bind(L_Add); + cc.add(a, b); + cc.ret(a); + + cc.bind(L_Sub); + cc.sub(a, b); + cc.ret(a); + + cc.bind(L_Mul); + cc.imul(a, b); + cc.ret(a); + + cc.bind(L_Div); + cc.cmp(b, 0); + cc.jz(L_Zero); + + x86::Gp zero = cc.newInt32("zero"); + cc.xor_(zero, zero); + cc.idiv(zero, a, b); + cc.ret(a); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int, int); + + Func func = ptr_as_func(_func); + + int a = 44; + int b = 3; + + int r0 = func(0, a, b); + int r1 = func(1, a, b); + int r2 = func(2, a, b); + int r3 = func(3, a, b); + int e0 = a + b; + int e1 = a - b; + int e2 = a * b; + int e3 = a / b; + + result.assignFormat("ret={%d %d %d %d}", r0, r1, r2, r3); + expect.assignFormat("ret={%d %d %d %d}", e0, e1, e2, e3); + + return result.eq(expect); + } +}; + +// ============================================================================ +// [X86Test_MiscMultiFunc] +// ============================================================================ + +class X86Test_MiscMultiFunc : public X86TestCase { +public: + X86Test_MiscMultiFunc() : X86TestCase("MiscMultiFunc") {} + + static void add(TestApp& app) { + app.add(new X86Test_MiscMultiFunc()); + } + + virtual void compile(x86::Compiler& cc) { + FuncNode* f1 = cc.newFunc(FuncSignatureT(CallConv::kIdHost)); + FuncNode* f2 = cc.newFunc(FuncSignatureT(CallConv::kIdHost)); + + { + x86::Gp a = cc.newInt32("a"); + x86::Gp b = cc.newInt32("b"); + + cc.addFunc(f1); + cc.setArg(0, a); + cc.setArg(1, b); + + InvokeNode* invokeNode; + cc.invoke(&invokeNode, f2->label(), FuncSignatureT(CallConv::kIdHost)); + invokeNode->setArg(0, a); + invokeNode->setArg(1, b); + invokeNode->setRet(0, a); + + cc.ret(a); + cc.endFunc(); + } + + { + x86::Gp a = cc.newInt32("a"); + x86::Gp b = cc.newInt32("b"); + + cc.addFunc(f2); + cc.setArg(0, a); + cc.setArg(1, b); + + cc.add(a, b); + cc.ret(a); + cc.endFunc(); + } + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (*Func)(int, int); + + Func func = ptr_as_func(_func); + + int resultRet = func(56, 22); + int expectRet = 56 + 22; + + result.assignFormat("ret=%d", resultRet); + expect.assignFormat("ret=%d", expectRet); + + return result.eq(expect); + } +}; + +// ============================================================================ +// [X86Test_MiscUnfollow] +// ============================================================================ + +// Global (I didn't find a better way to test this). +static jmp_buf globalJmpBuf; + +class X86Test_MiscUnfollow : public X86TestCase { +public: + X86Test_MiscUnfollow() : X86TestCase("MiscUnfollow") {} + + static void add(TestApp& app) { + app.add(new X86Test_MiscUnfollow()); + } + + virtual void compile(x86::Compiler& cc) { + // NOTE: Fastcall calling convention is the most appropriate here, as all + // arguments will be passed by registers and there won't be any stack + // misalignment when we call the `handler()`. This was failing on OSX + // when targeting 32-bit. + cc.addFunc(FuncSignatureT(CallConv::kIdFastCall)); + + x86::Gp a = cc.newInt32("a"); + x86::Gp b = cc.newIntPtr("b"); + Label tramp = cc.newLabel(); + + cc.setArg(0, a); + cc.setArg(1, b); + + cc.cmp(a, 0); + cc.jz(tramp); + + cc.ret(a); + + cc.bind(tramp); + cc.unfollow().jmp(b); + + cc.endFunc(); + } + + virtual bool run(void* _func, String& result, String& expect) { + typedef int (ASMJIT_FASTCALL *Func)(int, void*); + + Func func = ptr_as_func(_func); + + int resultRet = 0; + int expectRet = 1; + + if (!setjmp(globalJmpBuf)) + resultRet = func(0, (void*)handler); + else + resultRet = 1; + + result.assignFormat("ret={%d}", resultRet); + expect.assignFormat("ret={%d}", expectRet); + + return resultRet == expectRet; + } + + static void ASMJIT_FASTCALL handler() { longjmp(globalJmpBuf, 1); } +}; + +// ============================================================================ +// [Export] +// ============================================================================ + +void compiler_add_x86_tests(TestApp& app) { + // Base tests. + app.addT(); + app.addT(); + app.addT(); + + // Jump tests. + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + + // Alloc tests. + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + + // Function call tests. + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); + + // Miscellaneous tests. + app.addT(); + app.addT(); + app.addT(); + app.addT(); + app.addT(); +} + +#endif diff --git a/test/asmjit_test_opcode.cpp b/test/asmjit_test_opcode.cpp index d61a6e4..939cb78 100644 --- a/test/asmjit_test_opcode.cpp +++ b/test/asmjit_test_opcode.cpp @@ -48,7 +48,7 @@ static const char* archToString(uint32_t arch) noexcept { case Environment::kArchARM : return "ARM"; case Environment::kArchThumb : return "Thumb"; case Environment::kArchAArch64 : return "AArch64"; - case Environment::kArchMIPS_LE : return "MIPS"; + case Environment::kArchMIPS32_LE: return "MIPS32"; case Environment::kArchMIPS64_LE: return "MIPS64"; default: return "Unknown"; } diff --git a/test/asmjit_test_unit.cpp b/test/asmjit_test_unit.cpp index 3aac740..14b93cf 100644 --- a/test/asmjit_test_unit.cpp +++ b/test/asmjit_test_unit.cpp @@ -42,7 +42,7 @@ static const char* archToString(uint32_t arch) noexcept { case Environment::kArchARM : return "ARM"; case Environment::kArchThumb : return "Thumb"; case Environment::kArchAArch64 : return "AArch64"; - case Environment::kArchMIPS_LE : return "MIPS"; + case Environment::kArchMIPS32_LE: return "MIPS"; case Environment::kArchMIPS64_LE: return "MIPS64"; default: return "Unknown"; } diff --git a/test/asmjit_test_x86_instinfo.cpp b/test/asmjit_test_x86_instinfo.cpp index 75ab1c5..8f58263 100644 --- a/test/asmjit_test_x86_instinfo.cpp +++ b/test/asmjit_test_x86_instinfo.cpp @@ -79,6 +79,41 @@ static void printInfo(uint32_t arch, const BaseInst& inst, const Operand_* opera sb.append("\n"); } + if (rw.readFlags() | rw.writeFlags()) { + sb.append("Flags: \n"); + + struct FlagMap { + uint32_t flag; + char name[4]; + }; + + static const FlagMap flagMap[] = { + { x86::Status::kCF, "CF" }, + { x86::Status::kOF, "OF" }, + { x86::Status::kSF, "SF" }, + { x86::Status::kZF, "ZF" }, + { x86::Status::kAF, "AF" }, + { x86::Status::kPF, "PF" }, + { x86::Status::kDF, "DF" }, + { x86::Status::kIF, "IF" }, + { x86::Status::kAC, "AC" }, + { x86::Status::kC0, "C0" }, + { x86::Status::kC1, "C1" }, + { x86::Status::kC2, "C2" }, + { x86::Status::kC3, "C3" } + }; + + sb.append(" "); + for (uint32_t f = 0; f < 13; f++) { + char c = accessLetter((rw.readFlags() & flagMap[f].flag) != 0, + (rw.writeFlags() & flagMap[f].flag) != 0); + if (c != '_') + sb.appendFormat("%s=%c ", flagMap[f].name, c); + } + + sb.append("\n"); + } + // CPU Features // ------------ diff --git a/test/cmdline.h b/test/cmdline.h new file mode 100644 index 0000000..caafa6c --- /dev/null +++ b/test/cmdline.h @@ -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 +#include +#include + +// ============================================================================ +// [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 diff --git a/tools/ci-run.sh b/tools/ci-run.sh index 15e4245..e7d9278 100755 --- a/tools/ci-run.sh +++ b/tools/ci-run.sh @@ -33,7 +33,7 @@ if [ -f ${BUILD_DIR}/asmjit_test_x86_instinfo ]; then eval "$RUN_CMD ${BUILD_DIR}/asmjit_test_x86_instinfo" fi -if [ -f ${BUILD_DIR}asmjit_test_x86_cc ]; then +if [ -f ${BUILD_DIR}asmjit_test_compiler ]; then echo "" - eval "$RUN_CMD ${BUILD_DIR}/asmjit_test_x86_cc" + eval "$RUN_CMD ${BUILD_DIR}/asmjit_test_compiler" fi