Commit 04042076 authored by Andreas Krämer's avatar Andreas Krämer
Browse files

Merge branch 'master' into charmmfixes

parents d59f19e4 2ff59259
......@@ -7,73 +7,668 @@
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
// [Dependencies]
#include "../base/cpuinfo.h"
#include "../base/utils.h"
#if defined(ASMJIT_ARCH_X86) || defined(ASMJIT_ARCH_X64)
#include "../x86/x86cpuinfo.h"
#else
// ?
#endif
// [Dependencies - Posix]
#if defined(ASMJIT_OS_POSIX)
#if ASMJIT_OS_POSIX
# include <errno.h>
# include <sys/statvfs.h>
# include <sys/utsname.h>
# include <unistd.h>
#endif // ASMJIT_OS_POSIX
#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
# if ASMJIT_CC_MSC_GE(14, 0, 0)
# include <intrin.h> // Required by `__cpuid()` and `_xgetbv()`.
# endif // _MSC_VER >= 1400
#endif
#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
# if ASMJIT_OS_LINUX
# include <sys/auxv.h> // Required by `getauxval()`.
# endif
#endif
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::CpuInfo - DetectHwThreadsCount]
// [asmjit::CpuInfo - Detect ARM]
// ============================================================================
uint32_t CpuInfo::detectHwThreadsCount() {
#if defined(ASMJIT_OS_WINDOWS)
// ARM information has to be retrieved by the OS (this is how ARM was designed).
#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
#if ASMJIT_ARCH_ARM32
static ASMJIT_INLINE void armPopulateBaselineA32Features(CpuInfo* cpuInfo) noexcept {
cpuInfo->_archInfo.init(ArchInfo::kTypeA32);
}
#endif // ASMJIT_ARCH_ARM32
#if ASMJIT_ARCH_ARM64
static ASMJIT_INLINE void armPopulateBaselineA64Features(CpuInfo* cpuInfo) noexcept {
cpuInfo->_archInfo.init(ArchInfo::kTypeA64);
// Thumb (including all variations) is supported on A64 (but not accessible from A64).
cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB);
cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB2);
// A64 is based on ARMv8 and newer.
cpuInfo->addFeature(CpuInfo::kArmFeatureV6);
cpuInfo->addFeature(CpuInfo::kArmFeatureV7);
cpuInfo->addFeature(CpuInfo::kArmFeatureV8);
// A64 comes with these features by default.
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2);
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv3);
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv4);
cpuInfo->addFeature(CpuInfo::kArmFeatureEDSP);
cpuInfo->addFeature(CpuInfo::kArmFeatureASIMD);
cpuInfo->addFeature(CpuInfo::kArmFeatureIDIVA);
cpuInfo->addFeature(CpuInfo::kArmFeatureIDIVT);
}
#endif // ASMJIT_ARCH_ARM64
#if ASMJIT_OS_WINDOWS
//! \internal
//!
//! Detect ARM CPU features on Windows.
//!
//! The detection is based on `IsProcessorFeaturePresent()` API call.
static ASMJIT_INLINE void armDetectCpuInfoOnWindows(CpuInfo* cpuInfo) noexcept {
#if ASMJIT_ARCH_ARM32
armPopulateBaselineA32Features(cpuInfo);
// Windows for ARM requires at least ARMv7 with DSP extensions.
cpuInfo->addFeature(CpuInfo::kArmFeatureV6);
cpuInfo->addFeature(CpuInfo::kArmFeatureV7);
cpuInfo->addFeature(CpuInfo::kArmFeatureEDSP);
// Windows for ARM requires VFPv3.
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2);
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv3);
// Windows for ARM requires and uses THUMB2.
cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB);
cpuInfo->addFeature(CpuInfo::kArmFeatureTHUMB2);
#else
armPopulateBaselineA64Features(cpuInfo);
#endif
// Windows for ARM requires ASIMD.
cpuInfo->addFeature(CpuInfo::kArmFeatureASIMD);
// Detect additional CPU features by calling `IsProcessorFeaturePresent()`.
struct WinPFPMapping {
uint32_t pfpId;
uint32_t featureId;
};
static const WinPFPMapping mapping[] = {
{ PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE , CpuInfo::kArmFeatureVFPv4 },
{ PF_ARM_VFP_32_REGISTERS_AVAILABLE , CpuInfo::kArmFeatureVFP_D32 },
{ PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE, CpuInfo::kArmFeatureIDIVT },
{ PF_ARM_64BIT_LOADSTORE_ATOMIC , CpuInfo::kArmFeatureAtomics64 }
};
for (uint32_t i = 0; i < ASMJIT_ARRAY_SIZE(mapping); i++)
if (::IsProcessorFeaturePresent(mapping[i].pfpId))
cpuInfo->addFeature(mapping[i].featureId);
}
#endif // ASMJIT_OS_WINDOWS
#if ASMJIT_OS_LINUX
struct LinuxHWCapMapping {
uint32_t hwcapMask;
uint32_t featureId;
};
static void armDetectHWCaps(CpuInfo* cpuInfo, unsigned long type, const LinuxHWCapMapping* mapping, size_t length) noexcept {
unsigned long mask = getauxval(type);
for (size_t i = 0; i < length; i++)
if ((mask & mapping[i].hwcapMask) == mapping[i].hwcapMask)
cpuInfo->addFeature(mapping[i].featureId);
}
//! \internal
//!
//! Detect ARM CPU features on Linux.
//!
//! The detection is based on `getauxval()`.
ASMJIT_FAVOR_SIZE static void armDetectCpuInfoOnLinux(CpuInfo* cpuInfo) noexcept {
#if ASMJIT_ARCH_ARM32
armPopulateBaselineA32Features(cpuInfo);
// `AT_HWCAP` provides ARMv7 (and less) related flags.
static const LinuxHWCapMapping hwCapMapping[] = {
{ /* HWCAP_VFP */ (1 << 6), CpuInfo::kArmFeatureVFPv2 },
{ /* HWCAP_EDSP */ (1 << 7), CpuInfo::kArmFeatureEDSP },
{ /* HWCAP_NEON */ (1 << 12), CpuInfo::kArmFeatureASIMD },
{ /* HWCAP_VFPv3 */ (1 << 13), CpuInfo::kArmFeatureVFPv3 },
{ /* HWCAP_VFPv4 */ (1 << 16), CpuInfo::kArmFeatureVFPv4 },
{ /* HWCAP_IDIVA */ (1 << 17), CpuInfo::kArmFeatureIDIVA },
{ /* HWCAP_IDIVT */ (1 << 18), CpuInfo::kArmFeatureIDIVT },
{ /* HWCAP_VFPD32 */ (1 << 19), CpuInfo::kArmFeatureVFP_D32 }
};
armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping));
// VFPv3 implies VFPv2.
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv3)) {
cpuInfo->addFeature(CpuInfo::kArmFeatureVFPv2);
}
// VFPv2 implies ARMv6.
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv2)) {
cpuInfo->addFeature(CpuInfo::kArmFeatureV6);
}
// VFPv3 or ASIMD implies ARMv7.
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureVFPv3) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureASIMD)) {
cpuInfo->addFeature(CpuInfo::kArmFeatureV7);
}
// `AT_HWCAP2` provides ARMv8+ related flags.
static const LinuxHWCapMapping hwCap2Mapping[] = {
{ /* HWCAP2_AES */ (1 << 0), CpuInfo::kArmFeatureAES },
{ /* HWCAP2_PMULL */ (1 << 1), CpuInfo::kArmFeaturePMULL },
{ /* HWCAP2_SHA1 */ (1 << 2), CpuInfo::kArmFeatureSHA1 },
{ /* HWCAP2_SHA2 */ (1 << 3), CpuInfo::kArmFeatureSHA256 },
{ /* HWCAP2_CRC32 */ (1 << 4), CpuInfo::kArmFeatureCRC32 }
};
armDetectHWCaps(cpuInfo, AT_HWCAP2, hwCap2Mapping, ASMJIT_ARRAY_SIZE(hwCap2Mapping));
if (cpuInfo->hasFeature(CpuInfo::kArmFeatureAES ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureCRC32 ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeaturePMULL ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureSHA1 ) ||
cpuInfo->hasFeature(CpuInfo::kArmFeatureSHA256)) {
cpuInfo->addFeature(CpuInfo::kArmFeatureV8);
}
#else
armPopulateBaselineA64Features(cpuInfo);
// `AT_HWCAP` provides ARMv8+ related flags.
static const LinuxHWCapMapping hwCapMapping[] = {
{ /* HWCAP_ASIMD */ (1 << 1), CpuInfo::kArmFeatureASIMD },
{ /* HWCAP_AES */ (1 << 3), CpuInfo::kArmFeatureAES },
{ /* HWCAP_CRC32 */ (1 << 7), CpuInfo::kArmFeatureCRC32 },
{ /* HWCAP_PMULL */ (1 << 4), CpuInfo::kArmFeaturePMULL },
{ /* HWCAP_SHA1 */ (1 << 5), CpuInfo::kArmFeatureSHA1 },
{ /* HWCAP_SHA2 */ (1 << 6), CpuInfo::kArmFeatureSHA256 },
{ /* HWCAP_ATOMICS */ (1 << 8), CpuInfo::kArmFeatureAtomics64 }
};
armDetectHWCaps(cpuInfo, AT_HWCAP, hwCapMapping, ASMJIT_ARRAY_SIZE(hwCapMapping));
// `AT_HWCAP2` is not used at the moment.
#endif
}
#endif // ASMJIT_OS_LINUX
ASMJIT_FAVOR_SIZE static void armDetectCpuInfo(CpuInfo* cpuInfo) noexcept {
#if ASMJIT_OS_WINDOWS
armDetectCpuInfoOnWindows(cpuInfo);
#elif ASMJIT_OS_LINUX
armDetectCpuInfoOnLinux(cpuInfo);
#else
# error "[asmjit] armDetectCpuInfo() - Unsupported OS."
#endif
}
#endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
// ============================================================================
// [asmjit::CpuInfo - Detect X86]
// ============================================================================
#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
//! \internal
//!
//! X86 CPUID result.
struct CpuIdResult {
uint32_t eax, ebx, ecx, edx;
};
//! \internal
//!
//! Content of XCR register, result of XGETBV instruction.
struct XGetBVResult {
uint32_t eax, edx;
};
#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(15, 0, 30729) && ASMJIT_ARCH_X64
//! \internal
//!
//! HACK: VS2008 or less, 64-bit mode - `__cpuidex` doesn't exist! However,
//! 64-bit calling convention specifies the first parameter to be passed by
//! ECX, so we may be lucky if compiler doesn't move the register, otherwise
//! the result would be wrong.
static void ASMJIT_NOINLINE void x86CallCpuIdWorkaround(uint32_t inEcx, uint32_t inEax, CpuIdResult* result) noexcept {
__cpuid(reinterpret_cast<int*>(result), inEax);
}
#endif
//! \internal
//!
//! Wrapper to call `cpuid` instruction.
static void ASMJIT_INLINE x86CallCpuId(CpuIdResult* result, uint32_t inEax, uint32_t inEcx = 0) noexcept {
#if ASMJIT_CC_MSC && ASMJIT_CC_MSC_GE(15, 0, 30729)
__cpuidex(reinterpret_cast<int*>(result), inEax, inEcx);
#elif ASMJIT_CC_MSC && ASMJIT_ARCH_X64
x86CallCpuIdWorkaround(inEcx, inEax, result);
#elif ASMJIT_CC_MSC && ASMJIT_ARCH_X86
uint32_t paramEax = inEax;
uint32_t paramEcx = inEcx;
uint32_t* out = reinterpret_cast<uint32_t*>(result);
__asm {
mov eax, paramEax
mov ecx, paramEcx
mov edi, out
cpuid
mov dword ptr[edi + 0], eax
mov dword ptr[edi + 4], ebx
mov dword ptr[edi + 8], ecx
mov dword ptr[edi + 12], edx
}
#elif (ASMJIT_CC_GCC || ASMJIT_CC_CLANG) && ASMJIT_ARCH_X86
__asm__ __volatile__(
"mov %%ebx, %%edi\n"
"cpuid\n"
"xchg %%edi, %%ebx\n"
: "=a"(result->eax),
"=D"(result->ebx),
"=c"(result->ecx),
"=d"(result->edx)
: "a"(inEax),
"c"(inEcx));
#elif (ASMJIT_CC_GCC || ASMJIT_CC_CLANG || ASMJIT_CC_INTEL) && ASMJIT_ARCH_X64
__asm__ __volatile__(
"mov %%rbx, %%rdi\n"
"cpuid\n"
"xchg %%rdi, %%rbx\n"
: "=a"(result->eax),
"=D"(result->ebx),
"=c"(result->ecx),
"=d"(result->edx)
: "a"(inEax),
"c"(inEcx));
#else
# error "[asmjit] x86CallCpuid() - Unsupported compiler."
#endif
}
//! \internal
//!
//! Wrapper to call `xgetbv` instruction.
static ASMJIT_INLINE void x86CallXGetBV(XGetBVResult* result, uint32_t inEcx) noexcept {
#if ASMJIT_CC_MSC_GE(16, 0, 40219) // 2010SP1+
uint64_t value = _xgetbv(inEcx);
result->eax = static_cast<uint32_t>(value & 0xFFFFFFFFU);
result->edx = static_cast<uint32_t>(value >> 32);
#elif ASMJIT_CC_GCC || ASMJIT_CC_CLANG
uint32_t outEax;
uint32_t outEdx;
// Replaced, because the world is not perfect:
// __asm__ __volatile__("xgetbv" : "=a"(outEax), "=d"(outEdx) : "c"(inEcx));
__asm__ __volatile__(".byte 0x0F, 0x01, 0xd0" : "=a"(outEax), "=d"(outEdx) : "c"(inEcx));
result->eax = outEax;
result->edx = outEdx;
#else
result->eax = 0;
result->edx = 0;
#endif
}
//! \internal
//!
//! Map a 12-byte vendor string returned by `cpuid` into a `CpuInfo::Vendor` ID.
static ASMJIT_INLINE uint32_t x86GetCpuVendorID(const char* vendorString) noexcept {
struct VendorData {
uint32_t id;
char text[12];
};
static const VendorData vendorList[] = {
{ CpuInfo::kVendorIntel , { 'G', 'e', 'n', 'u', 'i', 'n', 'e', 'I', 'n', 't', 'e', 'l' } },
{ CpuInfo::kVendorAMD , { 'A', 'u', 't', 'h', 'e', 'n', 't', 'i', 'c', 'A', 'M', 'D' } },
{ CpuInfo::kVendorVIA , { 'V', 'I', 'A', 0 , 'V', 'I', 'A', 0 , 'V', 'I', 'A', 0 } },
{ CpuInfo::kVendorVIA , { 'C', 'e', 'n', 't', 'a', 'u', 'r', 'H', 'a', 'u', 'l', 's' } }
};
uint32_t dw0 = reinterpret_cast<const uint32_t*>(vendorString)[0];
uint32_t dw1 = reinterpret_cast<const uint32_t*>(vendorString)[1];
uint32_t dw2 = reinterpret_cast<const uint32_t*>(vendorString)[2];
for (uint32_t i = 0; i < ASMJIT_ARRAY_SIZE(vendorList); i++) {
if (dw0 == reinterpret_cast<const uint32_t*>(vendorList[i].text)[0] &&
dw1 == reinterpret_cast<const uint32_t*>(vendorList[i].text)[1] &&
dw2 == reinterpret_cast<const uint32_t*>(vendorList[i].text)[2])
return vendorList[i].id;
}
return CpuInfo::kVendorNone;
}
static ASMJIT_INLINE void x86SimplifyBrandString(char* s) noexcept {
// Used to always clear the current character to ensure that the result
// doesn't contain garbage after the new zero terminator.
char* d = s;
char prev = 0;
char curr = s[0];
s[0] = '\0';
for (;;) {
if (curr == 0)
break;
if (curr == ' ') {
if (prev == '@' || s[1] == ' ' || s[1] == '@')
goto L_Skip;
}
d[0] = curr;
d++;
prev = curr;
L_Skip:
curr = *++s;
s[0] = '\0';
}
d[0] = '\0';
}
ASMJIT_FAVOR_SIZE static void x86DetectCpuInfo(CpuInfo* cpuInfo) noexcept {
uint32_t i, maxId;
CpuIdResult regs;
XGetBVResult xcr0 = { 0, 0 };
cpuInfo->_archInfo.init(ArchInfo::kTypeHost);
cpuInfo->addFeature(CpuInfo::kX86FeatureI486);
// --------------------------------------------------------------------------
// [CPUID EAX=0x0]
// --------------------------------------------------------------------------
// Get vendor string/id.
x86CallCpuId(&regs, 0x0);
maxId = regs.eax;
::memcpy(cpuInfo->_vendorString + 0, &regs.ebx, 4);
::memcpy(cpuInfo->_vendorString + 4, &regs.edx, 4);
::memcpy(cpuInfo->_vendorString + 8, &regs.ecx, 4);
cpuInfo->_vendorId = x86GetCpuVendorID(cpuInfo->_vendorString);
// --------------------------------------------------------------------------
// [CPUID EAX=0x1]
// --------------------------------------------------------------------------
if (maxId >= 0x1) {
// Get feature flags in ECX/EDX and family/model in EAX.
x86CallCpuId(&regs, 0x1);
// Fill family and model fields.
cpuInfo->_family = (regs.eax >> 8) & 0x0F;
cpuInfo->_model = (regs.eax >> 4) & 0x0F;
cpuInfo->_stepping = (regs.eax ) & 0x0F;
// Use extended family and model fields.
if (cpuInfo->_family == 0x0F) {
cpuInfo->_family += ((regs.eax >> 20) & 0xFF);
cpuInfo->_model += ((regs.eax >> 16) & 0x0F) << 4;
}
cpuInfo->_x86Data._processorType = ((regs.eax >> 12) & 0x03);
cpuInfo->_x86Data._brandIndex = ((regs.ebx ) & 0xFF);
cpuInfo->_x86Data._flushCacheLineSize = ((regs.ebx >> 8) & 0xFF) * 8;
cpuInfo->_x86Data._maxLogicalProcessors = ((regs.ebx >> 16) & 0xFF);
if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE3);
if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeaturePCLMULQDQ);
if (regs.ecx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureMONITOR);
if (regs.ecx & 0x00000200U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSSE3);
if (regs.ecx & 0x00002000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMPXCHG16B);
if (regs.ecx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4_1);
if (regs.ecx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4_2);
if (regs.ecx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMOVBE);
if (regs.ecx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeaturePOPCNT);
if (regs.ecx & 0x02000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAESNI);
if (regs.ecx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVE);
if (regs.ecx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureOSXSAVE);
if (regs.ecx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDRAND);
if (regs.edx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDTSC);
if (regs.edx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureMSR);
if (regs.edx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMPXCHG8B);
if (regs.edx & 0x00008000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCMOV);
if (regs.edx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLFLUSH);
if (regs.edx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMMX);
if (regs.edx & 0x01000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFXSR);
if (regs.edx & 0x02000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE)
.addFeature(CpuInfo::kX86FeatureMMX2);
if (regs.edx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE)
.addFeature(CpuInfo::kX86FeatureSSE2);
if (regs.edx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMT);
// Get the content of XCR0 if supported by CPU and enabled by OS.
if ((regs.ecx & 0x0C000000U) == 0x0C000000U) {
x86CallXGetBV(&xcr0, 0);
}
// Detect AVX+.
if (regs.ecx & 0x10000000U) {
// - XCR0[2:1] == 11b
// XMM & YMM states need to be enabled by OS.
if ((xcr0.eax & 0x00000006U) == 0x00000006U) {
cpuInfo->addFeature(CpuInfo::kX86FeatureAVX);
if (regs.ecx & 0x00001000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFMA);
if (regs.ecx & 0x20000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureF16C);
}
}
}
// --------------------------------------------------------------------------
// [CPUID EAX=0x7]
// --------------------------------------------------------------------------
// Detect new features if the processor supports CPUID-07.
bool maybeMPX = false;
if (maxId >= 0x7) {
x86CallCpuId(&regs, 0x7);
if (regs.ebx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureFSGSBASE);
if (regs.ebx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureBMI);
if (regs.ebx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureHLE);
if (regs.ebx & 0x00000080U) cpuInfo->addFeature(CpuInfo::kX86FeatureSMEP);
if (regs.ebx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeatureBMI2);
if (regs.ebx & 0x00000200U) cpuInfo->addFeature(CpuInfo::kX86FeatureERMS);
if (regs.ebx & 0x00000800U) cpuInfo->addFeature(CpuInfo::kX86FeatureRTM);
if (regs.ebx & 0x00004000U) maybeMPX = true;
if (regs.ebx & 0x00040000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDSEED);
if (regs.ebx & 0x00080000U) cpuInfo->addFeature(CpuInfo::kX86FeatureADX);
if (regs.ebx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSMAP);
if (regs.ebx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeaturePCOMMIT);
if (regs.ebx & 0x00800000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLFLUSHOPT);
if (regs.ebx & 0x01000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLWB);
if (regs.ebx & 0x20000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureSHA);
if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeaturePREFETCHWT1);
// TSX is supported if at least one of `HLE` and `RTM` is supported.
if (regs.ebx & 0x00000810U) cpuInfo->addFeature(CpuInfo::kX86FeatureTSX);
// Detect AVX2.
if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX)) {
if (regs.ebx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX2);
}
// Detect AVX-512+.
if (regs.ebx & 0x00010000U) {
// - XCR0[2:1] == 11b
// XMM/YMM states need to be enabled by OS.
// - XCR0[7:5] == 111b
// Upper 256-bit of ZMM0-XMM15 and ZMM16-ZMM31 need to be enabled by the OS.
if ((xcr0.eax & 0x000000E6U) == 0x000000E6U) {
cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_F);
if (regs.ebx & 0x00020000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_DQ);
if (regs.ebx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_IFMA);
if (regs.ebx & 0x04000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_PFI);
if (regs.ebx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_ERI);
if (regs.ebx & 0x10000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_CDI);
if (regs.ebx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_BW);
if (regs.ebx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VL);
if (regs.ecx & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VBMI);
if (regs.ecx & 0x00004000U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_VPOPCNTDQ);
if (regs.edx & 0x00000004U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_4VNNIW);
if (regs.edx & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureAVX512_4FMAPS);
}
}
}
// --------------------------------------------------------------------------
// [CPUID EAX=0xD]
// --------------------------------------------------------------------------
if (maxId >= 0xD) {
x86CallCpuId(&regs, 0xD, 0);
// Both CPUID result and XCR0 has to be enabled to have support for MPX.
if (((regs.eax & xcr0.eax) & 0x00000018U) == 0x00000018U && maybeMPX)
cpuInfo->addFeature(CpuInfo::kX86FeatureMPX);
x86CallCpuId(&regs, 0xD, 1);
if (regs.eax & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVEOPT);
if (regs.eax & 0x00000002U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVEC);
if (regs.eax & 0x00000008U) cpuInfo->addFeature(CpuInfo::kX86FeatureXSAVES);
}
// --------------------------------------------------------------------------
// [CPUID EAX=0x80000000...maxId]
// --------------------------------------------------------------------------
// The highest EAX that we understand.
uint32_t kHighestProcessedEAX = 0x80000008U;
// Several CPUID calls are required to get the whole branc string. It's easy
// to copy one DWORD at a time instead of performing a byte copy.
uint32_t* brand = reinterpret_cast<uint32_t*>(cpuInfo->_brandString);
i = maxId = 0x80000000U;
do {
x86CallCpuId(&regs, i);
switch (i) {
case 0x80000000U:
maxId = std::min<uint32_t>(regs.eax, kHighestProcessedEAX);
break;
case 0x80000001U:
if (regs.ecx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureLAHFSAHF);
if (regs.ecx & 0x00000020U) cpuInfo->addFeature(CpuInfo::kX86FeatureLZCNT);
if (regs.ecx & 0x00000040U) cpuInfo->addFeature(CpuInfo::kX86FeatureSSE4A);
if (regs.ecx & 0x00000080U) cpuInfo->addFeature(CpuInfo::kX86FeatureMSSE);
if (regs.ecx & 0x00000100U) cpuInfo->addFeature(CpuInfo::kX86FeaturePREFETCHW);
if (regs.ecx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureTBM);
if (regs.edx & 0x00100000U) cpuInfo->addFeature(CpuInfo::kX86FeatureNX);
if (regs.edx & 0x00200000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFXSROPT);
if (regs.edx & 0x00400000U) cpuInfo->addFeature(CpuInfo::kX86FeatureMMX2);
if (regs.edx & 0x08000000U) cpuInfo->addFeature(CpuInfo::kX86FeatureRDTSCP);
if (regs.edx & 0x40000000U) cpuInfo->addFeature(CpuInfo::kX86Feature3DNOW2)
.addFeature(CpuInfo::kX86FeatureMMX2);
if (regs.edx & 0x80000000U) cpuInfo->addFeature(CpuInfo::kX86Feature3DNOW);
if (cpuInfo->hasFeature(CpuInfo::kX86FeatureAVX)) {
if (regs.ecx & 0x00000800U) cpuInfo->addFeature(CpuInfo::kX86FeatureXOP);
if (regs.ecx & 0x00010000U) cpuInfo->addFeature(CpuInfo::kX86FeatureFMA4);
}
// These seem to be only supported by AMD.
if (cpuInfo->getVendorId() == CpuInfo::kVendorAMD) {
if (regs.ecx & 0x00000010U) cpuInfo->addFeature(CpuInfo::kX86FeatureALTMOVCR8);
}
break;
case 0x80000002U:
case 0x80000003U:
case 0x80000004U:
*brand++ = regs.eax;
*brand++ = regs.ebx;
*brand++ = regs.ecx;
*brand++ = regs.edx;
// Go directly to the last one.
if (i == 0x80000004U) i = 0x80000008U - 1;
break;
case 0x80000008U:
if (regs.ebx & 0x00000001U) cpuInfo->addFeature(CpuInfo::kX86FeatureCLZERO);
break;
}
} while (++i <= maxId);
// Simplify CPU brand string by removing unnecessary spaces.
x86SimplifyBrandString(cpuInfo->_brandString);
}
#endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
// ============================================================================
// [asmjit::CpuInfo - Detect - HWThreadsCount]
// ============================================================================
static ASMJIT_INLINE uint32_t cpuDetectHWThreadsCount() noexcept {
#if ASMJIT_OS_WINDOWS
SYSTEM_INFO info;
::GetSystemInfo(&info);
return info.dwNumberOfProcessors;
#elif defined(ASMJIT_OS_POSIX) && defined(_SC_NPROCESSORS_ONLN)
// It seems that sysconf returns the number of "logical" processors on both
// mac and linux. So we get the number of "online logical" processors.
#elif ASMJIT_OS_POSIX && defined(_SC_NPROCESSORS_ONLN)
long res = ::sysconf(_SC_NPROCESSORS_ONLN);
if (res == -1) return 1;
if (res <= 0) return 1;
return static_cast<uint32_t>(res);
#else
return 1;
#endif
}
// ============================================================================
// [asmjit::CpuInfo - Detect]
// ============================================================================
ASMJIT_FAVOR_SIZE void CpuInfo::detect() noexcept {
reset();
#if ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
armDetectCpuInfo(this);
#endif // ASMJIT_ARCH_ARM32 || ASMJIT_ARCH_ARM64
#if ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
x86DetectCpuInfo(this);
#endif // ASMJIT_ARCH_X86 || ASMJIT_ARCH_X64
_hwThreadsCount = cpuDetectHWThreadsCount();
}
// ============================================================================
// [asmjit::CpuInfo - GetHost]
// ============================================================================
#if defined(ASMJIT_ARCH_X86) || defined(ASMJIT_ARCH_X64)
struct AutoX86CpuInfo : public X86CpuInfo {
ASMJIT_INLINE AutoX86CpuInfo() : X86CpuInfo() {
X86CpuUtil::detect(this);
}
struct HostCpuInfo : public CpuInfo {
ASMJIT_INLINE HostCpuInfo() noexcept : CpuInfo() { detect(); }
};
#else
#error "AsmJit - Unsupported CPU."
#endif
const CpuInfo* CpuInfo::getHost() {
#if defined(ASMJIT_ARCH_X86) || defined(ASMJIT_ARCH_X64)
static AutoX86CpuInfo cpuInfo;
#else
#error "AsmJit - Unsupported CPU."
#endif
return &cpuInfo;
const CpuInfo& CpuInfo::getHost() noexcept {
static HostCpuInfo host;
return host;
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
......@@ -8,132 +8,358 @@
#ifndef _ASMJIT_BASE_CPUINFO_H
#define _ASMJIT_BASE_CPUINFO_H
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Dependencies]
#include "../base/arch.h"
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_general
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::CpuVendor]
// [asmjit::CpuFeatures]
// ============================================================================
//! Cpu vendor ID.
//!
//! Vendor IDs are specific to AsmJit library. During the library initialization
//! AsmJit checks host CPU and tries to identify the vendor based on the CPUID
//! calls. Some manufacturers changed their vendor strings and AsmJit is aware
//! of that - it checks multiple combinations and decides which vendor ID should
//! be used.
ASMJIT_ENUM(CpuVendor) {
//! No/Unknown vendor.
kCpuVendorNone = 0,
//! Intel vendor.
kCpuVendorIntel = 1,
//! AMD vendor.
kCpuVendorAmd = 2,
//! VIA vendor.
kCpuVendorVia = 3
class CpuFeatures {
public:
typedef uintptr_t BitWord;
enum {
kMaxFeatures = 128,
kBitWordSize = static_cast<int>(sizeof(BitWord)) * 8,
kNumBitWords = kMaxFeatures / kBitWordSize
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE CpuFeatures() noexcept { reset(); }
ASMJIT_INLINE CpuFeatures(const CpuFeatures& other) noexcept = default;
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void init(const CpuFeatures& other) noexcept { ::memcpy(this, &other, sizeof(*this)); }
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
//! Get all features as `BitWord` array.
ASMJIT_INLINE BitWord* getBits() noexcept { return _bits; }
//! Get all features as `BitWord` array (const).
ASMJIT_INLINE const BitWord* getBits() const noexcept { return _bits; }
//! Get if feature `feature` is present.
ASMJIT_INLINE bool has(uint32_t feature) const noexcept {
ASMJIT_ASSERT(feature < kMaxFeatures);
uint32_t idx = feature / kBitWordSize;
uint32_t bit = feature % kBitWordSize;
return static_cast<bool>((_bits[idx] >> bit) & 0x1);
}
//! Get if all features as defined by `other` are present.
ASMJIT_INLINE bool hasAll(const CpuFeatures& other) const noexcept {
for (uint32_t i = 0; i < kNumBitWords; i++)
if ((_bits[i] & other._bits[i]) != other._bits[i])
return false;
return true;
}
//! Add a CPU `feature`.
ASMJIT_INLINE CpuFeatures& add(uint32_t feature) noexcept {
ASMJIT_ASSERT(feature < kMaxFeatures);
uint32_t idx = feature / kBitWordSize;
uint32_t bit = feature % kBitWordSize;
_bits[idx] |= static_cast<BitWord>(1) << bit;
return *this;
}
//! Remove a CPU `feature`.
ASMJIT_INLINE CpuFeatures& remove(uint32_t feature) noexcept {
ASMJIT_ASSERT(feature < kMaxFeatures);
uint32_t idx = feature / kBitWordSize;
uint32_t bit = feature % kBitWordSize;
_bits[idx] &= ~(static_cast<BitWord>(1) << bit);
return *this;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
BitWord _bits[kNumBitWords];
};
// ============================================================================
// [asmjit::CpuInfo]
// ============================================================================
//! Base cpu information.
struct CpuInfo {
ASMJIT_NO_COPY(CpuInfo)
//! CPU information.
class CpuInfo {
public:
//! CPU vendor ID.
ASMJIT_ENUM(Vendor) {
kVendorNone = 0, //!< Generic or unknown.
kVendorIntel = 1, //!< Intel vendor.
kVendorAMD = 2, //!< AMD vendor.
kVendorVIA = 3 //!< VIA vendor.
};
//! \internal
enum {
kFeaturesPerUInt32 = static_cast<int>(sizeof(uint32_t)) * 8
//! ARM/ARM64 CPU features.
ASMJIT_ENUM(ArmFeatures) {
kArmFeatureV6 = 1, //!< ARMv6 instruction set.
kArmFeatureV7, //!< ARMv7 instruction set.
kArmFeatureV8, //!< ARMv8 instruction set.
kArmFeatureTHUMB, //!< CPU provides THUMB v1 instruction set (THUMB mode).
kArmFeatureTHUMB2, //!< CPU provides THUMB v2 instruction set (THUMB mode).
kArmFeatureVFPv2, //!< CPU provides VFPv2 instruction set.
kArmFeatureVFPv3, //!< CPU provides VFPv3 instruction set.
kArmFeatureVFPv4, //!< CPU provides VFPv4 instruction set.
kArmFeatureVFP_D32, //!< CPU provides 32 VFP-D (64-bit) registers.
kArmFeatureEDSP, //!< CPU provides EDSP extensions.
kArmFeatureASIMD, //!< CPU provides 'Advanced SIMD'.
kArmFeatureIDIVA, //!< CPU provides hardware SDIV and UDIV (ARM mode).
kArmFeatureIDIVT, //!< CPU provides hardware SDIV and UDIV (THUMB mode).
kArmFeatureAES, //!< CPU provides AES instructions (ARM64 only).
kArmFeatureCRC32, //!< CPU provides CRC32 instructions.
kArmFeaturePMULL, //!< CPU provides PMULL instructions (ARM64 only).
kArmFeatureSHA1, //!< CPU provides SHA1 instructions.
kArmFeatureSHA256, //!< CPU provides SHA256 instructions.
kArmFeatureAtomics64, //!< CPU provides 64-bit load/store atomics (ARM64 only).
kArmFeaturesCount //!< Count of ARM/ARM64 CPU features.
};
//! X86/X64 CPU features.
ASMJIT_ENUM(X86Features) {
kX86FeatureI486 = 1, //!< CPU is at least I486.
kX86FeatureNX, //!< CPU has Not-Execute-Bit.
kX86FeatureMT, //!< CPU has multi-threading.
kX86FeatureALTMOVCR8, //!< CPU supports `LOCK MOV CR8` (AMD CPUs).
kX86FeatureCMOV, //!< CPU has CMOV.
kX86FeatureCMPXCHG8B, //!< CPU has CMPXCHG8B.
kX86FeatureCMPXCHG16B, //!< CPU has CMPXCHG16B (x64).
kX86FeatureMSR, //!< CPU has RDMSR/WRMSR.
kX86FeatureRDTSC, //!< CPU has RDTSC.
kX86FeatureRDTSCP, //!< CPU has RDTSCP.
kX86FeatureCLFLUSH, //!< CPU has CLFUSH.
kX86FeatureCLFLUSHOPT, //!< CPU has CLFUSHOPT.
kX86FeatureCLWB, //!< CPU has CLWB.
kX86FeatureCLZERO, //!< CPU has CLZERO.
kX86FeaturePCOMMIT, //!< CPU has PCOMMIT.
kX86FeaturePREFETCHW, //!< CPU has PREFETCHW.
kX86FeaturePREFETCHWT1, //!< CPU has PREFETCHWT1.
kX86FeatureLAHFSAHF, //!< CPU has LAHF/SAHF.
kX86FeatureFXSR, //!< CPU has FXSAVE/FXRSTOR.
kX86FeatureFXSROPT, //!< CPU has FXSAVE/FXRSTOR (optimized).
kX86FeatureMMX, //!< CPU has MMX.
kX86FeatureMMX2, //!< CPU has extended MMX.
kX86Feature3DNOW, //!< CPU has 3DNOW.
kX86Feature3DNOW2, //!< CPU has 3DNOW2 (enhanced).
kX86FeatureGEODE, //!< CPU has GEODE extensions (few additions to 3DNOW).
kX86FeatureSSE, //!< CPU has SSE.
kX86FeatureSSE2, //!< CPU has SSE2.
kX86FeatureSSE3, //!< CPU has SSE3.
kX86FeatureSSSE3, //!< CPU has SSSE3.
kX86FeatureSSE4A, //!< CPU has SSE4.A.
kX86FeatureSSE4_1, //!< CPU has SSE4.1.
kX86FeatureSSE4_2, //!< CPU has SSE4.2.
kX86FeatureMSSE, //!< CPU has Misaligned SSE (MSSE).
kX86FeatureMONITOR, //!< CPU has MONITOR and MWAIT.
kX86FeatureMOVBE, //!< CPU has MOVBE.
kX86FeaturePOPCNT, //!< CPU has POPCNT.
kX86FeatureLZCNT, //!< CPU has LZCNT.
kX86FeatureAESNI, //!< CPU has AESNI.
kX86FeaturePCLMULQDQ, //!< CPU has PCLMULQDQ.
kX86FeatureRDRAND, //!< CPU has RDRAND.
kX86FeatureRDSEED, //!< CPU has RDSEED.
kX86FeatureSMAP, //!< CPU has SMAP (supervisor-mode access prevention).
kX86FeatureSMEP, //!< CPU has SMEP (supervisor-mode execution prevention).
kX86FeatureSHA, //!< CPU has SHA-1 and SHA-256.
kX86FeatureXSAVE, //!< CPU has XSAVE support (XSAVE/XRSTOR, XSETBV/XGETBV, and XCR).
kX86FeatureXSAVEC, //!< CPU has XSAVEC support (XSAVEC).
kX86FeatureXSAVES, //!< CPU has XSAVES support (XSAVES/XRSTORS).
kX86FeatureXSAVEOPT, //!< CPU has XSAVEOPT support (XSAVEOPT/XSAVEOPT64).
kX86FeatureOSXSAVE, //!< CPU has XSAVE enabled by OS.
kX86FeatureAVX, //!< CPU has AVX.
kX86FeatureAVX2, //!< CPU has AVX2.
kX86FeatureF16C, //!< CPU has F16C.
kX86FeatureFMA, //!< CPU has FMA.
kX86FeatureFMA4, //!< CPU has FMA4.
kX86FeatureXOP, //!< CPU has XOP.
kX86FeatureBMI, //!< CPU has BMI (bit manipulation instructions #1).
kX86FeatureBMI2, //!< CPU has BMI2 (bit manipulation instructions #2).
kX86FeatureADX, //!< CPU has ADX (multi-precision add-carry instruction extensions).
kX86FeatureTBM, //!< CPU has TBM (trailing bit manipulation).
kX86FeatureMPX, //!< CPU has MPX (memory protection extensions).
kX86FeatureHLE, //!< CPU has HLE.
kX86FeatureRTM, //!< CPU has RTM.
kX86FeatureTSX, //!< CPU has TSX.
kX86FeatureERMS, //!< CPU has ERMS (enhanced REP MOVSB/STOSB).
kX86FeatureFSGSBASE, //!< CPU has FSGSBASE.
kX86FeatureAVX512_F, //!< CPU has AVX512-F (foundation).
kX86FeatureAVX512_CDI, //!< CPU has AVX512-CDI (conflict detection).
kX86FeatureAVX512_PFI, //!< CPU has AVX512-PFI (prefetch instructions).
kX86FeatureAVX512_ERI, //!< CPU has AVX512-ERI (exponential and reciprocal).
kX86FeatureAVX512_DQ, //!< CPU has AVX512-DQ (DWORD/QWORD).
kX86FeatureAVX512_BW, //!< CPU has AVX512-BW (BYTE/WORD).
kX86FeatureAVX512_VL, //!< CPU has AVX512-VL (vector length extensions).
kX86FeatureAVX512_IFMA, //!< CPU has AVX512-IFMA (integer fused-multiply-add using 52-bit precision).
kX86FeatureAVX512_VBMI, //!< CPU has AVX512-VBMI (vector byte manipulation).
kX86FeatureAVX512_VPOPCNTDQ, //!< CPU has AVX512-VPOPCNTDQ (VPOPCNT[D|Q] instructions).
kX86FeatureAVX512_4VNNIW, //!< CPU has AVX512-VNNIW (vector NN instructions word variable precision).
kX86FeatureAVX512_4FMAPS, //!< CPU has AVX512-FMAPS (FMA packed single).
kX86FeaturesCount //!< Count of X86/X64 CPU features.
};
// --------------------------------------------------------------------------
// [ArmInfo]
// --------------------------------------------------------------------------
struct ArmData {
};
// --------------------------------------------------------------------------
// [X86Info]
// --------------------------------------------------------------------------
struct X86Data {
uint32_t _processorType; //!< Processor type.
uint32_t _brandIndex; //!< Brand index.
uint32_t _flushCacheLineSize; //!< Flush cache line size (in bytes).
uint32_t _maxLogicalProcessors; //!< Maximum number of addressable IDs for logical processors.
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE CpuInfo(uint32_t size) : _size(size) {}
ASMJIT_INLINE CpuInfo() noexcept { reset(); }
ASMJIT_INLINE CpuInfo(const CpuInfo& other) noexcept = default;
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
//! Initialize CpuInfo to the given architecture, see \ArchInfo.
ASMJIT_INLINE void initArch(uint32_t archType, uint32_t archMode = 0) noexcept {
_archInfo.init(archType, archMode);
}
ASMJIT_INLINE void init(const CpuInfo& other) noexcept { ::memcpy(this, &other, sizeof(*this)); }
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
// --------------------------------------------------------------------------
// [Detect]
// --------------------------------------------------------------------------
ASMJIT_API void detect() noexcept;
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get CPU vendor string.
ASMJIT_INLINE const char* getVendorString() const { return _vendorString; }
//! Get CPU brand string.
ASMJIT_INLINE const char* getBrandString() const { return _brandString; }
//! Get generic architecture information.
ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _archInfo; }
//! Get CPU architecture type, see \ArchInfo::Type.
ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archInfo.getType(); }
//! Get CPU architecture sub-type, see \ArchInfo::SubType.
ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return _archInfo.getSubType(); }
//! Get CPU vendor ID.
ASMJIT_INLINE uint32_t getVendorId() const { return _vendorId; }
//! Get CPU vendor ID.
ASMJIT_INLINE uint32_t getVendorId() const noexcept { return _vendorId; }
//! Get CPU family ID.
ASMJIT_INLINE uint32_t getFamily() const { return _family; }
ASMJIT_INLINE uint32_t getFamily() const noexcept { return _family; }
//! Get CPU model ID.
ASMJIT_INLINE uint32_t getModel() const { return _model; }
ASMJIT_INLINE uint32_t getModel() const noexcept { return _model; }
//! Get CPU stepping.
ASMJIT_INLINE uint32_t getStepping() const { return _stepping; }
ASMJIT_INLINE uint32_t getStepping() const noexcept { return _stepping; }
//! Get number of hardware threads available.
ASMJIT_INLINE uint32_t getHwThreadsCount() const { return _hwThreadsCount; }
ASMJIT_INLINE uint32_t getHwThreadsCount() const noexcept {
return _hwThreadsCount;
}
//! Get all CPU features.
ASMJIT_INLINE const CpuFeatures& getFeatures() const noexcept { return _features; }
//! Get whether CPU has a `feature`.
ASMJIT_INLINE bool hasFeature(uint32_t feature) const {
ASMJIT_ASSERT(feature < sizeof(_features) * 8);
ASMJIT_INLINE bool hasFeature(uint32_t feature) const noexcept { return _features.has(feature); }
//! Add a CPU `feature`.
ASMJIT_INLINE CpuInfo& addFeature(uint32_t feature) noexcept { _features.add(feature); return *this; }
//! Get CPU vendor string.
ASMJIT_INLINE const char* getVendorString() const noexcept { return _vendorString; }
//! Get CPU brand string.
ASMJIT_INLINE const char* getBrandString() const noexcept { return _brandString; }
return static_cast<bool>(
(_features[feature / kFeaturesPerUInt32] >> (feature % kFeaturesPerUInt32)) & 0x1);
// --------------------------------------------------------------------------
// [Accessors - ARM]
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// [Accessors - X86]
// --------------------------------------------------------------------------
//! Get processor type.
ASMJIT_INLINE uint32_t getX86ProcessorType() const noexcept {
return _x86Data._processorType;
}
//! Add a CPU `feature`.
ASMJIT_INLINE CpuInfo& addFeature(uint32_t feature) {
ASMJIT_ASSERT(feature < sizeof(_features) * 8);
//! Get brand index.
ASMJIT_INLINE uint32_t getX86BrandIndex() const noexcept {
return _x86Data._brandIndex;
}
_features[feature / kFeaturesPerUInt32] |= (1U << (feature % kFeaturesPerUInt32));
return *this;
//! Get flush cache line size.
ASMJIT_INLINE uint32_t getX86FlushCacheLineSize() const noexcept {
return _x86Data._flushCacheLineSize;
}
//! Get maximum logical processors count.
ASMJIT_INLINE uint32_t getX86MaxLogicalProcessors() const noexcept {
return _x86Data._maxLogicalProcessors;
}
// --------------------------------------------------------------------------
// [Statics]
// --------------------------------------------------------------------------
//! Detect the number of hardware threads.
static ASMJIT_API uint32_t detectHwThreadsCount();
//! Get host cpu.
static ASMJIT_API const CpuInfo* getHost();
//! Get the host CPU information.
ASMJIT_API static const CpuInfo& getHost() noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Size of the structure in bytes.
uint32_t _size;
//! Cpu short vendor string.
char _vendorString[16];
//! Cpu long vendor string (brand).
char _brandString[64];
//! Cpu vendor id, see \ref CpuVendor.
uint32_t _vendorId;
//! Cpu family ID.
uint32_t _family;
//! Cpu model ID.
uint32_t _model;
//! Cpu stepping.
uint32_t _stepping;
//! Number of hardware threads.
uint32_t _hwThreadsCount;
//! Cpu features bitfield.
uint32_t _features[4];
ArchInfo _archInfo; //!< CPU architecture information.
uint32_t _vendorId; //!< CPU vendor id, see \ref Vendor.
uint32_t _family; //!< CPU family ID.
uint32_t _model; //!< CPU model ID.
uint32_t _stepping; //!< CPU stepping.
uint32_t _hwThreadsCount; //!< Number of hardware threads.
CpuFeatures _features; //!< CPU features.
char _vendorString[16]; //!< CPU vendor string.
char _brandString[64]; //!< CPU brand string.
// Architecture specific data.
union {
ArmData _armData;
X86Data _x86Data;
};
};
//! \}
......@@ -141,7 +367,7 @@ struct CpuInfo {
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_CPUINFO_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/cputicks.h"
// [Dependencies - Posix]
#if defined(ASMJIT_OS_POSIX)
# include <time.h>
# include <unistd.h>
#endif // ASMJIT_OS_POSIX
// [Dependencies - Mac]
#if defined(ASMJIT_OS_MAC)
# include <mach/mach_time.h>
#endif // ASMJIT_OS_MAC
// [Dependencies - Windows]
#if defined(ASMJIT_OS_WINDOWS)
// `_InterlockedCompareExchange` is only available as intrinsic (MS Compiler).
# if defined(_MSC_VER) && _MSC_VER >= 1400
# include <intrin.h>
# pragma intrinsic(_InterlockedCompareExchange)
# else
# define _InterlockedCompareExchange InterlockedCompareExchange
# endif // _MSC_VER
#endif // ASMJIT_OS_WINDOWS
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::CpuTicks - Windows]
// ============================================================================
#if defined(ASMJIT_OS_WINDOWS)
static volatile uint32_t CpuTicks_hiResOk;
static volatile double CpuTicks_hiResFreq;
uint32_t CpuTicks::now() {
do {
uint32_t hiResOk = CpuTicks_hiResOk;
if (hiResOk == 1) {
LARGE_INTEGER now;
if (!::QueryPerformanceCounter(&now))
break;
return (int64_t)(double(now.QuadPart) / CpuTicks_hiResFreq);
}
if (hiResOk == 0) {
LARGE_INTEGER qpf;
if (!::QueryPerformanceFrequency(&qpf)) {
_InterlockedCompareExchange((LONG*)&CpuTicks_hiResOk, 0xFFFFFFFF, 0);
break;
}
LARGE_INTEGER now;
if (!::QueryPerformanceCounter(&now)) {
_InterlockedCompareExchange((LONG*)&CpuTicks_hiResOk, 0xFFFFFFFF, 0);
break;
}
double freqDouble = double(qpf.QuadPart) / 1000.0;
CpuTicks_hiResFreq = freqDouble;
_InterlockedCompareExchange((LONG*)&CpuTicks_hiResOk, 1, 0);
return static_cast<uint32_t>(
static_cast<int64_t>(double(now.QuadPart) / freqDouble) & 0xFFFFFFFF);
}
} while (0);
// Bail to a less precise GetTickCount().
return ::GetTickCount();
}
// ============================================================================
// [asmjit::CpuTicks - Mac]
// ============================================================================
#elif defined(ASMJIT_OS_MAC)
static mach_timebase_info_data_t CpuTicks_machTime;
uint32_t CpuTicks::now() {
// Initialize the first time CpuTicks::now() is called (See Apple's QA1398).
if (CpuTicks_machTime.denom == 0) {
if (mach_timebase_info(&CpuTicks_machTime) != KERN_SUCCESS);
return 0;
}
// mach_absolute_time() returns nanoseconds, we need just milliseconds.
uint64_t t = mach_absolute_time() / 1000000;
t = t * CpuTicks_machTime.numer / CpuTicks_machTime.denom;
return static_cast<uint32_t>(t & 0xFFFFFFFFU);
}
// ============================================================================
// [asmjit::CpuTicks - Posix]
// ============================================================================
#else
uint32_t CpuTicks::now() {
#if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
return 0;
uint64_t t = (uint64_t(ts.tv_sec ) * 1000) + (uint64_t(ts.tv_nsec) / 1000000);
return static_cast<uint32_t>(t & 0xFFFFFFFFU);
#else // _POSIX_MONOTONIC_CLOCK
#error "AsmJit - Unsupported OS."
return 0;
#endif // _POSIX_MONOTONIC_CLOCK
}
#endif // ASMJIT_OS
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_CPUTICKS_H
#define _ASMJIT_BASE_CPUTICKS_H
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \{
// ============================================================================
// [asmjit::CpuTicks]
// ============================================================================
//! CPU ticks utilities.
struct CpuTicks {
//! Get the current CPU ticks for benchmarking (1ms resolution).
static ASMJIT_API uint32_t now();
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_CPUTICKS_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_ERROR_H
#define _ASMJIT_BASE_ERROR_H
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_general
//! \{
// ============================================================================
// [asmjit::ErrorCode]
// ============================================================================
//! AsmJit error codes.
ASMJIT_ENUM(ErrorCode) {
//! No error (success).
//!
//! This is default state and state you want.
kErrorOk = 0,
//! Heap memory allocation failed.
kErrorNoHeapMemory = 1,
//! Virtual memory allocation failed.
kErrorNoVirtualMemory = 2,
//! Invalid argument.
kErrorInvalidArgument = 3,
//! Invalid state.
kErrorInvalidState = 4,
//! No code generated.
//!
//! Returned by runtime if the code-generator contains no code.
kErrorNoCodeGenerated = 5,
//! Code generated is too large to fit in memory reserved.
//!
//! Returned by `StaticRuntime` in case that the code generated is too large
//! to fit in the memory already reserved for it.
kErrorCodeTooLarge = 6,
//! Label is already bound.
kErrorLabelAlreadyBound = 7,
//! Unknown instruction (an instruction ID is out of bounds or instruction
//! name is invalid).
kErrorUnknownInst = 8,
//! Illegal instruction.
//!
//! This status code can also be returned in X64 mode if AH, BH, CH or DH
//! registers have been used together with a REX prefix. The instruction
//! is not encodable in such case.
//!
//! Example of raising `kErrorIllegalInst` error.
//!
//! ~~~
//! // Invalid address size.
//! a.mov(dword_ptr(eax), al);
//!
//! // Undecodable instruction - AH used with R10, however R10 can only be
//! // encoded by using REX prefix, which conflicts with AH.
//! a.mov(byte_ptr(r10), ah);
//! ~~~
//!
//! \note In debug mode assertion is raised instead of returning an error.
kErrorIllegalInst = 9,
//! Illegal (unencodable) addressing used.
kErrorIllegalAddresing = 10,
//! Illegal (unencodable) displacement used.
//!
//! X86/X64
//! -------
//!
//! Short form of jump instruction has been used, but the displacement is out
//! of bounds.
kErrorIllegalDisplacement = 11,
//! A variable has been assigned more than once to a function argument (Compiler).
kErrorOverlappedArgs = 12,
//! Count of AsmJit error codes.
kErrorCount = 13
};
// ============================================================================
// [asmjit::Error]
// ============================================================================
//! AsmJit error type (unsigned integer).
typedef uint32_t Error;
// ============================================================================
// [asmjit::ErrorHandler]
// ============================================================================
//! Error handler.
//!
//! Error handler can be used to override the default behavior of `CodeGen`
//! error handling and propagation. See `handleError` on how to override it.
//!
//! Please note that `addRef` and `release` functions are used, but there is
//! no reference counting implemented by default, reimplement to change the
//! default behavior.
struct ASMJIT_VCLASS ErrorHandler {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `ErrorHandler` instance.
ASMJIT_API ErrorHandler();
//! Destroy the `ErrorHandler` instance.
ASMJIT_API virtual ~ErrorHandler();
// --------------------------------------------------------------------------
// [Interface]
// --------------------------------------------------------------------------
//! Reference this error handler.
//!
//! \note This member function is provided for convenience. The default
//! implementation does nothing. If you are working in environment where
//! multiple `ErrorHandler` instances are used by a different code generators
//! you may provide your own functionality for reference counting. In that
//! case `addRef()` and `release()` functions should be overridden.
ASMJIT_API virtual ErrorHandler* addRef() const;
//! Release this error handler.
//!
//! \note This member function is provided for convenience. See `addRef()`
//! for more detailed information related to reference counting.
ASMJIT_API virtual void release();
//! Error handler (pure).
//!
//! Error handler is called when an error happened. An error can happen in
//! many places, but error handler is mostly used by `Assembler` and
//! `Compiler` classes to report anything that may cause incorrect code
//! generation. There are multiple ways how the error handler can be used
//! and each has it's pros/cons.
//!
//! AsmJit library doesn't use exceptions and can be compiled with or without
//! exception handling support. Even if the AsmJit library is compiled without
//! exceptions it is exception-safe and handleError() can report an incoming
//! error by throwing an exception of any type. It's guaranteed that the
//! exception won't be catched by AsmJit and will be propagated to the code
//! calling AsmJit `Assembler` or `Compiler` methods. Alternative to
//! throwing an exception is using `setjmp()` and `longjmp()` pair available
//! in the standard C library.
//!
//! If the exception or setjmp() / longjmp() mechanism is used, the state of
//! the `BaseAssember` or `Compiler` is unchanged and if it's possible the
//! execution (instruction serialization) can continue. However if the error
//! happened during any phase that translates or modifies the stored code
//! (for example relocation done by `Assembler` or analysis/translation
//! done by `Compiler`) the execution can't continue and the error will
//! be also stored in `Assembler` or `Compiler`.
//!
//! Finally, if no exceptions nor setjmp() / longjmp() mechanisms were used,
//! you can still implement a compatible handling by returning from your
//! error handler. Returning `true` means that error was reported and AsmJit
//! should continue execution, but `false` sets the rror immediately to the
//! `Assembler` or `Compiler` and execution shouldn't continue (this
//! is the default behavior in case no error handler is used).
virtual bool handleError(Error code, const char* message) = 0;
};
// ============================================================================
// [asmjit::ErrorUtil]
// ============================================================================
//! Error utilities.
struct ErrorUtil {
#if !defined(ASMJIT_DISABLE_NAMES)
//! Get a printable version of AsmJit `Error` code.
static ASMJIT_API const char* asString(Error code);
#endif // ASMJIT_DISABLE_NAMES
};
//! \}
// ============================================================================
// [ASMJIT_PROPAGATE_ERROR]
// ============================================================================
//! \internal
//!
//! Used by AsmJit to return the `_Exp_` result if it's an error.
#define ASMJIT_PROPAGATE_ERROR(_Exp_) \
do { \
::asmjit::Error errval_ = (_Exp_); \
if (errval_ != ::asmjit::kErrorOk) \
return errval_; \
} while (0)
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_ERROR_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/arch.h"
#include "../base/func.h"
#if defined(ASMJIT_BUILD_X86)
#include "../x86/x86internal_p.h"
#include "../x86/x86operand.h"
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
#include "../arm/arminternal_p.h"
#include "../arm/armoperand.h"
#endif // ASMJIT_BUILD_ARM
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::CallConv - Init / Reset]
// ============================================================================
ASMJIT_FAVOR_SIZE Error CallConv::init(uint32_t ccId) noexcept {
reset();
#if defined(ASMJIT_BUILD_X86)
if (CallConv::isX86Family(ccId))
return X86Internal::initCallConv(*this, ccId);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (CallConv::isArmFamily(ccId))
return ArmInternal::initCallConv(*this, ccId);
#endif // ASMJIT_BUILD_ARM
return DebugUtils::errored(kErrorInvalidArgument);
}
// ============================================================================
// [asmjit::FuncDetail - Init / Reset]
// ============================================================================
ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& sign) {
uint32_t ccId = sign.getCallConv();
CallConv& cc = _callConv;
uint32_t argCount = sign.getArgCount();
if (ASMJIT_UNLIKELY(argCount > kFuncArgCount))
return DebugUtils::errored(kErrorInvalidArgument);
ASMJIT_PROPAGATE(cc.init(ccId));
uint32_t gpSize = (cc.getArchType() == ArchInfo::kTypeX86) ? 4 : 8;
uint32_t deabstractDelta = TypeId::deabstractDeltaOfSize(gpSize);
const uint8_t* args = sign.getArgs();
for (uint32_t i = 0; i < argCount; i++) {
Value& arg = _args[i];
arg.initTypeId(TypeId::deabstract(args[i], deabstractDelta));
}
_argCount = static_cast<uint8_t>(argCount);
uint32_t ret = sign.getRet();
if (ret != TypeId::kVoid) {
_rets[0].initTypeId(TypeId::deabstract(ret, deabstractDelta));
_retCount = 1;
}
#if defined(ASMJIT_BUILD_X86)
if (CallConv::isX86Family(ccId))
return X86Internal::initFuncDetail(*this, sign, gpSize);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (CallConv::isArmFamily(ccId))
return ArmInternal::initFuncDetail(*this, sign, gpSize);
#endif // ASMJIT_BUILD_ARM
// We should never bubble here as if `cc.init()` succeeded then there has to
// be an implementation for the current architecture. However, stay safe.
return DebugUtils::errored(kErrorInvalidArgument);
}
// ============================================================================
// [asmjit::FuncFrameLayout - Init / Reset]
// ============================================================================
ASMJIT_FAVOR_SIZE Error FuncFrameLayout::init(const FuncDetail& func, const FuncFrameInfo& ffi) noexcept {
uint32_t ccId = func.getCallConv().getId();
#if defined(ASMJIT_BUILD_X86)
if (CallConv::isX86Family(ccId))
return X86Internal::initFrameLayout(*this, func, ffi);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (CallConv::isArmFamily(ccId))
return ArmInternal::initFrameLayout(*this, func, ffi);
#endif // ASMJIT_BUILD_ARM
return DebugUtils::errored(kErrorInvalidArgument);
}
// ============================================================================
// [asmjit::FuncArgsMapper]
// ============================================================================
ASMJIT_FAVOR_SIZE Error FuncArgsMapper::updateFrameInfo(FuncFrameInfo& ffi) const noexcept {
const FuncDetail* func = getFuncDetail();
if (!func) return DebugUtils::errored(kErrorInvalidState);
uint32_t ccId = func->getCallConv().getId();
#if defined(ASMJIT_BUILD_X86)
if (CallConv::isX86Family(ccId))
return X86Internal::argsToFrameInfo(*this, ffi);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (CallConv::isArmFamily(ccId))
return ArmInternal::argsToFrameInfo(*this, ffi);
#endif // ASMJIT_BUILD_X86
return DebugUtils::errored(kErrorInvalidArch);
}
// ============================================================================
// [asmjit::FuncUtils]
// ============================================================================
ASMJIT_FAVOR_SIZE Error FuncUtils::emitProlog(CodeEmitter* emitter, const FuncFrameLayout& layout) {
#if defined(ASMJIT_BUILD_X86)
if (emitter->getArchInfo().isX86Family())
return X86Internal::emitProlog(static_cast<X86Emitter*>(emitter), layout);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (emitter->getArchInfo().isArmFamily())
return ArmInternal::emitProlog(static_cast<ArmEmitter*>(emitter), layout);
#endif // ASMJIT_BUILD_ARM
return DebugUtils::errored(kErrorInvalidArch);
}
ASMJIT_FAVOR_SIZE Error FuncUtils::emitEpilog(CodeEmitter* emitter, const FuncFrameLayout& layout) {
#if defined(ASMJIT_BUILD_X86)
if (emitter->getArchInfo().isX86Family())
return X86Internal::emitEpilog(static_cast<X86Emitter*>(emitter), layout);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (emitter->getArchInfo().isArmFamily())
return ArmInternal::emitEpilog(static_cast<ArmEmitter*>(emitter), layout);
#endif // ASMJIT_BUILD_ARM
return DebugUtils::errored(kErrorInvalidArch);
}
ASMJIT_FAVOR_SIZE Error FuncUtils::allocArgs(CodeEmitter* emitter, const FuncFrameLayout& layout, const FuncArgsMapper& args) {
#if defined(ASMJIT_BUILD_X86)
if (emitter->getArchInfo().isX86Family())
return X86Internal::allocArgs(static_cast<X86Emitter*>(emitter), layout, args);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
if (emitter->getArchInfo().isArmFamily())
return ArmInternal::allocArgs(static_cast<ArmEmitter*>(emitter), layout, args);
#endif // ASMJIT_BUILD_ARM
return DebugUtils::errored(kErrorInvalidArch);
}
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_FUNC_H
#define _ASMJIT_BASE_FUNC_H
#include "../asmjit_build.h"
// [Dependencies]
#include "../base/arch.h"
#include "../base/operand.h"
#include "../base/utils.h"
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [Forward Declarations]
// ============================================================================
class CodeEmitter;
// ============================================================================
// [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 JIT code-generation easier.
struct CallConv {
//! Calling convention id.
ASMJIT_ENUM(Id) {
//! None or invalid (can't be used).
kIdNone = 0,
// ------------------------------------------------------------------------
// [Universal]
// ------------------------------------------------------------------------
// TODO: To make this possible we need to know target ARCH and ABI.
/*
// Universal calling conventions are applicable to any target and are
// converted to target dependent conventions at runtime. The purpose of
// these conventions is to make using functions less target dependent.
kIdCDecl = 1,
kIdStdCall = 2,
kIdFastCall = 3,
//! AsmJit specific calling convention designed for calling functions
//! inside a multimedia code like 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...
kIdFastEval2 = 10,
kIdFastEval3 = 11,
kIdFastEval4 = 12,
*/
// ------------------------------------------------------------------------
// [X86]
// ------------------------------------------------------------------------
//! X86 `__cdecl` calling convention (used by C runtime and libraries).
kIdX86CDecl = 16,
//! X86 `__stdcall` calling convention (used mostly by WinAPI).
kIdX86StdCall = 17,
//! X86 `__thiscall` calling convention (MSVC/Intel).
kIdX86MsThisCall = 18,
//! X86 `__fastcall` convention (MSVC/Intel).
kIdX86MsFastCall = 19,
//! X86 `__fastcall` convention (GCC and Clang).
kIdX86GccFastCall = 20,
//! X86 `regparm(1)` convention (GCC and Clang).
kIdX86GccRegParm1 = 21,
//! X86 `regparm(2)` convention (GCC and Clang).
kIdX86GccRegParm2 = 22,
//! X86 `regparm(3)` convention (GCC and Clang).
kIdX86GccRegParm3 = 23,
kIdX86FastEval2 = 29,
kIdX86FastEval3 = 30,
kIdX86FastEval4 = 31,
//! X64 calling convention defined by WIN64-ABI.
//!
//! Links:
//! * <http://msdn.microsoft.com/en-us/library/9b372w95.aspx>.
kIdX86Win64 = 32,
//! X64 calling convention used by Unix platforms (SYSV/AMD64-ABI).
kIdX86SysV64 = 33,
kIdX64FastEval2 = 45,
kIdX64FastEval3 = 46,
kIdX64FastEval4 = 47,
// ------------------------------------------------------------------------
// [ARM]
// ------------------------------------------------------------------------
//! Legacy calling convention, floating point arguments are passed via GP registers.
kIdArm32SoftFP = 48,
//! Modern calling convention, uses VFP registers to pass floating point arguments.
kIdArm32HardFP = 49,
// ------------------------------------------------------------------------
// [Internal]
// ------------------------------------------------------------------------
_kIdX86Start = 16, //!< \internal
_kIdX86End = 31, //!< \internal
_kIdX64Start = 32, //!< \internal
_kIdX64End = 47, //!< \internal
_kIdArmStart = 48, //!< \internal
_kIdArmEnd = 49, //!< \internal
// ------------------------------------------------------------------------
// [Host]
// ------------------------------------------------------------------------
#if defined(ASMJIT_DOCGEN)
//! Default calling convention based on the current C++ compiler's settings.
//!
//! NOTE: This should be always the same as `kIdHostCDecl`, but some
//! compilers allow to override the default calling convention. Overriding
//! is not detected at the moment.
kIdHost = DETECTED_AT_COMPILE_TIME,
//! Default CDECL calling convention based on the current C++ compiler's settings.
kIdHostCDecl = DETECTED_AT_COMPILE_TIME,
//! Default STDCALL calling convention based on the current C++ compiler's settings.
//!
//! NOTE: If not defined by the host then it's the same as `kIdHostCDecl`.
kIdHostStdCall = DETECTED_AT_COMPILE_TIME,
//! Compatibility for `__fastcall` calling convention.
//!
//! NOTE: If not defined by the host then it's the same as `kIdHostCDecl`.
kIdHostFastCall = DETECTED_AT_COMPILE_TIME
#elif ASMJIT_ARCH_X86
kIdHost = kIdX86CDecl,
kIdHostCDecl = kIdX86CDecl,
kIdHostStdCall = kIdX86StdCall,
kIdHostFastCall = ASMJIT_CC_MSC ? kIdX86MsFastCall :
ASMJIT_CC_GCC ? kIdX86GccFastCall :
ASMJIT_CC_CLANG ? kIdX86GccFastCall : kIdNone,
kIdHostFastEval2 = kIdX86FastEval2,
kIdHostFastEval3 = kIdX86FastEval3,
kIdHostFastEval4 = kIdX86FastEval4
#elif ASMJIT_ARCH_X64
kIdHost = ASMJIT_OS_WINDOWS ? kIdX86Win64 : kIdX86SysV64,
kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host.
kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host.
kIdHostFastCall = kIdHost, // Doesn't exist, redirected to host.
kIdHostFastEval2 = kIdX64FastEval2,
kIdHostFastEval3 = kIdX64FastEval3,
kIdHostFastEval4 = kIdX64FastEval4
#elif ASMJIT_ARCH_ARM32
# if defined(__SOFTFP__)
kIdHost = kIdArm32SoftFP,
# else
kIdHost = kIdArm32HardFP,
# endif
// These don't exist on ARM.
kIdHostCDecl = kIdHost, // Doesn't exist, redirected to host.
kIdHostStdCall = kIdHost, // Doesn't exist, redirected to host.
kIdHostFastCall = kIdHost // Doesn't exist, redirected to host.
#else
# error "[asmjit] Couldn't determine the target's calling convention."
#endif
};
//! Calling convention algorithm.
//!
//! This is AsmJit specific. It basically describes how should AsmJit convert
//! the function arguments defined by `FuncSignature` into register ids or
//! stack offsets. The default algorithm is a standard algorithm that assigns
//! registers first, and then assigns stack. The Win64 algorithm does register
//! shadowing as defined by `WIN64` calling convention - it applies to 64-bit
//! calling conventions only.
ASMJIT_ENUM(Algorithm) {
kAlgorithmDefault = 0, //!< Default algorithm (cross-platform).
kAlgorithmWin64 = 1 //!< WIN64 specific algorithm.
};
//! Calling convention flags.
ASMJIT_ENUM(Flags) {
kFlagCalleePopsStack = 0x01, //!< Callee is responsible for cleaning up the stack.
kFlagPassFloatsByVec = 0x02, //!< Pass F32 and F64 arguments by VEC128 register.
kFlagVectorCall = 0x04, //!< This is a '__vectorcall' calling convention.
kFlagIndirectVecArgs = 0x08 //!< Pass vector arguments indirectly (as a pointer).
};
//! Internal limits of AsmJit/CallConv.
ASMJIT_ENUM(Limits) {
kMaxVRegKinds = Globals::kMaxVRegKinds,
kNumRegArgsPerKind = 8
};
//! Passed registers' order.
union RegOrder {
uint8_t id[kNumRegArgsPerKind]; //!< Passed registers, ordered.
uint32_t packed[(kNumRegArgsPerKind + 3) / 4];
};
// --------------------------------------------------------------------------
// [Utilities]
// --------------------------------------------------------------------------
static ASMJIT_INLINE bool isX86Family(uint32_t ccId) noexcept { return ccId >= _kIdX86Start && ccId <= _kIdX64End; }
static ASMJIT_INLINE bool isArmFamily(uint32_t ccId) noexcept { return ccId >= _kIdArmStart && ccId <= _kIdArmEnd; }
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
ASMJIT_API Error init(uint32_t ccId) noexcept;
ASMJIT_INLINE void reset() noexcept {
::memset(this, 0, sizeof(*this));
::memset(_passedOrder, 0xFF, sizeof(_passedOrder));
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get calling convention id, see \ref Id.
ASMJIT_INLINE uint32_t getId() const noexcept { return _id; }
//! Set calling convention id, see \ref Id.
ASMJIT_INLINE void setId(uint32_t id) noexcept { _id = static_cast<uint8_t>(id); }
//! Get architecture type.
ASMJIT_INLINE uint32_t getArchType() const noexcept { return _archType; }
//! Set architecture type.
ASMJIT_INLINE void setArchType(uint32_t archType) noexcept { _archType = static_cast<uint8_t>(archType); }
//! Get calling convention algorithm, see \ref Algorithm.
ASMJIT_INLINE uint32_t getAlgorithm() const noexcept { return _algorithm; }
//! Set calling convention algorithm, see \ref Algorithm.
ASMJIT_INLINE void setAlgorithm(uint32_t algorithm) noexcept { _algorithm = static_cast<uint8_t>(algorithm); }
//! Get if the calling convention has the given `flag` set.
ASMJIT_INLINE bool hasFlag(uint32_t flag) const noexcept { return (_flags & flag) != 0; }
//! Get calling convention flags, see \ref Flags.
ASMJIT_INLINE uint32_t getFlags() const noexcept { return _flags; }
//! Add calling convention flags, see \ref Flags.
ASMJIT_INLINE void setFlags(uint32_t flag) noexcept { _flags = flag; };
//! Add calling convention flags, see \ref Flags.
ASMJIT_INLINE void addFlags(uint32_t flag) noexcept { _flags |= flag; };
//! Get a natural stack alignment.
ASMJIT_INLINE uint32_t getNaturalStackAlignment() const noexcept { return _naturalStackAlignment; }
//! Set 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.
ASMJIT_INLINE void setNaturalStackAlignment(uint32_t value) noexcept {
ASMJIT_ASSERT(value < 256);
_naturalStackAlignment = static_cast<uint8_t>(value);
}
//! Get if this calling convention specifies 'SpillZone'.
ASMJIT_INLINE bool hasSpillZone() const noexcept { return _spillZoneSize != 0; }
//! Get size of 'SpillZone'.
ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _spillZoneSize; }
//! Set size of 'SpillZone'.
ASMJIT_INLINE void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = static_cast<uint8_t>(size); }
//! Get if this calling convention specifies 'RedZone'.
ASMJIT_INLINE bool hasRedZone() const noexcept { return _redZoneSize != 0; }
//! Get size of 'RedZone'.
ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _redZoneSize; }
//! Set size of 'RedZone'.
ASMJIT_INLINE void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = static_cast<uint16_t>(size); }
ASMJIT_INLINE const uint8_t* getPassedOrder(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _passedOrder[kind].id;
}
ASMJIT_INLINE uint32_t getPassedRegs(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _passedRegs[kind];
}
ASMJIT_INLINE void _setPassedPacked(uint32_t kind, uint32_t p0, uint32_t p1) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_passedOrder[kind].packed[0] = p0;
_passedOrder[kind].packed[1] = p1;
}
ASMJIT_INLINE void setPassedToNone(uint32_t kind) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_setPassedPacked(kind, ASMJIT_PACK32_4x8(0xFF, 0xFF, 0xFF, 0xFF),
ASMJIT_PACK32_4x8(0xFF, 0xFF, 0xFF, 0xFF));
_passedRegs[kind] = 0;
}
ASMJIT_INLINE void setPassedOrder(uint32_t kind, 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(kind < kMaxVRegKinds);
_setPassedPacked(kind, ASMJIT_PACK32_4x8(a0, a1, a2, a3),
ASMJIT_PACK32_4x8(a4, a5, a6, a7));
// NOTE: This should always be called with all arguments known at compile
// time, so even if it looks scary it should be translated to a single
// instruction.
_passedRegs[kind] = (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) ;
}
ASMJIT_INLINE uint32_t getPreservedRegs(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _preservedRegs[kind];
}
ASMJIT_INLINE void setPreservedRegs(uint32_t kind, uint32_t regs) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_preservedRegs[kind] = regs;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint8_t _id; //!< Calling convention id, see \ref Id.
uint8_t _archType; //!< Architecture type (see \ref ArchInfo::Type).
uint8_t _algorithm; //!< Calling convention algorithm.
uint8_t _flags; //!< Calling convention flags.
uint8_t _naturalStackAlignment; //!< Natural stack alignment as defined by OS/ABI.
uint8_t _spillZoneSize; //!< Spill zone size (WIN64 == 32 bytes).
uint16_t _redZoneSize; //!< Red zone size (AMD64 == 128 bytes).
RegOrder _passedOrder[kMaxVRegKinds]; //!< Passed registers' order, per kind.
uint32_t _passedRegs[kMaxVRegKinds]; //!< Mask of all passed registers, per kind.
uint32_t _preservedRegs[kMaxVRegKinds];//!< Mask of all preserved registers, per kind.
};
// ============================================================================
// [asmjit::FuncArgIndex]
// ============================================================================
//! Function argument index (lo/hi).
ASMJIT_ENUM(FuncArgIndex) {
//! Maximum number of function arguments supported by AsmJit.
kFuncArgCount = 16,
//! Extended maximum number of arguments (used internally).
kFuncArgCountLoHi = kFuncArgCount * 2,
//! Index to the LO part of function argument (default).
//!
//! This value is typically omitted and added only if there is HI argument
//! accessed.
kFuncArgLo = 0,
//! Index to the HI part of function argument.
//!
//! HI part of function argument depends on target architecture. On x86 it's
//! typically used to transfer 64-bit integers (they form a pair of 32-bit
//! integers).
kFuncArgHi = kFuncArgCount
};
// ============================================================================
// [asmjit::FuncSignature]
// ============================================================================
//! Function signature.
//!
//! Contains information about function return type, count of arguments and
//! their TypeIds. Function signature is a low level structure which doesn't
//! contain platform specific or calling convention specific information.
struct FuncSignature {
enum {
//! Doesn't have variable number of arguments (`...`).
kNoVarArgs = 0xFF
};
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
//! Initialize the function signature.
ASMJIT_INLINE void init(uint32_t ccId, uint32_t ret, const uint8_t* args, uint32_t argCount) noexcept {
ASMJIT_ASSERT(ccId <= 0xFF);
ASMJIT_ASSERT(argCount <= 0xFF);
_callConv = static_cast<uint8_t>(ccId);
_argCount = static_cast<uint8_t>(argCount);
_vaIndex = kNoVarArgs;
_ret = ret;
_args = args;
}
ASMJIT_INLINE void reset() noexcept {
memset(this, 0, sizeof(*this));
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get the function's calling convention.
ASMJIT_INLINE uint32_t getCallConv() const noexcept { return _callConv; }
//! Get if the function has variable number of arguments (...).
ASMJIT_INLINE bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; }
//! Get the variable arguments (...) index, `kNoVarArgs` if none.
ASMJIT_INLINE uint32_t getVAIndex() const noexcept { return _vaIndex; }
//! Get the number of function arguments.
ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _argCount; }
ASMJIT_INLINE bool hasRet() const noexcept { return _ret != TypeId::kVoid; }
//! Get the return value type.
ASMJIT_INLINE uint32_t getRet() const noexcept { return _ret; }
//! Get the type of the argument at index `i`.
ASMJIT_INLINE uint32_t getArg(uint32_t i) const noexcept {
ASMJIT_ASSERT(i < _argCount);
return _args[i];
}
//! Get the array of function arguments' types.
ASMJIT_INLINE const uint8_t* getArgs() const noexcept { return _args; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint8_t _callConv; //!< Calling convention id.
uint8_t _argCount; //!< Count of arguments.
uint8_t _vaIndex; //!< Index to a first vararg or `kNoVarArgs`.
uint8_t _ret; //!< TypeId of a return value.
const uint8_t* _args; //!< TypeIds of function arguments.
};
// ============================================================================
// [asmjit::FuncSignatureT]
// ============================================================================
//! \internal
#define T(TYPE) TypeIdOf<TYPE>::kTypeId
//! Static function signature (no arguments).
template<typename RET>
class FuncSignature0 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature0(uint32_t ccId = CallConv::kIdHost) noexcept {
init(ccId, T(RET), nullptr, 0);
}
};
//! Static function signature (1 argument).
template<typename RET, typename A0>
class FuncSignature1 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature1(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (2 arguments).
template<typename RET, typename A0, typename A1>
class FuncSignature2 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature2(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (3 arguments).
template<typename RET, typename A0, typename A1, typename A2>
class FuncSignature3 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature3(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (4 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3>
class FuncSignature4 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature4(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (5 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4>
class FuncSignature5 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature5(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (6 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
class FuncSignature6 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature6(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (7 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
class FuncSignature7 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature7(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (8 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
class FuncSignature8 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature8(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (9 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
class FuncSignature9 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature9(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7), T(A8) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
//! Static function signature (10 arguments).
template<typename RET, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
class FuncSignature10 : public FuncSignature {
public:
ASMJIT_INLINE FuncSignature10(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { T(A0), T(A1), T(A2), T(A3), T(A4), T(A5), T(A6), T(A7), T(A8), T(A9) };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
#if ASMJIT_CC_HAS_VARIADIC_TEMPLATES
//! Static function signature (variadic).
template<typename RET, typename... ARGS>
class FuncSignatureT : public FuncSignature {
public:
ASMJIT_INLINE FuncSignatureT(uint32_t ccId = CallConv::kIdHost) noexcept {
static const uint8_t args[] = { (T(ARGS))... };
init(ccId, T(RET), args, ASMJIT_ARRAY_SIZE(args));
}
};
#endif // ASMJIT_CC_HAS_VARIADIC_TEMPLATES
#undef T
// ============================================================================
// [asmjit::FuncSignatureX]
// ============================================================================
//! Dynamic function signature.
class FuncSignatureX : public FuncSignature {
public:
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE FuncSignatureX(uint32_t ccId = CallConv::kIdHost) noexcept {
init(ccId, TypeId::kVoid, _builderArgList, 0);
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE void setCallConv(uint32_t ccId) noexcept {
ASMJIT_ASSERT(ccId <= 0xFF);
_callConv = static_cast<uint8_t>(ccId);
}
//! Set the return type to `retType`.
ASMJIT_INLINE void setRet(uint32_t retType) noexcept { _ret = retType; }
//! Set the return type based on `T`.
template<typename T>
ASMJIT_INLINE void setRetT() noexcept { setRet(TypeIdOf<T>::kTypeId); }
//! Set the argument at index `i` to the `type`
ASMJIT_INLINE void setArg(uint32_t i, uint32_t type) noexcept {
ASMJIT_ASSERT(i < _argCount);
_builderArgList[i] = type;
}
//! Set the argument at index `i` to the type based on `T`.
template<typename T>
ASMJIT_INLINE void setArgT(uint32_t i) noexcept { setArg(i, TypeIdOf<T>::kTypeId); }
//! Append an argument of `type` to the function prototype.
ASMJIT_INLINE void addArg(uint32_t type) noexcept {
ASMJIT_ASSERT(_argCount < kFuncArgCount);
_builderArgList[_argCount++] = static_cast<uint8_t>(type);
}
//! Append an argument of type based on `T` to the function prototype.
template<typename T>
ASMJIT_INLINE void addArgT() noexcept { addArg(TypeIdOf<T>::kTypeId); }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint8_t _builderArgList[kFuncArgCount];
};
// ============================================================================
// [asmjit::FuncDetail]
// ============================================================================
//! Function detail - CallConv and expanded FuncSignature.
//!
//! Function details is architecture and OS dependent representation of function.
//! It contains calling convention and expanded function signature so all
//! arguments have assigned either register type & id or stack address.
class FuncDetail {
public:
ASMJIT_ENUM(Limits) {
kMaxVRegKinds = Globals::kMaxVRegKinds
};
//! Argument or return value as defined by `FuncSignature`, but with register
//! or stack address (and other metadata) assigned.
struct Value {
ASMJIT_ENUM(Parts) {
kTypeIdShift = 24,
kTypeIdMask = 0xFF000000U,
kRegTypeShift = 8,
kRegTypeMask = 0x0000FF00U,
kRegIdShift = 0,
kRegIdMask = 0x000000FFU,
kStackOffsetShift = 0,
kStackOffsetMask = 0x0000FFFFU,
kIsByReg = 0x00010000U,
kIsByStack = 0x00020000U,
kIsIndirect = 0x00040000U
};
//! Get if this value is initialized (i.e. contains a valid data).
ASMJIT_INLINE bool isInitialized() const noexcept { return _value != 0; }
//! Initialize this in/out by a given `typeId`.
ASMJIT_INLINE void initTypeId(uint32_t typeId) noexcept { _value = typeId << kTypeIdShift; }
//! Initialize this in/out by a given `typeId`, `regType`, and `regId`.
ASMJIT_INLINE void initReg(uint32_t typeId, uint32_t regType, uint32_t regId) noexcept {
_value = (typeId << kTypeIdShift) | (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsByReg;
}
//! Initialize this in/out by a given `typeId` and `offset`.
ASMJIT_INLINE void initStack(uint32_t typeId, uint32_t stackOffset) noexcept {
_value = (typeId << kTypeIdShift) | (stackOffset << kStackOffsetShift) | kIsByStack;
}
//! Reset the value to its uninitialized and unassigned state.
ASMJIT_INLINE void reset() noexcept { _value = 0; }
ASMJIT_INLINE void assignToReg(uint32_t regType, uint32_t regId) noexcept {
ASMJIT_ASSERT(!isAssigned());
_value |= (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsByReg;
}
ASMJIT_INLINE void assignToStack(int32_t offset) noexcept {
ASMJIT_ASSERT(!isAssigned());
_value |= (offset << kStackOffsetShift) | kIsByStack;
}
//! Get if this argument is passed by register.
ASMJIT_INLINE bool byReg() const noexcept { return (_value & kIsByReg) != 0; }
//! Get if this argument is passed by stack.
ASMJIT_INLINE bool byStack() const noexcept { return (_value & kIsByStack) != 0; }
//! Get if this argument is passed by register.
ASMJIT_INLINE bool isAssigned() const noexcept { return (_value & (kIsByReg | kIsByStack)) != 0; }
//! Get if this argument is passed through a pointer (used by WIN64 to pass XMM|YMM|ZMM).
ASMJIT_INLINE bool isIndirect() const noexcept { return (_value & kIsIndirect) != 0; }
//! Get virtual type of this argument or return value.
ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _value >> kTypeIdShift; }
//! Get a register type of the register used to pass the argument or return the value.
ASMJIT_INLINE uint32_t getRegType() const noexcept { return (_value & kRegTypeMask) >> kRegTypeShift; }
//! Get a physical id of the register used to pass the argument or return the value.
ASMJIT_INLINE uint32_t getRegId() const noexcept { return (_value & kRegIdMask) >> kRegIdShift; }
//! Get a stack offset of this argument (always positive).
ASMJIT_INLINE int32_t getStackOffset() const noexcept { return (_value & kStackOffsetMask) >> kStackOffsetShift; }
uint32_t _value;
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE FuncDetail() noexcept { reset(); }
ASMJIT_INLINE FuncDetail(const FuncDetail& other) noexcept {
::memcpy(this, &other, sizeof(*this));
}
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
//! Initialize this `FuncDetail` to the given signature.
ASMJIT_API Error init(const FuncSignature& sign);
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
// --------------------------------------------------------------------------
// [Accessors - Calling Convention]
// --------------------------------------------------------------------------
//! Get the function's calling convention, see `CallConv`.
ASMJIT_INLINE const CallConv& getCallConv() const noexcept { return _callConv; }
//! Get CallConv flags, see \ref CallConv::Flags.
ASMJIT_INLINE uint32_t getFlags() const noexcept { return _callConv.getFlags(); }
//! Check if a CallConv `flag` is set, see \ref CallConv::Flags.
ASMJIT_INLINE bool hasFlag(uint32_t ccFlag) const noexcept { return _callConv.hasFlag(ccFlag); }
// --------------------------------------------------------------------------
// [Accessors - Arguments and Return]
// --------------------------------------------------------------------------
//! Get count of function return values.
ASMJIT_INLINE uint32_t getRetCount() const noexcept { return _retCount; }
//! Get the number of function arguments.
ASMJIT_INLINE uint32_t getArgCount() const noexcept { return _argCount; }
//! Get whether the function has a return value.
ASMJIT_INLINE bool hasRet() const noexcept { return _retCount != 0; }
//! Get function return value.
ASMJIT_INLINE Value& getRet(size_t index = 0) noexcept {
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets));
return _rets[index];
}
//! Get function return value (const).
ASMJIT_INLINE const Value& getRet(size_t index = 0) const noexcept {
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_rets));
return _rets[index];
}
//! Get function arguments array.
ASMJIT_INLINE Value* getArgs() noexcept { return _args; }
//! Get function arguments array (const).
ASMJIT_INLINE const Value* getArgs() const noexcept { return _args; }
ASMJIT_INLINE bool hasArg(size_t index) const noexcept {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
return _args[index].isInitialized();
}
//! Get function argument at index `index`.
ASMJIT_INLINE Value& getArg(size_t index) noexcept {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
return _args[index];
}
//! Get function argument at index `index`.
ASMJIT_INLINE const Value& getArg(size_t index) const noexcept {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
return _args[index];
}
ASMJIT_INLINE void resetArg(size_t index) noexcept {
ASMJIT_ASSERT(index < kFuncArgCountLoHi);
_args[index].reset();
}
//! Get if the function passes one or more argument by stack.
ASMJIT_INLINE bool hasStackArgs() const noexcept { return _argStackSize != 0; }
//! Get stack size needed for function arguments passed on the stack.
ASMJIT_INLINE uint32_t getArgStackSize() const noexcept { return _argStackSize; }
ASMJIT_INLINE uint32_t getNaturalStackAlignment() const noexcept { return _callConv.getNaturalStackAlignment(); }
ASMJIT_INLINE uint32_t getSpillZoneSize() const noexcept { return _callConv.getSpillZoneSize(); }
ASMJIT_INLINE uint32_t getRedZoneSize() const noexcept { return _callConv.getRedZoneSize(); }
ASMJIT_INLINE uint32_t getPassedRegs(uint32_t kind) const noexcept { return _callConv.getPassedRegs(kind); }
ASMJIT_INLINE uint32_t getPreservedRegs(uint32_t kind) const noexcept { return _callConv.getPreservedRegs(kind); }
ASMJIT_INLINE uint32_t getUsedRegs(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _usedRegs[kind];
}
ASMJIT_INLINE void addUsedRegs(uint32_t kind, uint32_t regs) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_usedRegs[kind] |= regs;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
CallConv _callConv; //!< Calling convention.
uint8_t _argCount; //!< Number of function arguments.
uint8_t _retCount; //!< Number of function return values.
uint32_t _usedRegs[kMaxVRegKinds]; //!< Registers that contains arguments (signature dependent).
uint32_t _argStackSize; //!< Size of arguments passed by stack.
Value _rets[2]; //!< Function return values.
Value _args[kFuncArgCountLoHi]; //!< Function arguments.
};
// ============================================================================
// [asmjit::FuncFrameInfo]
// ============================================================================
//! Function-frame information.
//!
//! This structure can be used to create a function frame in a cross-platform
//! way. It contains information about the function's stack to be used and
//! registers to be saved and restored. Based on this information in can
//! calculate the optimal layout of a function as \ref FuncFrameLayout.
struct FuncFrameInfo {
ASMJIT_ENUM(Limits) {
kMaxVRegKinds = Globals::kMaxVRegKinds
};
//! Attributes.
//!
//! Attributes are designed in a way that all are initially false, and user
//! or function-frame finalizer sets them when necessary. Architecture-specific
//! attributes are prefixed with the architecture name.
ASMJIT_ENUM(Attributes) {
kAttrPreserveFP = 0x00000001U, //!< Preserve frame pointer (EBP|RBP).
kAttrCompactPE = 0x00000002U, //!< Use smaller, but possibly slower prolog/epilog.
kAttrHasCalls = 0x00000004U, //!< Function calls other functions (is not leaf).
kX86AttrAlignedVecSR = 0x00010000U, //!< Use aligned save/restore of VEC regs.
kX86AttrMmxCleanup = 0x00020000U, //!< Emit EMMS instruction in epilog (X86).
kX86AttrAvxCleanup = 0x00040000U, //!< Emit VZEROUPPER instruction in epilog (X86).
kX86AttrAvxEnabled = 0x00080000U //!< Use AVX instead of SSE for all operations (X86).
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE FuncFrameInfo() noexcept { reset(); }
ASMJIT_INLINE FuncFrameInfo(const FuncFrameInfo& other) noexcept {
::memcpy(this, &other, sizeof(*this));
}
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() noexcept {
::memset(this, 0, sizeof(*this));
_stackArgsRegId = Globals::kInvalidRegId;
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get frame-info flags, see \ref Attributes.
ASMJIT_INLINE uint32_t getAttributes() const noexcept { return _attributes; }
//! Check if a frame-info `flag` is set, see \ref Attributes.
ASMJIT_INLINE bool hasAttribute(uint32_t attr) const noexcept { return (_attributes & attr) != 0; }
//! Add `flags` to the frame-info, see \ref Attributes.
ASMJIT_INLINE void addAttributes(uint32_t attrs) noexcept { _attributes |= attrs; }
//! Clear `flags` from the frame-info, see \ref Attributes.
ASMJIT_INLINE void clearAttributes(uint32_t attrs) noexcept { _attributes &= ~attrs; }
//! Get if the function preserves frame pointer (EBP|ESP on X86).
ASMJIT_INLINE bool hasPreservedFP() const noexcept { return (_attributes & kAttrPreserveFP) != 0; }
//! Enable preserved frame pointer.
ASMJIT_INLINE void enablePreservedFP() noexcept { _attributes |= kAttrPreserveFP; }
//! Disable preserved frame pointer.
ASMJIT_INLINE void disablePreservedFP() noexcept { _attributes &= ~kAttrPreserveFP; }
//! Get if the function prolog and epilog should be compacted (as small as possible).
ASMJIT_INLINE bool hasCompactPE() const noexcept { return (_attributes & kAttrCompactPE) != 0; }
//! Enable compact prolog/epilog.
ASMJIT_INLINE void enableCompactPE() noexcept { _attributes |= kAttrCompactPE; }
//! Disable compact prolog/epilog.
ASMJIT_INLINE void disableCompactPE() noexcept { _attributes &= ~kAttrCompactPE; }
//! Get if the function calls other functions.
ASMJIT_INLINE bool hasCalls() const noexcept { return (_attributes & kAttrHasCalls) != 0; }
//! Set `kFlagHasCalls` to true.
ASMJIT_INLINE void enableCalls() noexcept { _attributes |= kAttrHasCalls; }
//! Set `kFlagHasCalls` to false.
ASMJIT_INLINE void disableCalls() noexcept { _attributes &= ~kAttrHasCalls; }
//! Get if the function contains MMX cleanup - 'emms' instruction in epilog.
ASMJIT_INLINE bool hasMmxCleanup() const noexcept { return (_attributes & kX86AttrMmxCleanup) != 0; }
//! Enable MMX cleanup.
ASMJIT_INLINE void enableMmxCleanup() noexcept { _attributes |= kX86AttrMmxCleanup; }
//! Disable MMX cleanup.
ASMJIT_INLINE void disableMmxCleanup() noexcept { _attributes &= ~kX86AttrMmxCleanup; }
//! Get if the function contains AVX cleanup - 'vzeroupper' instruction in epilog.
ASMJIT_INLINE bool hasAvxCleanup() const noexcept { return (_attributes & kX86AttrAvxCleanup) != 0; }
//! Enable AVX cleanup.
ASMJIT_INLINE void enableAvxCleanup() noexcept { _attributes |= kX86AttrAvxCleanup; }
//! Disable AVX cleanup.
ASMJIT_INLINE void disableAvxCleanup() noexcept { _attributes &= ~kX86AttrAvxCleanup; }
//! Get if the function contains AVX cleanup - 'vzeroupper' instruction in epilog.
ASMJIT_INLINE bool isAvxEnabled() const noexcept { return (_attributes & kX86AttrAvxEnabled) != 0; }
//! Enable AVX cleanup.
ASMJIT_INLINE void enableAvx() noexcept { _attributes |= kX86AttrAvxEnabled; }
//! Disable AVX cleanup.
ASMJIT_INLINE void disableAvx() noexcept { _attributes &= ~kX86AttrAvxEnabled; }
//! Get which registers (by `kind`) are saved/restored in prolog/epilog, respectively.
ASMJIT_INLINE uint32_t getDirtyRegs(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _dirtyRegs[kind];
}
//! Set which registers (by `kind`) are saved/restored in prolog/epilog, respectively.
ASMJIT_INLINE void setDirtyRegs(uint32_t kind, uint32_t regs) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_dirtyRegs[kind] = regs;
}
//! Add registers (by `kind`) to saved/restored registers.
ASMJIT_INLINE void addDirtyRegs(uint32_t kind, uint32_t regs) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_dirtyRegs[kind] |= regs;
}
ASMJIT_INLINE void setAllDirty() noexcept {
_dirtyRegs[0] = 0xFFFFFFFFU;
_dirtyRegs[1] = 0xFFFFFFFFU;
_dirtyRegs[2] = 0xFFFFFFFFU;
_dirtyRegs[3] = 0xFFFFFFFFU;
}
ASMJIT_INLINE void setAllDirty(uint32_t kind) noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
_dirtyRegs[kind] = 0xFFFFFFFFU;
}
//! Get stack-frame size used by the function.
ASMJIT_INLINE uint32_t getStackFrameSize() const noexcept { return _stackFrameSize; }
//! Get call-frame size used by the function.
ASMJIT_INLINE uint32_t getCallFrameSize() const noexcept { return _callFrameSize; }
//! Get minimum stack-frame alignment required by the function.
ASMJIT_INLINE uint32_t getStackFrameAlignment() const noexcept { return _stackFrameAlignment; }
//! Get minimum call-frame alignment required by the function.
ASMJIT_INLINE uint32_t getCallFrameAlignment() const noexcept { return _callFrameAlignment; }
ASMJIT_INLINE void setStackFrameSize(uint32_t size) noexcept { _stackFrameSize = size; }
ASMJIT_INLINE void setCallFrameSize(uint32_t size) noexcept { _callFrameSize = size; }
ASMJIT_INLINE void setStackFrameAlignment(uint32_t value) noexcept {
ASMJIT_ASSERT(value < 256);
_stackFrameAlignment = static_cast<uint8_t>(value);
}
ASMJIT_INLINE void setCallFrameAlignment(uint32_t value) noexcept {
ASMJIT_ASSERT(value < 256);
_callFrameAlignment = static_cast<uint8_t>(value);
}
ASMJIT_INLINE void mergeStackFrameSize(uint32_t size) noexcept { _stackFrameSize = std::max<uint32_t>(_stackFrameSize, size); }
ASMJIT_INLINE void mergeCallFrameSize(uint32_t size) noexcept { _callFrameSize = std::max<uint32_t>(_callFrameSize, size); }
ASMJIT_INLINE void mergeStackFrameAlignment(uint32_t value) noexcept {
ASMJIT_ASSERT(value < 256);
_stackFrameAlignment = static_cast<uint8_t>(std::max<uint32_t>(_stackFrameAlignment, value));
}
ASMJIT_INLINE void mergeCallFrameAlignment(uint32_t value) noexcept {
ASMJIT_ASSERT(value < 256);
_callFrameAlignment = static_cast<uint8_t>(std::max<uint32_t>(_callFrameAlignment, value));
}
ASMJIT_INLINE bool hasStackArgsRegId() const noexcept {
return _stackArgsRegId != Globals::kInvalidRegId;
}
ASMJIT_INLINE uint32_t getStackArgsRegId() const noexcept { return _stackArgsRegId; }
ASMJIT_INLINE void setStackArgsRegId(uint32_t regId) { _stackArgsRegId = regId; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint32_t _attributes; //!< Function attributes.
uint32_t _dirtyRegs[kMaxVRegKinds]; //!< Registers used by the function.
uint8_t _stackFrameAlignment; //!< Minimum alignment of stack-frame.
uint8_t _callFrameAlignment; //!< Minimum alignment of call-frame.
uint8_t _stackArgsRegId; //!< Register that holds base-address to arguments passed by stack.
uint32_t _stackFrameSize; //!< Size of a stack-frame used by the function.
uint32_t _callFrameSize; //!< Size of a call-frame (not part of _stackFrameSize).
};
// ============================================================================
// [asmjit::FuncFrameLayout]
// ============================================================================
//! Function-frame layout.
//!
//! Function layout is used directly by prolog and epilog insertion helpers. It
//! contains only information necessary to insert proper prolog and epilog, and
//! should be always calculated from \ref FuncDetail and \ref FuncFrameInfo, where
//! \ref FuncDetail defines function's calling convention and signature, and \ref
//! FuncFrameInfo specifies how much stack is used, and which registers are dirty.
struct FuncFrameLayout {
ASMJIT_ENUM(Limits) {
kMaxVRegKinds = Globals::kMaxVRegKinds
};
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
ASMJIT_API Error init(const FuncDetail& func, const FuncFrameInfo& ffi) noexcept;
ASMJIT_INLINE void reset() noexcept { ::memset(this, 0, sizeof(*this)); }
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool hasPreservedFP() const noexcept { return static_cast<bool>(_preservedFP); }
ASMJIT_INLINE bool hasDsaSlotUsed() const noexcept { return static_cast<bool>(_dsaSlotUsed); }
ASMJIT_INLINE bool hasAlignedVecSR() const noexcept { return static_cast<bool>(_alignedVecSR); }
ASMJIT_INLINE bool hasDynamicAlignment() const noexcept { return static_cast<bool>(_dynamicAlignment); }
ASMJIT_INLINE bool hasMmxCleanup() const noexcept { return static_cast<bool>(_mmxCleanup); }
ASMJIT_INLINE bool hasAvxCleanup() const noexcept { return static_cast<bool>(_avxCleanup); }
ASMJIT_INLINE bool isAvxEnabled() const noexcept { return static_cast<bool>(_avxEnabled); }
ASMJIT_INLINE uint32_t getSavedRegs(uint32_t kind) const noexcept {
ASMJIT_ASSERT(kind < kMaxVRegKinds);
return _savedRegs[kind];
}
//! Get stack size.
ASMJIT_INLINE uint32_t getStackSize() const noexcept { return _stackSize; }
//! Get stack alignment.
ASMJIT_INLINE uint32_t getStackAlignment() const noexcept { return _stackAlignment; }
//! Get the offset needed to access the function's stack (it skips call-stack).
ASMJIT_INLINE uint32_t getStackBaseOffset() const noexcept { return _stackBaseOffset; }
//! Get stack size required to save GP registers.
ASMJIT_INLINE uint32_t getGpStackSize() const noexcept { return _gpStackSize; }
//! Get stack size required to save VEC registers.
ASMJIT_INLINE uint32_t getVecStackSize() const noexcept { return _vecStackSize; }
ASMJIT_INLINE uint32_t getGpStackOffset() const noexcept { return _gpStackOffset; }
ASMJIT_INLINE uint32_t getVecStackOffset() const noexcept { return _vecStackOffset; }
ASMJIT_INLINE uint32_t getStackArgsRegId() const noexcept { return _stackArgsRegId; }
ASMJIT_INLINE uint32_t getStackArgsOffset() const noexcept { return _stackArgsOffset; }
ASMJIT_INLINE bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; }
ASMJIT_INLINE uint32_t getStackAdjustment() const noexcept { return _stackAdjustment; }
ASMJIT_INLINE bool hasCalleeStackCleanup() const noexcept { return _calleeStackCleanup != 0; }
ASMJIT_INLINE uint32_t getCalleeStackCleanup() const noexcept { return _calleeStackCleanup; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint8_t _stackAlignment; //!< Final stack alignment of the functions.
uint8_t _stackBaseRegId; //!< GP register that holds address of base stack address.
uint8_t _stackArgsRegId; //!< GP register that holds address of the first argument passed by stack.
uint32_t _savedRegs[kMaxVRegKinds]; //!< Registers that will be saved/restored in prolog/epilog.
uint32_t _preservedFP : 1; //!< Function preserves frame-pointer.
uint32_t _dsaSlotUsed : 1; //!< True if `_dsaSlot` contains a valid memory slot/offset.
uint32_t _alignedVecSR : 1; //!< Use instructions that perform aligned ops to save/restore XMM regs.
uint32_t _dynamicAlignment : 1; //!< Function must dynamically align the stack.
uint32_t _mmxCleanup : 1; //!< Emit 'emms' in epilog (X86).
uint32_t _avxCleanup : 1; //!< Emit 'vzeroupper' in epilog (X86).
uint32_t _avxEnabled : 1; //!< Use AVX instead of SSE for SIMD saves/restores (X86).
uint32_t _stackSize; //!< Stack size (sum of function's stack and call stack).
uint32_t _stackBaseOffset; //!< Stack offset (non-zero if kFlagHasCalls is set).
uint32_t _stackAdjustment; //!< Stack adjustment in prolog/epilog.
uint32_t _stackArgsOffset; //!< Offset to the first argument passed by stack of _stackArgsRegId.
uint32_t _dsaSlot; //!< Memory slot where the prolog inserter stores previous (unaligned) ESP.
uint16_t _calleeStackCleanup; //!< How many bytes the callee should add to the stack (X86 STDCALL).
uint16_t _gpStackSize; //!< Stack size required to save GP regs.
uint16_t _vecStackSize; //!< Stack size required to save VEC regs.
uint32_t _gpStackOffset; //!< Offset where saved GP regs are stored.
uint32_t _vecStackOffset; //!< Offset where saved GP regs are stored.
};
// ============================================================================
// [asmjit::FuncArgsMapper]
// ============================================================================
//! Assign a physical register to each function argument.
//!
//! This is used to specify where each function argument should be shuffled
//! or allocated (in case it's passed by stack).
class FuncArgsMapper {
public:
struct Value {
// NOTE: The layout is compatible with FuncDetail::Value except stack.
ASMJIT_ENUM(Parts) {
kTypeIdShift = 24,
kTypeIdMask = 0xFF000000U,
kRegTypeShift = 8,
kRegTypeMask = 0x0000FF00U,
kRegIdShift = 0,
kRegIdMask = 0x000000FFU,
kIsAssigned = 0x00010000U
};
//! Get if this value is initialized (i.e. contains a valid data).
ASMJIT_INLINE bool isAssigned() const noexcept { return _value != 0; }
//! Initialize this in/out by a given `typeId`, `regType`, and `regId`.
ASMJIT_INLINE void assign(uint32_t typeId, uint32_t regType, uint32_t regId) noexcept {
_value = (typeId << kTypeIdShift) | (regType << kRegTypeShift) | (regId << kRegIdShift) | kIsAssigned;
}
//! Reset the value to its unassigned state.
ASMJIT_INLINE void reset() noexcept { _value = 0; }
//! Get virtual type of this argument or return value.
ASMJIT_INLINE uint32_t getTypeId() const noexcept { return _value >> kTypeIdShift; }
//! Get a register type of the register used to pass the argument or return the value.
ASMJIT_INLINE uint32_t getRegType() const noexcept { return (_value & kRegTypeMask) >> kRegTypeShift; }
//! Get a physical id of the register used to pass the argument or return the value.
ASMJIT_INLINE uint32_t getRegId() const noexcept { return (_value & kRegIdMask) >> kRegIdShift; }
uint32_t _value;
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
explicit ASMJIT_INLINE FuncArgsMapper(const FuncDetail* fd) noexcept { reset(fd); }
ASMJIT_INLINE FuncArgsMapper(const FuncArgsMapper& other) noexcept {
::memcpy(this, &other, sizeof(*this));
}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset(const FuncDetail* fd = nullptr) noexcept {
_funcDetail = fd;
::memset(_args, 0, sizeof(_args));
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE const FuncDetail* getFuncDetail() const noexcept { return _funcDetail; }
ASMJIT_INLINE void setFuncDetail(const FuncDetail* fd) noexcept { _funcDetail = fd; }
ASMJIT_INLINE Value& getArg(size_t index) noexcept {
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args));
return _args[index];
}
ASMJIT_INLINE const Value& getArg(size_t index) const noexcept {
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args));
return _args[index];
}
ASMJIT_INLINE bool isAssigned(size_t index) const noexcept {
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args));
return _args[index].isAssigned();
}
ASMJIT_INLINE void assign(size_t index, const Reg& reg, uint32_t typeId = TypeId::kVoid) noexcept {
// Not designed for virtual registers.
ASMJIT_ASSERT(index < ASMJIT_ARRAY_SIZE(_args));
ASMJIT_ASSERT(reg.isPhysReg());
_args[index].assign(typeId, reg.getType(), reg.getId());
}
// NOTE: All `assignAll()` methods are shortcuts to assign all arguments at
// once, however, since registers are passed all at once these initializers
// don't provide any way to pass TypeId and/or to keep any argument between
// the arguments passed uninitialized.
ASMJIT_INLINE void assignAll(const Reg& a0) noexcept {
assign(0, a0);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1) noexcept {
assign(0, a0); assign(1, a1);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3);
assign(4, a4);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3);
assign(4, a4); assign(5, a5);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5, const Reg& a6) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3);
assign(4, a4); assign(5, a5); assign(6, a6);
}
ASMJIT_INLINE void assignAll(const Reg& a0, const Reg& a1, const Reg& a2, const Reg& a3, const Reg& a4, const Reg& a5, const Reg& a6, const Reg& a7) noexcept {
assign(0, a0); assign(1, a1); assign(2, a2); assign(3, a3);
assign(4, a4); assign(5, a5); assign(6, a6); assign(7, a7);
}
// --------------------------------------------------------------------------
// [Utilities]
// --------------------------------------------------------------------------
//! Update `FuncFrameInfo` accordingly to FuncArgsMapper.
//!
//! This method must be called if you use `FuncArgsMapper` and you plan to
//! use `FuncUtils::allocArgs()` to remap all arguments after the prolog is
//! inserted.
ASMJIT_API Error updateFrameInfo(FuncFrameInfo& ffi) const noexcept;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
const FuncDetail* _funcDetail; //!< Function detail.
Value _args[kFuncArgCountLoHi]; //!< Mapping of each function argument.
};
// ============================================================================
// [asmjit::FuncUtils]
// ============================================================================
struct FuncUtils {
ASMJIT_API static Error emitProlog(CodeEmitter* emitter, const FuncFrameLayout& layout);
ASMJIT_API static Error emitEpilog(CodeEmitter* emitter, const FuncFrameLayout& layout);
ASMJIT_API static Error allocArgs(CodeEmitter* emitter, const FuncFrameLayout& layout, const FuncArgsMapper& args);
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_FUNC_H
......@@ -7,24 +7,112 @@
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
// [Dependencies]
#include "../base/globals.h"
#include "../base/utils.h"
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Assert]
// [asmjit::DebugUtils]
// ============================================================================
void assertionFailed(const char* exp, const char* file, int line) {
::fprintf(stderr, "Assertion failed: %s\n, file %s, line %d\n", exp, file, line);
#if !defined(ASMJIT_DISABLE_TEXT)
static const char errorMessages[] =
"Ok\0"
"No heap memory\0"
"No virtual memory\0"
"Invalid argument\0"
"Invalid state\0"
"Invalid architecture\0"
"Not initialized\0"
"Already initialized\0"
"Feature not enabled\0"
"Slot occupied\0"
"No code generated\0"
"Code too large\0"
"Invalid label\0"
"Label index overflow\0"
"Label already bound\0"
"Label already defined\0"
"Label name too long\0"
"Invalid label name\0"
"Invalid parent label\0"
"Non-local label can't have parent\0"
"Relocation index overflow\0"
"Invalid relocation entry\0"
"Invalid instruction\0"
"Invalid register type\0"
"Invalid register kind\0"
"Invalid register's physical id\0"
"Invalid register's virtual id\0"
"Invalid prefix combination\0"
"Invalid lock prefix\0"
"Invalid xacquire prefix\0"
"Invalid xrelease prefix\0"
"Invalid rep prefix\0"
"Invalid rex prefix\0"
"Invalid mask, expected {k}\0"
"Invalid use of {k}\0"
"Invalid use of {k}{z}\0"
"Invalid broadcast {1tox}\0"
"Invalid {er} or {sae} option\0"
"Invalid address\0"
"Invalid address index\0"
"Invalid address scale\0"
"Invalid use of 64-bit address\0"
"Invalid displacement\0"
"Invalid segment\0"
"Invalid immediate value\0"
"Invalid operand size\0"
"Ambiguous operand size\0"
"Operand size mismatch\0"
"Invalid type-info\0"
"Invalid use of a low 8-bit GPB register\0"
"Invalid use of a 64-bit GPQ register in 32-bit mode\0"
"Invalid use of an 80-bit float\0"
"Not consecutive registers\0"
"No more physical registers\0"
"Overlapped registers\0"
"Overlapping register and arguments base-address register\0"
"Unknown error\0";
#endif // ASMJIT_DISABLE_TEXT
ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept {
#if !defined(ASMJIT_DISABLE_TEXT)
return Utils::findPackedString(errorMessages, std::min<Error>(err, kErrorCount));
#else
static const char noMessage[] = "";
return noMessage;
#endif
}
ASMJIT_FAVOR_SIZE void DebugUtils::debugOutput(const char* str) noexcept {
#if ASMJIT_OS_WINDOWS
::OutputDebugStringA(str);
#else
::fputs(str, stderr);
#endif
}
ASMJIT_FAVOR_SIZE void DebugUtils::assertionFailed(const char* file, int line, const char* msg) noexcept {
char str[1024];
snprintf(str, 1024,
"[asmjit] Assertion failed at %s (line %d):\n"
"[asmjit] %s\n", file, line, msg);
// Support buggy `snprintf` implementations.
str[1023] = '\0';
debugOutput(str);
::abort();
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
......@@ -8,34 +8,25 @@
#ifndef _ASMJIT_BASE_GLOBALS_H
#define _ASMJIT_BASE_GLOBALS_H
// [Dependencies - AsmJit]
#include "../build.h"
// [Dependencies]
#include "../asmjit_build.h"
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_general
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::Ptr / SignedPtr]
// [asmjit::Globals]
// ============================================================================
//! 64-bit unsigned pointer, compatible with JIT and non-JIT generators.
//!
//! This is the preferred pointer type to use with AsmJit library. It has a
//! capability to hold any pointer for any architecture making it an ideal
//! candidate for cross-platform code generation.
typedef uint64_t Ptr;
//! 64-bit signed pointer, like \ref Ptr, but made signed.
typedef int64_t SignedPtr;
enum { kInvalidValue = 0xFFFFFFFFU };
// ============================================================================
// [asmjit::GlobalDefs]
// ============================================================================
//! AsmJit globals.
namespace Globals {
//! Invalid index
//!
......@@ -45,133 +36,306 @@ typedef int64_t SignedPtr;
static const size_t kInvalidIndex = ~static_cast<size_t>(0);
//! Invalid base address.
static const Ptr kNoBaseAddress = static_cast<Ptr>(static_cast<SignedPtr>(-1));
static const uint64_t kNoBaseAddress = ~static_cast<uint64_t>(0);
//! Global constants.
ASMJIT_ENUM(GlobalDefs) {
//! Invalid value or operand id.
kInvalidValue = 0xFFFFFFFF,
//! Invalid register index.
kInvalidReg = 0xFF,
//! Invalid variable type.
kInvalidVar = 0xFF,
//! Global definitions.
ASMJIT_ENUM(Defs) {
//! Invalid register id.
kInvalidRegId = 0xFF,
//! Host memory allocator overhead.
//!
//! The overhead is decremented from all zone allocators so the operating
//! system doesn't have allocate extra virtual page to keep tract of the
//! requested memory block.
//!
//! The number is actually a guess.
kMemAllocOverhead = sizeof(intptr_t) * 4,
kAllocOverhead = static_cast<int>(sizeof(intptr_t) * 4),
//! Aggressive growing strategy threshold.
kAllocThreshold = 8192 * 1024
};
ASMJIT_ENUM(Limits) {
//! Count of register kinds that are important to Function API and CodeCompiler.
//! The target architecture can define more register kinds for special registers,
//! but these will never map to virtual registers and will never be used to pass
//! and return function arguments and function return values, respectively.
kMaxVRegKinds = 4,
//! Memory grow threshold.
//! Maximum number of physical registers of all kinds of all supported
//! architectures. This is only important for \ref CodeCompiler and its
//! \ref RAPass (register allocator pass).
//!
//! After the grow threshold is reached the capacity won't be doubled
//! anymore.
kMemAllocGrowMax = 8192 * 1024
//! NOTE: The distribution of these registers is architecture specific.
kMaxPhysRegs = 64,
//! Maximum alignment.
kMaxAlignment = 64,
//! Maximum label or symbol length in bytes (take into consideration that a
//! single UTF-8 character can take more than single byte to encode it).
kMaxLabelLength = 2048
};
} // Globals namespace
// ============================================================================
// [asmjit::ArchId]
// [asmjit::Error]
// ============================================================================
//! CPU architecture identifier.
ASMJIT_ENUM(ArchId) {
//! No/Unknown architecture.
kArchNone = 0,
//! AsmJit error type (uint32_t).
typedef uint32_t Error;
//! X86 architecture.
kArchX86 = 1,
//! X64 architecture, also called AMD64.
kArchX64 = 2,
//! AsmJit error codes.
ASMJIT_ENUM(ErrorCode) {
//! No error (success).
//!
//! This is default state and state you want.
kErrorOk = 0,
//! Arm architecture.
kArchArm = 4,
//! Heap memory allocation failed.
kErrorNoHeapMemory,
#if defined(ASMJIT_ARCH_X86)
kArchHost = kArchX86,
#endif // ASMJIT_ARCH_X86
//! Virtual memory allocation failed.
kErrorNoVirtualMemory,
#if defined(ASMJIT_ARCH_X64)
kArchHost = kArchX64,
#endif // ASMJIT_ARCH_X64
//! Invalid argument.
kErrorInvalidArgument,
#if defined(ASMJIT_ARCH_ARM)
kArchHost = kArchArm,
#endif // ASMJIT_ARCH_ARM
//! Invalid state.
//!
//! If this error is returned it means that either you are doing something
//! wrong or AsmJit caught itself by doing something wrong. This error should
//! not be underestimated.
kErrorInvalidState,
//! Whether the host is 64-bit.
kArchHost64Bit = sizeof(intptr_t) >= 8
};
//! Invalid or incompatible architecture.
kErrorInvalidArch,
//! \}
//! The object is not initialized.
kErrorNotInitialized,
//! The object is already initialized.
kErrorAlreadyInitialized,
//! Built-in feature was disabled at compile time and it's not available.
kErrorFeatureNotEnabled,
//! CodeHolder can't have attached more than one \ref Assembler at a time.
kErrorSlotOccupied,
//! No code generated.
//!
//! Returned by runtime if the \ref CodeHolder contains no code.
kErrorNoCodeGenerated,
//! Code generated is larger than allowed.
kErrorCodeTooLarge,
//! Attempt to use uninitialized label.
kErrorInvalidLabel,
//! Label index overflow - a single `Assembler` instance can hold more than
//! 2 billion labels (2147483391 to be exact). If there is an attempt to
//! create more labels this error is returned.
kErrorLabelIndexOverflow,
//! Label is already bound.
kErrorLabelAlreadyBound,
//! Label is already defined (named labels).
kErrorLabelAlreadyDefined,
//! Label name is too long.
kErrorLabelNameTooLong,
//! Label must always be local if it's anonymous (without a name).
kErrorInvalidLabelName,
//! Parent id passed to `CodeHolder::newNamedLabelId()` was invalid.
kErrorInvalidParentLabel,
//! Parent id specified for a non-local (global) label.
kErrorNonLocalLabelCantHaveParent,
//! Relocation index overflow.
kErrorRelocIndexOverflow,
//! Invalid relocation entry.
kErrorInvalidRelocEntry,
//! Invalid instruction.
kErrorInvalidInstruction,
//! Invalid register type.
kErrorInvalidRegType,
//! Invalid register kind.
kErrorInvalidRegKind,
//! Invalid register's physical id.
kErrorInvalidPhysId,
//! Invalid register's virtual id.
kErrorInvalidVirtId,
//! Invalid prefix combination.
kErrorInvalidPrefixCombination,
//! Invalid LOCK prefix.
kErrorInvalidLockPrefix,
//! Invalid XACQUIRE prefix.
kErrorInvalidXAcquirePrefix,
//! Invalid XACQUIRE prefix.
kErrorInvalidXReleasePrefix,
//! Invalid REP prefix.
kErrorInvalidRepPrefix,
//! Invalid REX prefix.
kErrorInvalidRexPrefix,
//! Invalid mask register (not 'k').
kErrorInvalidKMaskReg,
//! Invalid {k} use (not supported by the instruction).
kErrorInvalidKMaskUse,
//! Invalid {k}{z} use (not supported by the instruction).
kErrorInvalidKZeroUse,
//! Invalid broadcast - Currently only related to invalid use of AVX-512 {1tox}.
kErrorInvalidBroadcast,
//! Invalid 'embedded-rounding' {er} or 'suppress-all-exceptions' {sae} (AVX-512).
kErrorInvalidEROrSAE,
//! Invalid address used (not encodable).
kErrorInvalidAddress,
//! Invalid index register used in memory address (not encodable).
kErrorInvalidAddressIndex,
//! Invalid address scale (not encodable).
kErrorInvalidAddressScale,
//! Invalid use of 64-bit address.
kErrorInvalidAddress64Bit,
//! Invalid displacement (not encodable).
kErrorInvalidDisplacement,
//! Invalid segment (X86).
kErrorInvalidSegment,
//! Invalid immediate (out of bounds on X86 and invalid pattern on ARM).
kErrorInvalidImmediate,
//! Invalid operand size.
kErrorInvalidOperandSize,
//! Ambiguous operand size (memory has zero size while it's required to determine the operation type.
kErrorAmbiguousOperandSize,
//! Mismatching operand size (size of multiple operands doesn't match the operation size).
kErrorOperandSizeMismatch,
//! Invalid TypeId.
kErrorInvalidTypeId,
//! Invalid use of a 8-bit GPB-HIGH register.
kErrorInvalidUseOfGpbHi,
//! Invalid use of a 64-bit GPQ register in 32-bit mode.
kErrorInvalidUseOfGpq,
//! Invalid use of an 80-bit float (TypeId::kF80).
kErrorInvalidUseOfF80,
//! Some registers in the instruction muse be consecutive (some ARM and AVX512 neural-net instructions).
kErrorNotConsecutiveRegs,
//! AsmJit requires a physical register, but no one is available.
kErrorNoMorePhysRegs,
//! A variable has been assigned more than once to a function argument (CodeCompiler).
kErrorOverlappedRegs,
//! Invalid register to hold stack arguments offset.
kErrorOverlappingStackRegWithRegArg,
//! Count of AsmJit error codes.
kErrorCount
};
// ============================================================================
// [asmjit::Init / NoInit]
// [asmjit::Internal]
// ============================================================================
#if !defined(ASMJIT_DOCGEN)
struct _Init {};
static const _Init Init = {};
namespace Internal {
#if defined(ASMJIT_CUSTOM_ALLOC) && \
defined(ASMJIT_CUSTOM_REALLOC) && \
defined(ASMJIT_CUSTOM_FREE)
static ASMJIT_INLINE void* allocMemory(size_t size) noexcept { return ASMJIT_CUSTOM_ALLOC(size); }
static ASMJIT_INLINE void* reallocMemory(void* p, size_t size) noexcept { return ASMJIT_CUSTOM_REALLOC(p, size); }
static ASMJIT_INLINE void releaseMemory(void* p) noexcept { ASMJIT_CUSTOM_FREE(p); }
#elif !defined(ASMJIT_CUSTOM_ALLOC) && \
!defined(ASMJIT_CUSTOM_REALLOC) && \
!defined(ASMJIT_CUSTOM_FREE)
static ASMJIT_INLINE void* allocMemory(size_t size) noexcept { return ::malloc(size); }
static ASMJIT_INLINE void* reallocMemory(void* p, size_t size) noexcept { return ::realloc(p, size); }
static ASMJIT_INLINE void releaseMemory(void* p) noexcept { ::free(p); }
#else
# error "[asmjit] You must provide either none or all of ASMJIT_CUSTOM_[ALLOC|REALLOC|FREE]"
#endif
struct _NoInit {};
static const _NoInit NoInit = {};
#endif // !ASMJIT_DOCGEN
//! Cast designed to cast between function and void* pointers.
template<typename Dst, typename Src>
static ASMJIT_INLINE Dst ptr_cast(Src p) noexcept { return (Dst)p; }
} // Internal namespace
template<typename Func>
static ASMJIT_INLINE Func ptr_as_func(void* func) noexcept { return Internal::ptr_cast<Func, void*>(func); }
template<typename Func>
static ASMJIT_INLINE void* func_as_ptr(Func func) noexcept { return Internal::ptr_cast<void*, Func>(func); }
// ============================================================================
// [asmjit::Assert]
// [asmjit::DebugUtils]
// ============================================================================
//! \addtogroup asmjit_base_general
//! \{
namespace DebugUtils {
//! Called in debug build on assertion failure.
//! Returns the error `err` passed.
//!
//! Provided for debugging purposes. Putting a breakpoint inside `errored` can
//! help with tracing the origin of any error reported / returned by AsmJit.
static ASMJIT_INLINE Error errored(Error err) noexcept { return err; }
//! Get a printable version of `asmjit::Error` code.
ASMJIT_API const char* errorAsString(Error err) noexcept;
//! Called to output debugging message(s).
ASMJIT_API void debugOutput(const char* str) noexcept;
//! Called on assertion failure.
//!
//! \param exp Expression that failed.
//! \param file Source file name where it happened.
//! \param line Line in the source file.
//! \param msg Message to display.
//!
//! If you have problems with assertions put a breakpoint at assertionFailed()
//! function (asmjit/base/globals.cpp) and check the call stack to locate the
//! failing code.
ASMJIT_API void assertionFailed(const char* exp, const char* file, int line);
ASMJIT_API void ASMJIT_NORETURN assertionFailed(const char* file, int line, const char* msg) noexcept;
#if defined(ASMJIT_DEBUG)
#define ASMJIT_ASSERT(_Exp_) \
do { \
if (!(_Exp_)) ::asmjit::assertionFailed(#_Exp_, __FILE__, __LINE__); \
# define ASMJIT_ASSERT(exp) \
do { \
if (ASMJIT_LIKELY(exp)) \
break; \
::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, #exp); \
} while (0)
# define ASMJIT_NOT_REACHED() \
do { \
::asmjit::DebugUtils::assertionFailed(__FILE__, __LINE__, \
"ASMJIT_NOT_REACHED has been reached"); \
ASMJIT_ASSUME(0); \
} while (0)
#else
#define ASMJIT_ASSERT(_Exp_) ASMJIT_NOP()
# define ASMJIT_ASSERT(exp) ASMJIT_NOP
# define ASMJIT_NOT_REACHED() ASMJIT_ASSUME(0)
#endif // DEBUG
//! \}
//! \internal
//!
//! Used by AsmJit to propagate a possible `Error` produced by `...` to the caller.
#define ASMJIT_PROPAGATE(...) \
do { \
::asmjit::Error _err = __VA_ARGS__; \
if (ASMJIT_UNLIKELY(_err)) \
return _err; \
} while (0)
} // asmjit namespace
} // DebugUtils namespace
// ============================================================================
// [asmjit_cast<>]
// [asmjit::Init / NoInit]
// ============================================================================
//! \addtogroup asmjit_base_util
//! \{
#if !defined(ASMJIT_DOCGEN)
struct _Init {};
static const _Init Init = {};
//! Cast used to cast pointer to function. It's like reinterpret_cast<>,
//! but uses internally C style cast to work with MinGW.
//!
//! If you are using single compiler and `reinterpret_cast<>` works for you,
//! there is no reason to use `asmjit_cast<>`. If you are writing
//! cross-platform software with various compiler support, consider using
//! `asmjit_cast<>` instead of `reinterpret_cast<>`.
template<typename T, typename Z>
static ASMJIT_INLINE T asmjit_cast(Z* p) { return (T)p; }
struct _NoInit {};
static const _NoInit NoInit = {};
#endif // !ASMJIT_DOCGEN
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_GLOBALS_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Guard]
#include "../asmjit_build.h"
#if defined(ASMJIT_BUILD_X86)
// [Dependencies]
#include "../base/arch.h"
#include "../base/inst.h"
#if defined(ASMJIT_BUILD_X86)
# include "../x86/x86instimpl_p.h"
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
# include "../arm/arminstimpl_p.h"
#endif // ASMJIT_BUILD_ARM
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Inst - Validate]
// ============================================================================
#if !defined(ASMJIT_DISABLE_VALIDATION)
Error Inst::validate(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count) noexcept {
#if defined(ASMJIT_BUILD_X86)
if (ArchInfo::isX86Family(archType))
return X86InstImpl::validate(archType, detail, operands, count);
#endif
#if defined(ASMJIT_BUILD_ARM)
if (ArchInfo::isArmFamily(archType))
return ArmInstImpl::validate(archType, detail, operands, count);
#endif
return DebugUtils::errored(kErrorInvalidArch);
}
#endif
// ============================================================================
// [asmjit::Inst - CheckFeatures]
// ============================================================================
#if !defined(ASMJIT_DISABLE_EXTENSIONS)
Error Inst::checkFeatures(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count, CpuFeatures& out) noexcept {
#if defined(ASMJIT_BUILD_X86)
if (ArchInfo::isX86Family(archType))
return X86InstImpl::checkFeatures(archType, detail, operands, count, out);
#endif
#if defined(ASMJIT_BUILD_ARM)
if (ArchInfo::isArmFamily(archType))
return ArmInstImpl::checkFeatures(archType, detail, operands, count, out);
#endif
return DebugUtils::errored(kErrorInvalidArch);
}
#endif // !defined(ASMJIT_DISABLE_EXTENSIONS)
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // ASMJIT_BUILD_X86
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_INST_H
#define _ASMJIT_BASE_INST_H
// [Dependencies]
#include "../base/cpuinfo.h"
#include "../base/operand.h"
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::Inst]
// ============================================================================
//! Definitions and utilities related to instructions used by all architectures.
struct Inst {
ASMJIT_ENUM(Id) {
kIdNone = 0 //!< Invalid or uninitialized instruction id.
};
//! Describes an instruction's jump type, if any.
ASMJIT_ENUM(JumpType) {
kJumpTypeNone = 0, //!< Instruction doesn't jump (regular instruction).
kJumpTypeDirect = 1, //!< Instruction is a unconditional (direct) jump.
kJumpTypeConditional = 2, //!< Instruction is a conditional jump.
kJumpTypeCall = 3, //!< Instruction is a function call.
kJumpTypeReturn = 4 //!< Instruction is a function return.
};
// --------------------------------------------------------------------------
// [Detail]
// --------------------------------------------------------------------------
//! Instruction id, options, and extraReg packed in a single structure. This
//! structure exists to simplify analysis and validation API that requires a
//! lot of information about the instruction to be processed.
class Detail {
public:
ASMJIT_INLINE Detail() noexcept
: instId(0),
options(0),
extraReg() {}
explicit ASMJIT_INLINE Detail(uint32_t instId, uint32_t options = 0) noexcept
: instId(instId),
options(options),
extraReg() {}
ASMJIT_INLINE Detail(uint32_t instId, uint32_t options, const RegOnly& reg) noexcept
: instId(instId),
options(options),
extraReg(reg) {}
ASMJIT_INLINE Detail(uint32_t instId, uint32_t options, const Reg& reg) noexcept
: instId(instId),
options(options) { extraReg.init(reg); }
// ------------------------------------------------------------------------
// [Accessors]
// ------------------------------------------------------------------------
ASMJIT_INLINE bool hasExtraReg() const noexcept { return extraReg.isValid(); }
// ------------------------------------------------------------------------
// [Members]
// ------------------------------------------------------------------------
uint32_t instId;
uint32_t options;
RegOnly extraReg;
};
// --------------------------------------------------------------------------
// [API]
// --------------------------------------------------------------------------
#if !defined(ASMJIT_DISABLE_VALIDATION)
//! Validate the given instruction.
ASMJIT_API static Error validate(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count) noexcept;
#endif // !ASMJIT_DISABLE_VALIDATION
#if !defined(ASMJIT_DISABLE_EXTENSIONS)
//! Check CPU features required to execute the given instruction.
ASMJIT_API static Error checkFeatures(uint32_t archType, const Detail& detail, const Operand_* operands, uint32_t count, CpuFeatures& out) noexcept;
#endif // !defined(ASMJIT_DISABLE_EXTENSIONS)
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_INST_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/intutil.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
#if defined(ASMJIT_TEST)
UNIT(base_intutil) {
uint32_t i;
INFO("IntTraits<>.");
EXPECT(IntTraits<signed char>::kIsSigned,
"IntTraits<signed char> should report signed.");
EXPECT(IntTraits<unsigned char>::kIsUnsigned,
"IntTraits<unsigned char> should report unsigned.");
EXPECT(IntTraits<signed short>::kIsSigned,
"IntTraits<signed short> should report signed.");
EXPECT(IntTraits<unsigned short>::kIsUnsigned,
"IntTraits<unsigned short> should report unsigned.");
EXPECT(IntTraits<int>::kIsSigned,
"IntTraits<int> should report signed.");
EXPECT(IntTraits<unsigned int>::kIsUnsigned,
"IntTraits<unsigned int> should report unsigned.");
EXPECT(IntTraits<long>::kIsSigned,
"IntTraits<long> should report signed.");
EXPECT(IntTraits<unsigned long>::kIsUnsigned,
"IntTraits<unsigned long> should report unsigned.");
EXPECT(IntTraits<intptr_t>::kIsSigned,
"IntTraits<intptr_t> should report signed.");
EXPECT(IntTraits<uintptr_t>::kIsUnsigned,
"IntTraits<uintptr_t> should report unsigned.");
EXPECT(IntTraits<intptr_t>::kIsIntPtr,
"IntTraits<intptr_t> should report intptr_t type.");
EXPECT(IntTraits<uintptr_t>::kIsIntPtr,
"IntTraits<uintptr_t> should report intptr_t type.");
INFO("IntUtil::iMin()/iMax().");
EXPECT(IntUtil::iMin<int>(0, -1) == -1,
"IntUtil::iMin<int> should return a minimum value.");
EXPECT(IntUtil::iMin<int>(-1, -2) == -2,
"IntUtil::iMin<int> should return a minimum value.");
EXPECT(IntUtil::iMin<int>(1, 2) == 1,
"IntUtil::iMin<int> should return a minimum value.");
EXPECT(IntUtil::iMax<int>(0, -1) == 0,
"IntUtil::iMax<int> should return a maximum value.");
EXPECT(IntUtil::iMax<int>(-1, -2) == -1,
"IntUtil::iMax<int> should return a maximum value.");
EXPECT(IntUtil::iMax<int>(1, 2) == 2,
"IntUtil::iMax<int> should return a maximum value.");
INFO("IntUtil::inInterval().");
EXPECT(IntUtil::inInterval<int>(11, 10, 20) == true,
"IntUtil::inInterval<int> should return true if inside.");
EXPECT(IntUtil::inInterval<int>(101, 10, 20) == false,
"IntUtil::inInterval<int> should return false if outside.");
INFO("IntUtil::isInt8().");
EXPECT(IntUtil::isInt8(-128) == true,
"IntUtil::isInt8<> should return true if inside.");
EXPECT(IntUtil::isInt8(127) == true,
"IntUtil::isInt8<> should return true if inside.");
EXPECT(IntUtil::isInt8(-129) == false,
"IntUtil::isInt8<> should return false if outside.");
EXPECT(IntUtil::isInt8(128) == false,
"IntUtil::isInt8<> should return false if outside.");
INFO("IntUtil::isUInt8().");
EXPECT(IntUtil::isUInt8(255) == true,
"IntUtil::isUInt8<> should return true if inside.");
EXPECT(IntUtil::isUInt8(256) == false,
"IntUtil::isUInt8<> should return false if outside.");
EXPECT(IntUtil::isUInt8(-1) == false,
"IntUtil::isUInt8<> should return false if negative.");
INFO("IntUtil::isInt16().");
EXPECT(IntUtil::isInt16(-32768) == true,
"IntUtil::isInt16<> should return true if inside.");
EXPECT(IntUtil::isInt16(32767) == true,
"IntUtil::isInt16<> should return true if inside.");
EXPECT(IntUtil::isInt16(-32769) == false,
"IntUtil::isInt16<> should return false if outside.");
EXPECT(IntUtil::isInt16(32768) == false,
"IntUtil::isInt16<> should return false if outside.");
INFO("IntUtil::isUInt16().");
EXPECT(IntUtil::isUInt16(65535) == true,
"IntUtil::isUInt16<> should return true if inside.");
EXPECT(IntUtil::isUInt16(65536) == false,
"IntUtil::isUInt16<> should return false if outside.");
EXPECT(IntUtil::isUInt16(-1) == false,
"IntUtil::isUInt16<> should return false if negative.");
INFO("IntUtil::isInt32().");
EXPECT(IntUtil::isInt32(2147483647) == true,
"IntUtil::isInt32<int> should return true if inside.");
EXPECT(IntUtil::isInt32(-2147483647 - 1) == true,
"IntUtil::isInt32<int> should return true if inside.");
EXPECT(IntUtil::isInt32(ASMJIT_UINT64_C(2147483648)) == false,
"IntUtil::isInt32<int> should return false if outside.");
EXPECT(IntUtil::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == false,
"IntUtil::isInt32<int> should return false if outside.");
EXPECT(IntUtil::isInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false,
"IntUtil::isInt32<int> should return false if outside.");
INFO("IntUtil::isUInt32().");
EXPECT(IntUtil::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF)) == true,
"IntUtil::isUInt32<int> should return true if inside.");
EXPECT(IntUtil::isUInt32(ASMJIT_UINT64_C(0xFFFFFFFF) + 1) == false,
"IntUtil::isUInt32<int> should return false if outside.");
EXPECT(IntUtil::isUInt32(-1) == false,
"IntUtil::isUInt32<int> should return false if negative.");
INFO("IntUtil::isPower2().");
for (i = 0; i < 64; i++) {
EXPECT(IntUtil::isPowerOf2(static_cast<uint64_t>(1) << i) == true,
"IntUtil::isPower2() didn't report power of 2.");
EXPECT(IntUtil::isPowerOf2((static_cast<uint64_t>(1) << i) ^ 0x001101) == false,
"IntUtil::isPower2() didn't report not power of 2.");
}
INFO("IntUtil::mask().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::mask(i) == (1 << i),
"IntUtil::mask(%u) should return %X.", i, (1 << i));
}
INFO("IntUtil::bits().");
for (i = 0; i < 32; i++) {
uint32_t expectedBits = 0;
for (uint32_t b = 0; b < i; b++)
expectedBits |= static_cast<uint32_t>(1) << b;
EXPECT(IntUtil::bits(i) == expectedBits,
"IntUtil::bits(%u) should return %X.", i, expectedBits);
}
INFO("IntUtil::hasBit().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::hasBit((1 << i), i) == true,
"IntUtil::hasBit(%X, %u) should return true.", (1 << i), i);
}
INFO("IntUtil::bitCount().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::bitCount((1 << i)) == 1,
"IntUtil::bitCount(%X) should return true.", (1 << i));
}
EXPECT(IntUtil::bitCount(0x000000F0) == 4, "");
EXPECT(IntUtil::bitCount(0x10101010) == 4, "");
EXPECT(IntUtil::bitCount(0xFF000000) == 8, "");
EXPECT(IntUtil::bitCount(0xFFFFFFF7) == 31, "");
EXPECT(IntUtil::bitCount(0x7FFFFFFF) == 31, "");
INFO("IntUtil::findFirstBit().");
for (i = 0; i < 32; i++) {
EXPECT(IntUtil::findFirstBit((1 << i)) == i,
"IntUtil::findFirstBit(%X) should return %u.", (1 << i), i);
}
INFO("IntUtil::isAligned().");
EXPECT(IntUtil::isAligned<size_t>(0xFFFF, 4) == false, "");
EXPECT(IntUtil::isAligned<size_t>(0xFFF4, 4) == true , "");
EXPECT(IntUtil::isAligned<size_t>(0xFFF8, 8) == true , "");
EXPECT(IntUtil::isAligned<size_t>(0xFFF0, 16) == true , "");
INFO("IntUtil::alignTo().");
EXPECT(IntUtil::alignTo<size_t>(0xFFFF, 4) == 0x10000, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF4, 4) == 0x0FFF4, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF8, 8) == 0x0FFF8, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF0, 16) == 0x0FFF0, "");
EXPECT(IntUtil::alignTo<size_t>(0xFFF0, 32) == 0x10000, "");
INFO("IntUtil::alignToPowerOf2().");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0xFFFF) == 0x10000, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0xF123) == 0x10000, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0x0F00) == 0x01000, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0x0100) == 0x00100, "");
EXPECT(IntUtil::alignToPowerOf2<size_t>(0x1001) == 0x02000, "");
INFO("IntUtil::deltaTo().");
EXPECT(IntUtil::deltaTo<size_t>(0xFFFF, 4) == 1, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF4, 4) == 0, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF8, 8) == 0, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF0, 16) == 0, "");
EXPECT(IntUtil::deltaTo<size_t>(0xFFF0, 32) == 16, "");
}
#endif // ASMJIT_TEST
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_INTUTIL_H
#define _ASMJIT_BASE_INTUTIL_H
// [Dependencies - AsmJit]
#include "../base/globals.h"
#if defined(_MSC_VER) && _MSC_VER >= 1400
# include <intrin.h>
# pragma intrinsic(_BitScanForward)
#endif // ASMJIT_OS_WINDOWS
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \{
// ============================================================================
// [asmjit::IntTraits]
// ============================================================================
//! \internal
template<typename T>
struct IntTraits {
enum {
kIsSigned = static_cast<T>(~static_cast<T>(0)) < static_cast<T>(0),
kIsUnsigned = !kIsSigned,
kIs8Bit = sizeof(T) == 1,
kIs16Bit = sizeof(T) == 2,
kIs32Bit = sizeof(T) == 4,
kIs64Bit = sizeof(T) == 8,
kIsIntPtr = sizeof(T) == sizeof(intptr_t)
};
};
// \internal
template<size_t Size, int IsSigned>
struct AsInt_ { typedef int64_t Int; };
template<> struct AsInt_<1, 0> { typedef int Int; };
template<> struct AsInt_<1, 1> { typedef int Int; };
template<> struct AsInt_<2, 0> { typedef int Int; };
template<> struct AsInt_<2, 1> { typedef int Int; };
template<> struct AsInt_<4, 1> { typedef int Int; };
// \internal
//
// Map an integer `T` to an `int` or `int64_t`, depending on the type. Used
// internally by AsmJit to dispatch an argument of arbitrary integer type into
// a function that accepts either `int` or `int64_t`.
template<typename T>
struct AsInt {
typedef typename AsInt_<sizeof(T), IntTraits<T>::kIsSigned>::Int Int;
};
template<typename T>
ASMJIT_INLINE typename AsInt<T>::Int asInt(T value) {
return static_cast<typename AsInt<T>::Int>(value);
}
// ============================================================================
// [asmjit::IntUtil]
// ============================================================================
//! Integer utilities.
struct IntUtil {
// --------------------------------------------------------------------------
// [Float <-> Int]
// --------------------------------------------------------------------------
//! \internal
union Float {
int32_t i;
float f;
};
//! \internal
union Double {
int64_t i;
double d;
};
//! Bit-cast `float` to 32-bit integer.
static ASMJIT_INLINE int32_t floatAsInt(float f) { Float m; m.f = f; return m.i; }
//! Bit-cast 32-bit integer to `float`.
static ASMJIT_INLINE float intAsFloat(int32_t i) { Float m; m.i = i; return m.f; }
//! Bit-cast `double` to 64-bit integer.
static ASMJIT_INLINE int64_t doubleAsInt(double d) { Double m; m.d = d; return m.i; }
//! Bit-cast 64-bit integer to `double`.
static ASMJIT_INLINE double intAsDouble(int64_t i) { Double m; m.i = i; return m.d; }
// --------------------------------------------------------------------------
// [AsmJit - Pack / Unpack]
// --------------------------------------------------------------------------
//! Pack two 8-bit integer and one 16-bit integer into a 32-bit integer as it
//! is an array of `{u0,u1,w2}`.
static ASMJIT_INLINE uint32_t pack32_2x8_1x16(uint32_t u0, uint32_t u1, uint32_t w2) {
#if defined(ASMJIT_ARCH_LE)
return u0 + (u1 << 8) + (w2 << 16);
#else
return (u0 << 24) + (u1 << 16) + (w2);
#endif
}
//! Pack four 8-bit integer into a 32-bit integer as it is an array of `{u0,u1,u2,u3}`.
static ASMJIT_INLINE uint32_t pack32_4x8(uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) {
#if defined(ASMJIT_ARCH_LE)
return u0 + (u1 << 8) + (u2 << 16) + (u3 << 24);
#else
return (u0 << 24) + (u1 << 16) + (u2 << 8) + u3;
#endif
}
//! Pack two 32-bit integer into a 64-bit integer as it is an array of `{u0,u1}`.
static ASMJIT_INLINE uint64_t pack64_2x32(uint32_t u0, uint32_t u1) {
#if defined(ASMJIT_ARCH_LE)
return (static_cast<uint64_t>(u1) << 32) + u0;
#else
return (static_cast<uint64_t>(u0) << 32) + u1;
#endif
}
// --------------------------------------------------------------------------
// [AsmJit - Min/Max]
// --------------------------------------------------------------------------
// NOTE: Because some environments declare min() and max() as macros, it has
// been decided to use different name so we never collide with them.
//! Get minimum value of `a` and `b`.
template<typename T>
static ASMJIT_INLINE T iMin(const T& a, const T& b) { return a < b ? a : b; }
//! Get maximum value of `a` and `b`.
template<typename T>
static ASMJIT_INLINE T iMax(const T& a, const T& b) { return a > b ? a : b; }
// --------------------------------------------------------------------------
// [AsmJit - MaxUInt]
// --------------------------------------------------------------------------
//! Get maximum unsigned value of `T`.
template<typename T>
static ASMJIT_INLINE T maxUInt() { return ~T(0); }
// --------------------------------------------------------------------------
// [AsmJit - InInterval]
// --------------------------------------------------------------------------
//! Get whether `x` is greater or equal than `start` and less or equal than `end`.
template<typename T>
static ASMJIT_INLINE bool inInterval(const T& x, const T& start, const T& end) {
return x >= start && x <= end;
}
// --------------------------------------------------------------------------
// [AsmJit - IsInt/IsUInt]
// --------------------------------------------------------------------------
//! Get whether the given integer `x` can be casted to 8-bit signed integer.
template<typename T>
static ASMJIT_INLINE bool isInt8(T x) {
if (IntTraits<T>::kIsSigned)
return sizeof(T) <= sizeof(int8_t) ? true : x >= T(-128) && x <= T(127);
else
return x <= T(127);
}
//! Get whether the given integer `x` can be casted to 8-bit unsigned integer.
template<typename T>
static ASMJIT_INLINE bool isUInt8(T x) {
if (IntTraits<T>::kIsSigned)
return x >= T(0) && (sizeof(T) <= sizeof(uint8_t) ? true : x <= T(255));
else
return sizeof(T) <= sizeof(uint8_t) ? true : x <= T(255);
}
//! Get whether the given integer `x` can be casted to 16-bit signed integer.
template<typename T>
static ASMJIT_INLINE bool isInt16(T x) {
if (IntTraits<T>::kIsSigned)
return sizeof(T) <= sizeof(int16_t) ? true : x >= T(-32768) && x <= T(32767);
else
return x >= T(0) && (sizeof(T) <= sizeof(int16_t) ? true : x <= T(32767));
}
//! Get whether the given integer `x` can be casted to 16-bit unsigned integer.
template<typename T>
static ASMJIT_INLINE bool isUInt16(T x) {
if (IntTraits<T>::kIsSigned)
return x >= T(0) && (sizeof(T) <= sizeof(uint16_t) ? true : x <= T(65535));
else
return sizeof(T) <= sizeof(uint16_t) ? true : x <= T(65535);
}
//! Get whether the given integer `x` can be casted to 32-bit signed integer.
template<typename T>
static ASMJIT_INLINE bool isInt32(T x) {
if (IntTraits<T>::kIsSigned)
return sizeof(T) <= sizeof(int32_t) ? true : x >= T(-2147483647) - 1 && x <= T(2147483647);
else
return x >= T(0) && (sizeof(T) <= sizeof(int32_t) ? true : x <= T(2147483647));
}
//! Get whether the given integer `x` can be casted to 32-bit unsigned integer.
template<typename T>
static ASMJIT_INLINE bool isUInt32(T x) {
if (IntTraits<T>::kIsSigned)
return x >= T(0) && (sizeof(T) <= sizeof(uint32_t) ? true : x <= T(4294967295U));
else
return sizeof(T) <= sizeof(uint32_t) ? true : x <= T(4294967295U);
}
// --------------------------------------------------------------------------
// [AsmJit - IsPowerOf2]
// --------------------------------------------------------------------------
//! Get whether the `n` value is a power of two (only one bit is set).
template<typename T>
static ASMJIT_INLINE bool isPowerOf2(T n) {
return n != 0 && (n & (n - 1)) == 0;
}
// --------------------------------------------------------------------------
// [AsmJit - Mask]
// --------------------------------------------------------------------------
//! Generate a bit-mask that has `x` bit set.
static ASMJIT_INLINE uint32_t mask(uint32_t x) {
ASMJIT_ASSERT(x < 32);
return (1U << x);
}
//! Generate a bit-mask that has `x0` and `x1` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1) {
return mask(x0) | mask(x1);
}
//! Generate a bit-mask that has `x0`, `x1` and `x2` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2) {
return mask(x0) | mask(x1) | mask(x2);
}
//! Generate a bit-mask that has `x0`, `x1`, `x2` and `x3` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3);
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3` and `x4` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) ;
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4` and `x5` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) | mask(x5) ;
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5` and `x6` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) | mask(x5) | mask(x6) ;
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5`, `x6` and `x7` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) | mask(x5) | mask(x6) | mask(x7) ;
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5`, `x6`, `x7` and `x8` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7, uint32_t x8) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) | mask(x5) | mask(x6) | mask(x7) |
mask(x8) ;
}
//! Generate a bit-mask that has `x0`, `x1`, `x2`, `x3`, `x4`, `x5`, `x6`, `x7`, `x8` and `x9` bits set.
static ASMJIT_INLINE uint32_t mask(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4, uint32_t x5, uint32_t x6, uint32_t x7, uint32_t x8, uint32_t x9) {
return mask(x0) | mask(x1) | mask(x2) | mask(x3) |
mask(x4) | mask(x5) | mask(x6) | mask(x7) |
mask(x8) | mask(x9) ;
}
// --------------------------------------------------------------------------
// [AsmJit - Bits]
// --------------------------------------------------------------------------
//! Generate a bit-mask that has `x` most significant bits set.
static ASMJIT_INLINE uint32_t bits(uint32_t x) {
// Shifting more bits that the type has has undefined behavior. Everything
// we need is that application shouldn't crash because of that, but the
// content of register after shift is not defined. So in case that the
// requested shift is too large for the type we correct this undefined
// behavior by setting all bits to ones (this is why we generate an overflow
// mask).
uint32_t overflow = static_cast<uint32_t>(
-static_cast<int32_t>(x >= sizeof(uint32_t) * 8));
return ((static_cast<uint32_t>(1) << x) - 1U) | overflow;
}
// --------------------------------------------------------------------------
// [AsmJit - HasBit]
// --------------------------------------------------------------------------
//! Get whether `x` has bit `n` set.
static ASMJIT_INLINE bool hasBit(uint32_t x, uint32_t n) {
return static_cast<bool>((x >> n) & 0x1);
}
// --------------------------------------------------------------------------
// [AsmJit - BitCount]
// --------------------------------------------------------------------------
//! Get count of bits in `x`.
//!
//! Taken from http://graphics.stanford.edu/~seander/bithacks.html .
static ASMJIT_INLINE uint32_t bitCount(uint32_t x) {
x = x - ((x >> 1) & 0x55555555U);
x = (x & 0x33333333U) + ((x >> 2) & 0x33333333U);
return (((x + (x >> 4)) & 0x0F0F0F0FU) * 0x01010101U) >> 24;
}
// --------------------------------------------------------------------------
// [AsmJit - FindFirstBit]
// --------------------------------------------------------------------------
//! \internal
static ASMJIT_INLINE uint32_t findFirstBitSlow(uint32_t mask) {
// This is a reference (slow) implementation of findFirstBit(), used when
// we don't have compiler support for this task. The implementation speed
// has been improved to check for 2 bits per iteration.
uint32_t i = 1;
while (mask != 0) {
uint32_t two = mask & 0x3;
if (two != 0x0)
return i - (two & 0x1);
i += 2;
mask >>= 2;
}
return 0xFFFFFFFFU;
}
//! Find a first bit in `mask`.
static ASMJIT_INLINE uint32_t findFirstBit(uint32_t mask) {
#if defined(_MSC_VER) && _MSC_VER >= 1400
DWORD i;
if (_BitScanForward(&i, mask)) {
ASMJIT_ASSERT(findFirstBitSlow(mask) == i);
return static_cast<uint32_t>(i);
}
return 0xFFFFFFFFU;
#else
return findFirstBitSlow(mask);
#endif
}
// --------------------------------------------------------------------------
// [AsmJit - Misc]
// --------------------------------------------------------------------------
static ASMJIT_INLINE uint32_t keepNOnesFromRight(uint32_t mask, uint32_t nBits) {
uint32_t m = 0x1;
do {
nBits -= (mask & m) == 0;
m <<= 1;
if (nBits == 0) {
m -= 1;
mask &= m;
break;
}
} while (m);
return mask;
}
static ASMJIT_INLINE uint32_t indexNOnesFromRight(uint8_t* dst, uint32_t mask, uint32_t nBits) {
uint32_t totalBits = nBits;
uint8_t i = 0;
uint32_t m = 0x1;
do {
if (mask & m) {
*dst++ = i;
if (--nBits == 0)
break;
}
m <<= 1;
i++;
} while (m);
return totalBits - nBits;
}
// --------------------------------------------------------------------------
// [AsmJit - Alignment]
// --------------------------------------------------------------------------
template<typename T>
static ASMJIT_INLINE bool isAligned(T base, T alignment) {
return (base % alignment) == 0;
}
//! Align `base` to `alignment`.
template<typename T>
static ASMJIT_INLINE T alignTo(T base, T alignment) {
return (base + (alignment - 1)) & ~(alignment - 1);
}
template<typename T>
static ASMJIT_INLINE T alignToPowerOf2(T base) {
// Implementation is from "Hacker's Delight" by Henry S. Warren, Jr.
base -= 1;
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4293)
#endif // _MSC_VER
base = base | (base >> 1);
base = base | (base >> 2);
base = base | (base >> 4);
// 8/16/32 constants are multiplied by the condition to prevent a compiler
// complaining about the 'shift count >= type width' (GCC).
if (sizeof(T) >= 2) base = base | (base >> ( 8 * (sizeof(T) >= 2))); // Base >> 8.
if (sizeof(T) >= 4) base = base | (base >> (16 * (sizeof(T) >= 4))); // Base >> 16.
if (sizeof(T) >= 8) base = base | (base >> (32 * (sizeof(T) >= 8))); // Base >> 32.
#if defined(_MSC_VER)
# pragma warning(pop)
#endif // _MSC_VER
return base + 1;
}
//! Get delta required to align `base` to `alignment`.
template<typename T>
static ASMJIT_INLINE T deltaTo(T base, T alignment) {
return alignTo(base, alignment) - base;
}
};
// ============================================================================
// [asmjit::UInt64]
// ============================================================================
union UInt64 {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64 fromUInt64(uint64_t val) {
UInt64 data;
data.setUInt64(val);
return data;
}
ASMJIT_INLINE UInt64 fromUInt64(const UInt64& val) {
UInt64 data;
data.setUInt64(val);
return data;
}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() {
if (kArchHost64Bit) {
u64 = 0;
}
else {
u32[0] = 0;
u32[1] = 0;
}
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE uint64_t getUInt64() const {
return u64;
}
ASMJIT_INLINE UInt64& setUInt64(uint64_t val) {
u64 = val;
return *this;
}
ASMJIT_INLINE UInt64& setUInt64(const UInt64& val) {
if (kArchHost64Bit) {
u64 = val.u64;
}
else {
u32[0] = val.u32[0];
u32[1] = val.u32[1];
}
return *this;
}
ASMJIT_INLINE UInt64& setPacked_2x32(uint32_t u0, uint32_t u1) {
if (kArchHost64Bit) {
u64 = IntUtil::pack64_2x32(u0, u1);
}
else {
u32[0] = u0;
u32[1] = u1;
}
return *this;
}
// --------------------------------------------------------------------------
// [Add]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& add(uint64_t val) {
u64 += val;
return *this;
}
ASMJIT_INLINE UInt64& add(const UInt64& val) {
if (kArchHost64Bit) {
u64 += val.u64;
}
else {
u32[0] += val.u32[0];
u32[1] += val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [Sub]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& sub(uint64_t val) {
u64 -= val;
return *this;
}
ASMJIT_INLINE UInt64& sub(const UInt64& val) {
if (kArchHost64Bit) {
u64 -= val.u64;
}
else {
u32[0] -= val.u32[0];
u32[1] -= val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [And]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& and_(uint64_t val) {
u64 &= val;
return *this;
}
ASMJIT_INLINE UInt64& and_(const UInt64& val) {
if (kArchHost64Bit) {
u64 &= val.u64;
}
else {
u32[0] &= val.u32[0];
u32[1] &= val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [AndNot]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& andNot(uint64_t val) {
u64 &= ~val;
return *this;
}
ASMJIT_INLINE UInt64& andNot(const UInt64& val) {
if (kArchHost64Bit) {
u64 &= ~val.u64;
}
else {
u32[0] &= ~val.u32[0];
u32[1] &= ~val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [Or]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& or_(uint64_t val) {
u64 |= val;
return *this;
}
ASMJIT_INLINE UInt64& or_(const UInt64& val) {
if (kArchHost64Bit) {
u64 |= val.u64;
}
else {
u32[0] |= val.u32[0];
u32[1] |= val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [Xor]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& xor_(uint64_t val) {
u64 ^= val;
return *this;
}
ASMJIT_INLINE UInt64& xor_(const UInt64& val) {
if (kArchHost64Bit) {
u64 ^= val.u64;
}
else {
u32[0] ^= val.u32[0];
u32[1] ^= val.u32[1];
}
return *this;
}
// --------------------------------------------------------------------------
// [Eq]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool isZero() const {
return kArchHost64Bit ? u64 == 0 : (u32[0] | u32[1]) == 0;
}
ASMJIT_INLINE bool isNonZero() const {
return kArchHost64Bit ? u64 != 0 : (u32[0] | u32[1]) != 0;
}
ASMJIT_INLINE bool eq(uint64_t val) const {
return u64 == val;
}
ASMJIT_INLINE bool eq(const UInt64& val) const {
return kArchHost64Bit ? u64 == val.u64 : (u32[0] == val.u32[0]) & (u32[1] == val.u32[1]);
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE UInt64& operator+=(uint64_t val) { return add(val); }
ASMJIT_INLINE UInt64& operator+=(const UInt64& val) { return add(val); }
ASMJIT_INLINE UInt64& operator-=(uint64_t val) { return sub(val); }
ASMJIT_INLINE UInt64& operator-=(const UInt64& val) { return sub(val); }
ASMJIT_INLINE UInt64& operator&=(uint64_t val) { return and_(val); }
ASMJIT_INLINE UInt64& operator&=(const UInt64& val) { return and_(val); }
ASMJIT_INLINE UInt64& operator|=(uint64_t val) { return or_(val); }
ASMJIT_INLINE UInt64& operator|=(const UInt64& val) { return or_(val); }
ASMJIT_INLINE UInt64& operator^=(uint64_t val) { return xor_(val); }
ASMJIT_INLINE UInt64& operator^=(const UInt64& val) { return xor_(val); }
ASMJIT_INLINE bool operator==(uint64_t val) const { return eq(val); }
ASMJIT_INLINE bool operator==(const UInt64& val) const { return eq(val); }
ASMJIT_INLINE bool operator!=(uint64_t val) const { return !eq(val); }
ASMJIT_INLINE bool operator!=(const UInt64& val) const { return !eq(val); }
ASMJIT_INLINE bool operator<(uint64_t val) const { return u64 < val; }
ASMJIT_INLINE bool operator<(const UInt64& val) const { return u64 < val.u64; }
ASMJIT_INLINE bool operator<=(uint64_t val) const { return u64 <= val; }
ASMJIT_INLINE bool operator<=(const UInt64& val) const { return u64 <= val.u64; }
ASMJIT_INLINE bool operator>(uint64_t val) const { return u64 > val; }
ASMJIT_INLINE bool operator>(const UInt64& val) const { return u64 > val.u64; }
ASMJIT_INLINE bool operator>=(uint64_t val) const { return u64 >= val; }
ASMJIT_INLINE bool operator>=(const UInt64& val) const { return u64 >= val.u64; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uint64_t u64;
uint32_t u32[2];
uint16_t u16[4];
uint8_t u8[8];
struct {
#if defined(ASMJIT_ARCH_LE)
uint32_t lo, hi;
#else
uint32_t hi, lo;
#endif // ASMJIT_ARCH_LE
};
};
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_INTUTIL_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Guard]
#include "../build.h"
#if !defined(ASMJIT_DISABLE_LOGGER)
// [Dependencies - AsmJit]
#include "../base/intutil.h"
#include "../base/logger.h"
#include "../base/string.h"
// [Dependencies - C]
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Logger - Construction / Destruction]
// ============================================================================
Logger::Logger() {
_options = 0;
::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation));
}
Logger::~Logger() {}
// ============================================================================
// [asmjit::Logger - Logging]
// ============================================================================
void Logger::logFormat(uint32_t style, const char* fmt, ...) {
char buf[1024];
size_t len;
va_list ap;
va_start(ap, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (len >= sizeof(buf))
len = sizeof(buf) - 1;
logString(style, buf, len);
}
void Logger::logBinary(uint32_t style, const void* data, size_t size) {
static const char prefix[] = ".data ";
static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
const uint8_t* s = static_cast<const uint8_t*>(data);
size_t i = size;
char buffer[128];
::memcpy(buffer, prefix, ASMJIT_ARRAY_SIZE(prefix) - 1);
while (i) {
uint32_t n = static_cast<uint32_t>(IntUtil::iMin<size_t>(i, 16));
char* p = buffer + ASMJIT_ARRAY_SIZE(prefix) - 1;
i -= n;
do {
uint32_t c = s[0];
p[0] = hex[c >> 4];
p[1] = hex[c & 15];
p += 2;
s += 1;
} while (--n);
*p++ = '\n';
logString(style, buffer, (size_t)(p - buffer));
}
}
// ============================================================================
// [asmjit::Logger - LogBinary]
// ============================================================================
void Logger::setOption(uint32_t id, bool value) {
if (id >= kLoggerOptionCount)
return;
uint32_t mask = 1 << id;
if (value)
_options |= mask;
else
_options &= ~mask;
}
// ============================================================================
// [asmjit::Logger - Indentation]
// ============================================================================
void Logger::setIndentation(const char* indentation) {
::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation));
if (!indentation)
return;
size_t length = StringUtil::nlen(indentation, ASMJIT_ARRAY_SIZE(_indentation) - 1);
::memcpy(_indentation, indentation, length);
}
// ============================================================================
// [asmjit::FileLogger - Construction / Destruction]
// ============================================================================
FileLogger::FileLogger(FILE* stream) : _stream(NULL) {
setStream(stream);
}
FileLogger::~FileLogger() {}
// ============================================================================
// [asmjit::FileLogger - Logging]
// ============================================================================
void FileLogger::logString(uint32_t style, const char* buf, size_t len) {
if (!_stream)
return;
if (len == kInvalidIndex)
len = strlen(buf);
fwrite(buf, 1, len, _stream);
}
// ============================================================================
// [asmjit::StringLogger - Construction / Destruction]
// ============================================================================
StringLogger::StringLogger() {}
StringLogger::~StringLogger() {}
// ============================================================================
// [asmjit::StringLogger - Logging]
// ============================================================================
void StringLogger::logString(uint32_t style, const char* buf, size_t len) {
_stringBuilder.appendString(buf, len);
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_LOGGER
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Guard]
#include "../asmjit_build.h"
#if !defined(ASMJIT_DISABLE_LOGGING)
// [Dependencies]
#include "../base/codeholder.h"
#include "../base/codeemitter.h"
#include "../base/logging.h"
#include "../base/utils.h"
#if !defined(ASMJIT_DISABLE_BUILDER)
# include "../base/codebuilder.h"
#endif // !ASMJIT_DISABLE_BUILDER
#if !defined(ASMJIT_DISABLE_COMPILER)
# include "../base/codecompiler.h"
#else
namespace asmjit { class VirtReg; }
#endif // !ASMJIT_DISABLE_COMPILER
#if defined(ASMJIT_BUILD_X86)
# include "../x86/x86logging_p.h"
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
# include "../arm/armlogging_p.h"
#endif // ASMJIT_BUILD_ARM
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Logger - Construction / Destruction]
// ============================================================================
Logger::Logger() noexcept {
_options = 0;
::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation));
}
Logger::~Logger() noexcept {}
// ============================================================================
// [asmjit::Logger - Logging]
// ============================================================================
Error Logger::logf(const char* fmt, ...) noexcept {
Error err;
va_list ap;
va_start(ap, fmt);
err = logv(fmt, ap);
va_end(ap);
return err;
}
Error Logger::logv(const char* fmt, va_list ap) noexcept {
char buf[1024];
size_t len = vsnprintf(buf, sizeof(buf), fmt, ap);
if (len >= sizeof(buf))
len = sizeof(buf) - 1;
return log(buf, len);
}
Error Logger::logBinary(const void* data, size_t size) noexcept {
static const char prefix[] = ".data ";
static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
const uint8_t* s = static_cast<const uint8_t*>(data);
size_t i = size;
char buffer[128];
::memcpy(buffer, prefix, ASMJIT_ARRAY_SIZE(prefix) - 1);
while (i) {
uint32_t n = static_cast<uint32_t>(std::min<size_t>(i, 16));
char* p = buffer + ASMJIT_ARRAY_SIZE(prefix) - 1;
i -= n;
do {
uint32_t c = s[0];
p[0] = hex[c >> 4];
p[1] = hex[c & 15];
p += 2;
s += 1;
} while (--n);
*p++ = '\n';
ASMJIT_PROPAGATE(log(buffer, (size_t)(p - buffer)));
}
return kErrorOk;
}
// ============================================================================
// [asmjit::Logger - Indentation]
// ============================================================================
void Logger::setIndentation(const char* indentation) noexcept {
::memset(_indentation, 0, ASMJIT_ARRAY_SIZE(_indentation));
if (!indentation)
return;
size_t length = Utils::strLen(indentation, ASMJIT_ARRAY_SIZE(_indentation) - 1);
::memcpy(_indentation, indentation, length);
}
// ============================================================================
// [asmjit::FileLogger - Construction / Destruction]
// ============================================================================
FileLogger::FileLogger(FILE* stream) noexcept : _stream(nullptr) { setStream(stream); }
FileLogger::~FileLogger() noexcept {}
// ============================================================================
// [asmjit::FileLogger - Logging]
// ============================================================================
Error FileLogger::_log(const char* buf, size_t len) noexcept {
if (!_stream)
return kErrorOk;
if (len == Globals::kInvalidIndex)
len = strlen(buf);
fwrite(buf, 1, len, _stream);
return kErrorOk;
}
// ============================================================================
// [asmjit::StringLogger - Construction / Destruction]
// ============================================================================
StringLogger::StringLogger() noexcept {}
StringLogger::~StringLogger() noexcept {}
// ============================================================================
// [asmjit::StringLogger - Logging]
// ============================================================================
Error StringLogger::_log(const char* buf, size_t len) noexcept {
return _stringBuilder.appendString(buf, len);
}
// ============================================================================
// [asmjit::Logging]
// ============================================================================
Error Logging::formatLabel(
StringBuilder& sb,
uint32_t logOptions,
const CodeEmitter* emitter,
uint32_t labelId) noexcept {
const LabelEntry* le = emitter->getCode()->getLabelEntry(labelId);
if (ASMJIT_UNLIKELY(!le))
return sb.appendFormat("InvalidLabel[Id=%u]", static_cast<unsigned int>(labelId));
if (le->hasName()) {
if (le->hasParent()) {
uint32_t parentId = le->getParentId();
const LabelEntry* pe = emitter->getCode()->getLabelEntry(parentId);
if (ASMJIT_UNLIKELY(!pe))
ASMJIT_PROPAGATE(sb.appendFormat("InvalidLabel[Id=%u]", static_cast<unsigned int>(labelId)));
else if (ASMJIT_UNLIKELY(!pe->hasName()))
ASMJIT_PROPAGATE(sb.appendFormat("L%u", Operand::unpackId(parentId)));
else
ASMJIT_PROPAGATE(sb.appendString(pe->getName()));
ASMJIT_PROPAGATE(sb.appendChar('.'));
}
return sb.appendString(le->getName());
}
else {
return sb.appendFormat("L%u", Operand::unpackId(labelId));
}
}
Error Logging::formatRegister(
StringBuilder& sb,
uint32_t logOptions,
const CodeEmitter* emitter,
uint32_t archType,
uint32_t regType,
uint32_t regId) noexcept {
#if defined(ASMJIT_BUILD_X86)
return X86Logging::formatRegister(sb, logOptions, emitter, archType, regType, regId);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
return ArmLogging::formatRegister(sb, logOptions, emitter, archType, regType, regId);
#endif // ASMJIT_BUILD_ARM
return kErrorInvalidArch;
}
Error Logging::formatOperand(
StringBuilder& sb,
uint32_t logOptions,
const CodeEmitter* emitter,
uint32_t archType,
const Operand_& op) noexcept {
#if defined(ASMJIT_BUILD_X86)
return X86Logging::formatOperand(sb, logOptions, emitter, archType, op);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
return ArmLogging::formatOperand(sb, logOptions, emitter, archType, op);
#endif // ASMJIT_BUILD_ARM
return kErrorInvalidArch;
}
Error Logging::formatInstruction(
StringBuilder& sb,
uint32_t logOptions,
const CodeEmitter* emitter,
uint32_t archType,
const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept {
#if defined(ASMJIT_BUILD_X86)
return X86Logging::formatInstruction(sb, logOptions, emitter, archType, detail, opArray, opCount);
#endif // ASMJIT_BUILD_X86
#if defined(ASMJIT_BUILD_ARM)
return ArmLogging::formatInstruction(sb, logOptions, emitter, archType, detail, opArray, opCount);
#endif // ASMJIT_BUILD_ARM
return kErrorInvalidArch;
}
#if !defined(ASMJIT_DISABLE_BUILDER)
static Error formatTypeId(StringBuilder& sb, uint32_t typeId) noexcept {
if (typeId == TypeId::kVoid)
return sb.appendString("void");
if (!TypeId::isValid(typeId))
return sb.appendString("unknown");
const char* typeName = "unknown";
uint32_t typeSize = TypeId::sizeOf(typeId);
uint32_t elementId = TypeId::elementOf(typeId);
switch (elementId) {
case TypeId::kIntPtr : typeName = "intptr" ; break;
case TypeId::kUIntPtr: typeName = "uintptr"; break;
case TypeId::kI8 : typeName = "i8" ; break;
case TypeId::kU8 : typeName = "u8" ; break;
case TypeId::kI16 : typeName = "i16" ; break;
case TypeId::kU16 : typeName = "u16" ; break;
case TypeId::kI32 : typeName = "i32" ; break;
case TypeId::kU32 : typeName = "u32" ; break;
case TypeId::kI64 : typeName = "i64" ; break;
case TypeId::kU64 : typeName = "u64" ; break;
case TypeId::kF32 : typeName = "f32" ; break;
case TypeId::kF64 : typeName = "f64" ; break;
case TypeId::kF80 : typeName = "f80" ; break;
case TypeId::kMask8 : typeName = "mask8" ; break;
case TypeId::kMask16 : typeName = "mask16" ; break;
case TypeId::kMask32 : typeName = "mask32" ; break;
case TypeId::kMask64 : typeName = "mask64" ; break;
case TypeId::kMmx32 : typeName = "mmx32" ; break;
case TypeId::kMmx64 : typeName = "mmx64" ; break;
}
uint32_t elementSize = TypeId::sizeOf(elementId);
if (typeSize > elementSize) {
unsigned int numElements = typeSize / elementSize;
return sb.appendFormat("%sx%u", typeName, numElements);
}
else {
return sb.appendString(typeName);
}
}
static Error formatFuncDetailValue(
StringBuilder& sb,
uint32_t logOptions,
const CodeEmitter* emitter,
FuncDetail::Value value) noexcept {
uint32_t typeId = value.getTypeId();
ASMJIT_PROPAGATE(formatTypeId(sb, typeId));
if (value.byReg()) {
ASMJIT_PROPAGATE(sb.appendChar(':'));
ASMJIT_PROPAGATE(Logging::formatRegister(sb, logOptions, emitter, emitter->getArchType(), value.getRegType(), value.getRegId()));
}
if (value.byStack()) {
ASMJIT_PROPAGATE(sb.appendFormat(":[%d]", static_cast<int>(value.getStackOffset())));
}
return kErrorOk;
}
static Error formatFuncRets(
StringBuilder& sb,
uint32_t logOptions,
const CodeEmitter* emitter,
const FuncDetail& fd,
VirtReg* const* vRegs) noexcept {
if (!fd.hasRet())
return sb.appendString("void");
for (uint32_t i = 0; i < fd.getRetCount(); i++) {
if (i) ASMJIT_PROPAGATE(sb.appendString(", "));
ASMJIT_PROPAGATE(formatFuncDetailValue(sb, logOptions, emitter, fd.getRet(i)));
#if !defined(ASMJIT_DISABLE_COMPILER)
if (vRegs)
ASMJIT_PROPAGATE(sb.appendFormat(" {%s}", vRegs[i]->getName()));
#endif // !ASMJIT_DISABLE_COMPILER
}
return kErrorOk;
}
static Error formatFuncArgs(
StringBuilder& sb,
uint32_t logOptions,
const CodeEmitter* emitter,
const FuncDetail& fd,
VirtReg* const* vRegs) noexcept {
for (uint32_t i = 0; i < fd.getArgCount(); i++) {
if (i) ASMJIT_PROPAGATE(sb.appendString(", "));
ASMJIT_PROPAGATE(formatFuncDetailValue(sb, logOptions, emitter, fd.getArg(i)));
#if !defined(ASMJIT_DISABLE_COMPILER)
if (vRegs)
ASMJIT_PROPAGATE(sb.appendFormat(" {%s}", vRegs[i]->getName()));
#endif // !ASMJIT_DISABLE_COMPILER
}
return kErrorOk;
}
Error Logging::formatNode(
StringBuilder& sb,
uint32_t logOptions,
const CodeBuilder* cb,
const CBNode* node_) noexcept {
if (node_->hasPosition())
ASMJIT_PROPAGATE(sb.appendFormat("<%04u> ", node_->getPosition()));
switch (node_->getType()) {
case CBNode::kNodeInst: {
const CBInst* node = node_->as<CBInst>();
ASMJIT_PROPAGATE(
Logging::formatInstruction(sb, logOptions, cb,
cb->getArchType(),
node->getInstDetail(), node->getOpArray(), node->getOpCount()));
break;
}
case CBNode::kNodeLabel: {
const CBLabel* node = node_->as<CBLabel>();
ASMJIT_PROPAGATE(sb.appendFormat("L%u:", Operand::unpackId(node->getId())));
break;
}
case CBNode::kNodeData: {
const CBData* node = node_->as<CBData>();
ASMJIT_PROPAGATE(sb.appendFormat(".embed (%u bytes)", node->getSize()));
break;
}
case CBNode::kNodeAlign: {
const CBAlign* node = node_->as<CBAlign>();
ASMJIT_PROPAGATE(
sb.appendFormat(".align %u (%s)",
node->getAlignment(),
node->getMode() == kAlignCode ? "code" : "data"));
break;
}
case CBNode::kNodeComment: {
const CBComment* node = node_->as<CBComment>();
ASMJIT_PROPAGATE(sb.appendFormat("; %s", node->getInlineComment()));
break;
}
case CBNode::kNodeSentinel: {
ASMJIT_PROPAGATE(sb.appendString("[sentinel]"));
break;
}
#if !defined(ASMJIT_DISABLE_COMPILER)
case CBNode::kNodeFunc: {
const CCFunc* node = node_->as<CCFunc>();
ASMJIT_PROPAGATE(formatLabel(sb, logOptions, cb, node->getId()));
ASMJIT_PROPAGATE(sb.appendString(": ["));
ASMJIT_PROPAGATE(formatFuncRets(sb, logOptions, cb, node->getDetail(), nullptr));
ASMJIT_PROPAGATE(sb.appendString("]"));
ASMJIT_PROPAGATE(sb.appendString("("));
ASMJIT_PROPAGATE(formatFuncArgs(sb, logOptions, cb, node->getDetail(), node->getArgs()));
ASMJIT_PROPAGATE(sb.appendString(")"));
break;
}
case CBNode::kNodeFuncExit: {
ASMJIT_PROPAGATE(sb.appendString("[ret]"));
break;
}
case CBNode::kNodeFuncCall: {
const CCFuncCall* node = node_->as<CCFuncCall>();
ASMJIT_PROPAGATE(
Logging::formatInstruction(sb, logOptions, cb,
cb->getArchType(),
node->getInstDetail(), node->getOpArray(), node->getOpCount()));
break;
}
#endif // !ASMJIT_DISABLE_COMPILER
default: {
ASMJIT_PROPAGATE(sb.appendFormat("[unknown (type=%u)]", node_->getType()));
break;
}
}
return kErrorOk;
}
#endif // !ASMJIT_DISABLE_BUILDER
Error Logging::formatLine(StringBuilder& sb, const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept {
size_t currentLen = sb.getLength();
size_t commentLen = comment ? Utils::strLen(comment, kMaxCommentLength) : 0;
ASMJIT_ASSERT(binLen >= dispLen);
if ((binLen != 0 && binLen != Globals::kInvalidIndex) || commentLen) {
size_t align = kMaxInstLength;
char sep = ';';
for (size_t i = (binLen == Globals::kInvalidIndex); i < 2; i++) {
size_t begin = sb.getLength();
// Append align.
if (currentLen < align)
ASMJIT_PROPAGATE(sb.appendChars(' ', align - currentLen));
// Append separator.
if (sep) {
ASMJIT_PROPAGATE(sb.appendChar(sep));
ASMJIT_PROPAGATE(sb.appendChar(' '));
}
// Append binary data or comment.
if (i == 0) {
ASMJIT_PROPAGATE(sb.appendHex(binData, binLen - dispLen - imLen));
ASMJIT_PROPAGATE(sb.appendChars('.', dispLen * 2));
ASMJIT_PROPAGATE(sb.appendHex(binData + binLen - imLen, imLen));
if (commentLen == 0) break;
}
else {
ASMJIT_PROPAGATE(sb.appendString(comment, commentLen));
}
currentLen += sb.getLength() - begin;
align += kMaxBinaryLength;
sep = '|';
}
}
return sb.appendChar('\n');
}
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_LOGGING
......@@ -5,129 +5,114 @@
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_LOGGER_H
#define _ASMJIT_BASE_LOGGER_H
#ifndef _ASMJIT_BASE_LOGGING_H
#define _ASMJIT_BASE_LOGGING_H
#include "../build.h"
#if !defined(ASMJIT_DISABLE_LOGGER)
// [Dependencies - AsmJit]
// [Dependencies]
#include "../base/inst.h"
#include "../base/string.h"
// [Dependencies - C]
#include <stdarg.h>
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base_util
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::LoggerOption]
// ============================================================================
//! Logger options.
ASMJIT_ENUM(LoggerOption) {
//! Whether to output instructions also in binary form.
kLoggerOptionBinaryForm = 0,
//! Whether to output immediates as hexadecimal numbers.
kLoggerOptionHexImmediate = 1,
//! Whether to output displacements as hexadecimal numbers.
kLoggerOptionHexDisplacement = 2,
//! Count of logger options.
kLoggerOptionCount = 3
};
#if !defined(ASMJIT_DISABLE_LOGGING)
// ============================================================================
// [asmjit::LoggerStyle]
// [Forward Declarations]
// ============================================================================
//! Logger style.
ASMJIT_ENUM(LoggerStyle) {
kLoggerStyleDefault = 0,
kLoggerStyleDirective = 1,
kLoggerStyleLabel = 2,
kLoggerStyleData = 3,
kLoggerStyleComment = 4,
class CodeEmitter;
class Reg;
struct Operand_;
kLoggerStyleCount = 5
};
#if !defined(ASMJIT_DISABLE_BUILDER)
class CodeBuilder;
class CBNode;
#endif // !ASMJIT_DISABLE_BUILDER
// ============================================================================
// [asmjit::Logger]
// ============================================================================
//! Abstract logging class.
//! Abstract logging interface and helpers.
//!
//! This class can be inherited and reimplemented to fit into your logging
//! subsystem. When reimplementing use `Logger::log()` method to log into
//! subsystem. When reimplementing use `Logger::_log()` method to log into
//! a custom stream.
//!
//! This class also contain `_enabled` member that can be used to enable
//! or disable logging.
struct ASMJIT_VCLASS Logger {
ASMJIT_NO_COPY(Logger)
//! There are two \ref Logger implementations offered by AsmJit:
//! - \ref FileLogger - allows to log into a `FILE*` stream.
//! - \ref StringLogger - logs into a \ref StringBuilder.
class ASMJIT_VIRTAPI Logger {
public:
ASMJIT_NONCOPYABLE(Logger)
// --------------------------------------------------------------------------
// [Options]
// --------------------------------------------------------------------------
//! Logger options.
ASMJIT_ENUM(Options) {
kOptionBinaryForm = 0x00000001, //! Output instructions also in binary form.
kOptionImmExtended = 0x00000002, //! Output a meaning of some immediates.
kOptionHexImmediate = 0x00000004, //! Output constants in hexadecimal form.
kOptionHexDisplacement = 0x00000008 //! Output displacements in hexadecimal form.
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a `Logger` instance.
ASMJIT_API Logger();
ASMJIT_API Logger() noexcept;
//! Destroy the `Logger` instance.
ASMJIT_API virtual ~Logger();
ASMJIT_API virtual ~Logger() noexcept;
// --------------------------------------------------------------------------
// [Logging]
// --------------------------------------------------------------------------
//! Log output.
virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex) = 0;
//! Log `str` - must be reimplemented.
virtual Error _log(const char* str, size_t len) noexcept = 0;
//! Log a string `str`, which is either null terminated or having `len` length.
ASMJIT_INLINE Error log(const char* str, size_t len = Globals::kInvalidIndex) noexcept { return _log(str, len); }
//! Log a content of a `StringBuilder` `str`.
ASMJIT_INLINE Error log(const StringBuilder& str) noexcept { return _log(str.getData(), str.getLength()); }
//! Log formatter message (like sprintf) sending output to `logString()` method.
ASMJIT_API void logFormat(uint32_t style, const char* fmt, ...);
//! Format the message by using `sprintf()` and then send to `log()`.
ASMJIT_API Error logf(const char* fmt, ...) noexcept;
//! Format the message by using `vsprintf()` and then send to `log()`.
ASMJIT_API Error logv(const char* fmt, va_list ap) noexcept;
//! Log binary data.
ASMJIT_API void logBinary(uint32_t style, const void* data, size_t size);
ASMJIT_API Error logBinary(const void* data, size_t size) noexcept;
// --------------------------------------------------------------------------
// [Options]
// --------------------------------------------------------------------------
//! Get all logger options as a single integer.
ASMJIT_INLINE uint32_t getOptions() const {
return _options;
}
ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; }
//! Get the given logger option.
ASMJIT_INLINE bool getOption(uint32_t id) const {
ASMJIT_ASSERT(id < kLoggerOptionCount);
return static_cast<bool>((_options >> id) & 0x1);
}
//! Set the given logger option.
ASMJIT_API void setOption(uint32_t id, bool value);
ASMJIT_INLINE bool hasOption(uint32_t option) const noexcept { return (_options & option) != 0; }
ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; }
ASMJIT_INLINE void clearOptions(uint32_t options) noexcept { _options &= ~options; }
// --------------------------------------------------------------------------
// [Indentation]
// --------------------------------------------------------------------------
//! Get indentation.
ASMJIT_INLINE const char* getIndentation() const {
return _indentation;
}
ASMJIT_INLINE const char* getIndentation() const noexcept { return _indentation; }
//! Set indentation.
ASMJIT_API void setIndentation(const char* indentation);
ASMJIT_API void setIndentation(const char* indentation) noexcept;
//! Reset indentation.
ASMJIT_INLINE void resetIndentation() {
setIndentation(NULL);
}
ASMJIT_INLINE void resetIndentation() noexcept { setIndentation(nullptr); }
// --------------------------------------------------------------------------
// [Members]
......@@ -144,42 +129,39 @@ struct ASMJIT_VCLASS Logger {
// [asmjit::FileLogger]
// ============================================================================
//! Logger that can log to standard C `FILE*` stream.
struct ASMJIT_VCLASS FileLogger : public Logger {
ASMJIT_NO_COPY(FileLogger)
//! Logger that can log to a `FILE*` stream.
class ASMJIT_VIRTAPI FileLogger : public Logger {
public:
ASMJIT_NONCOPYABLE(FileLogger)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new `FileLogger` that logs to a `FILE` stream.
ASMJIT_API FileLogger(FILE* stream = NULL);
ASMJIT_API FileLogger(FILE* stream = nullptr) noexcept;
//! Destroy the `FileLogger`.
ASMJIT_API virtual ~FileLogger();
ASMJIT_API virtual ~FileLogger() noexcept;
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get `FILE*` stream.
//!
//! \note Return value can be `NULL`.
ASMJIT_INLINE FILE* getStream() const {
return _stream;
}
//! Get the logging out put stream or null.
ASMJIT_INLINE FILE* getStream() const noexcept { return _stream; }
//! Set `FILE*` stream, can be set to `NULL` to disable logging, although
//! the `CodeGen` will still call `logString` even if there is no stream.
ASMJIT_INLINE void setStream(FILE* stream) {
_stream = stream;
}
//! Set the logging output stream to `stream` or null.
//!
//! NOTE: If the `stream` is null it will disable logging, but it won't
//! stop calling `log()` unless the logger is detached from the
//! \ref Assembler.
ASMJIT_INLINE void setStream(FILE* stream) noexcept { _stream = stream; }
// --------------------------------------------------------------------------
// [Logging]
// --------------------------------------------------------------------------
ASMJIT_API virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex);
ASMJIT_API Error _log(const char* buf, size_t len = Globals::kInvalidIndex) noexcept override;
// --------------------------------------------------------------------------
// [Members]
......@@ -193,19 +175,19 @@ struct ASMJIT_VCLASS FileLogger : public Logger {
// [asmjit::StringLogger]
// ============================================================================
//! String logger.
struct ASMJIT_VCLASS StringLogger : public Logger {
ASMJIT_NO_COPY(StringLogger)
//! Logger that stores everything in an internal string buffer.
class ASMJIT_VIRTAPI StringLogger : public Logger {
public:
ASMJIT_NONCOPYABLE(StringLogger)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create new `StringLogger`.
ASMJIT_API StringLogger();
ASMJIT_API StringLogger() noexcept;
//! Destroy the `StringLogger`.
ASMJIT_API virtual ~StringLogger();
ASMJIT_API virtual ~StringLogger() noexcept;
// --------------------------------------------------------------------------
// [Accessors]
......@@ -214,36 +196,93 @@ struct ASMJIT_VCLASS StringLogger : public Logger {
//! Get `char*` pointer which represents the resulting string.
//!
//! The pointer is owned by `StringLogger`, it can't be modified or freed.
ASMJIT_INLINE const char* getString() const {
return _stringBuilder.getData();
}
ASMJIT_INLINE const char* getString() const noexcept { return _stringBuilder.getData(); }
//! Clear the resulting string.
ASMJIT_INLINE void clearString() {
_stringBuilder.clear();
}
ASMJIT_INLINE void clearString() noexcept { _stringBuilder.clear(); }
//! Get the length of the string returned by `getString()`.
ASMJIT_INLINE size_t getLength() const noexcept { return _stringBuilder.getLength(); }
// --------------------------------------------------------------------------
// [Logging]
// --------------------------------------------------------------------------
ASMJIT_API virtual void logString(uint32_t style, const char* buf, size_t len = kInvalidIndex);
ASMJIT_API Error _log(const char* buf, size_t len = Globals::kInvalidIndex) noexcept override;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Output.
//! Output string.
StringBuilder _stringBuilder;
};
// ============================================================================
// [asmjit::Logging]
// ============================================================================
struct Logging {
ASMJIT_API static Error formatRegister(
StringBuilder& sb,
uint32_t logOptions,
const CodeEmitter* emitter,
uint32_t archType,
uint32_t regType,
uint32_t regId) noexcept;
ASMJIT_API static Error formatLabel(
StringBuilder& sb,
uint32_t logOptions,
const CodeEmitter* emitter,
uint32_t labelId) noexcept;
ASMJIT_API static Error formatOperand(
StringBuilder& sb,
uint32_t logOptions,
const CodeEmitter* emitter,
uint32_t archType,
const Operand_& op) noexcept;
ASMJIT_API static Error formatInstruction(
StringBuilder& sb,
uint32_t logOptions,
const CodeEmitter* emitter,
uint32_t archType,
const Inst::Detail& detail, const Operand_* opArray, uint32_t opCount) noexcept;
#if !defined(ASMJIT_DISABLE_BUILDER)
ASMJIT_API static Error formatNode(
StringBuilder& sb,
uint32_t logOptions,
const CodeBuilder* cb,
const CBNode* node_) noexcept;
#endif // !ASMJIT_DISABLE_BUILDER
// Only used by AsmJit internals, not available to users.
#if defined(ASMJIT_EXPORTS)
enum {
// Has to be big to be able to hold all metadata compiler can assign to a
// single instruction.
kMaxCommentLength = 512,
kMaxInstLength = 40,
kMaxBinaryLength = 26
};
static Error formatLine(
StringBuilder& sb,
const uint8_t* binData, size_t binLen, size_t dispLen, size_t imLen, const char* comment) noexcept;
#endif // ASMJIT_EXPORTS
};
#else
class Logger;
#endif // !ASMJIT_DISABLE_LOGGING
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
// [Guard]
#endif // !ASMJIT_DISABLE_LOGGER
#endif // _ASMJIT_BASE_LOGGER_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_MISC_P_H
#define _ASMJIT_BASE_MISC_P_H
// [Dependencies]
#include "../asmjit_build.h"
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
//! \internal
//!
//! Macro used to populate a table with 16 elements starting at `I`.
#define ASMJIT_TABLE_16(DEF, I) DEF(I + 0), DEF(I + 1), DEF(I + 2), DEF(I + 3), \
DEF(I + 4), DEF(I + 5), DEF(I + 6), DEF(I + 7), \
DEF(I + 8), DEF(I + 9), DEF(I + 10), DEF(I + 11), \
DEF(I + 12), DEF(I + 13), DEF(I + 14), DEF(I + 15)
#define ASMJIT_TABLE_T_8(TABLE, VALUE, I) \
TABLE< I + 0 >::VALUE, TABLE< I + 1 >::VALUE, \
TABLE< I + 2 >::VALUE, TABLE< I + 3 >::VALUE, \
TABLE< I + 4 >::VALUE, TABLE< I + 5 >::VALUE, \
TABLE< I + 6 >::VALUE, TABLE< I + 7 >::VALUE
#define ASMJIT_TABLE_T_16(TABLE, VALUE, I) \
ASMJIT_TABLE_T_8(TABLE, VALUE, I), \
ASMJIT_TABLE_T_8(TABLE, VALUE, I + 8)
#define ASMJIT_TABLE_T_32(TABLE, VALUE, I) \
ASMJIT_TABLE_T_16(TABLE, VALUE, I), \
ASMJIT_TABLE_T_16(TABLE, VALUE, I + 16)
#define ASMJIT_TABLE_T_64(TABLE, VALUE, I) \
ASMJIT_TABLE_T_32(TABLE, VALUE, I), \
ASMJIT_TABLE_T_32(TABLE, VALUE, I + 32)
#define ASMJIT_TABLE_T_128(TABLE, VALUE, I) \
ASMJIT_TABLE_T_64(TABLE, VALUE, I), \
ASMJIT_TABLE_T_64(TABLE, VALUE, I + 64)
#define ASMJIT_TABLE_T_256(TABLE, VALUE, I) \
ASMJIT_TABLE_T_128(TABLE, VALUE, I), \
ASMJIT_TABLE_T_128(TABLE, VALUE, I + 128)
#define ASMJIT_TABLE_T_512(TABLE, VALUE, I) \
ASMJIT_TABLE_T_256(TABLE, VALUE, I), \
ASMJIT_TABLE_T_256(TABLE, VALUE, I + 256)
#define ASMJIT_TABLE_T_1024(TABLE, VALUE, I) \
ASMJIT_TABLE_T_512(TABLE, VALUE, I), \
ASMJIT_TABLE_T_512(TABLE, VALUE, I + 512)
//! \}
} // asmjit namespace
//! \}
// [Api-End]
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_MISC_P_H
......@@ -7,32 +7,203 @@
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies - AsmJit]
#include "../base/globals.h"
// [Dependencies]
#include "../base/operand.h"
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::Operand]
// [asmjit::TypeId]
// ============================================================================
// Prevent static initialization.
struct Operand {
uint8_t op;
uint8_t size;
uint8_t reserved_2_1;
uint8_t reserved_3_1;
uint32_t id;
uint64_t reserved_8_8;
template<int ID>
struct TypeIdSizeOf_T {
enum {
kValue = (ID == TypeId::kI8 ) ? 1 :
(ID == TypeId::kU8 ) ? 1 :
(ID == TypeId::kI16 ) ? 2 :
(ID == TypeId::kU16 ) ? 2 :
(ID == TypeId::kI32 ) ? 4 :
(ID == TypeId::kU32 ) ? 4 :
(ID == TypeId::kI64 ) ? 8 :
(ID == TypeId::kU64 ) ? 8 :
(ID == TypeId::kF32 ) ? 4 :
(ID == TypeId::kF64 ) ? 8 :
(ID == TypeId::kF80 ) ? 10 :
(ID == TypeId::kMask8 ) ? 1 :
(ID == TypeId::kMask16) ? 2 :
(ID == TypeId::kMask32) ? 4 :
(ID == TypeId::kMask64) ? 8 :
(ID == TypeId::kMmx32 ) ? 4 :
(ID == TypeId::kMmx64 ) ? 8 :
(ID >= TypeId::_kVec32Start && ID <= TypeId::_kVec32End ) ? 4 :
(ID >= TypeId::_kVec64Start && ID <= TypeId::_kVec64End ) ? 8 :
(ID >= TypeId::_kVec128Start && ID <= TypeId::_kVec128End) ? 16 :
(ID >= TypeId::_kVec256Start && ID <= TypeId::_kVec256End) ? 32 :
(ID >= TypeId::_kVec512Start && ID <= TypeId::_kVec512End) ? 64 : 0
};
};
ASMJIT_VAR const Operand noOperand;
const Operand noOperand = { 0, 0, 0, 0, kInvalidValue, 0 };
template<int ID>
struct TypeIdElementOf_T {
enum {
kValue = (ID == TypeId::kMask8 ) ? TypeId::kU8 :
(ID == TypeId::kMask16) ? TypeId::kU16 :
(ID == TypeId::kMask32) ? TypeId::kU32 :
(ID == TypeId::kMask64) ? TypeId::kU64 :
(ID == TypeId::kMmx32 ) ? TypeId::kI32 :
(ID == TypeId::kMmx64 ) ? TypeId::kI64 :
(ID >= TypeId::kI8 && ID <= TypeId::kF80 ) ? ID :
(ID >= TypeId::_kVec32Start && ID <= TypeId::_kVec32End ) ? ID - TypeId::_kVec32Start + TypeId::kI8 :
(ID >= TypeId::_kVec64Start && ID <= TypeId::_kVec64End ) ? ID - TypeId::_kVec64Start + TypeId::kI8 :
(ID >= TypeId::_kVec128Start && ID <= TypeId::_kVec128End) ? ID - TypeId::_kVec128Start + TypeId::kI8 :
(ID >= TypeId::_kVec256Start && ID <= TypeId::_kVec256End) ? ID - TypeId::_kVec256Start + TypeId::kI8 :
(ID >= TypeId::_kVec512Start && ID <= TypeId::_kVec512End) ? ID - TypeId::_kVec512Start + TypeId::kI8 : 0
};
};
#define R(TMPL, I) TMPL<I + 0>::kValue, TMPL<I + 1>::kValue, \
TMPL<I + 2>::kValue, TMPL<I + 3>::kValue, \
TMPL<I + 4>::kValue, TMPL<I + 5>::kValue, \
TMPL<I + 6>::kValue, TMPL<I + 7>::kValue, \
TMPL<I + 8>::kValue, TMPL<I + 9>::kValue, \
TMPL<I + 10>::kValue, TMPL<I + 11>::kValue, \
TMPL<I + 12>::kValue, TMPL<I + 13>::kValue, \
TMPL<I + 14>::kValue, TMPL<I + 15>::kValue
ASMJIT_API const TypeId::Info TypeId::_info = {
// SizeOf[128]
{
R(TypeIdSizeOf_T, 0), R(TypeIdSizeOf_T, 16),
R(TypeIdSizeOf_T, 32), R(TypeIdSizeOf_T, 48),
R(TypeIdSizeOf_T, 64), R(TypeIdSizeOf_T, 80),
R(TypeIdSizeOf_T, 96), R(TypeIdSizeOf_T, 112)
},
// ElementOf[128]
{
R(TypeIdElementOf_T, 0), R(TypeIdElementOf_T, 16),
R(TypeIdElementOf_T, 32), R(TypeIdElementOf_T, 48),
R(TypeIdElementOf_T, 64), R(TypeIdElementOf_T, 80),
R(TypeIdElementOf_T, 96), R(TypeIdElementOf_T, 112)
}
};
#undef R
// ============================================================================
// [asmjit::Operand - Test]
// ============================================================================
#if defined(ASMJIT_TEST)
UNIT(base_operand) {
INFO("Checking operand sizes");
EXPECT(sizeof(Operand) == 16);
EXPECT(sizeof(Reg) == 16);
EXPECT(sizeof(Mem) == 16);
EXPECT(sizeof(Imm) == 16);
EXPECT(sizeof(Label) == 16);
INFO("Checking basic functionality of Operand");
Operand a, b;
Operand dummy;
EXPECT(a.isNone() == true);
EXPECT(a.isReg() == false);
EXPECT(a.isMem() == false);
EXPECT(a.isImm() == false);
EXPECT(a.isLabel() == false);
EXPECT(a == b);
EXPECT(a._any.reserved8_4 == 0, "Default constructed Operand should zero its 'reserved8_4' field");
EXPECT(a._any.reserved12_4 == 0, "Default constructed Operand should zero its 'reserved12_4' field");
INFO("Checking basic functionality of Label");
Label label;
EXPECT(label.isValid() == false);
EXPECT(label.getId() == 0);
INFO("Checking basic functionality of Reg");
EXPECT(Reg().isValid() == false,
"Default constructed Reg() should not be valid");
EXPECT(Reg()._any.reserved8_4 == 0,
"A default constructed Reg() should zero its 'reserved8_4' field");
EXPECT(Reg()._any.reserved12_4 == 0,
"A default constructed Reg() should zero its 'reserved12_4' field");
EXPECT(Reg().isReg() == false,
"Default constructed register should not isReg()");
EXPECT(dummy.as<Reg>().isValid() == false,
"Default constructed Operand casted to Reg should not be valid");
// Create some register (not specific to any architecture).
uint32_t rSig = Operand::kOpReg | (1 << Operand::kSignatureRegTypeShift) |
(2 << Operand::kSignatureRegKindShift) |
(8 << Operand::kSignatureSizeShift ) ;
Reg r1(Reg::fromSignature(rSig, 5));
EXPECT(r1.isValid() == true);
EXPECT(r1.isReg() == true);
EXPECT(r1.isReg(1) == true);
EXPECT(r1.isPhysReg() == true);
EXPECT(r1.isVirtReg() == false);
EXPECT(r1.getSignature() == rSig);
EXPECT(r1.getType() == 1);
EXPECT(r1.getKind() == 2);
EXPECT(r1.getSize() == 8);
EXPECT(r1.getId() == 5);
EXPECT(r1.isReg(1, 5) == true); // RegType and Id.
EXPECT(r1._any.reserved8_4 == 0, "Reg should have 'reserved8_4' zero");
EXPECT(r1._any.reserved12_4 == 0, "Reg should have 'reserved12_4' zero");
// The same type of register having different id.
Reg r2(r1, 6);
EXPECT(r2.isValid() == true);
EXPECT(r2.isReg() == true);
EXPECT(r2.isReg(1) == true);
EXPECT(r2.isPhysReg() == true);
EXPECT(r2.isVirtReg() == false);
EXPECT(r2.getSignature() == rSig);
EXPECT(r2.getType() == r1.getType());
EXPECT(r2.getKind() == r1.getKind());
EXPECT(r2.getSize() == r1.getSize());
EXPECT(r2.getId() == 6);
EXPECT(r2.isReg(1, 6) == true);
r1.reset();
EXPECT(!r1.isValid(),
"Reset register should not be valid");
EXPECT(!r1.isReg(),
"Reset register should not isReg()");
INFO("Checking basic functionality of Mem");
Mem m;
EXPECT(m.isMem() , "Default constructed Mem() should isMem()");
EXPECT(m == Mem() , "Two default constructed Mem() operands should be equal");
EXPECT(m.hasBase() == false , "Default constructed Mem() should not have base specified");
EXPECT(m.hasIndex() == false , "Default constructed Mem() should not have index specified");
EXPECT(m.has64BitOffset() == true , "Default constructed Mem() should report 64-bit offset");
EXPECT(m.getOffset() == 0 , "Default constructed Mem() should have be zero offset / address");
m.setOffset(-1);
EXPECT(m.getOffsetLo32() == -1 , "Memory operand must hold a 32-bit offset");
EXPECT(m.getOffset() == -1 , "32-bit offset must be sign extended to 64 bits");
int64_t x = int64_t(ASMJIT_UINT64_C(0xFF00FF0000000001));
m.setOffset(x);
EXPECT(m.getOffset() == x , "Memory operand must hold a 64-bit offset");
EXPECT(m.getOffsetLo32() == 1 , "Memory operand must return correct low offset DWORD");
EXPECT(m.getOffsetHi32() == 0xFF00FF00, "Memory operand must return correct high offset DWORD");
INFO("Checking basic functionality of Imm");
EXPECT(Imm(-1).getInt64() == int64_t(-1),
"Immediate values should by default sign-extend to 64-bits");
}
#endif // ASMJIT_TEST
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
......@@ -8,711 +8,1084 @@
#ifndef _ASMJIT_BASE_OPERAND_H
#define _ASMJIT_BASE_OPERAND_H
// [Dependencies - AsmJit]
#include "../base/intutil.h"
// [Dependencies]
#include "../base/utils.h"
// [Api-Begin]
#include "../apibegin.h"
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [Forward Declarations]
// ============================================================================
struct Assembler;
struct Compiler;
//! \addtogroup asmjit_base_general
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::OperandType]
// ============================================================================
//! Operand types that can be encoded in `Operand`.
ASMJIT_ENUM(OperandType) {
//! Invalid operand, used only internally (not initialized Operand).
kOperandTypeNone = 0,
//! Operand is a register.
kOperandTypeReg = 1,
//! Operand is a variable.
kOperandTypeVar = 2,
//! Operand is a memory.
kOperandTypeMem = 3,
//! Operand is an immediate value.
kOperandTypeImm = 4,
//! Operand is a label.
kOperandTypeLabel = 5
};
// ============================================================================
// [asmjit::OperandId]
// [asmjit::Operand_]
// ============================================================================
//! Operand id masks used to determine the operand type.
ASMJIT_ENUM(OperandId) {
//! Operand id refers to `Var`.
kOperandIdVar = 0x80000000U,
//! Operand id to real index mask.
kOperandIdNum = 0x7FFFFFFFU
};
// ============================================================================
// [asmjit::RegClass]
// ============================================================================
//! Constructor-less \ref Operand.
//!
//! Contains no initialization code and can be used safely to define an array
//! of operands that won't be initialized. This is a \ref Operand compatible
//! data structure designed to be statically initialized or `static const`.
struct Operand_ {
// --------------------------------------------------------------------------
// [Operand Type]
// --------------------------------------------------------------------------
//! Register class.
ASMJIT_ENUM(RegClass) {
//! Gp register class, compatible with all architectures.
kRegClassGp = 0
};
//! Operand types that can be encoded in \ref Operand.
ASMJIT_ENUM(OpType) {
kOpNone = 0, //!< Not an operand or not initialized.
kOpReg = 1, //!< Operand is a register.
kOpMem = 2, //!< Operand is a memory.
kOpImm = 3, //!< Operand is an immediate value.
kOpLabel = 4 //!< Operand is a label.
};
// ============================================================================
// [asmjit::SizeDefs]
// ============================================================================
// --------------------------------------------------------------------------
// [Operand Signature (Bits)]
// --------------------------------------------------------------------------
//! Common size of registers and pointers.
ASMJIT_ENUM(SizeDefs) {
//! 1 byte size (BYTE).
kSizeByte = 1,
//! 2 bytes size (WORD).
kSizeWord = 2,
//! 4 bytes size (DWORD).
kSizeDWord = 4,
//! 8 bytes size (QWORD).
kSizeQWord = 8,
//! 10 bytes size (TWORD).
kSizeTWord = 10,
//! 16 bytes size (OWORD / DQWORD).
kSizeOWord = 16,
//! 32 bytes size (YWORD / QQWORD).
kSizeYWord = 32
};
ASMJIT_ENUM(SignatureBits) {
// Operand type (3 least significant bits).
// |........|........|........|.....XXX|
kSignatureOpShift = 0,
kSignatureOpBits = 0x07U,
kSignatureOpMask = kSignatureOpBits << kSignatureOpShift,
// Operand size (8 most significant bits).
// |XXXXXXXX|........|........|........|
kSignatureSizeShift = 24,
kSignatureSizeBits = 0xFFU,
kSignatureSizeMask = kSignatureSizeBits << kSignatureSizeShift,
// Register type (5 bits).
// |........|........|........|XXXXX...|
kSignatureRegTypeShift = 3,
kSignatureRegTypeBits = 0x1FU,
kSignatureRegTypeMask = kSignatureRegTypeBits << kSignatureRegTypeShift,
// Register kind (4 bits).
// |........|........|....XXXX|........|
kSignatureRegKindShift = 8,
kSignatureRegKindBits = 0x0FU,
kSignatureRegKindMask = kSignatureRegKindBits << kSignatureRegKindShift,
// Memory base type (5 bits).
// |........|........|........|XXXXX...|
kSignatureMemBaseTypeShift = 3,
kSignatureMemBaseTypeBits = 0x1FU,
kSignatureMemBaseTypeMask = kSignatureMemBaseTypeBits << kSignatureMemBaseTypeShift,
// Memory index type (5 bits).
// |........|........|...XXXXX|........|
kSignatureMemIndexTypeShift = 8,
kSignatureMemIndexTypeBits = 0x1FU,
kSignatureMemIndexTypeMask = kSignatureMemIndexTypeBits << kSignatureMemIndexTypeShift,
// Memory base+index combined (10 bits).
// |........|........|...XXXXX|XXXXX...|
kSignatureMemBaseIndexShift = 3,
kSignatureMemBaseIndexBits = 0x3FFU,
kSignatureMemBaseIndexMask = kSignatureMemBaseIndexBits << kSignatureMemBaseIndexShift,
// Memory should be encoded as absolute immediate (X86|X64).
// |........|........|.XX.....|........|
kSignatureMemAddrTypeShift = 13,
kSignatureMemAddrTypeBits = 0x03U,
kSignatureMemAddrTypeMask = kSignatureMemAddrTypeBits << kSignatureMemAddrTypeShift,
// This memory operand represents a function argument's stack location (CodeCompiler)
// |........|........|.X......|........|
kSignatureMemArgHomeShift = 15,
kSignatureMemArgHomeBits = 0x01U,
kSignatureMemArgHomeFlag = kSignatureMemArgHomeBits << kSignatureMemArgHomeShift,
// This memory operand represents a virtual register's home-slot (CodeCompiler).
// |........|........|X.......|........|
kSignatureMemRegHomeShift = 16,
kSignatureMemRegHomeBits = 0x01U,
kSignatureMemRegHomeFlag = kSignatureMemRegHomeBits << kSignatureMemRegHomeShift
};
// ============================================================================
// [asmjit::MemType]
// ============================================================================
// --------------------------------------------------------------------------
// [Operand Id]
// --------------------------------------------------------------------------
//! Type of memory operand.
ASMJIT_ENUM(MemType) {
//! Memory operand is a combination of base register and optional index register
//! and displacement.
//!
//! The `Assembler` interprets `kMemTypeBaseIndex` and `kMemTypeStackIndex`
//! types the same way, but `Compiler` interprets `kMemTypeBaseIndex` as
//! `[base + index]` and `kMemTypeStackIndex` as `[stack(base) + index]`.
kMemTypeBaseIndex = 0,
//! Operand id helpers useful for id <-> index translation.
ASMJIT_ENUM(PackedId) {
//! Minimum valid packed-id.
kPackedIdMin = 0x00000100U,
//! Maximum valid packed-id.
kPackedIdMax = 0xFFFFFFFFU,
//! Count of valid packed-ids.
kPackedIdCount = kPackedIdMax - kPackedIdMin + 1
};
//! Memory operand is a combination of variable's memory location,
//! optional index register and displacement.
//!
//! The `Assembler` interprets `kMemTypeBaseIndex` and `kMemTypeStackIndex`
//! types in the same way, but `Compiler` interprets `kMemTypeBaseIndex` as
//! `[base + index]` and `kMemTypeStackIndex` as `[stack(base) + index]`.
kMemTypeStackIndex = 1,
//! Memory operand refers to the memory location specified by a label.
kMemTypeLabel = 2,
//! Memory operand is an absolute memory location.
//!
//! Supported mostly by x86, truncated to a 32-bit value when running in
//! 64-bit mode (x64).
kMemTypeAbsolute = 3
};
// --------------------------------------------------------------------------
// [Operand Utilities]
// --------------------------------------------------------------------------
// ============================================================================
// [asmjit::Operand]
// ============================================================================
//! Get if the given `id` is a valid packed-id that can be used by Operand.
//! Packed ids are those equal or greater than `kPackedIdMin` and lesser or
//! equal to `kPackedIdMax`. This concept was created to support virtual
//! registers and to make them distinguishable from physical ones. It allows
//! a single uint32_t to contain either physical register id or virtual
//! register id represented as `packed-id`. This concept is used also for
//! labels to make the API consistent.
static ASMJIT_INLINE bool isPackedId(uint32_t id) noexcept { return id - kPackedIdMin < kPackedIdCount; }
//! Convert a real-id into a packed-id that can be stored in Operand.
static ASMJIT_INLINE uint32_t packId(uint32_t id) noexcept { return id + kPackedIdMin; }
//! Convert a packed-id back to real-id.
static ASMJIT_INLINE uint32_t unpackId(uint32_t id) noexcept { return id - kPackedIdMin; }
//! Operand can contain register, memory location, immediate, or label.
struct Operand {
// --------------------------------------------------------------------------
// [Structs]
// [Operand Data]
// --------------------------------------------------------------------------
//! \internal
//!
//! Base operand data.
struct BaseOp {
//! Type of operand, see \ref OperandType.
uint8_t op;
//! Size of operand (register, address, immediate, or variable).
uint8_t size;
//! \internal
uint8_t reserved_2_1;
//! \internal
uint8_t reserved_3_1;
//! Operand id, identifier used by `Assembler` and `Compiler`.
//!
//! \note Uninitialized operand has always set id to `kInvalidValue`.
uint32_t id;
//! \internal
uint32_t reserved_8_4;
//! \internal
uint32_t reserved_12_4;
//! Any operand.
struct AnyData {
uint32_t signature; //!< Type of the operand (see \ref OpType) and other data.
uint32_t id; //!< Operand id or `0`.
uint32_t reserved8_4; //!< \internal
uint32_t reserved12_4; //!< \internal
};
//! \internal
//!
//! Register or Variable operand data.
struct VRegOp {
//! Type of operand, `kOperandTypeReg`.
uint8_t op;
//! Size of register or variable.
uint8_t size;
//! Register operand data.
struct RegData {
uint32_t signature; //!< Type of the operand (always \ref kOpReg) and other data.
uint32_t id; //!< Physical or virtual register id.
uint32_t reserved8_4; //!< \internal
uint32_t reserved12_4; //!< \internal
};
union {
//! Register code = (type << 8) | index.
uint16_t code;
//! Memory operand data.
struct MemData {
uint32_t signature; //!< Type of the operand (always \ref kOpMem) and other data.
uint32_t index; //!< INDEX register id or `0`.
//! Register type and index access.
// [BASE + OFF32] vs just [OFF64].
union {
uint64_t offset64; //!< 64-bit offset, combining low and high 32-bit parts.
struct {
#if defined(ASMJIT_ARCH_LE)
//! Register index.
uint8_t index;
//! Register type.
uint8_t type;
#if ASMJIT_ARCH_LE
uint32_t offsetLo32; //!< 32-bit low offset part.
uint32_t base; //!< 32-bit high offset part or BASE.
#else
//! Register type.
uint8_t type;
//! Register index.
uint8_t index;
uint32_t base; //!< 32-bit high offset part or BASE.
uint32_t offsetLo32; //!< 32-bit low offset part.
#endif
};
};
};
//! Variable id, used by `Compiler` to identify variables.
uint32_t id;
union {
struct {
//! Variable type.
uint32_t vType;
//! \internal
uint32_t reserved_12_4;
};
//! Immediate operand data.
struct ImmData {
uint32_t signature; //!< Type of the operand (always \ref kOpImm) and other data.
uint32_t id; //!< Immediate id (always `0`).
UInt64 value; //!< Immediate value.
};
//! \internal
//!
//! This is not needed or used, it's just to force compiler to always
//! align this struct to 8-bytes (so the struct is compatible to others
//! when it comes to alignment). It should fix VS linker warning as well.
uint64_t reserved8_8;
};
//! Label operand data.
struct LabelData {
uint32_t signature; //!< Type of the operand (always \ref kOpLabel) and other data.
uint32_t id; //!< Label id (`0` if not initialized).
uint32_t reserved8_4; //!< \internal
uint32_t reserved12_4; //!< \internal
};
// --------------------------------------------------------------------------
// [Init & Copy]
// --------------------------------------------------------------------------
//! \internal
//!
//! Memory or Variable operand data.
struct VMemOp {
//! Type of operand, `kOperandTypeMem`.
uint8_t op;
//! Size of the pointer in bytes.
uint8_t size;
//! Type of the memory operand, see `MemType`.
uint8_t type;
//! X86/X64 layout:
//! - segment [3 bits], see `X86Seg`.
//! - shift [2 bits], index register shift (0 to 3).
uint8_t flags;
//! Base register, variable or label id.
uint32_t base;
//! Index register or variable.
uint32_t index;
//! 32-bit displacement or absolute address.
int32_t displacement;
};
//! Initialize the operand to `other` (used by constructors).
ASMJIT_INLINE void _init(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); }
//! \internal
//!
//! Immediate operand data.
struct ImmOp {
//! Type of operand, `kOperandTypeImm`.
uint8_t op;
//! Size of immediate (or 0 to autodetect).
uint8_t size;
//! \internal
uint8_t reserved_2_1;
//! \internal
uint8_t reserved_3_1;
//! Operand id, always set to `kInvalidValue` (immediates don't have IDs).
uint32_t id;
ASMJIT_INLINE void _initReg(uint32_t signature, uint32_t rd) {
_init_packed_d0_d1(signature, rd);
_init_packed_d2_d3(0, 0);
}
union {
//! 8x8-bit signed immediate values.
int8_t _i8[8];
//! 8x8-bit unsigned immediate values.
uint8_t _u8[8];
//! 4x16-bit signed immediate values.
int16_t _i16[4];
//! 4x16-bit unsigned immediate values.
uint16_t _u16[4];
//! 2x32-bit signed immediate values.
int32_t _i32[2];
//! 2x32-bit unsigned immediate values.
uint32_t _u32[2];
//! 1x64-bit signed immediate value.
int64_t _i64[1];
//! 1x64-bit unsigned immediate value.
uint64_t _u64[1];
//! 2x SP-FP values.
float _f32[2];
//! 1x DP-FP value.
double _f64[1];
} value;
};
//! \internal
ASMJIT_INLINE void _init_packed_d0_d1(uint32_t d0, uint32_t d1) noexcept { _packed[0].setPacked_2x32(d0, d1); }
//! \internal
ASMJIT_INLINE void _init_packed_d2_d3(uint32_t d2, uint32_t d3) noexcept { _packed[1].setPacked_2x32(d2, d3); }
//! \internal
//!
//! Label operand data.
struct LabelOp {
//! Type of operand, `kOperandTypeLabel`.
uint8_t op;
//! Always zero, labels don't have size.
uint8_t size;
//! \internal
uint8_t reserved_2_1;
//! \internal
uint8_t reserved_3_1;
//! Operand id (`kInvalidValue` if the label is not initialized by code
//! generator).
uint32_t id;
//! \internal
uint32_t reserved_8_4;
//! \internal
uint32_t reserved_12_4;
};
//! Initialize the operand from `other` (used by operator overloads).
ASMJIT_INLINE void copyFrom(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); }
// --------------------------------------------------------------------------
// [Construction / Destruction]
// [Accessors]
// --------------------------------------------------------------------------
//! Create an uninitialized operand.
ASMJIT_INLINE Operand() {
_init_packed_op_sz_b0_b1_id(kOperandTypeNone, 0, 0, 0, kInvalidValue);
_init_packed_d2_d3(0, 0);
}
//! Get if the operand matches the given signature `sign`.
ASMJIT_INLINE bool hasSignature(uint32_t signature) const noexcept { return _signature == signature; }
//! Create a reference to `other` operand.
ASMJIT_INLINE Operand(const Operand& other) {
_init(other);
//! Get if the operand matches a signature of the `other` operand.
ASMJIT_INLINE bool hasSignature(const Operand_& other) const noexcept {
return _signature == other.getSignature();
}
explicit ASMJIT_INLINE Operand(const _NoInit&) {}
//! Get a 32-bit operand signature.
//!
//! Signature is first 4 bytes of the operand data. It's used mostly for
//! operand checking as it's much faster to check 4 bytes at once than having
//! to check these bytes individually.
ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; }
// --------------------------------------------------------------------------
// [Operand]
// --------------------------------------------------------------------------
//! Set the operand signature (see \ref getSignature).
//!
//! Improper use of `setSignature()` can lead to hard-to-debug errors.
ASMJIT_INLINE void setSignature(uint32_t signature) noexcept { _signature = signature; }
//! Clone `Operand`.
ASMJIT_INLINE Operand clone() const {
return Operand(*this);
}
ASMJIT_INLINE bool _hasSignatureData(uint32_t bits) const noexcept { return (_signature & bits) != 0; }
// --------------------------------------------------------------------------
// [Init & Copy]
// --------------------------------------------------------------------------
//! \internal
//!
//! Unpacks information from operand's signature.
ASMJIT_INLINE uint32_t _getSignatureData(uint32_t bits, uint32_t shift) const noexcept { return (_signature >> shift) & bits; }
//! \internal
//!
//! Initialize operand to `other` (used by constructors).
ASMJIT_INLINE void _init(const Operand& other) {
::memcpy(this, &other, sizeof(Operand));
}
//! Packs information to operand's signature.
ASMJIT_INLINE void _setSignatureData(uint32_t value, uint32_t bits, uint32_t shift) noexcept {
ASMJIT_ASSERT(value <= bits);
_signature = (_signature & ~(bits << shift)) | (value << shift);
}
ASMJIT_INLINE void _addSignatureData(uint32_t data) noexcept { _signature |= data; }
//! Clears specified bits in operand's signature.
ASMJIT_INLINE void _clearSignatureData(uint32_t bits, uint32_t shift) noexcept { _signature &= ~(bits << shift); }
//! Get type of the operand, see \ref OpType.
ASMJIT_INLINE uint32_t getOp() const noexcept { return _getSignatureData(kSignatureOpBits, kSignatureOpShift); }
//! Get if the operand is none (\ref kOpNone).
ASMJIT_INLINE bool isNone() const noexcept { return getOp() == 0; }
//! Get if the operand is a register (\ref kOpReg).
ASMJIT_INLINE bool isReg() const noexcept { return getOp() == kOpReg; }
//! Get if the operand is a memory location (\ref kOpMem).
ASMJIT_INLINE bool isMem() const noexcept { return getOp() == kOpMem; }
//! Get if the operand is an immediate (\ref kOpImm).
ASMJIT_INLINE bool isImm() const noexcept { return getOp() == kOpImm; }
//! Get if the operand is a label (\ref kOpLabel).
ASMJIT_INLINE bool isLabel() const noexcept { return getOp() == kOpLabel; }
//! Get if the operand is a physical register.
ASMJIT_INLINE bool isPhysReg() const noexcept { return isReg() && _reg.id < Globals::kInvalidRegId; }
//! Get if the operand is a virtual register.
ASMJIT_INLINE bool isVirtReg() const noexcept { return isReg() && isPackedId(_reg.id); }
//! Get if the operand specifies a size (i.e. the size is not zero).
ASMJIT_INLINE bool hasSize() const noexcept { return _hasSignatureData(kSignatureSizeMask); }
//! Get if the size of the operand matches `size`.
ASMJIT_INLINE bool hasSize(uint32_t size) const noexcept { return getSize() == size; }
//! Get size of the operand (in bytes).
//!
//! The value returned depends on the operand type:
//! * None - Should always return zero size.
//! * Reg - Should always return the size of the register. If the register
//! size depends on architecture (like \ref X86CReg and \ref X86DReg)
//! the size returned should be the greatest possible (so it should
//! return 64-bit size in such case).
//! * Mem - Size is optional and will be in most cases zero.
//! * Imm - Should always return zero size.
//! * Label - Should always return zero size.
ASMJIT_INLINE uint32_t getSize() const noexcept { return _getSignatureData(kSignatureSizeBits, kSignatureSizeShift); }
//! Get the operand id.
//!
//! The value returned should be interpreted accordingly to the operand type:
//! * None - Should be `0`.
//! * Reg - Physical or virtual register id.
//! * Mem - Multiple meanings - BASE address (register or label id), or
//! high value of a 64-bit absolute address.
//! * Imm - Should be `0`.
//! * Label - Label id if it was created by using `newLabel()` or `0`
//! if the label is invalid or uninitialized.
ASMJIT_INLINE uint32_t getId() const noexcept { return _any.id; }
ASMJIT_INLINE void _init_packed_op_sz_b0_b1_id(uint32_t op, uint32_t sz, uint32_t r0, uint32_t r1, uint32_t id) {
// This hack is not for performance, but to decrease the size of the binary
// generated when constructing AsmJit operands (mostly for third parties).
// Some compilers are not able to join four BYTE writes to a single DWORD
// write. Because the 'a', 'b', 'c' and 'd' variables are usually compile
// time constants the compiler can do a really nice job if they are joined
// by using bitwise operations.
_packed[0].setPacked_2x32(IntUtil::pack32_4x8(op, sz, r0, r1), id);
//! Get if the operand is 100% equal to `other`.
ASMJIT_INLINE bool isEqual(const Operand_& other) const noexcept {
return (_packed[0] == other._packed[0]) &
(_packed[1] == other._packed[1]) ;
}
ASMJIT_INLINE void _init_packed_op_sz_w0_id(uint32_t op, uint32_t sz, uint32_t w0, uint32_t id) {
_packed[0].setPacked_2x32(IntUtil::pack32_2x8_1x16(op, sz, w0), id);
//! Get if the operand is a register matching `rType`.
ASMJIT_INLINE bool isReg(uint32_t rType) const noexcept {
const uint32_t kMsk = (kSignatureOpBits << kSignatureOpShift) | (kSignatureRegTypeBits << kSignatureRegTypeShift);
const uint32_t kSgn = (kOpReg << kSignatureOpShift) | (rType << kSignatureRegTypeShift);
return (_signature & kMsk) == kSgn;
}
ASMJIT_INLINE void _init_packed_d0_d1(uint32_t u0, uint32_t u1) {
_packed[0].setPacked_2x32(u0, u1);
//! Get whether the operand is register and of `type` and `id`.
ASMJIT_INLINE bool isReg(uint32_t rType, uint32_t rId) const noexcept {
return isReg(rType) && getId() == rId;
}
ASMJIT_INLINE void _init_packed_d2_d3(uint32_t u2, uint32_t u3) {
_packed[1].setPacked_2x32(u2, u3);
//! Get whether the operand is a register or memory.
ASMJIT_INLINE bool isRegOrMem() const noexcept {
ASMJIT_ASSERT(kOpMem - kOpReg == 1);
return Utils::inInterval<uint32_t>(getOp(), kOpReg, kOpMem);
}
//! \internal
//! Cast this operand to `T` type.
template<typename T>
ASMJIT_INLINE T& as() noexcept { return static_cast<T&>(*this); }
//! Cast this operand to `T` type (const).
template<typename T>
ASMJIT_INLINE const T& as() const noexcept { return static_cast<const T&>(*this); }
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
//! Reset the `Operand` to none.
//!
//! None operand is defined the following way:
//! - Its signature is zero (kOpNone, and the rest zero as well).
//! - Its id is `0`.
//! - The reserved8_4 field is set to `0`.
//! - The reserved12_4 field is set to zero.
//!
//! In other words, reset operands have all members set to zero. Reset operand
//! must match the Operand state right after its construction. Alternatively,
//! if you have an array of operands, you can simply use `memset()`.
//!
//! ```
//! using namespace asmjit;
//!
//! Operand a;
//! Operand b;
//! assert(a == b);
//!
//! b = x86::eax;
//! assert(a != b);
//!
//! Initialize operand to `other` (used by assign operators).
ASMJIT_INLINE void _copy(const Operand& other) {
::memcpy(this, &other, sizeof(Operand));
//! b.reset();
//! assert(a == b);
//!
//! memset(&b, 0, sizeof(Operand));
//! assert(a == b);
//! ```
ASMJIT_INLINE void reset() noexcept {
_init_packed_d0_d1(kOpNone, 0);
_init_packed_d2_d3(0, 0);
}
// --------------------------------------------------------------------------
// [Data]
// [Operator Overload]
// --------------------------------------------------------------------------
template<typename T>
ASMJIT_INLINE T& getData() {
return reinterpret_cast<T&>(_base);
}
ASMJIT_INLINE bool operator==(const T& other) const noexcept { return isEqual(other); }
template<typename T>
ASMJIT_INLINE const T& getData() const {
return reinterpret_cast<const T&>(_base);
}
ASMJIT_INLINE bool operator!=(const T& other) const noexcept { return !isEqual(other); }
// --------------------------------------------------------------------------
// [Type]
// [Members]
// --------------------------------------------------------------------------
//! Get type of the operand, see \ref OperandType.
ASMJIT_INLINE uint32_t getOp() const { return _base.op; }
union {
AnyData _any; //!< Generic data.
RegData _reg; //!< Physical or virtual register data.
MemData _mem; //!< Memory address data.
ImmData _imm; //!< Immediate value data.
LabelData _label; //!< Label data.
uint32_t _signature; //!< Operand signature (first 32-bits).
UInt64 _packed[2]; //!< Operand packed into two 64-bit integers.
};
};
//! Get whether the operand is none - `kOperandTypeNone`.
ASMJIT_INLINE bool isNone() const { return (_base.op == kOperandTypeNone); }
//! Get whether the operand is a register - `kOperandTypeReg`.
ASMJIT_INLINE bool isReg() const { return (_base.op == kOperandTypeReg); }
//! Get whether the operand is a variable - `kOperandTypeVar`.
ASMJIT_INLINE bool isVar() const { return (_base.op == kOperandTypeVar); }
//! Get whether the operand is a memory address - `kOperandTypeMem`.
ASMJIT_INLINE bool isMem() const { return (_base.op == kOperandTypeMem); }
//! Get whether the operand is an immediate value - `kOperandTypeImm`.
ASMJIT_INLINE bool isImm() const { return (_base.op == kOperandTypeImm); }
//! Get whether the operand is a label - `kOperandTypeLabel`.
ASMJIT_INLINE bool isLabel() const { return (_base.op == kOperandTypeLabel); }
// ============================================================================
// [asmjit::Operand]
// ============================================================================
//! Operand can contain register, memory location, immediate, or label.
class Operand : public Operand_ {
public:
// --------------------------------------------------------------------------
// [Type - Combined]
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Get register type.
ASMJIT_INLINE uint32_t getRegType() const {
return _vreg.type;
}
//! Create an uninitialized operand.
ASMJIT_INLINE Operand() noexcept { reset(); }
//! Create a reference to `other` operand.
ASMJIT_INLINE Operand(const Operand& other) noexcept { _init(other); }
//! Create a reference to `other` operand.
explicit ASMJIT_INLINE Operand(const Operand_& other) noexcept { _init(other); }
//! Create a completely uninitialized operand (dangerous).
explicit ASMJIT_INLINE Operand(const _NoInit&) noexcept {}
//! Get register index.
ASMJIT_INLINE uint32_t getRegIndex() const {
return _vreg.index;
}
// --------------------------------------------------------------------------
// [Clone]
// --------------------------------------------------------------------------
//! Get whether the operand is register of `type`.
ASMJIT_INLINE bool isRegType(uint32_t type) const {
return (_packed[0].u32[0] & IntUtil::pack32_2x8_1x16(0xFF, 0, 0xFF00)) == IntUtil::pack32_2x8_1x16(kOperandTypeReg, 0, (type << 8));
}
//! Clone the `Operand`.
ASMJIT_INLINE Operand clone() const noexcept { return Operand(*this); }
//! Get whether the operand is register and of `type` and `index`.
ASMJIT_INLINE bool isRegCode(uint32_t type, uint32_t index) const {
return (_packed[0].u32[0] & IntUtil::pack32_2x8_1x16(0xFF, 0, 0xFFFF)) == IntUtil::pack32_2x8_1x16(kOperandTypeReg, 0, (type << 8) + index);
}
ASMJIT_INLINE Operand& operator=(const Operand_& other) noexcept { copyFrom(other); return *this; }
};
//! Get whether the operand is a register or memory.
ASMJIT_INLINE bool isRegOrMem() const {
ASMJIT_ASSERT(kOperandTypeReg == 1);
ASMJIT_ASSERT(kOperandTypeMem == 3);
return (static_cast<uint32_t>(_base.op) | 0x2U) == 0x3U;
}
// ============================================================================
// [asmjit::Label]
// ============================================================================
//! Get whether the operand is variable or memory.
ASMJIT_INLINE bool isVarOrMem() const {
ASMJIT_ASSERT(kOperandTypeVar == 2);
ASMJIT_ASSERT(kOperandTypeMem == 3);
return (static_cast<uint32_t>(_base.op) - 2U) <= 1;
}
//! Label (jump target or data location).
//!
//! Label represents a location in code typically used as a jump target, but
//! may be also a reference to some data or a static variable. Label has to be
//! explicitly created by CodeEmitter.
//!
//! Example of using labels:
//!
//! ~~~
//! // Create a CodeEmitter (for example X86Assembler).
//! X86Assembler a;
//!
//! // Create Label instance.
//! Label L1 = a.newLabel();
//!
//! // ... your code ...
//!
//! // Using label.
//! a.jump(L1);
//!
//! // ... your code ...
//!
//! // Bind label to the current position, see `CodeEmitter::bind()`.
//! a.bind(L1);
//! ~~~
class Label : public Operand {
public:
//! Type of the Label.
enum Type {
kTypeAnonymous = 0, //!< Anonymous (unnamed) label.
kTypeLocal = 1, //!< Local label (always has parentId).
kTypeGlobal = 2, //!< Global label (never has parentId).
kTypeCount = 3 //!< Number of label types.
};
// TODO: Find a better place, find a better name.
enum {
//! Label tag is used as a sub-type, forming a unique signature across all
//! operand types as 0x1 is never associated with any register (reg-type).
//! This means that a memory operand's BASE register can be constructed
//! from virtually any operand (register vs. label) by just assigning its
//! type (reg type or label-tag) and operand id.
kLabelTag = 0x1
};
// --------------------------------------------------------------------------
// [Size]
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Get size of the operand in bytes.
ASMJIT_INLINE uint32_t getSize() const {
return _base.size;
//! Create new, unassociated label.
ASMJIT_INLINE Label() noexcept : Operand(NoInit) { reset(); }
//! Create a reference to another label.
ASMJIT_INLINE Label(const Label& other) noexcept : Operand(other) {}
explicit ASMJIT_INLINE Label(uint32_t id) noexcept : Operand(NoInit) {
_init_packed_d0_d1(kOpLabel, id);
_init_packed_d2_d3(0, 0);
}
explicit ASMJIT_INLINE Label(const _NoInit&) noexcept : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [Id]
// [Reset]
// --------------------------------------------------------------------------
//! Get operand id.
//!
//! Operand id's are used internally by `Assembler` and `Compiler`.
//!
//! There is no way to change or remove operand id. Unneeded operands can be
//! simply reassigned by `operator=`.
ASMJIT_INLINE uint32_t getId() const {
return _base.id;
// TODO: I think that if operand is reset it shouldn't say it's a Label, it
// should be none like all other operands.
ASMJIT_INLINE void reset() noexcept {
_init_packed_d0_d1(kOpLabel, 0);
_init_packed_d2_d3(0, 0);
}
// --------------------------------------------------------------------------
// [Members]
// [Label Specific]
// --------------------------------------------------------------------------
union {
//! Base data.
BaseOp _base;
//! Register or variable data.
VRegOp _vreg;
//! Memory data.
VMemOp _vmem;
//! Immediate data.
ImmOp _imm;
//! Label data.
LabelOp _label;
//! Packed operand as two 64-bit integers.
UInt64 _packed[2];
};
//! Get if the label was created by CodeEmitter and has an assigned id.
ASMJIT_INLINE bool isValid() const noexcept { return _label.id != 0; }
//! Set label id.
ASMJIT_INLINE void setId(uint32_t id) { _label.id = id; }
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE Label& operator=(const Label& other) noexcept { copyFrom(other); return *this; }
};
// ============================================================================
// [asmjit::OperandUtil]
// [asmjit::Reg]
// ============================================================================
//! Operand utilities.
struct OperandUtil {
//! Make variable id.
static ASMJIT_INLINE uint32_t makeVarId(uint32_t id) {
return id | kOperandIdVar;
#define ASMJIT_DEFINE_REG_TRAITS(TRAITS_T, REG_T, TYPE, KIND, SIZE, COUNT, TYPE_ID) \
template<> \
struct TRAITS_T < TYPE > { \
typedef REG_T Reg; \
\
enum { \
kValid = 1, \
kCount = COUNT, \
kTypeId = TYPE_ID, \
\
kType = TYPE, \
kKind = KIND, \
kSize = SIZE, \
kSignature = (Operand::kOpReg << Operand::kSignatureOpShift ) | \
(kType << Operand::kSignatureRegTypeShift) | \
(kKind << Operand::kSignatureRegKindShift) | \
(kSize << Operand::kSignatureSizeShift ) \
}; \
} \
#define ASMJIT_DEFINE_ABSTRACT_REG(REG_T, BASE_T) \
public: \
/*! Default constructor doesn't setup anything, it's like `Operand()`. */ \
ASMJIT_INLINE REG_T() ASMJIT_NOEXCEPT \
: BASE_T() {} \
\
/*! Copy the `other` REG_T register operand. */ \
ASMJIT_INLINE REG_T(const REG_T& other) ASMJIT_NOEXCEPT \
: BASE_T(other) {} \
\
/*! Copy the `other` REG_T register operand having its id set to `rId` */ \
ASMJIT_INLINE REG_T(const Reg& other, uint32_t rId) ASMJIT_NOEXCEPT \
: BASE_T(other, rId) {} \
\
/*! Create a REG_T register operand based on `signature` and `rId`. */ \
ASMJIT_INLINE REG_T(const _Init& init, uint32_t signature, uint32_t rId) ASMJIT_NOEXCEPT \
: BASE_T(init, signature, rId) {} \
\
/*! Create a completely uninitialized REG_T register operand (garbage). */ \
explicit ASMJIT_INLINE REG_T(const _NoInit&) ASMJIT_NOEXCEPT \
: BASE_T(NoInit) {} \
\
/*! Clone the register operand. */ \
ASMJIT_INLINE REG_T clone() const ASMJIT_NOEXCEPT { return REG_T(*this); } \
\
/*! Create a new register from register type and id. */ \
static ASMJIT_INLINE REG_T fromTypeAndId(uint32_t rType, uint32_t rId) ASMJIT_NOEXCEPT { \
return REG_T(Init, signatureOf(rType), rId); \
} \
\
/*! Create a new register from signature and id. */ \
static ASMJIT_INLINE REG_T fromSignature(uint32_t signature, uint32_t rId) ASMJIT_NOEXCEPT { \
return REG_T(Init, signature, rId); \
} \
\
ASMJIT_INLINE REG_T& operator=(const REG_T& other) ASMJIT_NOEXCEPT { \
copyFrom(other); return *this; \
}
#define ASMJIT_DEFINE_FINAL_REG(REG_T, BASE_T, TRAITS_T) \
ASMJIT_DEFINE_ABSTRACT_REG(REG_T, BASE_T) \
\
/*! Create a REG_T register with `id`. */ \
explicit ASMJIT_INLINE REG_T(uint32_t rId) ASMJIT_NOEXCEPT \
: BASE_T(Init, kSignature, rId) {} \
\
enum { \
kThisType = TRAITS_T::kType, \
kThisKind = TRAITS_T::kKind, \
kThisSize = TRAITS_T::kSize, \
kSignature = TRAITS_T::kSignature \
};
//! Structure that contains core register information.
//!
//! This information is compatible with operand's signature (32-bit integer)
//! and `RegInfo` just provides easy way to access it.
struct RegInfo {
ASMJIT_INLINE uint32_t getSignature() const noexcept {
return _signature;
}
//! Make label id.
static ASMJIT_INLINE uint32_t makeLabelId(uint32_t id) {
return id;
ASMJIT_INLINE uint32_t getOp() const noexcept {
return (_signature >> Operand::kSignatureOpShift) & Operand::kSignatureOpBits;
}
//! Strip variable id bit so it becomes a pure index to `VarData[]` array.
static ASMJIT_INLINE uint32_t stripVarId(uint32_t id) {
return id & 0x7FFFFFFFU;
ASMJIT_INLINE uint32_t getType() const noexcept {
return (_signature >> Operand::kSignatureRegTypeShift) & Operand::kSignatureRegTypeBits;
}
//! Get whether the id refers to `Var`.
//!
//! \note The function will never return `true` if the id is `kInvalidValue`.
//! The trick is to compare a given id to -1 (kInvalidValue) so we check both
//! using only one comparison.
static ASMJIT_INLINE bool isVarId(uint32_t id) {
return static_cast<int32_t>(id) < -1;
ASMJIT_INLINE uint32_t getKind() const noexcept {
return (_signature >> Operand::kSignatureRegKindShift) & Operand::kSignatureRegKindBits;
}
//! Get whether the id refers to `Label`.
//!
//! \note The function will never return `true` if the id is `kInvalidValue`.
static ASMJIT_INLINE bool isLabelId(uint32_t id) {
return static_cast<int32_t>(id) >= 0;
ASMJIT_INLINE uint32_t getSize() const noexcept {
return (_signature >> Operand::kSignatureSizeShift) & Operand::kSignatureSizeBits;
}
uint32_t _signature;
};
// ============================================================================
// [asmjit::Reg]
// ============================================================================
//! Physical/Virtual register operand.
class Reg : public Operand {
public:
//! Architecture neutral register types.
//!
//! These must be reused by any platform that contains that types. All GP
//! and VEC registers are also allowed by design to be part of a BASE|INDEX
//! of a memory operand.
ASMJIT_ENUM(RegType) {
kRegNone = 0, //!< No register - unused, invalid, multiple meanings.
// (1 is used as a LabelTag)
kRegGp8Lo = 2, //!< 8-bit low general purpose register (X86).
kRegGp8Hi = 3, //!< 8-bit high general purpose register (X86).
kRegGp16 = 4, //!< 16-bit general purpose register (X86).
kRegGp32 = 5, //!< 32-bit general purpose register (X86|ARM).
kRegGp64 = 6, //!< 64-bit general purpose register (X86|ARM).
kRegVec32 = 7, //!< 32-bit view of a vector register (ARM).
kRegVec64 = 8, //!< 64-bit view of a vector register (ARM).
kRegVec128 = 9, //!< 128-bit view of a vector register (X86|ARM).
kRegVec256 = 10, //!< 256-bit view of a vector register (X86).
kRegVec512 = 11, //!< 512-bit view of a vector register (X86).
kRegVec1024 = 12, //!< 1024-bit view of a vector register (future).
kRegVec2048 = 13, //!< 2048-bit view of a vector register (future).
kRegIP = 14, //!< Universal id of IP/PC register (if separate).
kRegCustom = 15, //!< Start of platform dependent register types (must be honored).
kRegMax = 31 //!< Maximum possible register id of all architectures.
};
//! Architecture neutral register kinds.
ASMJIT_ENUM(Kind) {
kKindGp = 0, //!< General purpose register (X86|ARM).
kKindVec = 1, //!< Vector register (X86|ARM).
kKindMax = 15 //!< Maximum possible register kind of all architectures.
};
//! Base class for all register operands.
struct Reg : public Operand {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a dummy base register.
ASMJIT_INLINE Reg() : Operand(NoInit) {
_init_packed_op_sz_w0_id(kOperandTypeReg, 0, (kInvalidReg << 8) + kInvalidReg, kInvalidValue);
_init_packed_d2_d3(kInvalidVar, 0);
}
//! Create a new base register.
ASMJIT_INLINE Reg(uint32_t type, uint32_t index, uint32_t size) : Operand(NoInit) {
_init_packed_op_sz_w0_id(kOperandTypeReg, size, (type << 8) + index, kInvalidValue);
_init_packed_d2_d3(kInvalidVar, 0);
//! Create a dummy register operand.
ASMJIT_INLINE Reg() noexcept : Operand() {}
//! Create a new register operand which is the same as `other` .
ASMJIT_INLINE Reg(const Reg& other) noexcept : Operand(other) {}
//! Create a new register operand compatible with `other`, but with a different `rId`.
ASMJIT_INLINE Reg(const Reg& other, uint32_t rId) noexcept : Operand(NoInit) {
_init_packed_d0_d1(other._signature, rId);
_packed[1] = other._packed[1];
}
//! Create a new reference to `other`.
ASMJIT_INLINE Reg(const Reg& other) : Operand(other) {}
//! Create a new reference to `other` and change the index to `index`.
ASMJIT_INLINE Reg(const Reg& other, uint32_t index) : Operand(other) {
_vreg.index = static_cast<uint8_t>(index);
//! Create a register initialized to `signature` and `rId`.
ASMJIT_INLINE Reg(const _Init&, uint32_t signature, uint32_t rId) noexcept : Operand(NoInit) {
_initReg(signature, rId);
}
explicit ASMJIT_INLINE Reg(const _NoInit&) noexcept : Operand(NoInit) {}
explicit ASMJIT_INLINE Reg(const _NoInit&) : Operand(NoInit) {}
//! Create a new register based on `signature` and `rId`.
static ASMJIT_INLINE Reg fromSignature(uint32_t signature, uint32_t rId) noexcept { return Reg(Init, signature, rId); }
// --------------------------------------------------------------------------
// [Reg Specific]
// --------------------------------------------------------------------------
//! Clone `Reg` operand.
ASMJIT_INLINE Reg clone() const {
return Reg(*this);
}
//! Get if the register is valid (either virtual or physical).
ASMJIT_INLINE bool isValid() const noexcept { return _signature != 0; }
//! Get if this is a physical register.
ASMJIT_INLINE bool isPhysReg() const noexcept { return _reg.id < Globals::kInvalidRegId; }
//! Get if this is a virtual register (used by \ref CodeCompiler).
ASMJIT_INLINE bool isVirtReg() const noexcept { return isPackedId(_reg.id); }
//! Get whether register code is equal to `type`.
ASMJIT_INLINE bool isRegType(uint32_t type) const {
return _vreg.type == type;
}
//! Get if this register is the same as `other`.
//!
//! This is just an optimization. Registers by default only use the first
//! 8 bytes of the Operand, so this method takes advantage of this knowledge
//! and only compares these 8 bytes. If both operands were created correctly
//! then `isEqual()` and `isSame()` should give the same answer, however, if
//! some operands contains a garbage or other metadata in the upper 8 bytes
//! then `isSame()` may return `true` in cases where `isEqual()` returns
//! false. However. no such case is known at the moment.
ASMJIT_INLINE bool isSame(const Reg& other) const noexcept { return _packed[0] == other._packed[0]; }
//! Get if the register type matches `rType` - same as `isReg(rType)`, provided for convenience.
ASMJIT_INLINE bool isType(uint32_t rType) const noexcept { return (_signature & kSignatureRegTypeMask) == (rType << kSignatureRegTypeShift); }
//! Get if the register kind matches `rKind`.
ASMJIT_INLINE bool isKind(uint32_t rKind) const noexcept { return (_signature & kSignatureRegKindMask) == (rKind << kSignatureRegKindShift); }
//! Get if the register is a general purpose register (any size).
ASMJIT_INLINE bool isGp() const noexcept { return isKind(kKindGp); }
//! Get if the register is a vector register.
ASMJIT_INLINE bool isVec() const noexcept { return isKind(kKindVec); }
using Operand_::isReg;
//! Same as `isType()`, provided for convenience.
ASMJIT_INLINE bool isReg(uint32_t rType) const noexcept { return isType(rType); }
//! Get if the register type matches `type` and register id matches `rId`.
ASMJIT_INLINE bool isReg(uint32_t rType, uint32_t rId) const noexcept { return isType(rType) && getId() == rId; }
//! Get the register type.
ASMJIT_INLINE uint32_t getType() const noexcept { return _getSignatureData(kSignatureRegTypeBits, kSignatureRegTypeShift); }
//! Get the register kind.
ASMJIT_INLINE uint32_t getKind() const noexcept { return _getSignatureData(kSignatureRegKindBits, kSignatureRegKindShift); }
//! Clone the register operand.
ASMJIT_INLINE Reg clone() const noexcept { return Reg(*this); }
//! Cast this register to `RegT` by also changing its signature.
//!
//! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors.
template<typename RegT>
ASMJIT_INLINE RegT cloneAs() const noexcept { return RegT(Init, RegT::kSignature, getId()); }
//! Get whether register code is equal to `type`.
ASMJIT_INLINE bool isRegCode(uint32_t code) const {
return _vreg.code == code;
}
//! Cast this register to `other` by also changing its signature.
//!
//! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors.
template<typename RegT>
ASMJIT_INLINE RegT cloneAs(const RegT& other) const noexcept { return RegT(Init, other.getSignature(), getId()); }
//! Set the register id to `id`.
ASMJIT_INLINE void setId(uint32_t rId) noexcept { _reg.id = rId; }
//! Get whether register code is equal to `type`.
ASMJIT_INLINE bool isRegCode(uint32_t type, uint32_t index) const {
return _vreg.code == (type << 8) + index;
//! Set a 32-bit operand signature based on traits of `RegT`.
template<typename RegT>
ASMJIT_INLINE void setSignatureT() noexcept { _signature = RegT::kSignature; }
//! Set register's `signature` and `rId`.
ASMJIT_INLINE void setSignatureAndId(uint32_t signature, uint32_t rId) noexcept {
_signature = signature;
_reg.id = rId;
}
//! Get register code that equals to '(type << 8) + index'.
ASMJIT_INLINE uint32_t getRegCode() const {
return _vreg.code;
// --------------------------------------------------------------------------
// [Reg Statics]
// --------------------------------------------------------------------------
static ASMJIT_INLINE bool isGp(const Operand_& op) noexcept {
// Check operand type and register kind. Not interested in register type and size.
const uint32_t kSgn = (kOpReg << kSignatureOpShift ) |
(kKindGp << kSignatureRegKindShift) ;
return (op.getSignature() & (kSignatureOpMask | kSignatureRegKindMask)) == kSgn;
}
//! Get register type.
ASMJIT_INLINE uint32_t getRegType() const {
return _vreg.type;
//! Get if the `op` operand is either a low or high 8-bit GPB register.
static ASMJIT_INLINE bool isVec(const Operand_& op) noexcept {
// Check operand type and register kind. Not interested in register type and size.
const uint32_t kSgn = (kOpReg << kSignatureOpShift ) |
(kKindVec << kSignatureRegKindShift) ;
return (op.getSignature() & (kSignatureOpMask | kSignatureRegKindMask)) == kSgn;
}
//! Get register index.
ASMJIT_INLINE uint32_t getRegIndex() const {
return _vreg.index;
static ASMJIT_INLINE bool isGp(const Operand_& op, uint32_t rId) noexcept { return isGp(op) & (op.getId() == rId); }
static ASMJIT_INLINE bool isVec(const Operand_& op, uint32_t rId) noexcept { return isVec(op) & (op.getId() == rId); }
};
// ============================================================================
// [asmjit::RegOnly]
// ============================================================================
//! RegOnly is 8-byte version of `Reg` that only allows to store either `Reg`
//! or nothing. This class was designed to decrease the space consumed by each
//! extra "operand" in `CodeEmitter` and `CBInst` classes.
struct RegOnly {
// --------------------------------------------------------------------------
// [Init / Reset]
// --------------------------------------------------------------------------
//! Initialize the `RegOnly` instance to hold register `signature` and `id`.
ASMJIT_INLINE void init(uint32_t signature, uint32_t id) noexcept {
_signature = signature;
_id = id;
}
#define ASMJIT_REG_OP(_Type_) \
ASMJIT_INLINE _Type_ clone() const { \
return _Type_(*this); \
} \
\
/*! Set register `size`. */ \
ASMJIT_INLINE _Type_& setSize(uint32_t size) { \
_vreg.size = static_cast<uint8_t>(size); \
return *this; \
} \
\
/*! Set register `code`. */ \
ASMJIT_INLINE _Type_& setCode(uint32_t code) { \
_vreg.code = static_cast<uint16_t>(code); \
return *this; \
} \
\
/*! Set register `type` and `index`. */ \
ASMJIT_INLINE _Type_& setCode(uint32_t type, uint32_t index) { \
_vreg.type = static_cast<uint8_t>(type); \
_vreg.index = static_cast<uint8_t>(index); \
return *this; \
} \
\
/*! Set register `type`. */ \
ASMJIT_INLINE _Type_& setType(uint32_t type) { \
_vreg.type = static_cast<uint8_t>(type); \
return *this; \
} \
\
/*! Set register `index`. */ \
ASMJIT_INLINE _Type_& setIndex(uint32_t index) { \
_vreg.index = static_cast<uint8_t>(index); \
return *this; \
} \
\
ASMJIT_INLINE _Type_& operator=(const _Type_& other) { _copy(other); return *this; } \
\
ASMJIT_INLINE bool operator==(const _Type_& other) const { return _packed[0].u32[0] == other._packed[0].u32[0]; } \
ASMJIT_INLINE bool operator!=(const _Type_& other) const { return !operator==(other); }
ASMJIT_INLINE void init(const Reg& reg) noexcept { init(reg.getSignature(), reg.getId()); }
ASMJIT_INLINE void init(const RegOnly& reg) noexcept { init(reg.getSignature(), reg.getId()); }
//! Reset the `RegOnly` to none.
ASMJIT_INLINE void reset() noexcept { init(0, 0); }
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get if the `ExtraReg` is none (same as calling `Operand_::isNone()`).
ASMJIT_INLINE bool isNone() const noexcept { return _signature == 0; }
//! Get if the register is valid (either virtual or physical).
ASMJIT_INLINE bool isValid() const noexcept { return _signature != 0; }
//! Get if this is a physical register.
ASMJIT_INLINE bool isPhysReg() const noexcept { return _id < Globals::kInvalidRegId; }
//! Get if this is a virtual register (used by \ref CodeCompiler).
ASMJIT_INLINE bool isVirtReg() const noexcept { return Operand::isPackedId(_id); }
//! Get register signature or 0.
ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; }
//! Get register id or 0.
ASMJIT_INLINE uint32_t getId() const noexcept { return _id; }
//! \internal
//!
//! Unpacks information from operand's signature.
ASMJIT_INLINE uint32_t _getSignatureData(uint32_t bits, uint32_t shift) const noexcept { return (_signature >> shift) & bits; }
//! Get the register type.
ASMJIT_INLINE uint32_t getType() const noexcept { return _getSignatureData(Operand::kSignatureRegTypeBits, Operand::kSignatureRegTypeShift); }
//! Get the register kind.
ASMJIT_INLINE uint32_t getKind() const noexcept { return _getSignatureData(Operand::kSignatureRegKindBits, Operand::kSignatureRegKindShift); }
// --------------------------------------------------------------------------
// [ToReg]
// --------------------------------------------------------------------------
//! Convert back to `RegT` operand.
template<typename RegT>
ASMJIT_INLINE RegT toReg() const noexcept { return RegT(Init, _signature, _id); }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! Type of the operand, either `kOpNone` or `kOpReg`.
uint32_t _signature;
//! Physical or virtual register id.
uint32_t _id;
};
// ============================================================================
// [asmjit::BaseMem]
// [asmjit::Mem]
// ============================================================================
//! Base class for all memory operands.
struct BaseMem : public Operand {
//!
//! NOTE: It's tricky to pack all possible cases that define a memory operand
//! into just 16 bytes. The `Mem` 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.
//!
//! 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/X64). In that case its low 32 bits are stored in
//! DISPLACEMENT field and the remaining high 32 bits are stored in BASE.
//!
//! OTHER FIELDS - There is rest 8 bits that can be used for whatever purpose.
//! The X86Mem operand uses these bits to store segment override
//! prefix and index shift (scale).
class Mem : public Operand {
public:
enum AddrType {
kAddrTypeDefault = 0,
kAddrTypeAbs = 1,
kAddrTypeRel = 2,
kAddrTypeWrt = 3
};
// Shortcuts.
enum SignatureMem {
kSignatureMemAbs = kAddrTypeAbs << kSignatureMemAddrTypeShift,
kSignatureMemRel = kAddrTypeRel << kSignatureMemAddrTypeShift,
kSignatureMemWrt = kAddrTypeWrt << kSignatureMemAddrTypeShift
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE BaseMem() : Operand(NoInit) {
reset();
}
//! Construct a default `Mem` operand, that points to [0].
ASMJIT_INLINE Mem() noexcept : Operand(NoInit) { reset(); }
ASMJIT_INLINE Mem(const Mem& other) noexcept : Operand(other) {}
ASMJIT_INLINE BaseMem(const BaseMem& other) : Operand(other) {}
explicit ASMJIT_INLINE BaseMem(const _NoInit&) : Operand(NoInit) {}
ASMJIT_INLINE Mem(const _Init&,
uint32_t baseType, uint32_t baseId,
uint32_t indexType, uint32_t indexId,
int32_t off, uint32_t size, uint32_t flags) noexcept : Operand(NoInit) {
uint32_t signature = (baseType << kSignatureMemBaseTypeShift ) |
(indexType << kSignatureMemIndexTypeShift) |
(size << kSignatureSizeShift ) ;
_init_packed_d0_d1(kOpMem | signature | flags, indexId);
_mem.base = baseId;
_mem.offsetLo32 = static_cast<uint32_t>(off);
}
explicit ASMJIT_INLINE Mem(const _NoInit&) noexcept : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [BaseMem Specific]
// [Mem Specific]
// --------------------------------------------------------------------------
//! Clone `BaseMem` operand.
ASMJIT_INLINE BaseMem clone() const {
return BaseMem(*this);
}
//! Clone `Mem` operand.
ASMJIT_INLINE Mem clone() const noexcept { return Mem(*this); }
//! Reset `BaseMem` operand.
ASMJIT_INLINE void reset() {
_init_packed_op_sz_b0_b1_id(kOperandTypeMem, 0, kMemTypeBaseIndex, 0, kInvalidValue);
_init_packed_d2_d3(kInvalidValue, 0);
//! Reset the memory operand - after reset the memory points to [0].
ASMJIT_INLINE void reset() noexcept {
_init_packed_d0_d1(kOpMem, 0);
_init_packed_d2_d3(0, 0);
}
//! Get the type of the memory operand, see `MemType`.
ASMJIT_INLINE uint32_t getMemType() const {
return _vmem.type;
}
ASMJIT_INLINE bool hasAddrType() const noexcept { return _hasSignatureData(kSignatureMemAddrTypeMask); }
ASMJIT_INLINE uint32_t getAddrType() const noexcept { return _getSignatureData(kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); }
ASMJIT_INLINE void setAddrType(uint32_t addrType) noexcept { return _setSignatureData(addrType, kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); }
ASMJIT_INLINE void resetAddrType() noexcept { return _clearSignatureData(kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); }
//! Get whether the type of the memory operand is either `kMemTypeBaseIndex`
//! or `kMemTypeStackIndex`.
ASMJIT_INLINE bool isBaseIndexType() const {
return _vmem.type <= kMemTypeStackIndex;
}
ASMJIT_INLINE bool isAbs() const noexcept { return getAddrType() == kAddrTypeAbs; }
ASMJIT_INLINE bool isRel() const noexcept { return getAddrType() == kAddrTypeRel; }
ASMJIT_INLINE bool isWrt() const noexcept { return getAddrType() == kAddrTypeWrt; }
ASMJIT_INLINE void setAbs() noexcept { setAddrType(kAddrTypeAbs); }
ASMJIT_INLINE void setRel() noexcept { setAddrType(kAddrTypeRel); }
ASMJIT_INLINE void setWrt() noexcept { setAddrType(kAddrTypeWrt); }
ASMJIT_INLINE bool isArgHome() const noexcept { return _hasSignatureData(kSignatureMemArgHomeFlag); }
ASMJIT_INLINE bool isRegHome() const noexcept { return _hasSignatureData(kSignatureMemRegHomeFlag); }
ASMJIT_INLINE void setArgHome() noexcept { _signature |= kSignatureMemArgHomeFlag; }
ASMJIT_INLINE void setRegHome() noexcept { _signature |= kSignatureMemRegHomeFlag; }
ASMJIT_INLINE void clearArgHome() noexcept { _signature &= ~kSignatureMemArgHomeFlag; }
ASMJIT_INLINE void clearRegHome() noexcept { _signature &= ~kSignatureMemRegHomeFlag; }
//! Get if the memory operand has a BASE register or label specified.
ASMJIT_INLINE bool hasBase() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0; }
//! Get if the memory operand has an INDEX register specified.
ASMJIT_INLINE bool hasIndex() const noexcept { return (_signature & kSignatureMemIndexTypeMask) != 0; }
//! Get whether the memory operand has BASE and INDEX register.
ASMJIT_INLINE bool hasBaseOrIndex() const noexcept { return (_signature & kSignatureMemBaseIndexMask) != 0; }
//! Get whether the memory operand has BASE and INDEX register.
ASMJIT_INLINE bool hasBaseAndIndex() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0 && (_signature & kSignatureMemIndexTypeMask) != 0; }
//! Get if the BASE operand is a register (registers start after `kLabelTag`).
ASMJIT_INLINE bool hasBaseReg() const noexcept { return (_signature & kSignatureMemBaseTypeMask) > (Label::kLabelTag << kSignatureMemBaseTypeShift); }
//! Get if the BASE operand is a label.
ASMJIT_INLINE bool hasBaseLabel() const noexcept { return (_signature & kSignatureMemBaseTypeMask) == (Label::kLabelTag << kSignatureMemBaseTypeShift); }
//! Get if the INDEX operand is a register (registers start after `kLabelTag`).
ASMJIT_INLINE bool hasIndexReg() const noexcept { return (_signature & kSignatureMemIndexTypeMask) > (Label::kLabelTag << kSignatureMemIndexTypeShift); }
//! Get type of a BASE register (0 if this memory operand doesn't use the BASE register).
//!
//! NOTE: If the returned type is one (a value never associated to a register
//! type) the BASE is not register, but it's a label. One equals to `kLabelTag`.
//! You should always check `hasBaseLabel()` before using `getBaseId()` result.
ASMJIT_INLINE uint32_t getBaseType() const noexcept { return _getSignatureData(kSignatureMemBaseTypeBits, kSignatureMemBaseTypeShift); }
//! Get type of an INDEX register (0 if this memory operand doesn't use the INDEX register).
ASMJIT_INLINE uint32_t getIndexType() const noexcept { return _getSignatureData(kSignatureMemIndexTypeBits, kSignatureMemIndexTypeShift); }
//! Get both BASE (4:0 bits) and INDEX (9:5 bits) types combined into a single integer.
//!
//! This is used internally for BASE+INDEX validation.
ASMJIT_INLINE uint32_t getBaseIndexType() const noexcept { return _getSignatureData(kSignatureMemBaseIndexBits, kSignatureMemBaseIndexShift); }
//! Get id of the BASE register or label (if the BASE was specified as label).
ASMJIT_INLINE uint32_t getBaseId() const noexcept { return _mem.base; }
//! Get id of the INDEX register.
ASMJIT_INLINE uint32_t getIndexId() const noexcept { return _mem.index; }
//! Get whether the memory operand has base register.
ASMJIT_INLINE bool hasBase() const {
return _vmem.base != kInvalidValue;
ASMJIT_INLINE void _setBase(uint32_t rType, uint32_t rId) noexcept {
_setSignatureData(rType, kSignatureMemBaseTypeBits, kSignatureMemBaseTypeShift);
_mem.base = rId;
}
//! Get memory operand base id, or `kInvalidValue`.
ASMJIT_INLINE uint32_t getBase() const {
return _vmem.base;
ASMJIT_INLINE void _setIndex(uint32_t rType, uint32_t rId) noexcept {
_setSignatureData(rType, kSignatureMemIndexTypeBits, kSignatureMemIndexTypeShift);
_mem.index = rId;
}
ASMJIT_INLINE void setBase(const Reg& base) noexcept { return _setBase(base.getType(), base.getId()); }
ASMJIT_INLINE void setIndex(const Reg& index) noexcept { return _setIndex(index.getType(), index.getId()); }
//! Reset the memory operand's BASE register / label.
ASMJIT_INLINE void resetBase() noexcept { _setBase(0, 0); }
//! Reset the memory operand's INDEX register.
ASMJIT_INLINE void resetIndex() noexcept { _setIndex(0, 0); }
//! Set memory operand size.
ASMJIT_INLINE BaseMem& setSize(uint32_t size) {
_vmem.size = static_cast<uint8_t>(size);
return *this;
ASMJIT_INLINE void setSize(uint32_t size) noexcept {
_setSignatureData(size, kSignatureSizeBits, kSignatureSizeShift);
}
//! Get memory operand relative displacement.
ASMJIT_INLINE int32_t getDisplacement() const {
return _vmem.displacement;
ASMJIT_INLINE bool hasOffset() const noexcept {
int32_t lo = static_cast<int32_t>(_mem.offsetLo32);
int32_t hi = static_cast<int32_t>(_mem.base) & -static_cast<int32_t>(getBaseType() == 0);
return (lo | hi) != 0;
}
//! Set memory operand relative displacement.
ASMJIT_INLINE BaseMem& setDisplacement(int32_t disp) {
_vmem.displacement = disp;
return *this;
//! Get if the memory operand has 64-bit offset or absolute address.
//!
//! If this is true then `hasBase()` must always report false.
ASMJIT_INLINE bool has64BitOffset() const noexcept { return getBaseType() == 0; }
//! Get a 64-bit offset or absolute address.
ASMJIT_INLINE int64_t getOffset() const noexcept {
return has64BitOffset()
? static_cast<int64_t>(_mem.offset64)
: static_cast<int64_t>(static_cast<int32_t>(_mem.offsetLo32)); // Sign-Extend.
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
//! Get a lower part of a 64-bit offset or absolute address.
ASMJIT_INLINE int32_t getOffsetLo32() const noexcept { return static_cast<int32_t>(_mem.offsetLo32); }
//! Get a higher part of a 64-bit offset or absolute address.
//!
//! NOTE: This function is UNSAFE and returns garbage if `has64BitOffset()`
//! returns false. Never use it blindly without checking it.
ASMJIT_INLINE int32_t getOffsetHi32() const noexcept { return static_cast<int32_t>(_mem.base); }
ASMJIT_INLINE BaseMem& operator=(const BaseMem& other) {
_copy(other);
return *this;
//! Set a 64-bit offset or an absolute address to `offset`.
//!
//! NOTE: This functions attempts to set both high and low parts of a 64-bit
//! offset, however, if the operand has a BASE register it will store only the
//! low 32 bits of the offset / address as there is no way to store both BASE
//! and 64-bit offset, and there is currently no architecture that has such
//! capability targeted by AsmJit.
ASMJIT_INLINE void setOffset(int64_t offset) noexcept {
if (has64BitOffset())
_mem.offset64 = static_cast<uint64_t>(offset);
else
_mem.offsetLo32 = static_cast<int32_t>(offset & 0xFFFFFFFF);
}
ASMJIT_INLINE bool operator==(const BaseMem& other) const {
return (_packed[0] == other._packed[0]) & (_packed[1] == other._packed[1]);
//! Adjust the offset by a 64-bit `off`.
ASMJIT_INLINE void addOffset(int64_t off) noexcept {
if (has64BitOffset())
_mem.offset64 += static_cast<uint64_t>(off);
else
_mem.offsetLo32 += static_cast<uint32_t>(off & 0xFFFFFFFF);
}
//! Reset the memory offset to zero.
ASMJIT_INLINE void resetOffset() noexcept { setOffset(0); }
ASMJIT_INLINE bool operator!=(const BaseMem& other) const {
return !(*this == other);
//! Set a low 32-bit offset to `off`.
ASMJIT_INLINE void setOffsetLo32(int32_t off) noexcept {
_mem.offsetLo32 = static_cast<uint32_t>(off);
}
//! Adjust the offset by `off`.
//!
//! NOTE: This is a fast function that doesn't use the HI 32-bits of a
//! 64-bit offset. Use it only if you know that there is a BASE register
//! and the offset is only 32 bits anyway.
ASMJIT_INLINE void addOffsetLo32(int32_t off) noexcept {
_mem.offsetLo32 += static_cast<uint32_t>(off);
}
//! Reset the memory offset to zero.
ASMJIT_INLINE void resetOffsetLo32() noexcept { setOffsetLo32(0); }
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE Mem& operator=(const Mem& other) noexcept { copyFrom(other); return *this; }
};
// ============================================================================
......@@ -727,71 +1100,81 @@ struct BaseMem : public Operand {
//!
//! To create immediate operand use `imm()` or `imm_u()` non-members or `Imm`
//! constructors.
struct Imm : public Operand {
class Imm : public Operand {
public:
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! Create a new immediate value (initial value is 0).
Imm() : Operand(NoInit) {
_init_packed_op_sz_b0_b1_id(kOperandTypeImm, 0, 0, 0, kInvalidValue);
_imm.value._i64[0] = 0;
Imm() noexcept : Operand(NoInit) {
_init_packed_d0_d1(kOpImm, 0);
_imm.value.i64 = 0;
}
//! Create a new signed immediate value, assigning the value to `val`.
explicit Imm(int64_t val) : Operand(NoInit) {
_init_packed_op_sz_b0_b1_id(kOperandTypeImm, 0, 0, 0, kInvalidValue);
_imm.value._i64[0] = val;
explicit Imm(int64_t val) noexcept : Operand(NoInit) {
_init_packed_d0_d1(kOpImm, 0);
_imm.value.i64 = val;
}
//! Create a new immediate value from `other`.
ASMJIT_INLINE Imm(const Imm& other) : Operand(other) {}
ASMJIT_INLINE Imm(const Imm& other) noexcept : Operand(other) {}
explicit ASMJIT_INLINE Imm(const _NoInit&) : Operand(NoInit) {}
explicit ASMJIT_INLINE Imm(const _NoInit&) noexcept : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [Immediate Specific]
// --------------------------------------------------------------------------
//! Clone `Imm` operand.
ASMJIT_INLINE Imm clone() const {
return Imm(*this);
}
ASMJIT_INLINE Imm clone() const noexcept { return Imm(*this); }
//! Get whether the immediate can be casted to 8-bit signed integer.
ASMJIT_INLINE bool isInt8() const { return IntUtil::isInt8(_imm.value._i64[0]); }
ASMJIT_INLINE bool isInt8() const noexcept { return Utils::isInt8(_imm.value.i64); }
//! Get whether the immediate can be casted to 8-bit unsigned integer.
ASMJIT_INLINE bool isUInt8() const { return IntUtil::isUInt8(_imm.value._i64[0]); }
ASMJIT_INLINE bool isUInt8() const noexcept { return Utils::isUInt8(_imm.value.i64); }
//! Get whether the immediate can be casted to 16-bit signed integer.
ASMJIT_INLINE bool isInt16() const { return IntUtil::isInt16(_imm.value._i64[0]); }
ASMJIT_INLINE bool isInt16() const noexcept { return Utils::isInt16(_imm.value.i64); }
//! Get whether the immediate can be casted to 16-bit unsigned integer.
ASMJIT_INLINE bool isUInt16() const { return IntUtil::isUInt16(_imm.value._i64[0]); }
ASMJIT_INLINE bool isUInt16() const noexcept { return Utils::isUInt16(_imm.value.i64); }
//! Get whether the immediate can be casted to 32-bit signed integer.
ASMJIT_INLINE bool isInt32() const { return IntUtil::isInt32(_imm.value._i64[0]); }
ASMJIT_INLINE bool isInt32() const noexcept { return Utils::isInt32(_imm.value.i64); }
//! Get whether the immediate can be casted to 32-bit unsigned integer.
ASMJIT_INLINE bool isUInt32() const { return IntUtil::isUInt32(_imm.value._i64[0]); }
ASMJIT_INLINE bool isUInt32() const noexcept { return Utils::isUInt32(_imm.value.i64); }
//! Get immediate value as 8-bit signed integer.
ASMJIT_INLINE int8_t getInt8() const { return _imm.value._i8[_ASMJIT_ARCH_INDEX(8, 0)]; }
ASMJIT_INLINE int8_t getInt8() const noexcept { return static_cast<int8_t>(_imm.value.i32Lo & 0xFF); }
//! Get immediate value as 8-bit unsigned integer.
ASMJIT_INLINE uint8_t getUInt8() const { return _imm.value._u8[_ASMJIT_ARCH_INDEX(8, 0)]; }
ASMJIT_INLINE uint8_t getUInt8() const noexcept { return static_cast<uint8_t>(_imm.value.u32Lo & 0xFFU); }
//! Get immediate value as 16-bit signed integer.
ASMJIT_INLINE int16_t getInt16() const { return _imm.value._i16[_ASMJIT_ARCH_INDEX(4, 0)]; }
ASMJIT_INLINE int16_t getInt16() const noexcept { return static_cast<int16_t>(_imm.value.i32Lo & 0xFFFF);}
//! Get immediate value as 16-bit unsigned integer.
ASMJIT_INLINE uint16_t getUInt16() const { return _imm.value._u16[_ASMJIT_ARCH_INDEX(4, 0)]; }
ASMJIT_INLINE uint16_t getUInt16() const noexcept { return static_cast<uint16_t>(_imm.value.u32Lo & 0xFFFFU);}
//! Get immediate value as 32-bit signed integer.
ASMJIT_INLINE int32_t getInt32() const { return _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 0)]; }
ASMJIT_INLINE int32_t getInt32() const noexcept { return _imm.value.i32Lo; }
//! Get low 32-bit signed integer.
ASMJIT_INLINE int32_t getInt32Lo() const noexcept { return _imm.value.i32Lo; }
//! Get high 32-bit signed integer.
ASMJIT_INLINE int32_t getInt32Hi() const noexcept { return _imm.value.i32Hi; }
//! Get immediate value as 32-bit unsigned integer.
ASMJIT_INLINE uint32_t getUInt32() const { return _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)]; }
ASMJIT_INLINE uint32_t getUInt32() const noexcept { return _imm.value.u32Lo; }
//! Get low 32-bit signed integer.
ASMJIT_INLINE uint32_t getUInt32Lo() const noexcept { return _imm.value.u32Lo; }
//! Get high 32-bit signed integer.
ASMJIT_INLINE uint32_t getUInt32Hi() const noexcept { return _imm.value.u32Hi; }
//! Get immediate value as 64-bit signed integer.
ASMJIT_INLINE int64_t getInt64() const { return _imm.value._i64[0]; }
ASMJIT_INLINE int64_t getInt64() const noexcept { return _imm.value.i64; }
//! Get immediate value as 64-bit unsigned integer.
ASMJIT_INLINE uint64_t getUInt64() const { return _imm.value._u64[0]; }
ASMJIT_INLINE uint64_t getUInt64() const noexcept { return _imm.value.u64; }
//! Get immediate value as `intptr_t`.
ASMJIT_INLINE intptr_t getIntPtr() const {
ASMJIT_INLINE intptr_t getIntPtr() const noexcept {
if (sizeof(intptr_t) == sizeof(int64_t))
return static_cast<intptr_t>(getInt64());
else
......@@ -799,292 +1182,389 @@ struct Imm : public Operand {
}
//! Get immediate value as `uintptr_t`.
ASMJIT_INLINE uintptr_t getUIntPtr() const {
ASMJIT_INLINE uintptr_t getUIntPtr() const noexcept {
if (sizeof(uintptr_t) == sizeof(uint64_t))
return static_cast<uintptr_t>(getUInt64());
else
return static_cast<uintptr_t>(getUInt32());
}
//! Get low 32-bit signed integer.
ASMJIT_INLINE int32_t getInt32Lo() const { return _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 0)]; }
//! Get low 32-bit signed integer.
ASMJIT_INLINE uint32_t getUInt32Lo() const { return _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)]; }
//! Get high 32-bit signed integer.
ASMJIT_INLINE int32_t getInt32Hi() const { return _imm.value._i32[_ASMJIT_ARCH_INDEX(2, 1)]; }
//! Get high 32-bit signed integer.
ASMJIT_INLINE uint32_t getUInt32Hi() const { return _imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)]; }
//! Set immediate value to 8-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt8(int8_t val) {
if (kArchHost64Bit) {
_imm.value._i64[0] = static_cast<int64_t>(val);
}
else {
int32_t val32 = static_cast<int32_t>(val);
_imm.value._i32[_ASMJIT_ARCH_INDEX(2, 0)] = val32;
_imm.value._i32[_ASMJIT_ARCH_INDEX(2, 1)] = val32 >> 31;
}
return *this;
}
ASMJIT_INLINE void setInt8(int8_t val) noexcept { _imm.value.i64 = static_cast<int64_t>(val); }
//! Set immediate value to 8-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt8(uint8_t val) {
if (kArchHost64Bit) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
}
else {
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)] = static_cast<uint32_t>(val);
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0;
}
return *this;
}
ASMJIT_INLINE void setUInt8(uint8_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(val); }
//! Set immediate value to 16-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt16(int16_t val) {
if (kArchHost64Bit) {
_imm.value._i64[0] = static_cast<int64_t>(val);
}
else {
int32_t val32 = static_cast<int32_t>(val);
_imm.value._i32[_ASMJIT_ARCH_INDEX(2, 0)] = val32;
_imm.value._i32[_ASMJIT_ARCH_INDEX(2, 1)] = val32 >> 31;
}
return *this;
}
ASMJIT_INLINE void setInt16(int16_t val) noexcept { _imm.value.i64 = static_cast<int64_t>(val); }
//! Set immediate value to 16-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt16(uint16_t val) {
if (kArchHost64Bit) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
}
else {
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)] = static_cast<uint32_t>(val);
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0;
}
return *this;
}
ASMJIT_INLINE void setUInt16(uint16_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(val); }
//! Set immediate value to 32-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt32(int32_t val) {
if (kArchHost64Bit) {
_imm.value._i64[0] = static_cast<int64_t>(val);
}
else {
_imm.value._i32[_ASMJIT_ARCH_INDEX(2, 0)] = val;
_imm.value._i32[_ASMJIT_ARCH_INDEX(2, 1)] = val >> 31;
}
return *this;
}
ASMJIT_INLINE void setInt32(int32_t val) noexcept { _imm.value.i64 = static_cast<int64_t>(val); }
//! Set immediate value to 32-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt32(uint32_t val) {
if (kArchHost64Bit) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
}
else {
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)] = val;
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0;
}
return *this;
}
ASMJIT_INLINE void setUInt32(uint32_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(val); }
//! Set immediate value to 64-bit signed integer `val`.
ASMJIT_INLINE Imm& setInt64(int64_t val) {
_imm.value._i64[0] = val;
return *this;
}
ASMJIT_INLINE void setInt64(int64_t val) noexcept { _imm.value.i64 = val; }
//! Set immediate value to 64-bit unsigned integer `val`.
ASMJIT_INLINE Imm& setUInt64(uint64_t val) {
_imm.value._u64[0] = val;
return *this;
}
ASMJIT_INLINE void setUInt64(uint64_t val) noexcept { _imm.value.u64 = val; }
//! Set immediate value to intptr_t `val`.
ASMJIT_INLINE Imm& setIntPtr(intptr_t val) {
_imm.value._i64[0] = static_cast<int64_t>(val);
return *this;
}
ASMJIT_INLINE void setIntPtr(intptr_t val) noexcept { _imm.value.i64 = static_cast<int64_t>(val); }
//! Set immediate value to uintptr_t `val`.
ASMJIT_INLINE Imm& setUIntPtr(uintptr_t val) {
_imm.value._u64[0] = static_cast<uint64_t>(val);
return *this;
}
ASMJIT_INLINE void setUIntPtr(uintptr_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(val); }
//! Set immediate value as unsigned type to `val`.
ASMJIT_INLINE Imm& setPtr(void* p) { return setIntPtr((intptr_t)p); }
ASMJIT_INLINE void setPtr(void* p) noexcept { setIntPtr((uint64_t)p); }
//! Set immediate value to `val`.
template<typename T>
ASMJIT_INLINE void setValue(T val) noexcept { setIntPtr((int64_t)val); }
// --------------------------------------------------------------------------
// [Float]
// --------------------------------------------------------------------------
ASMJIT_INLINE Imm& setFloat(float f) {
_imm.value._f32[_ASMJIT_ARCH_INDEX(2, 0)] = f;
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0;
return *this;
ASMJIT_INLINE void setFloat(float f) noexcept {
_imm.value.f32Lo = f;
_imm.value.u32Hi = 0;
}
ASMJIT_INLINE Imm& setDouble(double d) {
_imm.value._f64[0] = d;
return *this;
ASMJIT_INLINE void setDouble(double d) noexcept {
_imm.value.f64 = d;
}
// --------------------------------------------------------------------------
// [Truncate]
// --------------------------------------------------------------------------
ASMJIT_INLINE Imm& truncateTo8Bits() {
if (kArchHost64Bit) {
_imm.value._u64[0] &= static_cast<uint64_t>(0x000000FFU);
ASMJIT_INLINE void truncateTo8Bits() noexcept {
if (ASMJIT_ARCH_64BIT) {
_imm.value.u64 &= static_cast<uint64_t>(0x000000FFU);
}
else {
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)] &= 0x000000FFU;
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0;
_imm.value.u32Lo &= 0x000000FFU;
_imm.value.u32Hi = 0;
}
return *this;
}
ASMJIT_INLINE Imm& truncateTo16Bits() {
if (kArchHost64Bit) {
_imm.value._u64[0] &= static_cast<uint64_t>(0x0000FFFFU);
ASMJIT_INLINE void truncateTo16Bits() noexcept {
if (ASMJIT_ARCH_64BIT) {
_imm.value.u64 &= static_cast<uint64_t>(0x0000FFFFU);
}
else {
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 0)] &= 0x0000FFFFU;
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0;
_imm.value.u32Lo &= 0x0000FFFFU;
_imm.value.u32Hi = 0;
}
return *this;
}
ASMJIT_INLINE Imm& truncateTo32Bits() {
_imm.value._u32[_ASMJIT_ARCH_INDEX(2, 1)] = 0;
return *this;
}
ASMJIT_INLINE void truncateTo32Bits() noexcept { _imm.value.u32Hi = 0; }
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
//! Assign `other` to the immediate operand.
ASMJIT_INLINE Imm& operator=(const Imm& other) {
_copy(other);
return *this;
}
ASMJIT_INLINE Imm& operator=(const Imm& other) noexcept { copyFrom(other); return *this; }
};
//! Create a signed immediate operand.
static ASMJIT_INLINE Imm imm(int64_t val) noexcept { return Imm(val); }
//! Create an unsigned immediate operand.
static ASMJIT_INLINE Imm imm_u(uint64_t val) noexcept { return Imm(static_cast<int64_t>(val)); }
//! Create an immediate operand from `p`.
template<typename T>
static ASMJIT_INLINE Imm imm_ptr(T p) noexcept { return Imm(static_cast<int64_t>((intptr_t)p)); }
// ============================================================================
// [asmjit::Label]
// [asmjit::TypeId]
// ============================================================================
//! Label (jump target or data location).
//!
//! Label represents a location in code typically used as jump targets, but may
//! be also reference data or static variables. Label has to be explicitly
//! created by a code-generator by calling `CodeGen::newLabel()` where `CodeGen`
//! is your code generator, which derives from `Assembler` or `Compiler`.
//!
//! Example of using labels:
//!
//! ~~~
//! // Create Assembler/Compiler.
//! host::Assembler a;
//!
//! // Create Label instance.
//! Label L_1(a);
//! Type-id.
//!
//! // ... your code ...
//!
//! // Using label.
//! a.jump(L_1);
//!
//! // ... your code ...
//!
//! // Bind label to the current position, see `CodeGen::bind()`.
//! a.bind(L_1);
//! ~~~
struct Label : public Operand {
//! This is an additional information that can be used to describe a physical
//! or virtual register. it's used mostly by CodeCompiler to describe register
//! representation (the kind of data stored in the register and the width used)
//! and it's also used by APIs that allow to describe and work with function
//! signatures.
struct TypeId {
// --------------------------------------------------------------------------
// [Construction / Destruction]
// [Id]
// --------------------------------------------------------------------------
//! Create new, unassociated label.
ASMJIT_INLINE Label() : Operand(NoInit) {
reset();
}
explicit ASMJIT_INLINE Label(uint32_t id) : Operand(NoInit) {
_init_packed_op_sz_b0_b1_id(kOperandTypeLabel, 0, 0, 0, id);
_init_packed_d2_d3(0, 0);
}
//! Create new initialized label.
explicit ASMJIT_INLINE Label(Assembler& a);
//! Create new initialized label.
explicit ASMJIT_INLINE Label(Compiler& c);
//! Create reference to another label.
ASMJIT_INLINE Label(const Label& other) : Operand(other) {}
explicit ASMJIT_INLINE Label(const _NoInit&) : Operand(NoInit) {}
enum Id {
kVoid = 0,
_kIntStart = 32,
_kIntEnd = 41,
kIntPtr = 32,
kUIntPtr = 33,
kI8 = 34,
kU8 = 35,
kI16 = 36,
kU16 = 37,
kI32 = 38,
kU32 = 39,
kI64 = 40,
kU64 = 41,
_kFloatStart = 42,
_kFloatEnd = 44,
kF32 = 42,
kF64 = 43,
kF80 = 44,
_kMaskStart = 45,
_kMaskEnd = 48,
kMask8 = 45,
kMask16 = 46,
kMask32 = 47,
kMask64 = 48,
_kMmxStart = 49,
_kMmxEnd = 50,
kMmx32 = 49,
kMmx64 = 50,
_kVec32Start = 51,
_kVec32End = 60,
kI8x4 = 51,
kU8x4 = 52,
kI16x2 = 53,
kU16x2 = 54,
kI32x1 = 55,
kU32x1 = 56,
kF32x1 = 59,
_kVec64Start = 61,
_kVec64End = 70,
kI8x8 = 61,
kU8x8 = 62,
kI16x4 = 63,
kU16x4 = 64,
kI32x2 = 65,
kU32x2 = 66,
kI64x1 = 67,
kU64x1 = 68,
kF32x2 = 69,
kF64x1 = 70,
_kVec128Start = 71,
_kVec128End = 80,
kI8x16 = 71,
kU8x16 = 72,
kI16x8 = 73,
kU16x8 = 74,
kI32x4 = 75,
kU32x4 = 76,
kI64x2 = 77,
kU64x2 = 78,
kF32x4 = 79,
kF64x2 = 80,
_kVec256Start = 81,
_kVec256End = 90,
kI8x32 = 81,
kU8x32 = 82,
kI16x16 = 83,
kU16x16 = 84,
kI32x8 = 85,
kU32x8 = 86,
kI64x4 = 87,
kU64x4 = 88,
kF32x8 = 89,
kF64x4 = 90,
_kVec512Start = 91,
_kVec512End = 100,
kI8x64 = 91,
kU8x64 = 92,
kI16x32 = 93,
kU16x32 = 94,
kI32x16 = 95,
kU32x16 = 96,
kI64x8 = 97,
kU64x8 = 98,
kF32x16 = 99,
kF64x8 = 100,
kCount = 101
};
// --------------------------------------------------------------------------
// [Reset]
// [TypeName - Used by Templates]
// --------------------------------------------------------------------------
ASMJIT_INLINE void reset() {
_init_packed_op_sz_b0_b1_id(kOperandTypeLabel, 0, 0, 0, kInvalidValue);
_init_packed_d2_d3(0, 0);
}
struct Int8 {}; //!< int8_t as C++ type-name.
struct UInt8 {}; //!< uint8_t as C++ type-name.
struct Int16 {}; //!< int16_t as C++ type-name.
struct UInt16 {}; //!< uint16_t as C++ type-name.
struct Int32 {}; //!< int32_t as C++ type-name.
struct UInt32 {}; //!< uint32_t as C++ type-name.
struct Int64 {}; //!< int64_t as C++ type-name.
struct UInt64 {}; //!< uint64_t as C++ type-name.
struct IntPtr {}; //!< intptr_t as C++ type-name.
struct UIntPtr {}; //!< uintptr_t as C++ type-name.
struct Float {}; //!< float as C++ type-name.
struct Double {}; //!< double as C++ type-name.
struct MmxReg {}; //!< MMX register as C++ type-name.
struct Vec128 {}; //!< SIMD128/XMM register as C++ type-name.
struct Vec256 {}; //!< SIMD256/YMM register as C++ type-name.
struct Vec512 {}; //!< SIMD512/ZMM register as C++ type-name.
// --------------------------------------------------------------------------
// [Label Specific]
// [Utilities]
// --------------------------------------------------------------------------
//! Get whether the label has been initialized by `Assembler` or `Compiler`.
ASMJIT_INLINE bool isInitialized() const {
return _label.id != kInvalidValue;
struct Info {
uint8_t sizeOf[128];
uint8_t elementOf[128];
};
ASMJIT_API static const Info _info;
static ASMJIT_INLINE bool isVoid(uint32_t typeId) noexcept { return typeId == 0; }
static ASMJIT_INLINE bool isValid(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kVec512End; }
static ASMJIT_INLINE bool isAbstract(uint32_t typeId) noexcept { return typeId >= kIntPtr && typeId <= kUIntPtr; }
static ASMJIT_INLINE bool isInt(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kIntEnd; }
static ASMJIT_INLINE bool isGpb(uint32_t typeId) noexcept { return typeId >= kI8 && typeId <= kU8; }
static ASMJIT_INLINE bool isGpw(uint32_t typeId) noexcept { return typeId >= kI16 && typeId <= kU16; }
static ASMJIT_INLINE bool isGpd(uint32_t typeId) noexcept { return typeId >= kI32 && typeId <= kU32; }
static ASMJIT_INLINE bool isGpq(uint32_t typeId) noexcept { return typeId >= kI64 && typeId <= kU64; }
static ASMJIT_INLINE bool isFloat(uint32_t typeId) noexcept { return typeId >= _kFloatStart && typeId <= _kFloatEnd; }
static ASMJIT_INLINE bool isMask(uint32_t typeId) noexcept { return typeId >= _kMaskStart && typeId <= _kMaskEnd; }
static ASMJIT_INLINE bool isMmx(uint32_t typeId) noexcept { return typeId >= _kMmxStart && typeId <= _kMmxEnd; }
static ASMJIT_INLINE bool isVec(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec512End; }
static ASMJIT_INLINE bool isVec32(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec32End; }
static ASMJIT_INLINE bool isVec64(uint32_t typeId) noexcept { return typeId >= _kVec64Start && typeId <= _kVec64End; }
static ASMJIT_INLINE bool isVec128(uint32_t typeId) noexcept { return typeId >= _kVec128Start && typeId <= _kVec128End; }
static ASMJIT_INLINE bool isVec256(uint32_t typeId) noexcept { return typeId >= _kVec256Start && typeId <= _kVec256End; }
static ASMJIT_INLINE bool isVec512(uint32_t typeId) noexcept { return typeId >= _kVec512Start && typeId <= _kVec512End; }
static ASMJIT_INLINE uint32_t sizeOf(uint32_t typeId) noexcept {
ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.sizeOf));
return _info.sizeOf[typeId];
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
static ASMJIT_INLINE uint32_t elementOf(uint32_t typeId) noexcept {
ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.elementOf));
return _info.elementOf[typeId];
}
ASMJIT_INLINE Label& operator=(const Label& other) { _copy(other); return *this; }
//! Get an offset to convert a `kIntPtr` and `kUIntPtr` TypeId into a
//! type that matches `gpSize` (general-purpose register size). If you
//! find such TypeId it's then only about adding the offset to it.
//!
//! For example:
//! ~~~
//! uint32_t gpSize = '4' or '8';
//! uint32_t deabstractDelta = TypeId::deabstractDeltaOfSize(gpSize);
//!
//! uint32_t typeId = 'some type-id';
//!
//! // Normalize some typeId into a non-abstract typeId.
//! if (TypeId::isAbstract(typeId)) typeId += deabstractDelta;
//!
//! // The same, but by using TypeId::deabstract() function.
//! typeId = TypeId::deabstract(typeId, deabstractDelta);
//! ~~~
static ASMJIT_INLINE uint32_t deabstractDeltaOfSize(uint32_t gpSize) noexcept {
return gpSize >= 8 ? kI64 - kIntPtr : kI32 - kIntPtr;
}
ASMJIT_INLINE bool operator==(const Label& other) const { return _base.id == other._base.id; }
ASMJIT_INLINE bool operator!=(const Label& other) const { return _base.id != other._base.id; }
static ASMJIT_INLINE uint32_t deabstract(uint32_t typeId, uint32_t deabstractDelta) noexcept {
return TypeId::isAbstract(typeId) ? typeId += deabstractDelta : typeId;
}
};
// ============================================================================
// [asmjit::Operand - Globals]
// ============================================================================
//! No operand, can be used to reset an operand by assignment or to refer to an
//! operand that doesn't exist.
ASMJIT_VAR const Operand noOperand;
//! TypeIdOf<> template allows to get a TypeId of a C++ type.
template<typename T> struct TypeIdOf {
// Don't provide anything if not specialized.
};
template<typename T> struct TypeIdOf<T*> {
enum { kTypeId = TypeId::kUIntPtr };
};
//! Create signed immediate value operand.
static ASMJIT_INLINE Imm imm(int64_t val) {
return Imm(val);
}
template<typename T>
struct TypeIdOfInt {
enum {
kSigned = int(~T(0) < T(0)),
kTypeId = (sizeof(T) == 1) ? (int)(kSigned ? TypeId::kI8 : TypeId::kU8 ) :
(sizeof(T) == 2) ? (int)(kSigned ? TypeId::kI16 : TypeId::kU16) :
(sizeof(T) == 4) ? (int)(kSigned ? TypeId::kI32 : TypeId::kU32) :
(sizeof(T) == 8) ? (int)(kSigned ? TypeId::kI64 : TypeId::kU64) : (int)TypeId::kVoid
};
};
//! Create unsigned immediate value operand.
static ASMJIT_INLINE Imm imm_u(uint64_t val) {
return Imm(static_cast<int64_t>(val));
}
#define ASMJIT_DEFINE_TYPE_ID(T, TYPE_ID) \
template<> \
struct TypeIdOf<T> { enum { kTypeId = TYPE_ID}; }
ASMJIT_DEFINE_TYPE_ID(signed char , TypeIdOfInt< signed char >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned char , TypeIdOfInt< unsigned char >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(short , TypeIdOfInt< short >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned short , TypeIdOfInt< unsigned short >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(int , TypeIdOfInt< int >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned int , TypeIdOfInt< unsigned int >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(long , TypeIdOfInt< long >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned long , TypeIdOfInt< unsigned long >::kTypeId);
#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(16, 0, 0)
ASMJIT_DEFINE_TYPE_ID(__int64 , TypeIdOfInt< __int64 >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned __int64 , TypeIdOfInt< unsigned __int64 >::kTypeId);
#else
ASMJIT_DEFINE_TYPE_ID(long long , TypeIdOfInt< long long >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned long long, TypeIdOfInt< unsigned long long >::kTypeId);
#endif
#if ASMJIT_CC_HAS_NATIVE_CHAR
ASMJIT_DEFINE_TYPE_ID(char , TypeIdOfInt< char >::kTypeId);
#endif
#if ASMJIT_CC_HAS_NATIVE_CHAR16_T
ASMJIT_DEFINE_TYPE_ID(char16_t , TypeIdOfInt< char16_t >::kTypeId);
#endif
#if ASMJIT_CC_HAS_NATIVE_CHAR32_T
ASMJIT_DEFINE_TYPE_ID(char32_t , TypeIdOfInt< char32_t >::kTypeId);
#endif
#if ASMJIT_CC_HAS_NATIVE_WCHAR_T
ASMJIT_DEFINE_TYPE_ID(wchar_t , TypeIdOfInt< wchar_t >::kTypeId);
#endif
//! Create void* pointer immediate value operand.
static ASMJIT_INLINE Imm imm_ptr(void* p) {
return Imm(static_cast<int64_t>((intptr_t)p));
}
ASMJIT_DEFINE_TYPE_ID(void , TypeId::kVoid);
ASMJIT_DEFINE_TYPE_ID(bool , TypeId::kI8);
ASMJIT_DEFINE_TYPE_ID(float , TypeId::kF32);
ASMJIT_DEFINE_TYPE_ID(double , TypeId::kF64);
ASMJIT_DEFINE_TYPE_ID(TypeId::Int8 , TypeId::kI8);
ASMJIT_DEFINE_TYPE_ID(TypeId::UInt8 , TypeId::kU8);
ASMJIT_DEFINE_TYPE_ID(TypeId::Int16 , TypeId::kI16);
ASMJIT_DEFINE_TYPE_ID(TypeId::UInt16 , TypeId::kU16);
ASMJIT_DEFINE_TYPE_ID(TypeId::Int32 , TypeId::kI32);
ASMJIT_DEFINE_TYPE_ID(TypeId::UInt32 , TypeId::kU32);
ASMJIT_DEFINE_TYPE_ID(TypeId::Int64 , TypeId::kI64);
ASMJIT_DEFINE_TYPE_ID(TypeId::UInt64 , TypeId::kU64);
ASMJIT_DEFINE_TYPE_ID(TypeId::IntPtr , TypeId::kIntPtr);
ASMJIT_DEFINE_TYPE_ID(TypeId::UIntPtr , TypeId::kUIntPtr);
ASMJIT_DEFINE_TYPE_ID(TypeId::Float , TypeId::kF32);
ASMJIT_DEFINE_TYPE_ID(TypeId::Double , TypeId::kF64);
ASMJIT_DEFINE_TYPE_ID(TypeId::MmxReg , TypeId::kMmx64);
ASMJIT_DEFINE_TYPE_ID(TypeId::Vec128 , TypeId::kI32x4);
ASMJIT_DEFINE_TYPE_ID(TypeId::Vec256 , TypeId::kI32x8);
ASMJIT_DEFINE_TYPE_ID(TypeId::Vec512 , TypeId::kI32x16);
//! \}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
#include "../asmjit_apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_OPERAND_H
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Export]
#define ASMJIT_EXPORTS
// [Dependencies]
#include "../base/osutils.h"
#include "../base/utils.h"
#if ASMJIT_OS_POSIX
# include <sys/types.h>
# include <sys/mman.h>
# include <time.h>
# include <unistd.h>
#endif // ASMJIT_OS_POSIX
#if ASMJIT_OS_MAC
# include <mach/mach_time.h>
#endif // ASMJIT_OS_MAC
#if ASMJIT_OS_WINDOWS
# if defined(_MSC_VER) && _MSC_VER >= 1400
# include <intrin.h>
# else
# define _InterlockedCompareExchange InterlockedCompareExchange
# endif // _MSC_VER
#endif // ASMJIT_OS_WINDOWS
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
// ============================================================================
// [asmjit::OSUtils - Virtual Memory]
// ============================================================================
// Windows specific implementation using `VirtualAllocEx` and `VirtualFree`.
#if ASMJIT_OS_WINDOWS
static ASMJIT_NOINLINE const VMemInfo& OSUtils_GetVMemInfo() noexcept {
static VMemInfo vmi;
if (ASMJIT_UNLIKELY(!vmi.hCurrentProcess)) {
SYSTEM_INFO info;
::GetSystemInfo(&info);
vmi.pageSize = Utils::alignToPowerOf2<uint32_t>(info.dwPageSize);
vmi.pageGranularity = info.dwAllocationGranularity;
vmi.hCurrentProcess = ::GetCurrentProcess();
}
return vmi;
};
VMemInfo OSUtils::getVirtualMemoryInfo() noexcept { return OSUtils_GetVMemInfo(); }
void* OSUtils::allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept {
return allocProcessMemory(static_cast<HANDLE>(0), size, allocated, flags);
}
Error OSUtils::releaseVirtualMemory(void* p, size_t size) noexcept {
return releaseProcessMemory(static_cast<HANDLE>(0), p, size);
}
void* OSUtils::allocProcessMemory(HANDLE hProcess, size_t size, size_t* allocated, uint32_t flags) noexcept {
if (size == 0)
return nullptr;
const VMemInfo& vmi = OSUtils_GetVMemInfo();
if (!hProcess) hProcess = vmi.hCurrentProcess;
// VirtualAllocEx rounds the allocated size to a page size automatically,
// but we need the `alignedSize` so we can store the real allocated size
// into `allocated` output.
size_t alignedSize = Utils::alignTo(size, vmi.pageSize);
// Windows XP SP2 / Vista+ allow data-execution-prevention (DEP).
DWORD protectFlags = 0;
if (flags & kVMExecutable)
protectFlags |= (flags & kVMWritable) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
else
protectFlags |= (flags & kVMWritable) ? PAGE_READWRITE : PAGE_READONLY;
LPVOID mBase = ::VirtualAllocEx(hProcess, nullptr, alignedSize, MEM_COMMIT | MEM_RESERVE, protectFlags);
if (ASMJIT_UNLIKELY(!mBase)) return nullptr;
ASMJIT_ASSERT(Utils::isAligned<size_t>(reinterpret_cast<size_t>(mBase), vmi.pageSize));
if (allocated) *allocated = alignedSize;
return mBase;
}
Error OSUtils::releaseProcessMemory(HANDLE hProcess, void* p, size_t size) noexcept {
const VMemInfo& vmi = OSUtils_GetVMemInfo();
if (!hProcess) hProcess = vmi.hCurrentProcess;
if (ASMJIT_UNLIKELY(!::VirtualFreeEx(hProcess, p, 0, MEM_RELEASE)))
return DebugUtils::errored(kErrorInvalidState);
return kErrorOk;
}
#endif // ASMJIT_OS_WINDOWS
// Posix specific implementation using `mmap()` and `munmap()`.
#if ASMJIT_OS_POSIX
// Mac uses MAP_ANON instead of MAP_ANONYMOUS.
#if !defined(MAP_ANONYMOUS)
# define MAP_ANONYMOUS MAP_ANON
#endif // MAP_ANONYMOUS
static const VMemInfo& OSUtils_GetVMemInfo() noexcept {
static VMemInfo vmi;
if (ASMJIT_UNLIKELY(!vmi.pageSize)) {
size_t pageSize = ::getpagesize();
vmi.pageSize = pageSize;
vmi.pageGranularity = std::max<size_t>(pageSize, 65536);
}
return vmi;
};
VMemInfo OSUtils::getVirtualMemoryInfo() noexcept { return OSUtils_GetVMemInfo(); }
void* OSUtils::allocVirtualMemory(size_t size, size_t* allocated, uint32_t flags) noexcept {
const VMemInfo& vmi = OSUtils_GetVMemInfo();
size_t alignedSize = Utils::alignTo<size_t>(size, vmi.pageSize);
int protection = PROT_READ;
if (flags & kVMWritable ) protection |= PROT_WRITE;
if (flags & kVMExecutable) protection |= PROT_EXEC;
void* mbase = ::mmap(nullptr, alignedSize, protection, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ASMJIT_UNLIKELY(mbase == MAP_FAILED)) return nullptr;
if (allocated) *allocated = alignedSize;
return mbase;
}
Error OSUtils::releaseVirtualMemory(void* p, size_t size) noexcept {
if (ASMJIT_UNLIKELY(::munmap(p, size) != 0))
return DebugUtils::errored(kErrorInvalidState);
return kErrorOk;
}
#endif // ASMJIT_OS_POSIX
// ============================================================================
// [asmjit::OSUtils - GetTickCount]
// ============================================================================
#if ASMJIT_OS_WINDOWS
static ASMJIT_INLINE uint32_t OSUtils_calcHiRes(const LARGE_INTEGER& now, double freq) noexcept {
return static_cast<uint32_t>(
(int64_t)(double(now.QuadPart) / freq) & 0xFFFFFFFF);
}
uint32_t OSUtils::getTickCount() noexcept {
static volatile uint32_t _hiResTicks;
static volatile double _hiResFreq;
do {
uint32_t hiResOk = _hiResTicks;
LARGE_INTEGER qpf, now;
// If for whatever reason this fails, bail to `GetTickCount()`.
if (!::QueryPerformanceCounter(&now)) break;
// Expected - if we ran through this at least once `hiResTicks` will be
// either 1 or 0xFFFFFFFF. If it's '1' then the Hi-Res counter is available
// and `QueryPerformanceCounter()` can be used.
if (hiResOk == 1) return OSUtils_calcHiRes(now, _hiResFreq);
// Hi-Res counter is not available, bail to `GetTickCount()`.
if (hiResOk != 0) break;
// Detect availability of Hi-Res counter, if not available, bail to `GetTickCount()`.
if (!::QueryPerformanceFrequency(&qpf)) {
_InterlockedCompareExchange((LONG*)&_hiResTicks, 0xFFFFFFFF, 0);
break;
}
double freq = double(qpf.QuadPart) / 1000.0;
_hiResFreq = freq;
_InterlockedCompareExchange((LONG*)&_hiResTicks, 1, 0);
return OSUtils_calcHiRes(now, freq);
} while (0);
return ::GetTickCount();
}
#elif ASMJIT_OS_MAC
uint32_t OSUtils::getTickCount() noexcept {
static mach_timebase_info_data_t _machTime;
// See Apple's QA1398.
if (ASMJIT_UNLIKELY(_machTime.denom == 0) || mach_timebase_info(&_machTime) != KERN_SUCCESS)
return 0;
// `mach_absolute_time()` returns nanoseconds, we want milliseconds.
uint64_t t = mach_absolute_time() / 1000000;
t = t * _machTime.numer / _machTime.denom;
return static_cast<uint32_t>(t & 0xFFFFFFFFU);
}
#elif defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0
uint32_t OSUtils::getTickCount() noexcept {
struct timespec ts;
if (ASMJIT_UNLIKELY(clock_gettime(CLOCK_MONOTONIC, &ts) != 0))
return 0;
uint64_t t = (uint64_t(ts.tv_sec ) * 1000) + (uint64_t(ts.tv_nsec) / 1000000);
return static_cast<uint32_t>(t & 0xFFFFFFFFU);
}
#else
#error "[asmjit] OSUtils::getTickCount() is not implemented for your target OS."
uint32_t OSUtils::getTickCount() noexcept { return 0; }
#endif
} // asmjit namespace
// [Api-End]
#include "../asmjit_apiend.h"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment