First draft of dynamic detours using Ayuto's DynamicHooks library
https://github.com/Ayuto/DynamicHooks
This commit is contained in:
+4304
File diff suppressed because it is too large
Load Diff
+6717
File diff suppressed because it is too large
Load Diff
+860
@@ -0,0 +1,860 @@
|
||||
// [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_COMPILER) && (defined(ASMJIT_BUILD_X86) || defined(ASMJIT_BUILD_X64))
|
||||
|
||||
// [Dependencies]
|
||||
#include "../base/containers.h"
|
||||
#include "../base/utils.h"
|
||||
#include "../x86/x86assembler.h"
|
||||
#include "../x86/x86compiler.h"
|
||||
#include "../x86/x86compilercontext_p.h"
|
||||
|
||||
// [Api-Begin]
|
||||
#include "../apibegin.h"
|
||||
|
||||
namespace asmjit {
|
||||
|
||||
// ============================================================================
|
||||
// [Debug]
|
||||
// ============================================================================
|
||||
|
||||
#if !defined(ASMJIT_DEBUG)
|
||||
#define ASMJIT_ASSERT_OPERAND(op) \
|
||||
do {} while(0)
|
||||
#else
|
||||
#define ASMJIT_ASSERT_OPERAND(op) \
|
||||
do { \
|
||||
if (op.isVar() || op.isLabel()) { \
|
||||
ASMJIT_ASSERT(op.getId() != kInvalidValue); \
|
||||
} \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86VarInfo]
|
||||
// ============================================================================
|
||||
|
||||
#define F(flag) VarInfo::kFlag##flag
|
||||
const VarInfo _x86VarInfo[] = {
|
||||
{ kVarTypeInt8 , 1 , kX86RegClassGp , kX86RegTypeGpbLo, 0 , "gpb" },
|
||||
{ kVarTypeUInt8 , 1 , kX86RegClassGp , kX86RegTypeGpbLo, 0 , "gpb" },
|
||||
{ kVarTypeInt16 , 2 , kX86RegClassGp , kX86RegTypeGpw , 0 , "gpw" },
|
||||
{ kVarTypeUInt16 , 2 , kX86RegClassGp , kX86RegTypeGpw , 0 , "gpw" },
|
||||
{ kVarTypeInt32 , 4 , kX86RegClassGp , kX86RegTypeGpd , 0 , "gpd" },
|
||||
{ kVarTypeUInt32 , 4 , kX86RegClassGp , kX86RegTypeGpd , 0 , "gpd" },
|
||||
{ kVarTypeInt64 , 8 , kX86RegClassGp , kX86RegTypeGpq , 0 , "gpq" },
|
||||
{ kVarTypeUInt64 , 8 , kX86RegClassGp , kX86RegTypeGpq , 0 , "gpq" },
|
||||
{ kVarTypeIntPtr , 0 , kX86RegClassGp , 0 , 0 , "" }, // Abstract.
|
||||
{ kVarTypeUIntPtr , 0 , kX86RegClassGp , 0 , 0 , "" }, // Abstract.
|
||||
{ kVarTypeFp32 , 4 , kX86RegClassFp , kX86RegTypeFp , F(SP) , "fp" },
|
||||
{ kVarTypeFp64 , 8 , kX86RegClassFp , kX86RegTypeFp , F(DP) , "fp" },
|
||||
{ kX86VarTypeMm , 8 , kX86RegClassMm , kX86RegTypeMm , 0 | F(SIMD), "mm" },
|
||||
{ kX86VarTypeK , 8 , kX86RegClassK , kX86RegTypeK , 0 , "k" },
|
||||
{ kX86VarTypeXmm , 16, kX86RegClassXyz, kX86RegTypeXmm , 0 | F(SIMD), "xmm" },
|
||||
{ kX86VarTypeXmmSs, 4 , kX86RegClassXyz, kX86RegTypeXmm , F(SP) , "xmm" },
|
||||
{ kX86VarTypeXmmPs, 16, kX86RegClassXyz, kX86RegTypeXmm , F(SP) | F(SIMD), "xmm" },
|
||||
{ kX86VarTypeXmmSd, 8 , kX86RegClassXyz, kX86RegTypeXmm , F(DP) , "xmm" },
|
||||
{ kX86VarTypeXmmPd, 16, kX86RegClassXyz, kX86RegTypeXmm , F(DP) | F(SIMD), "xmm" },
|
||||
{ kX86VarTypeYmm , 32, kX86RegClassXyz, kX86RegTypeYmm , 0 | F(SIMD), "ymm" },
|
||||
{ kX86VarTypeYmmPs, 32, kX86RegClassXyz, kX86RegTypeYmm , F(SP) | F(SIMD), "ymm" },
|
||||
{ kX86VarTypeYmmPd, 32, kX86RegClassXyz, kX86RegTypeYmm , F(DP) | F(SIMD), "ymm" },
|
||||
{ kX86VarTypeZmm , 64, kX86RegClassXyz, kX86RegTypeZmm , 0 | F(SIMD), "zmm" },
|
||||
{ kX86VarTypeZmmPs, 64, kX86RegClassXyz, kX86RegTypeZmm , F(SP) | F(SIMD), "zmm" },
|
||||
{ kX86VarTypeZmmPd, 64, kX86RegClassXyz, kX86RegTypeZmm , F(DP) | F(SIMD), "zmm" }
|
||||
};
|
||||
#undef F
|
||||
|
||||
#if defined(ASMJIT_BUILD_X86)
|
||||
const uint8_t _x86VarMapping[kX86VarTypeCount] = {
|
||||
/* 00: kVarTypeInt8 */ kVarTypeInt8,
|
||||
/* 01: kVarTypeUInt8 */ kVarTypeUInt8,
|
||||
/* 02: kVarTypeInt16 */ kVarTypeInt16,
|
||||
/* 03: kVarTypeUInt16 */ kVarTypeUInt16,
|
||||
/* 04: kVarTypeInt32 */ kVarTypeInt32,
|
||||
/* 05: kVarTypeUInt32 */ kVarTypeUInt32,
|
||||
/* 06: kVarTypeInt64 */ kInvalidVar, // Invalid in 32-bit mode.
|
||||
/* 07: kVarTypeUInt64 */ kInvalidVar, // Invalid in 32-bit mode.
|
||||
/* 08: kVarTypeIntPtr */ kVarTypeInt32, // Remapped to Int32.
|
||||
/* 09: kVarTypeUIntPtr */ kVarTypeUInt32, // Remapped to UInt32.
|
||||
/* 10: kVarTypeFp32 */ kVarTypeFp32,
|
||||
/* 11: kVarTypeFp64 */ kVarTypeFp64,
|
||||
/* 12: kX86VarTypeMm */ kX86VarTypeMm,
|
||||
/* 13: kX86VarTypeK */ kX86VarTypeK,
|
||||
/* 14: kX86VarTypeXmm */ kX86VarTypeXmm,
|
||||
/* 15: kX86VarTypeXmmSs */ kX86VarTypeXmmSs,
|
||||
/* 16: kX86VarTypeXmmPs */ kX86VarTypeXmmPs,
|
||||
/* 17: kX86VarTypeXmmSd */ kX86VarTypeXmmSd,
|
||||
/* 18: kX86VarTypeXmmPd */ kX86VarTypeXmmPd,
|
||||
/* 19: kX86VarTypeYmm */ kX86VarTypeYmm,
|
||||
/* 20: kX86VarTypeYmmPs */ kX86VarTypeYmmPs,
|
||||
/* 21: kX86VarTypeYmmPd */ kX86VarTypeYmmPd,
|
||||
/* 22: kX86VarTypeZmm */ kX86VarTypeZmm,
|
||||
/* 23: kX86VarTypeZmmPs */ kX86VarTypeZmmPs,
|
||||
/* 24: kX86VarTypeZmmPd */ kX86VarTypeZmmPd
|
||||
};
|
||||
#endif // ASMJIT_BUILD_X86
|
||||
|
||||
#if defined(ASMJIT_BUILD_X64)
|
||||
const uint8_t _x64VarMapping[kX86VarTypeCount] = {
|
||||
/* 00: kVarTypeInt8 */ kVarTypeInt8,
|
||||
/* 01: kVarTypeUInt8 */ kVarTypeUInt8,
|
||||
/* 02: kVarTypeInt16 */ kVarTypeInt16,
|
||||
/* 03: kVarTypeUInt16 */ kVarTypeUInt16,
|
||||
/* 04: kVarTypeInt32 */ kVarTypeInt32,
|
||||
/* 05: kVarTypeUInt32 */ kVarTypeUInt32,
|
||||
/* 06: kVarTypeInt64 */ kVarTypeInt64,
|
||||
/* 07: kVarTypeUInt64 */ kVarTypeUInt64,
|
||||
/* 08: kVarTypeIntPtr */ kVarTypeInt64, // Remapped to Int64.
|
||||
/* 09: kVarTypeUIntPtr */ kVarTypeUInt64, // Remapped to UInt64.
|
||||
/* 10: kVarTypeFp32 */ kVarTypeFp32,
|
||||
/* 11: kVarTypeFp64 */ kVarTypeFp64,
|
||||
/* 12: kX86VarTypeMm */ kX86VarTypeMm,
|
||||
/* 13: kX86VarTypeK */ kX86VarTypeK,
|
||||
/* 14: kX86VarTypeXmm */ kX86VarTypeXmm,
|
||||
/* 15: kX86VarTypeXmmSs */ kX86VarTypeXmmSs,
|
||||
/* 16: kX86VarTypeXmmPs */ kX86VarTypeXmmPs,
|
||||
/* 17: kX86VarTypeXmmSd */ kX86VarTypeXmmSd,
|
||||
/* 18: kX86VarTypeXmmPd */ kX86VarTypeXmmPd,
|
||||
/* 19: kX86VarTypeYmm */ kX86VarTypeYmm,
|
||||
/* 20: kX86VarTypeYmmPs */ kX86VarTypeYmmPs,
|
||||
/* 21: kX86VarTypeYmmPd */ kX86VarTypeYmmPd,
|
||||
/* 22: kX86VarTypeZmm */ kX86VarTypeZmm,
|
||||
/* 23: kX86VarTypeZmmPs */ kX86VarTypeZmmPs,
|
||||
/* 24: kX86VarTypeZmmPd */ kX86VarTypeZmmPd
|
||||
};
|
||||
#endif // ASMJIT_BUILD_X64
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86CallNode - Arg / Ret]
|
||||
// ============================================================================
|
||||
|
||||
bool X86CallNode::_setArg(uint32_t i, const Operand& op) noexcept {
|
||||
if ((i & ~kFuncArgHi) >= _x86Decl.getNumArgs())
|
||||
return false;
|
||||
|
||||
_args[i] = op;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool X86CallNode::_setRet(uint32_t i, const Operand& op) noexcept {
|
||||
if (i >= 2)
|
||||
return false;
|
||||
|
||||
_ret[i] = op;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Compiler - Construction / Destruction]
|
||||
// ============================================================================
|
||||
|
||||
X86Compiler::X86Compiler(X86Assembler* assembler) noexcept
|
||||
: Compiler(),
|
||||
zax(NoInit),
|
||||
zcx(NoInit),
|
||||
zdx(NoInit),
|
||||
zbx(NoInit),
|
||||
zsp(NoInit),
|
||||
zbp(NoInit),
|
||||
zsi(NoInit),
|
||||
zdi(NoInit) {
|
||||
|
||||
_regCount.reset();
|
||||
zax = x86::noGpReg;
|
||||
zcx = x86::noGpReg;
|
||||
zdx = x86::noGpReg;
|
||||
zbx = x86::noGpReg;
|
||||
zsp = x86::noGpReg;
|
||||
zbp = x86::noGpReg;
|
||||
zsi = x86::noGpReg;
|
||||
zdi = x86::noGpReg;
|
||||
|
||||
if (assembler != nullptr)
|
||||
attach(assembler);
|
||||
}
|
||||
|
||||
X86Compiler::~X86Compiler() noexcept {
|
||||
reset(true);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Compiler - Attach / Reset]
|
||||
// ============================================================================
|
||||
|
||||
Error X86Compiler::attach(Assembler* assembler) noexcept {
|
||||
ASMJIT_ASSERT(assembler != nullptr);
|
||||
|
||||
if (_assembler != nullptr)
|
||||
return kErrorInvalidState;
|
||||
|
||||
uint32_t arch = assembler->getArch();
|
||||
switch (arch) {
|
||||
#if defined(ASMJIT_BUILD_X86)
|
||||
case kArchX86:
|
||||
_targetVarMapping = _x86VarMapping;
|
||||
break;
|
||||
#endif // ASMJIT_BUILD_X86
|
||||
|
||||
#if defined(ASMJIT_BUILD_X64)
|
||||
case kArchX64:
|
||||
_targetVarMapping = _x64VarMapping;
|
||||
break;
|
||||
#endif // ASMJIT_BUILD_X64
|
||||
|
||||
default:
|
||||
return kErrorInvalidArch;
|
||||
}
|
||||
|
||||
assembler->_attached(this);
|
||||
|
||||
_arch = static_cast<uint8_t>(arch);
|
||||
_regSize = static_cast<uint8_t>(assembler->getRegSize());
|
||||
_regCount = static_cast<X86Assembler*>(assembler)->getRegCount();
|
||||
_finalized = false;
|
||||
|
||||
zax = static_cast<X86Assembler*>(assembler)->zax;
|
||||
zcx = static_cast<X86Assembler*>(assembler)->zcx;
|
||||
zdx = static_cast<X86Assembler*>(assembler)->zdx;
|
||||
zbx = static_cast<X86Assembler*>(assembler)->zbx;
|
||||
zsp = static_cast<X86Assembler*>(assembler)->zsp;
|
||||
zbp = static_cast<X86Assembler*>(assembler)->zbp;
|
||||
zsi = static_cast<X86Assembler*>(assembler)->zsi;
|
||||
zdi = static_cast<X86Assembler*>(assembler)->zdi;
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
void X86Compiler::reset(bool releaseMemory) noexcept {
|
||||
Compiler::reset(releaseMemory);
|
||||
|
||||
_regCount.reset();
|
||||
zax = x86::noGpReg;
|
||||
zcx = x86::noGpReg;
|
||||
zdx = x86::noGpReg;
|
||||
zbx = x86::noGpReg;
|
||||
zsp = x86::noGpReg;
|
||||
zbp = x86::noGpReg;
|
||||
zsi = x86::noGpReg;
|
||||
zdi = x86::noGpReg;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Compiler - Finalize]
|
||||
// ============================================================================
|
||||
|
||||
Error X86Compiler::finalize() noexcept {
|
||||
X86Assembler* assembler = getAssembler();
|
||||
if (assembler == nullptr)
|
||||
return kErrorOk;
|
||||
|
||||
// Flush the global constant pool.
|
||||
if (_globalConstPoolLabel.isInitialized()) {
|
||||
embedConstPool(_globalConstPoolLabel, _globalConstPool);
|
||||
|
||||
_globalConstPoolLabel.reset();
|
||||
_globalConstPool.reset();
|
||||
}
|
||||
|
||||
if (_firstNode == nullptr)
|
||||
return kErrorOk;
|
||||
|
||||
X86Context context(this);
|
||||
Error error = kErrorOk;
|
||||
|
||||
HLNode* node = _firstNode;
|
||||
HLNode* start;
|
||||
|
||||
// Find all functions and use the `X86Context` to translate/emit them.
|
||||
do {
|
||||
start = node;
|
||||
_resetTokenGenerator();
|
||||
|
||||
if (node->getType() == HLNode::kTypeFunc) {
|
||||
node = static_cast<X86FuncNode*>(start)->getEnd();
|
||||
error = context.compile(static_cast<X86FuncNode*>(start));
|
||||
|
||||
if (error != kErrorOk)
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
node = node->getNext();
|
||||
} while (node != nullptr && node->getType() != HLNode::kTypeFunc);
|
||||
|
||||
error = context.serialize(assembler, start, node);
|
||||
context.cleanup();
|
||||
context.reset(false);
|
||||
|
||||
if (error != kErrorOk)
|
||||
break;
|
||||
} while (node != nullptr);
|
||||
|
||||
reset(false);
|
||||
return error;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Compiler - Inst]
|
||||
// ============================================================================
|
||||
|
||||
//! Get compiler instruction item size without operands assigned.
|
||||
static ASMJIT_INLINE size_t X86Compiler_getInstSize(uint32_t code) noexcept {
|
||||
return Utils::inInterval<uint32_t>(code, _kX86InstIdJbegin, _kX86InstIdJend) ? sizeof(HLJump) : sizeof(HLInst);
|
||||
}
|
||||
|
||||
static HLInst* X86Compiler_newInst(X86Compiler* self, void* p, uint32_t code, uint32_t options, Operand* opList, uint32_t opCount) noexcept {
|
||||
if (Utils::inInterval<uint32_t>(code, _kX86InstIdJbegin, _kX86InstIdJend)) {
|
||||
HLJump* node = new(p) HLJump(self, code, options, opList, opCount);
|
||||
HLLabel* jTarget = nullptr;
|
||||
|
||||
if ((options & kInstOptionUnfollow) == 0) {
|
||||
if (opList[0].isLabel())
|
||||
jTarget = self->getHLLabel(static_cast<Label&>(opList[0]));
|
||||
else
|
||||
options |= kInstOptionUnfollow;
|
||||
}
|
||||
|
||||
node->orFlags(code == kX86InstIdJmp ? HLNode::kFlagIsJmp | HLNode::kFlagIsTaken : HLNode::kFlagIsJcc);
|
||||
node->_target = jTarget;
|
||||
node->_jumpNext = nullptr;
|
||||
|
||||
if (jTarget) {
|
||||
node->_jumpNext = static_cast<HLJump*>(jTarget->_from);
|
||||
jTarget->_from = node;
|
||||
jTarget->addNumRefs();
|
||||
}
|
||||
|
||||
// The 'jmp' is always taken, conditional jump can contain hint, we detect it.
|
||||
if (code == kX86InstIdJmp)
|
||||
node->orFlags(HLNode::kFlagIsTaken);
|
||||
else if (options & kInstOptionTaken)
|
||||
node->orFlags(HLNode::kFlagIsTaken);
|
||||
|
||||
node->addOptions(options);
|
||||
return node;
|
||||
}
|
||||
else {
|
||||
HLInst* node = new(p) HLInst(self, code, options, opList, opCount);
|
||||
node->addOptions(options);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::newInst(uint32_t code) noexcept {
|
||||
size_t size = X86Compiler_getInstSize(code);
|
||||
HLInst* inst = static_cast<HLInst*>(_zoneAllocator.alloc(size));
|
||||
|
||||
if (inst == nullptr)
|
||||
goto _NoMemory;
|
||||
|
||||
return X86Compiler_newInst(this, inst, code, getInstOptionsAndReset(), nullptr, 0);
|
||||
|
||||
_NoMemory:
|
||||
setLastError(kErrorNoHeapMemory);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::newInst(uint32_t code, const Operand& o0) noexcept {
|
||||
size_t size = X86Compiler_getInstSize(code);
|
||||
HLInst* inst = static_cast<HLInst*>(_zoneAllocator.alloc(size + 1 * sizeof(Operand)));
|
||||
|
||||
if (inst == nullptr)
|
||||
goto _NoMemory;
|
||||
|
||||
{
|
||||
Operand* opList = reinterpret_cast<Operand*>(reinterpret_cast<uint8_t*>(inst) + size);
|
||||
opList[0] = o0;
|
||||
ASMJIT_ASSERT_OPERAND(o0);
|
||||
return X86Compiler_newInst(this, inst, code, getInstOptionsAndReset(), opList, 1);
|
||||
}
|
||||
|
||||
_NoMemory:
|
||||
setLastError(kErrorNoHeapMemory);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::newInst(uint32_t code, const Operand& o0, const Operand& o1) noexcept {
|
||||
size_t size = X86Compiler_getInstSize(code);
|
||||
HLInst* inst = static_cast<HLInst*>(_zoneAllocator.alloc(size + 2 * sizeof(Operand)));
|
||||
|
||||
if (inst == nullptr)
|
||||
goto _NoMemory;
|
||||
|
||||
{
|
||||
Operand* opList = reinterpret_cast<Operand*>(reinterpret_cast<uint8_t*>(inst) + size);
|
||||
opList[0] = o0;
|
||||
opList[1] = o1;
|
||||
ASMJIT_ASSERT_OPERAND(o0);
|
||||
ASMJIT_ASSERT_OPERAND(o1);
|
||||
return X86Compiler_newInst(this, inst, code, getInstOptionsAndReset(), opList, 2);
|
||||
}
|
||||
|
||||
_NoMemory:
|
||||
setLastError(kErrorNoHeapMemory);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::newInst(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2) noexcept {
|
||||
size_t size = X86Compiler_getInstSize(code);
|
||||
HLInst* inst = static_cast<HLInst*>(_zoneAllocator.alloc(size + 3 * sizeof(Operand)));
|
||||
|
||||
if (inst == nullptr)
|
||||
goto _NoMemory;
|
||||
|
||||
{
|
||||
Operand* opList = reinterpret_cast<Operand*>(reinterpret_cast<uint8_t*>(inst) + size);
|
||||
opList[0] = o0;
|
||||
opList[1] = o1;
|
||||
opList[2] = o2;
|
||||
ASMJIT_ASSERT_OPERAND(o0);
|
||||
ASMJIT_ASSERT_OPERAND(o1);
|
||||
ASMJIT_ASSERT_OPERAND(o2);
|
||||
return X86Compiler_newInst(this, inst, code, getInstOptionsAndReset(), opList, 3);
|
||||
}
|
||||
|
||||
_NoMemory:
|
||||
setLastError(kErrorNoHeapMemory);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::newInst(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, const Operand& o3) noexcept {
|
||||
size_t size = X86Compiler_getInstSize(code);
|
||||
HLInst* inst = static_cast<HLInst*>(_zoneAllocator.alloc(size + 4 * sizeof(Operand)));
|
||||
|
||||
if (inst == nullptr)
|
||||
goto _NoMemory;
|
||||
|
||||
{
|
||||
Operand* opList = reinterpret_cast<Operand*>(reinterpret_cast<uint8_t*>(inst) + size);
|
||||
opList[0] = o0;
|
||||
opList[1] = o1;
|
||||
opList[2] = o2;
|
||||
opList[3] = o3;
|
||||
ASMJIT_ASSERT_OPERAND(o0);
|
||||
ASMJIT_ASSERT_OPERAND(o1);
|
||||
ASMJIT_ASSERT_OPERAND(o2);
|
||||
ASMJIT_ASSERT_OPERAND(o3);
|
||||
return X86Compiler_newInst(this, inst, code, getInstOptionsAndReset(), opList, 4);
|
||||
}
|
||||
|
||||
_NoMemory:
|
||||
setLastError(kErrorNoHeapMemory);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::newInst(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, const Operand& o3, const Operand& o4) noexcept {
|
||||
size_t size = X86Compiler_getInstSize(code);
|
||||
HLInst* inst = static_cast<HLInst*>(_zoneAllocator.alloc(size + 5 * sizeof(Operand)));
|
||||
|
||||
if (inst == nullptr)
|
||||
goto _NoMemory;
|
||||
|
||||
{
|
||||
Operand* opList = reinterpret_cast<Operand*>(reinterpret_cast<uint8_t*>(inst) + size);
|
||||
opList[0] = o0;
|
||||
opList[1] = o1;
|
||||
opList[2] = o2;
|
||||
opList[3] = o3;
|
||||
opList[4] = o4;
|
||||
ASMJIT_ASSERT_OPERAND(o0);
|
||||
ASMJIT_ASSERT_OPERAND(o1);
|
||||
ASMJIT_ASSERT_OPERAND(o2);
|
||||
ASMJIT_ASSERT_OPERAND(o3);
|
||||
ASMJIT_ASSERT_OPERAND(o4);
|
||||
return X86Compiler_newInst(this, inst, code, getInstOptionsAndReset(), opList, 5);
|
||||
}
|
||||
|
||||
_NoMemory:
|
||||
setLastError(kErrorNoHeapMemory);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code) noexcept {
|
||||
HLInst* node = newInst(code);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, const Operand& o0) noexcept {
|
||||
HLInst* node = newInst(code, o0);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, const Operand& o0, const Operand& o1) noexcept {
|
||||
HLInst* node = newInst(code, o0, o1);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2) noexcept {
|
||||
HLInst* node = newInst(code, o0, o1, o2);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, const Operand& o3) noexcept {
|
||||
HLInst* node = newInst(code, o0, o1, o2, o3);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, const Operand& o3, const Operand& o4) noexcept {
|
||||
HLInst* node = newInst(code, o0, o1, o2, o3, o4);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, int o0_) noexcept {
|
||||
Imm o0(o0_);
|
||||
HLInst* node = newInst(code, o0);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, uint64_t o0_) noexcept {
|
||||
Imm o0(o0_);
|
||||
HLInst* node = newInst(code, o0);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, const Operand& o0, int o1_) noexcept {
|
||||
Imm o1(o1_);
|
||||
HLInst* node = newInst(code, o0, o1);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, const Operand& o0, uint64_t o1_) noexcept {
|
||||
Imm o1(o1_);
|
||||
HLInst* node = newInst(code, o0, o1);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, const Operand& o0, const Operand& o1, int o2_) noexcept {
|
||||
Imm o2(o2_);
|
||||
HLInst* node = newInst(code, o0, o1, o2);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, const Operand& o0, const Operand& o1, uint64_t o2_) noexcept {
|
||||
Imm o2(o2_);
|
||||
HLInst* node = newInst(code, o0, o1, o2);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, int o3_) noexcept {
|
||||
Imm o3(o3_);
|
||||
HLInst* node = newInst(code, o0, o1, o2, o3);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
HLInst* X86Compiler::emit(uint32_t code, const Operand& o0, const Operand& o1, const Operand& o2, uint64_t o3_) noexcept {
|
||||
Imm o3(o3_);
|
||||
HLInst* node = newInst(code, o0, o1, o2, o3);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<HLInst*>(addNode(node));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Compiler - Func]
|
||||
// ============================================================================
|
||||
|
||||
X86FuncNode* X86Compiler::newFunc(const FuncPrototype& p) noexcept {
|
||||
X86FuncNode* func = newNode<X86FuncNode>();
|
||||
Error error;
|
||||
|
||||
if (func == nullptr)
|
||||
goto _NoMemory;
|
||||
|
||||
// Create helper nodes.
|
||||
func->_entryNode = newLabelNode();
|
||||
func->_exitNode = newLabelNode();
|
||||
func->_end = newNode<HLSentinel>();
|
||||
|
||||
if (func->_entryNode == nullptr || func->_exitNode == nullptr || func->_end == nullptr)
|
||||
goto _NoMemory;
|
||||
|
||||
// Function prototype.
|
||||
if ((error = func->_x86Decl.setPrototype(p)) != kErrorOk) {
|
||||
setLastError(error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Function arguments stack size. Since function requires _argStackSize to be
|
||||
// set, we have to copy it from X86FuncDecl.
|
||||
func->_argStackSize = func->_x86Decl.getArgStackSize();
|
||||
func->_redZoneSize = static_cast<uint16_t>(func->_x86Decl.getRedZoneSize());
|
||||
func->_spillZoneSize = static_cast<uint16_t>(func->_x86Decl.getSpillZoneSize());
|
||||
|
||||
// Expected/Required stack alignment.
|
||||
func->_expectedStackAlignment = getRuntime()->getStackAlignment();
|
||||
func->_requiredStackAlignment = 0;
|
||||
|
||||
// Allocate space for function arguments.
|
||||
func->_args = nullptr;
|
||||
if (func->getNumArgs() != 0) {
|
||||
func->_args = _zoneAllocator.allocT<VarData*>(func->getNumArgs() * sizeof(VarData*));
|
||||
if (func->_args == nullptr)
|
||||
goto _NoMemory;
|
||||
::memset(func->_args, 0, func->getNumArgs() * sizeof(VarData*));
|
||||
}
|
||||
|
||||
return func;
|
||||
|
||||
_NoMemory:
|
||||
setLastError(kErrorNoHeapMemory);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
X86FuncNode* X86Compiler::addFunc(const FuncPrototype& p) noexcept {
|
||||
X86FuncNode* func = newFunc(p);
|
||||
|
||||
if (func == nullptr) {
|
||||
setLastError(kErrorNoHeapMemory);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return static_cast<X86FuncNode*>(addFunc(func));
|
||||
}
|
||||
|
||||
HLSentinel* X86Compiler::endFunc() noexcept {
|
||||
X86FuncNode* func = getFunc();
|
||||
ASMJIT_ASSERT(func != nullptr);
|
||||
|
||||
// Add local constant pool at the end of the function (if exist).
|
||||
setCursor(func->getExitNode());
|
||||
|
||||
if (_localConstPoolLabel.isInitialized()) {
|
||||
embedConstPool(_localConstPoolLabel, _localConstPool);
|
||||
_localConstPoolLabel.reset();
|
||||
_localConstPool.reset();
|
||||
}
|
||||
|
||||
// Finalize.
|
||||
func->addFuncFlags(kFuncFlagIsFinished);
|
||||
_func = nullptr;
|
||||
|
||||
setCursor(func->getEnd());
|
||||
return func->getEnd();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Compiler - Ret]
|
||||
// ============================================================================
|
||||
|
||||
HLRet* X86Compiler::newRet(const Operand& o0, const Operand& o1) noexcept {
|
||||
HLRet* node = newNode<HLRet>(o0, o1);
|
||||
if (node == nullptr)
|
||||
goto _NoMemory;
|
||||
return node;
|
||||
|
||||
_NoMemory:
|
||||
setLastError(kErrorNoHeapMemory);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HLRet* X86Compiler::addRet(const Operand& o0, const Operand& o1) noexcept {
|
||||
HLRet* node = newRet(o0, o1);
|
||||
if (node == nullptr)
|
||||
return node;
|
||||
return static_cast<HLRet*>(addNode(node));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Compiler - Call]
|
||||
// ============================================================================
|
||||
|
||||
X86CallNode* X86Compiler::newCall(const Operand& o0, const FuncPrototype& p) noexcept {
|
||||
X86CallNode* node = newNode<X86CallNode>(o0);
|
||||
Error error;
|
||||
uint32_t nArgs;
|
||||
|
||||
if (node == nullptr)
|
||||
goto _NoMemory;
|
||||
|
||||
if ((error = node->_x86Decl.setPrototype(p)) != kErrorOk) {
|
||||
setLastError(error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If there are no arguments skip the allocation.
|
||||
if ((nArgs = p.getNumArgs()) == 0)
|
||||
return node;
|
||||
|
||||
node->_args = static_cast<Operand*>(_zoneAllocator.alloc(nArgs * sizeof(Operand)));
|
||||
if (node->_args == nullptr)
|
||||
goto _NoMemory;
|
||||
|
||||
::memset(node->_args, 0, nArgs * sizeof(Operand));
|
||||
return node;
|
||||
|
||||
_NoMemory:
|
||||
setLastError(kErrorNoHeapMemory);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
X86CallNode* X86Compiler::addCall(const Operand& o0, const FuncPrototype& p) noexcept {
|
||||
X86CallNode* node = newCall(o0, p);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
return static_cast<X86CallNode*>(addNode(node));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Compiler - Vars]
|
||||
// ============================================================================
|
||||
|
||||
Error X86Compiler::setArg(uint32_t argIndex, const Var& var) noexcept {
|
||||
X86FuncNode* func = getFunc();
|
||||
|
||||
if (func == nullptr)
|
||||
return kErrorInvalidArgument;
|
||||
|
||||
if (!isVarValid(var))
|
||||
return kErrorInvalidState;
|
||||
|
||||
VarData* vd = getVd(var);
|
||||
func->setArg(argIndex, vd);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error X86Compiler::_newVar(Var* var, uint32_t vType, const char* name) noexcept {
|
||||
ASMJIT_ASSERT(vType < kX86VarTypeCount);
|
||||
vType = _targetVarMapping[vType];
|
||||
ASMJIT_ASSERT(vType != kInvalidVar);
|
||||
|
||||
// The assertion won't be compiled in release build, however, we want to check
|
||||
// this anyway.
|
||||
if (vType == kInvalidVar) {
|
||||
static_cast<X86Var*>(var)->reset();
|
||||
return kErrorInvalidArgument;
|
||||
}
|
||||
|
||||
const VarInfo& vInfo = _x86VarInfo[vType];
|
||||
VarData* vd = _newVd(vInfo, name);
|
||||
|
||||
if (vd == nullptr) {
|
||||
static_cast<X86Var*>(var)->reset();
|
||||
return getLastError();
|
||||
}
|
||||
|
||||
var->_init_packed_op_sz_w0_id(Operand::kTypeVar, vInfo.getSize(), vInfo.getRegType() << 8, vd->getId());
|
||||
var->_vreg.vType = vType;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error X86Compiler::_newVar(Var* var, uint32_t vType, const char* fmt, va_list ap) noexcept {
|
||||
char name[64];
|
||||
|
||||
vsnprintf(name, ASMJIT_ARRAY_SIZE(name), fmt, ap);
|
||||
name[ASMJIT_ARRAY_SIZE(name) - 1] = '\0';
|
||||
return _newVar(var, vType, name);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Compiler - Stack]
|
||||
// ============================================================================
|
||||
|
||||
Error X86Compiler::_newStack(BaseMem* mem, uint32_t size, uint32_t alignment, const char* name) noexcept {
|
||||
if (size == 0)
|
||||
return kErrorInvalidArgument;
|
||||
|
||||
if (alignment > 64)
|
||||
alignment = 64;
|
||||
|
||||
VarInfo vi = { kInvalidVar, 0, kInvalidReg , kInvalidReg, 0, "" };
|
||||
VarData* vd = _newVd(vi, name);
|
||||
|
||||
if (vd == nullptr) {
|
||||
static_cast<X86Mem*>(mem)->reset();
|
||||
return getLastError();
|
||||
}
|
||||
|
||||
vd->_size = size;
|
||||
vd->_isStack = true;
|
||||
vd->_alignment = static_cast<uint8_t>(alignment);
|
||||
|
||||
static_cast<X86Mem*>(mem)->_init(kMemTypeStackIndex, vd->getId(), 0, 0);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Compiler - Const]
|
||||
// ============================================================================
|
||||
|
||||
Error X86Compiler::_newConst(BaseMem* mem, uint32_t scope, const void* data, size_t size) noexcept {
|
||||
Error error = kErrorOk;
|
||||
size_t offset;
|
||||
|
||||
Label* dstLabel;
|
||||
ConstPool* dstPool;
|
||||
|
||||
if (scope == kConstScopeLocal) {
|
||||
dstLabel = &_localConstPoolLabel;
|
||||
dstPool = &_localConstPool;
|
||||
}
|
||||
else if (scope == kConstScopeGlobal) {
|
||||
dstLabel = &_globalConstPoolLabel;
|
||||
dstPool = &_globalConstPool;
|
||||
}
|
||||
else {
|
||||
error = kErrorInvalidArgument;
|
||||
goto _OnError;
|
||||
}
|
||||
|
||||
error = dstPool->add(data, size, offset);
|
||||
if (error != kErrorOk)
|
||||
goto _OnError;
|
||||
|
||||
if (dstLabel->getId() == kInvalidValue) {
|
||||
*dstLabel = newLabel();
|
||||
if (!dstLabel->isInitialized()) {
|
||||
error = kErrorNoHeapMemory;
|
||||
goto _OnError;
|
||||
}
|
||||
}
|
||||
|
||||
*static_cast<X86Mem*>(mem) = x86::ptr(*dstLabel, static_cast<int32_t>(offset), static_cast<uint32_t>(size));
|
||||
return kErrorOk;
|
||||
|
||||
_OnError:
|
||||
return error;
|
||||
}
|
||||
|
||||
} // asmjit namespace
|
||||
|
||||
// [Api-End]
|
||||
#include "../apiend.h"
|
||||
|
||||
// [Guard]
|
||||
#endif // !ASMJIT_DISABLE_COMPILER && (ASMJIT_BUILD_X86 || ASMJIT_BUILD_X64)
|
||||
+7496
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,726 @@
|
||||
// [AsmJit]
|
||||
// Complete x86/x64 JIT and Remote Assembler for C++.
|
||||
//
|
||||
// [License]
|
||||
// Zlib - See LICENSE.md file in the package.
|
||||
|
||||
// [Guard]
|
||||
#ifndef _ASMJIT_X86_X86COMPILERCONTEXT_P_H
|
||||
#define _ASMJIT_X86_X86COMPILERCONTEXT_P_H
|
||||
|
||||
#include "../build.h"
|
||||
#if !defined(ASMJIT_DISABLE_COMPILER)
|
||||
|
||||
// [Dependencies]
|
||||
#include "../base/compiler.h"
|
||||
#include "../base/compilercontext_p.h"
|
||||
#include "../base/utils.h"
|
||||
#include "../x86/x86assembler.h"
|
||||
#include "../x86/x86compiler.h"
|
||||
|
||||
// [Api-Begin]
|
||||
#include "../apibegin.h"
|
||||
|
||||
namespace asmjit {
|
||||
|
||||
//! \addtogroup asmjit_x86
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86VarMap]
|
||||
// ============================================================================
|
||||
|
||||
struct X86VarMap : public VarMap {
|
||||
// --------------------------------------------------------------------------
|
||||
// [Accessors]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Get variable-attributes list as VarAttr data.
|
||||
ASMJIT_INLINE VarAttr* getVaList() const {
|
||||
return const_cast<VarAttr*>(_list);
|
||||
}
|
||||
|
||||
//! Get variable-attributes list as VarAttr data (by class).
|
||||
ASMJIT_INLINE VarAttr* getVaListByClass(uint32_t rc) const {
|
||||
return const_cast<VarAttr*>(_list) + _start.get(rc);
|
||||
}
|
||||
|
||||
//! Get position of variables (by class).
|
||||
ASMJIT_INLINE uint32_t getVaStart(uint32_t rc) const {
|
||||
return _start.get(rc);
|
||||
}
|
||||
|
||||
//! Get count of variables (by class).
|
||||
ASMJIT_INLINE uint32_t getVaCountByClass(uint32_t rc) const {
|
||||
return _count.get(rc);
|
||||
}
|
||||
|
||||
//! Get VarAttr at `index`.
|
||||
ASMJIT_INLINE VarAttr* getVa(uint32_t index) const {
|
||||
ASMJIT_ASSERT(index < _vaCount);
|
||||
return getVaList() + index;
|
||||
}
|
||||
|
||||
//! Get VarAttr of `c` class at `index`.
|
||||
ASMJIT_INLINE VarAttr* getVaByClass(uint32_t rc, uint32_t index) const {
|
||||
ASMJIT_ASSERT(index < _count._regs[rc]);
|
||||
return getVaListByClass(rc) + index;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Utils]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Find VarAttr.
|
||||
ASMJIT_INLINE VarAttr* findVa(VarData* vd) const {
|
||||
VarAttr* list = getVaList();
|
||||
uint32_t count = getVaCount();
|
||||
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
if (list[i].getVd() == vd)
|
||||
return &list[i];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//! Find VarAttr (by class).
|
||||
ASMJIT_INLINE VarAttr* findVaByClass(uint32_t rc, VarData* vd) const {
|
||||
VarAttr* list = getVaListByClass(rc);
|
||||
uint32_t count = getVaCountByClass(rc);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
if (list[i].getVd() == vd)
|
||||
return &list[i];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Members]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Special registers on input.
|
||||
//!
|
||||
//! Special register(s) restricted to one or more physical register. If there
|
||||
//! is more than one special register it means that we have to duplicate the
|
||||
//! variable content to all of them (it means that the same varible was used
|
||||
//! by two or more operands). We forget about duplicates after the register
|
||||
//! allocation finishes and marks all duplicates as non-assigned.
|
||||
X86RegMask _inRegs;
|
||||
|
||||
//! Special registers on output.
|
||||
//!
|
||||
//! Special register(s) used on output. Each variable can have only one
|
||||
//! special register on the output, 'X86VarMap' contains all registers from
|
||||
//! all 'VarAttr's.
|
||||
X86RegMask _outRegs;
|
||||
|
||||
//! Clobbered registers (by a function call).
|
||||
X86RegMask _clobberedRegs;
|
||||
|
||||
//! Start indexes of variables per register class.
|
||||
X86RegCount _start;
|
||||
//! Count of variables per register class.
|
||||
X86RegCount _count;
|
||||
|
||||
//! VarAttr list.
|
||||
VarAttr _list[1];
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86StateCell]
|
||||
// ============================================================================
|
||||
|
||||
//! X86/X64 state-cell.
|
||||
union X86StateCell {
|
||||
// --------------------------------------------------------------------------
|
||||
// [Accessors]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE uint32_t getState() const {
|
||||
return _state;
|
||||
}
|
||||
|
||||
ASMJIT_INLINE void setState(uint32_t state) {
|
||||
_state = static_cast<uint8_t>(state);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Reset]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void reset() { _packed = 0; }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Members]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
uint8_t _packed;
|
||||
|
||||
struct {
|
||||
uint8_t _state : 2;
|
||||
uint8_t _unused : 6;
|
||||
};
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86VarState]
|
||||
// ============================================================================
|
||||
|
||||
//! X86/X64 state.
|
||||
struct X86VarState : VarState {
|
||||
enum {
|
||||
//! Base index of GP registers.
|
||||
kGpIndex = 0,
|
||||
//! Count of GP registers.
|
||||
kGpCount = 16,
|
||||
|
||||
//! Base index of MMX registers.
|
||||
kMmIndex = kGpIndex + kGpCount,
|
||||
//! Count of Mm registers.
|
||||
kMmCount = 8,
|
||||
|
||||
//! Base index of XMM registers.
|
||||
kXmmIndex = kMmIndex + kMmCount,
|
||||
//! Count of XMM registers.
|
||||
kXmmCount = 16,
|
||||
|
||||
//! Count of all registers in `X86VarState`.
|
||||
kAllCount = kXmmIndex + kXmmCount
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Accessors]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE VarData** getList() {
|
||||
return _list;
|
||||
}
|
||||
|
||||
ASMJIT_INLINE VarData** getListByClass(uint32_t rc) {
|
||||
switch (rc) {
|
||||
case kX86RegClassGp : return _listGp;
|
||||
case kX86RegClassMm : return _listMm;
|
||||
case kX86RegClassXyz: return _listXmm;
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Clear]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE void reset(size_t numCells) {
|
||||
::memset(this, 0, kAllCount * sizeof(VarData*) +
|
||||
2 * sizeof(X86RegMask) +
|
||||
numCells * sizeof(X86StateCell));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Members]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
union {
|
||||
//! List of all allocated variables in one array.
|
||||
VarData* _list[kAllCount];
|
||||
|
||||
struct {
|
||||
//! Allocated GP registers.
|
||||
VarData* _listGp[kGpCount];
|
||||
//! Allocated MMX registers.
|
||||
VarData* _listMm[kMmCount];
|
||||
//! Allocated XMM registers.
|
||||
VarData* _listXmm[kXmmCount];
|
||||
};
|
||||
};
|
||||
|
||||
//! Occupied registers (mask).
|
||||
X86RegMask _occupied;
|
||||
//! Modified registers (mask).
|
||||
X86RegMask _modified;
|
||||
|
||||
//! Variables data, the length is stored in `X86Context`.
|
||||
X86StateCell _cells[1];
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Context]
|
||||
// ============================================================================
|
||||
|
||||
#if defined(ASMJIT_DEBUG)
|
||||
# define ASMJIT_X86_CHECK_STATE _checkState();
|
||||
#else
|
||||
# define ASMJIT_X86_CHECK_STATE
|
||||
#endif // ASMJIT_DEBUG
|
||||
|
||||
//! \internal
|
||||
//!
|
||||
//! Compiler context, used by `X86Compiler`.
|
||||
//!
|
||||
//! Compiler context takes care of generating function prolog and epilog, and
|
||||
//! also performs register allocation. It's used during the compilation phase
|
||||
//! and considered an implementation detail and asmjit consumers don't have
|
||||
//! access to it. The context is used once per function and it's reset after
|
||||
//! the function is processed.
|
||||
struct X86Context : public Context {
|
||||
ASMJIT_NO_COPY(X86Context)
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Construction / Destruction]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Create a new `X86Context` instance.
|
||||
X86Context(X86Compiler* compiler);
|
||||
//! Destroy the `X86Context` instance.
|
||||
virtual ~X86Context();
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Reset]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
virtual void reset(bool releaseMemory = false) override;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Arch]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE bool isX64() const { return _zsp.getSize() == 16; }
|
||||
ASMJIT_INLINE uint32_t getRegSize() const { return _zsp.getSize(); }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Accessors]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Get compiler as `X86Compiler`.
|
||||
ASMJIT_INLINE X86Compiler* getCompiler() const { return static_cast<X86Compiler*>(_compiler); }
|
||||
//! Get function as `X86FuncNode`.
|
||||
ASMJIT_INLINE X86FuncNode* getFunc() const { return reinterpret_cast<X86FuncNode*>(_func); }
|
||||
//! Get clobbered registers (global).
|
||||
ASMJIT_INLINE uint32_t getClobberedRegs(uint32_t rc) { return _clobberedRegs.get(rc); }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Helpers]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE X86VarMap* newVarMap(uint32_t vaCount) {
|
||||
return static_cast<X86VarMap*>(
|
||||
_zoneAllocator.alloc(sizeof(X86VarMap) + vaCount * sizeof(VarAttr)));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Emit]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
void emitLoad(VarData* vd, uint32_t regIndex, const char* reason);
|
||||
void emitSave(VarData* vd, uint32_t regIndex, const char* reason);
|
||||
void emitMove(VarData* vd, uint32_t toRegIndex, uint32_t fromRegIndex, const char* reason);
|
||||
void emitSwapGp(VarData* aVd, VarData* bVd, uint32_t aIndex, uint32_t bIndex, const char* reason);
|
||||
|
||||
void emitPushSequence(uint32_t regs);
|
||||
void emitPopSequence(uint32_t regs);
|
||||
|
||||
void emitConvertVarToVar(uint32_t dstType, uint32_t dstIndex, uint32_t srcType, uint32_t srcIndex);
|
||||
void emitMoveVarOnStack(uint32_t dstType, const X86Mem* dst, uint32_t srcType, uint32_t srcIndex);
|
||||
void emitMoveImmOnStack(uint32_t dstType, const X86Mem* dst, const Imm* src);
|
||||
|
||||
void emitMoveImmToReg(uint32_t dstType, uint32_t dstIndex, const Imm* src);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Register Management]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
void _checkState();
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Attach / Detach]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Attach.
|
||||
//!
|
||||
//! Attach a register to the 'VarData', changing 'VarData' members to show
|
||||
//! that the variable is currently alive and linking variable with the
|
||||
//! current 'X86VarState'.
|
||||
template<int C>
|
||||
ASMJIT_INLINE void attach(VarData* vd, uint32_t regIndex, bool modified) {
|
||||
ASMJIT_ASSERT(vd->getClass() == C);
|
||||
ASMJIT_ASSERT(regIndex != kInvalidReg);
|
||||
|
||||
// Prevent Esp allocation if C==Gp.
|
||||
ASMJIT_ASSERT(C != kX86RegClassGp || regIndex != kX86RegIndexSp);
|
||||
|
||||
uint32_t regMask = Utils::mask(regIndex);
|
||||
|
||||
vd->setState(kVarStateReg);
|
||||
vd->setModified(modified);
|
||||
vd->setRegIndex(regIndex);
|
||||
vd->addHomeIndex(regIndex);
|
||||
|
||||
_x86State.getListByClass(C)[regIndex] = vd;
|
||||
_x86State._occupied.or_(C, regMask);
|
||||
_x86State._modified.or_(C, static_cast<uint32_t>(modified) << regIndex);
|
||||
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
}
|
||||
|
||||
//! Detach.
|
||||
//!
|
||||
//! The opposite of 'Attach'. Detach resets the members in 'VarData'
|
||||
//! (regIndex, state and changed flags) and unlinks the variable with the
|
||||
//! current 'X86VarState'.
|
||||
template<int C>
|
||||
ASMJIT_INLINE void detach(VarData* vd, uint32_t regIndex, uint32_t vState) {
|
||||
ASMJIT_ASSERT(vd->getClass() == C);
|
||||
ASMJIT_ASSERT(vd->getRegIndex() == regIndex);
|
||||
ASMJIT_ASSERT(vState != kVarStateReg);
|
||||
|
||||
uint32_t regMask = Utils::mask(regIndex);
|
||||
|
||||
vd->setState(vState);
|
||||
vd->resetRegIndex();
|
||||
vd->setModified(false);
|
||||
|
||||
_x86State.getListByClass(C)[regIndex] = nullptr;
|
||||
_x86State._occupied.andNot(C, regMask);
|
||||
_x86State._modified.andNot(C, regMask);
|
||||
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Rebase]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Rebase.
|
||||
//!
|
||||
//! Change the register of the 'VarData' changing also the current 'X86VarState'.
|
||||
//! Rebase is nearly identical to 'Detach' and 'Attach' sequence, but doesn't
|
||||
//! change the `VarData`s modified flag.
|
||||
template<int C>
|
||||
ASMJIT_INLINE void rebase(VarData* vd, uint32_t newRegIndex, uint32_t oldRegIndex) {
|
||||
ASMJIT_ASSERT(vd->getClass() == C);
|
||||
|
||||
uint32_t newRegMask = Utils::mask(newRegIndex);
|
||||
uint32_t oldRegMask = Utils::mask(oldRegIndex);
|
||||
uint32_t bothRegMask = newRegMask ^ oldRegMask;
|
||||
|
||||
vd->setRegIndex(newRegIndex);
|
||||
|
||||
_x86State.getListByClass(C)[oldRegIndex] = nullptr;
|
||||
_x86State.getListByClass(C)[newRegIndex] = vd;
|
||||
|
||||
_x86State._occupied.xor_(C, bothRegMask);
|
||||
_x86State._modified.xor_(C, bothRegMask & -static_cast<int32_t>(vd->isModified()));
|
||||
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Load / Save]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Load.
|
||||
//!
|
||||
//! Load variable from its memory slot to a register, emitting 'Load'
|
||||
//! instruction and changing the variable state to allocated.
|
||||
template<int C>
|
||||
ASMJIT_INLINE void load(VarData* vd, uint32_t regIndex) {
|
||||
// Can be only called if variable is not allocated.
|
||||
ASMJIT_ASSERT(vd->getClass() == C);
|
||||
ASMJIT_ASSERT(vd->getState() != kVarStateReg);
|
||||
ASMJIT_ASSERT(vd->getRegIndex() == kInvalidReg);
|
||||
|
||||
emitLoad(vd, regIndex, "Load");
|
||||
attach<C>(vd, regIndex, false);
|
||||
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
}
|
||||
|
||||
//! Save.
|
||||
//!
|
||||
//! Save the variable into its home location, but keep it as allocated.
|
||||
template<int C>
|
||||
ASMJIT_INLINE void save(VarData* vd) {
|
||||
ASMJIT_ASSERT(vd->getClass() == C);
|
||||
ASMJIT_ASSERT(vd->getState() == kVarStateReg);
|
||||
ASMJIT_ASSERT(vd->getRegIndex() != kInvalidReg);
|
||||
|
||||
uint32_t regIndex = vd->getRegIndex();
|
||||
uint32_t regMask = Utils::mask(regIndex);
|
||||
|
||||
emitSave(vd, regIndex, "Save");
|
||||
|
||||
vd->setModified(false);
|
||||
_x86State._modified.andNot(C, regMask);
|
||||
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Move / Swap]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Move a register.
|
||||
//!
|
||||
//! Move register from one index to another, emitting 'Move' if needed. This
|
||||
//! function does nothing if register is already at the given index.
|
||||
template<int C>
|
||||
ASMJIT_INLINE void move(VarData* vd, uint32_t regIndex) {
|
||||
ASMJIT_ASSERT(vd->getClass() == C);
|
||||
ASMJIT_ASSERT(vd->getState() == kVarStateReg);
|
||||
ASMJIT_ASSERT(vd->getRegIndex() != kInvalidReg);
|
||||
|
||||
uint32_t oldIndex = vd->getRegIndex();
|
||||
if (regIndex != oldIndex) {
|
||||
emitMove(vd, regIndex, oldIndex, "Move");
|
||||
rebase<C>(vd, regIndex, oldIndex);
|
||||
}
|
||||
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
}
|
||||
|
||||
//! Swap two registers
|
||||
//!
|
||||
//! It's only possible to swap Gp registers.
|
||||
ASMJIT_INLINE void swapGp(VarData* aVd, VarData* bVd) {
|
||||
ASMJIT_ASSERT(aVd != bVd);
|
||||
|
||||
ASMJIT_ASSERT(aVd->getClass() == kX86RegClassGp);
|
||||
ASMJIT_ASSERT(aVd->getState() == kVarStateReg);
|
||||
ASMJIT_ASSERT(aVd->getRegIndex() != kInvalidReg);
|
||||
|
||||
ASMJIT_ASSERT(bVd->getClass() == kX86RegClassGp);
|
||||
ASMJIT_ASSERT(bVd->getState() == kVarStateReg);
|
||||
ASMJIT_ASSERT(bVd->getRegIndex() != kInvalidReg);
|
||||
|
||||
uint32_t aIndex = aVd->getRegIndex();
|
||||
uint32_t bIndex = bVd->getRegIndex();
|
||||
|
||||
emitSwapGp(aVd, bVd, aIndex, bIndex, "Swap");
|
||||
|
||||
aVd->setRegIndex(bIndex);
|
||||
bVd->setRegIndex(aIndex);
|
||||
|
||||
_x86State.getListByClass(kX86RegClassGp)[aIndex] = bVd;
|
||||
_x86State.getListByClass(kX86RegClassGp)[bIndex] = aVd;
|
||||
|
||||
uint32_t m = aVd->isModified() ^ bVd->isModified();
|
||||
_x86State._modified.xor_(kX86RegClassGp, (m << aIndex) | (m << bIndex));
|
||||
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Alloc / Spill]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Alloc.
|
||||
template<int C>
|
||||
ASMJIT_INLINE void alloc(VarData* vd, uint32_t regIndex) {
|
||||
ASMJIT_ASSERT(vd->getClass() == C);
|
||||
ASMJIT_ASSERT(regIndex != kInvalidReg);
|
||||
|
||||
uint32_t oldRegIndex = vd->getRegIndex();
|
||||
uint32_t oldState = vd->getState();
|
||||
uint32_t regMask = Utils::mask(regIndex);
|
||||
|
||||
ASMJIT_ASSERT(_x86State.getListByClass(C)[regIndex] == nullptr || regIndex == oldRegIndex);
|
||||
|
||||
if (oldState != kVarStateReg) {
|
||||
if (oldState == kVarStateMem)
|
||||
emitLoad(vd, regIndex, "Alloc");
|
||||
vd->setModified(false);
|
||||
}
|
||||
else if (oldRegIndex != regIndex) {
|
||||
emitMove(vd, regIndex, oldRegIndex, "Alloc");
|
||||
|
||||
_x86State.getListByClass(C)[oldRegIndex] = nullptr;
|
||||
regMask ^= Utils::mask(oldRegIndex);
|
||||
}
|
||||
else {
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
return;
|
||||
}
|
||||
|
||||
vd->setState(kVarStateReg);
|
||||
vd->setRegIndex(regIndex);
|
||||
vd->addHomeIndex(regIndex);
|
||||
|
||||
_x86State.getListByClass(C)[regIndex] = vd;
|
||||
_x86State._occupied.xor_(C, regMask);
|
||||
_x86State._modified.xor_(C, regMask & -static_cast<int32_t>(vd->isModified()));
|
||||
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
}
|
||||
|
||||
//! Spill.
|
||||
//!
|
||||
//! Spill variable/register, saves the content to the memory-home if modified.
|
||||
template<int C>
|
||||
ASMJIT_INLINE void spill(VarData* vd) {
|
||||
ASMJIT_ASSERT(vd->getClass() == C);
|
||||
|
||||
if (vd->getState() != kVarStateReg) {
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t regIndex = vd->getRegIndex();
|
||||
|
||||
ASMJIT_ASSERT(regIndex != kInvalidReg);
|
||||
ASMJIT_ASSERT(_x86State.getListByClass(C)[regIndex] == vd);
|
||||
|
||||
if (vd->isModified())
|
||||
emitSave(vd, regIndex, "Spill");
|
||||
detach<C>(vd, regIndex, kVarStateMem);
|
||||
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Modify]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
template<int C>
|
||||
ASMJIT_INLINE void modify(VarData* vd) {
|
||||
ASMJIT_ASSERT(vd->getClass() == C);
|
||||
|
||||
uint32_t regIndex = vd->getRegIndex();
|
||||
uint32_t regMask = Utils::mask(regIndex);
|
||||
|
||||
vd->setModified(true);
|
||||
_x86State._modified.or_(C, regMask);
|
||||
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Unuse]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Unuse.
|
||||
//!
|
||||
//! Unuse variable, it will be detached it if it's allocated then its state
|
||||
//! will be changed to kVarStateNone.
|
||||
template<int C>
|
||||
ASMJIT_INLINE void unuse(VarData* vd, uint32_t vState = kVarStateNone) {
|
||||
ASMJIT_ASSERT(vd->getClass() == C);
|
||||
ASMJIT_ASSERT(vState != kVarStateReg);
|
||||
|
||||
uint32_t regIndex = vd->getRegIndex();
|
||||
if (regIndex != kInvalidReg)
|
||||
detach<C>(vd, regIndex, vState);
|
||||
else
|
||||
vd->setState(vState);
|
||||
|
||||
ASMJIT_X86_CHECK_STATE
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [State]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Get state as `X86VarState`.
|
||||
ASMJIT_INLINE X86VarState* getState() const {
|
||||
return const_cast<X86VarState*>(&_x86State);
|
||||
}
|
||||
|
||||
virtual void loadState(VarState* src);
|
||||
virtual VarState* saveState();
|
||||
|
||||
virtual void switchState(VarState* src);
|
||||
virtual void intersectStates(VarState* a, VarState* b);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Memory]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_INLINE X86Mem getVarMem(VarData* vd) {
|
||||
(void)getVarCell(vd);
|
||||
|
||||
X86Mem mem(_memSlot);
|
||||
mem.setBase(vd->getId());
|
||||
return mem;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Fetch]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
virtual Error fetch();
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Annotate]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
virtual Error annotate();
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Translate]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
virtual Error translate();
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Serialize]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
virtual Error serialize(Assembler* assembler, HLNode* start, HLNode* stop);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Members]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Count of X86/X64 registers.
|
||||
X86RegCount _regCount;
|
||||
//! X86/X64 stack-pointer (esp or rsp).
|
||||
X86GpReg _zsp;
|
||||
//! X86/X64 frame-pointer (ebp or rbp).
|
||||
X86GpReg _zbp;
|
||||
//! Temporary memory operand.
|
||||
X86Mem _memSlot;
|
||||
|
||||
//! X86/X64 specific compiler state, linked to `_state`.
|
||||
X86VarState _x86State;
|
||||
//! Clobbered registers (for the whole function).
|
||||
X86RegMask _clobberedRegs;
|
||||
|
||||
//! Memory cell where is stored address used to restore manually
|
||||
//! aligned stack.
|
||||
VarCell* _stackFrameCell;
|
||||
|
||||
//! Global allocable registers mask.
|
||||
uint32_t _gaRegs[kX86RegClassCount];
|
||||
|
||||
//! Function arguments base pointer (register).
|
||||
uint8_t _argBaseReg;
|
||||
//! Function variables base pointer (register).
|
||||
uint8_t _varBaseReg;
|
||||
//! Whether to emit comments.
|
||||
uint8_t _emitComments;
|
||||
|
||||
//! Function arguments base offset.
|
||||
int32_t _argBaseOffset;
|
||||
//! Function variables base offset.
|
||||
int32_t _varBaseOffset;
|
||||
|
||||
//! Function arguments displacement.
|
||||
int32_t _argActualDisp;
|
||||
//! Function variables displacement.
|
||||
int32_t _varActualDisp;
|
||||
|
||||
//! Temporary string builder used for logging.
|
||||
StringBuilderTmp<256> _stringBuilder;
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
} // asmjit namespace
|
||||
|
||||
// [Api-End]
|
||||
#include "../apiend.h"
|
||||
|
||||
// [Guard]
|
||||
#endif // !ASMJIT_DISABLE_COMPILER
|
||||
#endif // _ASMJIT_X86_X86COMPILERCONTEXT_P_H
|
||||
@@ -0,0 +1,551 @@
|
||||
// [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_COMPILER) && (defined(ASMJIT_BUILD_X86) || defined(ASMJIT_BUILD_X64))
|
||||
|
||||
// [Dependencies]
|
||||
#include "../x86/x86compiler.h"
|
||||
#include "../x86/x86compilerfunc.h"
|
||||
|
||||
// [Api-Begin]
|
||||
#include "../apibegin.h"
|
||||
|
||||
namespace asmjit {
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86FuncDecl - Helpers]
|
||||
// ============================================================================
|
||||
|
||||
static ASMJIT_INLINE bool x86ArgIsInt(uint32_t aType) {
|
||||
ASMJIT_ASSERT(aType < kX86VarTypeCount);
|
||||
return Utils::inInterval<uint32_t>(aType, _kVarTypeIntStart, _kVarTypeIntEnd);
|
||||
}
|
||||
|
||||
static ASMJIT_INLINE bool x86ArgIsFp(uint32_t aType) {
|
||||
ASMJIT_ASSERT(aType < kX86VarTypeCount);
|
||||
return Utils::inInterval<uint32_t>(aType, _kVarTypeFpStart, _kVarTypeFpEnd);
|
||||
}
|
||||
|
||||
static ASMJIT_INLINE uint32_t x86ArgTypeToXmmType(uint32_t aType) {
|
||||
if (aType == kVarTypeFp32) return kX86VarTypeXmmSs;
|
||||
if (aType == kVarTypeFp64) return kX86VarTypeXmmSd;
|
||||
return aType;
|
||||
}
|
||||
|
||||
//! Get an architecture depending on the calling convention `callConv`.
|
||||
//!
|
||||
//! Returns `kArchNone`, `kArchX86`, or `kArchX64`.
|
||||
static ASMJIT_INLINE uint32_t x86GetArchFromCConv(uint32_t callConv) {
|
||||
if (Utils::inInterval<uint32_t>(callConv, _kCallConvX86Start, _kCallConvX86End)) return kArchX86;
|
||||
if (Utils::inInterval<uint32_t>(callConv, _kCallConvX64Start, _kCallConvX64End)) return kArchX64;
|
||||
|
||||
return kArchNone;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86FuncDecl - SetPrototype]
|
||||
// ============================================================================
|
||||
|
||||
#define R(_Index_) kX86RegIndex##_Index_
|
||||
static uint32_t X86FuncDecl_initConv(X86FuncDecl* self, uint32_t arch, uint32_t callConv) {
|
||||
// Setup defaults.
|
||||
self->_argStackSize = 0;
|
||||
self->_redZoneSize = 0;
|
||||
self->_spillZoneSize = 0;
|
||||
|
||||
self->_callConv = static_cast<uint8_t>(callConv);
|
||||
self->_calleePopsStack = false;
|
||||
self->_argsDirection = kFuncDirRTL;
|
||||
|
||||
self->_passed.reset();
|
||||
self->_preserved.reset();
|
||||
|
||||
::memset(self->_passedOrderGp, kInvalidReg, ASMJIT_ARRAY_SIZE(self->_passedOrderGp));
|
||||
::memset(self->_passedOrderXyz, kInvalidReg, ASMJIT_ARRAY_SIZE(self->_passedOrderXyz));
|
||||
|
||||
switch (arch) {
|
||||
// ------------------------------------------------------------------------
|
||||
// [X86 Support]
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#if defined(ASMJIT_BUILD_X86)
|
||||
case kArchX86: {
|
||||
self->_preserved.set(kX86RegClassGp, Utils::mask(R(Bx), R(Sp), R(Bp), R(Si), R(Di)));
|
||||
|
||||
switch (callConv) {
|
||||
case kCallConvX86CDecl:
|
||||
break;
|
||||
|
||||
case kCallConvX86StdCall:
|
||||
self->_calleePopsStack = true;
|
||||
break;
|
||||
|
||||
case kCallConvX86MsThisCall:
|
||||
self->_calleePopsStack = true;
|
||||
self->_passed.set(kX86RegClassGp, Utils::mask(R(Cx)));
|
||||
self->_passedOrderGp[0] = R(Cx);
|
||||
break;
|
||||
|
||||
case kCallConvX86MsFastCall:
|
||||
self->_calleePopsStack = true;
|
||||
self->_passed.set(kX86RegClassGp, Utils::mask(R(Cx), R(Cx)));
|
||||
self->_passedOrderGp[0] = R(Cx);
|
||||
self->_passedOrderGp[1] = R(Dx);
|
||||
break;
|
||||
|
||||
case kCallConvX86BorlandFastCall:
|
||||
self->_calleePopsStack = true;
|
||||
self->_argsDirection = kFuncDirLTR;
|
||||
self->_passed.set(kX86RegClassGp, Utils::mask(R(Ax), R(Dx), R(Cx)));
|
||||
self->_passedOrderGp[0] = R(Ax);
|
||||
self->_passedOrderGp[1] = R(Dx);
|
||||
self->_passedOrderGp[2] = R(Cx);
|
||||
break;
|
||||
|
||||
case kCallConvX86GccFastCall:
|
||||
self->_calleePopsStack = true;
|
||||
self->_passed.set(kX86RegClassGp, Utils::mask(R(Cx), R(Dx)));
|
||||
self->_passedOrderGp[0] = R(Cx);
|
||||
self->_passedOrderGp[1] = R(Dx);
|
||||
break;
|
||||
|
||||
case kCallConvX86GccRegParm1:
|
||||
self->_passed.set(kX86RegClassGp, Utils::mask(R(Ax)));
|
||||
self->_passedOrderGp[0] = R(Ax);
|
||||
break;
|
||||
|
||||
case kCallConvX86GccRegParm2:
|
||||
self->_passed.set(kX86RegClassGp, Utils::mask(R(Ax), R(Dx)));
|
||||
self->_passedOrderGp[0] = R(Ax);
|
||||
self->_passedOrderGp[1] = R(Dx);
|
||||
break;
|
||||
|
||||
case kCallConvX86GccRegParm3:
|
||||
self->_passed.set(kX86RegClassGp, Utils::mask(R(Ax), R(Dx), R(Cx)));
|
||||
self->_passedOrderGp[0] = R(Ax);
|
||||
self->_passedOrderGp[1] = R(Dx);
|
||||
self->_passedOrderGp[2] = R(Cx);
|
||||
break;
|
||||
|
||||
default:
|
||||
return kErrorInvalidArgument;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
#endif // ASMJIT_BUILD_X86
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// [X64 Support]
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#if defined(ASMJIT_BUILD_X64)
|
||||
case kArchX64: {
|
||||
switch (callConv) {
|
||||
case kCallConvX64Win:
|
||||
self->_spillZoneSize = 32;
|
||||
|
||||
self->_passed.set(kX86RegClassGp, Utils::mask(R(Cx), R(Dx), 8, 9));
|
||||
self->_passedOrderGp[0] = R(Cx);
|
||||
self->_passedOrderGp[1] = R(Dx);
|
||||
self->_passedOrderGp[2] = 8;
|
||||
self->_passedOrderGp[3] = 9;
|
||||
|
||||
self->_passed.set(kX86RegClassXyz, Utils::mask(0, 1, 2, 3));
|
||||
self->_passedOrderXyz[0] = 0;
|
||||
self->_passedOrderXyz[1] = 1;
|
||||
self->_passedOrderXyz[2] = 2;
|
||||
self->_passedOrderXyz[3] = 3;
|
||||
|
||||
self->_preserved.set(kX86RegClassGp , Utils::mask(R(Bx), R(Sp), R(Bp), R(Si), R(Di), 12, 13, 14, 15));
|
||||
self->_preserved.set(kX86RegClassXyz, Utils::mask(6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
|
||||
break;
|
||||
|
||||
case kCallConvX64Unix:
|
||||
self->_redZoneSize = 128;
|
||||
|
||||
self->_passed.set(kX86RegClassGp, Utils::mask(R(Di), R(Si), R(Dx), R(Cx), 8, 9));
|
||||
self->_passedOrderGp[0] = R(Di);
|
||||
self->_passedOrderGp[1] = R(Si);
|
||||
self->_passedOrderGp[2] = R(Dx);
|
||||
self->_passedOrderGp[3] = R(Cx);
|
||||
self->_passedOrderGp[4] = 8;
|
||||
self->_passedOrderGp[5] = 9;
|
||||
|
||||
self->_passed.set(kX86RegClassXyz, Utils::mask(0, 1, 2, 3, 4, 5, 6, 7));
|
||||
self->_passedOrderXyz[0] = 0;
|
||||
self->_passedOrderXyz[1] = 1;
|
||||
self->_passedOrderXyz[2] = 2;
|
||||
self->_passedOrderXyz[3] = 3;
|
||||
self->_passedOrderXyz[4] = 4;
|
||||
self->_passedOrderXyz[5] = 5;
|
||||
self->_passedOrderXyz[6] = 6;
|
||||
self->_passedOrderXyz[7] = 7;
|
||||
|
||||
self->_preserved.set(kX86RegClassGp, Utils::mask(R(Bx), R(Sp), R(Bp), 12, 13, 14, 15));
|
||||
break;
|
||||
|
||||
default:
|
||||
return kErrorInvalidArgument;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
#endif // ASMJIT_BUILD_X64
|
||||
|
||||
default:
|
||||
return kErrorInvalidArgument;
|
||||
}
|
||||
}
|
||||
#undef R
|
||||
|
||||
static Error X86FuncDecl_initFunc(X86FuncDecl* self, uint32_t arch,
|
||||
uint32_t ret, const uint32_t* args, uint32_t numArgs) {
|
||||
|
||||
ASMJIT_ASSERT(numArgs <= kFuncArgCount);
|
||||
|
||||
uint32_t callConv = self->_callConv;
|
||||
uint32_t regSize = (arch == kArchX86) ? 4 : 8;
|
||||
|
||||
int32_t i = 0;
|
||||
int32_t gpPos = 0;
|
||||
int32_t xmmPos = 0;
|
||||
int32_t stackOffset = 0;
|
||||
const uint8_t* varMapping = nullptr;
|
||||
|
||||
#if defined(ASMJIT_BUILD_X86)
|
||||
if (arch == kArchX86)
|
||||
varMapping = _x86VarMapping;
|
||||
#endif // ASMJIT_BUILD_X86
|
||||
|
||||
#if defined(ASMJIT_BUILD_X64)
|
||||
if (arch == kArchX64)
|
||||
varMapping = _x64VarMapping;
|
||||
#endif // ASMJIT_BUILD_X64
|
||||
|
||||
ASMJIT_ASSERT(varMapping != nullptr);
|
||||
self->_numArgs = static_cast<uint8_t>(numArgs);
|
||||
self->_retCount = 0;
|
||||
|
||||
for (i = 0; i < static_cast<int32_t>(numArgs); i++) {
|
||||
FuncInOut& arg = self->getArg(i);
|
||||
arg._varType = static_cast<uint8_t>(varMapping[args[i]]);
|
||||
arg._regIndex = kInvalidReg;
|
||||
arg._stackOffset = kFuncStackInvalid;
|
||||
}
|
||||
|
||||
for (; i < kFuncArgCount; i++) {
|
||||
self->_args[i].reset();
|
||||
}
|
||||
|
||||
self->_rets[0].reset();
|
||||
self->_rets[1].reset();
|
||||
self->_argStackSize = 0;
|
||||
self->_used.reset();
|
||||
|
||||
if (ret != kInvalidVar) {
|
||||
ret = varMapping[ret];
|
||||
switch (ret) {
|
||||
case kVarTypeInt64:
|
||||
case kVarTypeUInt64:
|
||||
// 64-bit value is returned in EDX:EAX on x86.
|
||||
#if defined(ASMJIT_BUILD_X86)
|
||||
if (arch == kArchX86) {
|
||||
self->_retCount = 2;
|
||||
self->_rets[0]._varType = kVarTypeUInt32;
|
||||
self->_rets[0]._regIndex = kX86RegIndexAx;
|
||||
self->_rets[1]._varType = static_cast<uint8_t>(ret - 2);
|
||||
self->_rets[1]._regIndex = kX86RegIndexDx;
|
||||
}
|
||||
ASMJIT_FALLTHROUGH;
|
||||
#endif // ASMJIT_BUILD_X86
|
||||
|
||||
case kVarTypeInt8:
|
||||
case kVarTypeUInt8:
|
||||
case kVarTypeInt16:
|
||||
case kVarTypeUInt16:
|
||||
case kVarTypeInt32:
|
||||
case kVarTypeUInt32:
|
||||
self->_retCount = 1;
|
||||
self->_rets[0]._varType = static_cast<uint8_t>(ret);
|
||||
self->_rets[0]._regIndex = kX86RegIndexAx;
|
||||
break;
|
||||
|
||||
case kX86VarTypeMm:
|
||||
self->_retCount = 1;
|
||||
self->_rets[0]._varType = static_cast<uint8_t>(ret);
|
||||
self->_rets[0]._regIndex = 0;
|
||||
break;
|
||||
|
||||
case kVarTypeFp32:
|
||||
self->_retCount = 1;
|
||||
if (arch == kArchX86) {
|
||||
self->_rets[0]._varType = kVarTypeFp32;
|
||||
self->_rets[0]._regIndex = 0;
|
||||
}
|
||||
else {
|
||||
self->_rets[0]._varType = kX86VarTypeXmmSs;
|
||||
self->_rets[0]._regIndex = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case kVarTypeFp64:
|
||||
self->_retCount = 1;
|
||||
if (arch == kArchX86) {
|
||||
self->_rets[0]._varType = kVarTypeFp64;
|
||||
self->_rets[0]._regIndex = 0;
|
||||
}
|
||||
else {
|
||||
self->_rets[0]._varType = kX86VarTypeXmmSd;
|
||||
self->_rets[0]._regIndex = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case kX86VarTypeXmm:
|
||||
case kX86VarTypeXmmSs:
|
||||
case kX86VarTypeXmmSd:
|
||||
case kX86VarTypeXmmPs:
|
||||
case kX86VarTypeXmmPd:
|
||||
self->_retCount = 1;
|
||||
self->_rets[0]._varType = static_cast<uint8_t>(ret);
|
||||
self->_rets[0]._regIndex = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->_numArgs == 0)
|
||||
return kErrorOk;
|
||||
|
||||
#if defined(ASMJIT_BUILD_X86)
|
||||
if (arch == kArchX86) {
|
||||
// Register arguments (Integer), always left-to-right.
|
||||
for (i = 0; i != static_cast<int32_t>(numArgs); i++) {
|
||||
FuncInOut& arg = self->getArg(i);
|
||||
uint32_t varType = varMapping[arg.getVarType()];
|
||||
|
||||
if (!x86ArgIsInt(varType) || gpPos >= ASMJIT_ARRAY_SIZE(self->_passedOrderGp))
|
||||
continue;
|
||||
|
||||
if (self->_passedOrderGp[gpPos] == kInvalidReg)
|
||||
continue;
|
||||
|
||||
arg._regIndex = self->_passedOrderGp[gpPos++];
|
||||
self->_used.or_(kX86RegClassGp, Utils::mask(arg.getRegIndex()));
|
||||
}
|
||||
|
||||
// Stack arguments.
|
||||
int32_t iStart = static_cast<int32_t>(numArgs - 1);
|
||||
int32_t iEnd = -1;
|
||||
int32_t iStep = -1;
|
||||
|
||||
if (self->_argsDirection == kFuncDirLTR) {
|
||||
iStart = 0;
|
||||
iEnd = static_cast<int32_t>(numArgs);
|
||||
iStep = 1;
|
||||
}
|
||||
|
||||
for (i = iStart; i != iEnd; i += iStep) {
|
||||
FuncInOut& arg = self->getArg(i);
|
||||
uint32_t varType = varMapping[arg.getVarType()];
|
||||
|
||||
if (arg.hasRegIndex())
|
||||
continue;
|
||||
|
||||
if (x86ArgIsInt(varType)) {
|
||||
stackOffset -= 4;
|
||||
arg._stackOffset = static_cast<int16_t>(stackOffset);
|
||||
}
|
||||
else if (x86ArgIsFp(varType)) {
|
||||
int32_t size = static_cast<int32_t>(_x86VarInfo[varType].getSize());
|
||||
stackOffset -= size;
|
||||
arg._stackOffset = static_cast<int16_t>(stackOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ASMJIT_BUILD_X86
|
||||
|
||||
#if defined(ASMJIT_BUILD_X64)
|
||||
if (arch == kArchX64) {
|
||||
if (callConv == kCallConvX64Win) {
|
||||
int32_t argMax = Utils::iMin<int32_t>(numArgs, 4);
|
||||
|
||||
// Register arguments (GP/XMM), always left-to-right.
|
||||
for (i = 0; i != argMax; i++) {
|
||||
FuncInOut& arg = self->getArg(i);
|
||||
uint32_t varType = varMapping[arg.getVarType()];
|
||||
|
||||
if (x86ArgIsInt(varType) && i < ASMJIT_ARRAY_SIZE(self->_passedOrderGp)) {
|
||||
arg._regIndex = self->_passedOrderGp[i];
|
||||
self->_used.or_(kX86RegClassGp, Utils::mask(arg.getRegIndex()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (x86ArgIsFp(varType) && i < ASMJIT_ARRAY_SIZE(self->_passedOrderXyz)) {
|
||||
arg._varType = static_cast<uint8_t>(x86ArgTypeToXmmType(varType));
|
||||
arg._regIndex = self->_passedOrderXyz[i];
|
||||
self->_used.or_(kX86RegClassXyz, Utils::mask(arg.getRegIndex()));
|
||||
}
|
||||
}
|
||||
|
||||
// Stack arguments (always right-to-left).
|
||||
for (i = numArgs - 1; i != -1; i--) {
|
||||
FuncInOut& arg = self->getArg(i);
|
||||
uint32_t varType = varMapping[arg.getVarType()];
|
||||
|
||||
if (arg.hasRegIndex())
|
||||
continue;
|
||||
|
||||
if (x86ArgIsInt(varType)) {
|
||||
stackOffset -= 8; // Always 8 bytes.
|
||||
arg._stackOffset = stackOffset;
|
||||
}
|
||||
else if (x86ArgIsFp(varType)) {
|
||||
stackOffset -= 8; // Always 8 bytes (float/double).
|
||||
arg._stackOffset = stackOffset;
|
||||
}
|
||||
}
|
||||
|
||||
// 32 bytes shadow space (X64W calling convention specific).
|
||||
stackOffset -= 4 * 8;
|
||||
}
|
||||
else {
|
||||
// Register arguments (Gp), always left-to-right.
|
||||
for (i = 0; i != static_cast<int32_t>(numArgs); i++) {
|
||||
FuncInOut& arg = self->getArg(i);
|
||||
uint32_t varType = varMapping[arg.getVarType()];
|
||||
|
||||
if (!x86ArgIsInt(varType) || gpPos >= ASMJIT_ARRAY_SIZE(self->_passedOrderGp))
|
||||
continue;
|
||||
|
||||
if (self->_passedOrderGp[gpPos] == kInvalidReg)
|
||||
continue;
|
||||
|
||||
arg._regIndex = self->_passedOrderGp[gpPos++];
|
||||
self->_used.or_(kX86RegClassGp, Utils::mask(arg.getRegIndex()));
|
||||
}
|
||||
|
||||
// Register arguments (XMM), always left-to-right.
|
||||
for (i = 0; i != static_cast<int32_t>(numArgs); i++) {
|
||||
FuncInOut& arg = self->getArg(i);
|
||||
uint32_t varType = varMapping[arg.getVarType()];
|
||||
|
||||
if (x86ArgIsFp(varType)) {
|
||||
arg._varType = static_cast<uint8_t>(x86ArgTypeToXmmType(varType));
|
||||
arg._regIndex = self->_passedOrderXyz[xmmPos++];
|
||||
self->_used.or_(kX86RegClassXyz, Utils::mask(arg.getRegIndex()));
|
||||
}
|
||||
}
|
||||
|
||||
// Stack arguments.
|
||||
for (i = numArgs - 1; i != -1; i--) {
|
||||
FuncInOut& arg = self->getArg(i);
|
||||
uint32_t varType = varMapping[arg.getVarType()];
|
||||
|
||||
if (arg.hasRegIndex())
|
||||
continue;
|
||||
|
||||
if (x86ArgIsInt(varType)) {
|
||||
stackOffset -= 8;
|
||||
arg._stackOffset = static_cast<int16_t>(stackOffset);
|
||||
}
|
||||
else if (x86ArgIsFp(varType)) {
|
||||
int32_t size = static_cast<int32_t>(_x86VarInfo[varType].getSize());
|
||||
|
||||
stackOffset -= size;
|
||||
arg._stackOffset = static_cast<int16_t>(stackOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ASMJIT_BUILD_X64
|
||||
|
||||
// Modify the stack offset, thus in result all parameters would have positive
|
||||
// non-zero stack offset.
|
||||
for (i = 0; i < static_cast<int32_t>(numArgs); i++) {
|
||||
FuncInOut& arg = self->getArg(i);
|
||||
if (!arg.hasRegIndex()) {
|
||||
arg._stackOffset += static_cast<uint16_t>(static_cast<int32_t>(regSize) - stackOffset);
|
||||
}
|
||||
}
|
||||
|
||||
self->_argStackSize = static_cast<uint32_t>(-stackOffset);
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
Error X86FuncDecl::setPrototype(const FuncPrototype& p) {
|
||||
uint32_t callConv = p.getCallConv();
|
||||
uint32_t arch = x86GetArchFromCConv(callConv);
|
||||
|
||||
if (arch == kArchNone)
|
||||
return kErrorInvalidArgument;
|
||||
|
||||
if (p.getNumArgs() > kFuncArgCount)
|
||||
return kErrorInvalidArgument;
|
||||
|
||||
// Validate that the required convention is supported by the current asmjit
|
||||
// configuration, if only one target is compiled.
|
||||
#if defined(ASMJIT_BUILD_X86) && !defined(ASMJIT_BUILD_X64)
|
||||
if (arch == kArchX64)
|
||||
return kErrorInvalidState;
|
||||
#endif // ASMJIT_BUILD_X86 && !ASMJIT_BUILD_X64
|
||||
|
||||
#if !defined(ASMJIT_BUILD_X86) && defined(ASMJIT_BUILD_X64)
|
||||
if (arch == kArchX86)
|
||||
return kErrorInvalidState;
|
||||
#endif // !ASMJIT_BUILD_X86 && ASMJIT_BUILD_X64
|
||||
|
||||
ASMJIT_PROPAGATE_ERROR(X86FuncDecl_initConv(this, arch, callConv));
|
||||
ASMJIT_PROPAGATE_ERROR(X86FuncDecl_initFunc(this, arch, p.getRet(), p.getArgs(), p.getNumArgs()));
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86FuncDecl - Reset]
|
||||
// ============================================================================
|
||||
|
||||
void X86FuncDecl::reset() {
|
||||
uint32_t i;
|
||||
|
||||
_callConv = kCallConvNone;
|
||||
_calleePopsStack = false;
|
||||
_argsDirection = kFuncDirRTL;
|
||||
_reserved0 = 0;
|
||||
|
||||
_numArgs = 0;
|
||||
_retCount = 0;
|
||||
|
||||
_argStackSize = 0;
|
||||
_redZoneSize = 0;
|
||||
_spillZoneSize = 0;
|
||||
|
||||
for (i = 0; i < ASMJIT_ARRAY_SIZE(_args); i++)
|
||||
_args[i].reset();
|
||||
|
||||
_rets[0].reset();
|
||||
_rets[1].reset();
|
||||
|
||||
_used.reset();
|
||||
_passed.reset();
|
||||
_preserved.reset();
|
||||
|
||||
::memset(_passedOrderGp, kInvalidReg, ASMJIT_ARRAY_SIZE(_passedOrderGp));
|
||||
::memset(_passedOrderXyz, kInvalidReg, ASMJIT_ARRAY_SIZE(_passedOrderXyz));
|
||||
}
|
||||
|
||||
} // asmjit namespace
|
||||
|
||||
// [Api-End]
|
||||
#include "../apiend.h"
|
||||
|
||||
// [Guard]
|
||||
#endif // !ASMJIT_DISABLE_COMPILER && (ASMJIT_BUILD_X86 || ASMJIT_BUILD_X64)
|
||||
@@ -0,0 +1,133 @@
|
||||
// [AsmJit]
|
||||
// Complete x86/x64 JIT and Remote Assembler for C++.
|
||||
//
|
||||
// [License]
|
||||
// Zlib - See LICENSE.md file in the package.
|
||||
|
||||
// [Guard]
|
||||
#ifndef _ASMJIT_X86_X86COMPILERFUNC_P_H
|
||||
#define _ASMJIT_X86_X86COMPILERFUNC_P_H
|
||||
|
||||
#include "../build.h"
|
||||
#if !defined(ASMJIT_DISABLE_COMPILER)
|
||||
|
||||
// [Dependencies]
|
||||
#include "../base/compilerfunc.h"
|
||||
#include "../x86/x86operand.h"
|
||||
|
||||
// [Api-Begin]
|
||||
#include "../apibegin.h"
|
||||
|
||||
namespace asmjit {
|
||||
|
||||
//! \addtogroup asmjit_x86
|
||||
//! \{
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::TypeId]
|
||||
// ============================================================================
|
||||
|
||||
#if !defined(ASMJIT_DOCGEN)
|
||||
ASMJIT_TYPE_ID(X86MmReg, kX86VarTypeMm);
|
||||
ASMJIT_TYPE_ID(X86MmVar, kX86VarTypeMm);
|
||||
ASMJIT_TYPE_ID(X86XmmReg, kX86VarTypeXmm);
|
||||
ASMJIT_TYPE_ID(X86XmmVar, kX86VarTypeXmm);
|
||||
ASMJIT_TYPE_ID(X86YmmReg, kX86VarTypeYmm);
|
||||
ASMJIT_TYPE_ID(X86YmmVar, kX86VarTypeYmm);
|
||||
ASMJIT_TYPE_ID(X86ZmmReg, kX86VarTypeZmm);
|
||||
ASMJIT_TYPE_ID(X86ZmmVar, kX86VarTypeZmm);
|
||||
#endif // !ASMJIT_DOCGEN
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86FuncDecl]
|
||||
// ============================================================================
|
||||
|
||||
//! X86 function, including calling convention, arguments and their
|
||||
//! register indices or stack positions.
|
||||
struct X86FuncDecl : public FuncDecl {
|
||||
// --------------------------------------------------------------------------
|
||||
// [Construction / Destruction]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Create a new `X86FuncDecl` instance.
|
||||
ASMJIT_INLINE X86FuncDecl() { reset(); }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Accessors - X86]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Get used registers mask for the given register class `rc`.
|
||||
//!
|
||||
//! NOTE: The result depends on the function calling convention AND the
|
||||
//! function prototype. Returned mask contains only registers actually used
|
||||
//! to pass function arguments.
|
||||
ASMJIT_INLINE uint32_t getUsed(uint32_t rc) const { return _used.get(rc); }
|
||||
|
||||
//! Get passed registers mask for the given register class `rc`.
|
||||
//!
|
||||
//! NOTE: The result depends on the function calling convention used; the
|
||||
//! prototype of the function doesn't affect the mask returned.
|
||||
ASMJIT_INLINE uint32_t getPassed(uint32_t rc) const { return _passed.get(rc); }
|
||||
|
||||
//! Get preserved registers mask for the given register class `rc`.
|
||||
//!
|
||||
//! NOTE: The result depends on the function calling convention used; the
|
||||
//! prototype of the function doesn't affect the mask returned.
|
||||
ASMJIT_INLINE uint32_t getPreserved(uint32_t rc) const { return _preserved.get(rc); }
|
||||
|
||||
//! Get ther order of passed registers (GP).
|
||||
//!
|
||||
//! NOTE: The result depends on the function calling convention used; the
|
||||
//! prototype of the function doesn't affect the mask returned.
|
||||
ASMJIT_INLINE const uint8_t* getPassedOrderGp() const { return _passedOrderGp; }
|
||||
|
||||
//! Get ther order of passed registers (XMM/YMM/ZMM).
|
||||
//!
|
||||
//! NOTE: The result depends on the function calling convention used; the
|
||||
//! prototype of the function doesn't affect the mask returned.
|
||||
ASMJIT_INLINE const uint8_t* getPassedOrderXyz() const { return _passedOrderXyz; }
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [SetPrototype]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Set function prototype.
|
||||
//!
|
||||
//! This will set function calling convention and setup arguments variables.
|
||||
//!
|
||||
//! NOTE: This function will allocate variables, it can be called only once.
|
||||
ASMJIT_API Error setPrototype(const FuncPrototype& p);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Reset]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
ASMJIT_API void reset();
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// [Members]
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
//! Used registers.
|
||||
X86RegMask _used;
|
||||
//! Passed registers (defined by the calling convention).
|
||||
X86RegMask _passed;
|
||||
//! Preserved registers (defined by the calling convention).
|
||||
X86RegMask _preserved;
|
||||
|
||||
//! Order of registers used to pass GP function arguments.
|
||||
uint8_t _passedOrderGp[8];
|
||||
//! Order of registers used to pass XMM/YMM/ZMM function arguments.
|
||||
uint8_t _passedOrderXyz[8];
|
||||
};
|
||||
|
||||
//! \}
|
||||
|
||||
} // asmjit namespace
|
||||
|
||||
// [Api-End]
|
||||
#include "../apiend.h"
|
||||
|
||||
// [Guard]
|
||||
#endif // !ASMJIT_DISABLE_COMPILER
|
||||
#endif // _ASMJIT_X86_X86COMPILERFUNC_P_H
|
||||
+3094
File diff suppressed because it is too large
Load Diff
+2192
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
// [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_BUILD_X86) || defined(ASMJIT_BUILD_X64)
|
||||
|
||||
// [Dependencies]
|
||||
#include "../x86/x86operand.h"
|
||||
|
||||
// [Api-Begin]
|
||||
#include "../apibegin.h"
|
||||
|
||||
namespace asmjit {
|
||||
namespace x86 {
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::X86Mem - abs[]]
|
||||
// ============================================================================
|
||||
|
||||
X86Mem ptr_abs(Ptr p, int32_t disp, uint32_t size) noexcept {
|
||||
X86Mem m(NoInit);
|
||||
|
||||
m._init_packed_op_sz_b0_b1_id(Operand::kTypeMem, size, kMemTypeAbsolute, 0, kInvalidValue);
|
||||
m._vmem.index = kInvalidValue;
|
||||
m._vmem.displacement = static_cast<int32_t>((intptr_t)(p + disp));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
X86Mem ptr_abs(Ptr p, const X86Reg& index, uint32_t shift, int32_t disp, uint32_t size) noexcept {
|
||||
X86Mem m(NoInit);
|
||||
uint32_t flags = shift << kX86MemShiftIndex;
|
||||
|
||||
if (index.isGp())
|
||||
flags |= X86Mem::_getGpdFlags(index);
|
||||
else if (index.isXmm())
|
||||
flags |= kX86MemVSibXmm << kX86MemVSibIndex;
|
||||
else if (index.isYmm())
|
||||
flags |= kX86MemVSibYmm << kX86MemVSibIndex;
|
||||
|
||||
m._init_packed_op_sz_b0_b1_id(Operand::kTypeMem, size, kMemTypeAbsolute, flags, kInvalidValue);
|
||||
m._vmem.index = index.getRegIndex();
|
||||
m._vmem.displacement = static_cast<int32_t>((intptr_t)(p + disp));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
#if !defined(ASMJIT_DISABLE_COMPILER)
|
||||
X86Mem ptr_abs(Ptr p, const X86Var& index, uint32_t shift, int32_t disp, uint32_t size) noexcept {
|
||||
X86Mem m(NoInit);
|
||||
uint32_t flags = shift << kX86MemShiftIndex;
|
||||
|
||||
const Var& index_ = reinterpret_cast<const Var&>(index);
|
||||
uint32_t indexRegType = index_.getRegType();
|
||||
|
||||
if (indexRegType <= kX86RegTypeGpq)
|
||||
flags |= X86Mem::_getGpdFlags(reinterpret_cast<const Var&>(index));
|
||||
else if (indexRegType == kX86RegTypeXmm)
|
||||
flags |= kX86MemVSibXmm << kX86MemVSibIndex;
|
||||
else if (indexRegType == kX86RegTypeYmm)
|
||||
flags |= kX86MemVSibYmm << kX86MemVSibIndex;
|
||||
|
||||
m._init_packed_op_sz_b0_b1_id(Operand::kTypeMem, size, kMemTypeAbsolute, flags, kInvalidValue);
|
||||
m._vmem.index = index_.getId();
|
||||
m._vmem.displacement = static_cast<int32_t>((intptr_t)(p + disp));
|
||||
|
||||
return m;
|
||||
}
|
||||
#endif // !ASMJIT_DISABLE_COMPILER
|
||||
|
||||
} // x86 namespace
|
||||
} // asmjit namespace
|
||||
|
||||
// [Api-End]
|
||||
#include "../apiend.h"
|
||||
|
||||
// [Guard]
|
||||
#endif // ASMJIT_BUILD_X86 || ASMJIT_BUILD_X64
|
||||
+2592
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,84 @@
|
||||
// [AsmJit]
|
||||
// Complete x86/x64 JIT and Remote Assembler for C++.
|
||||
//
|
||||
// [License]
|
||||
// Zlib - See LICENSE.md file in the package.
|
||||
|
||||
// [Export]
|
||||
#define ASMJIT_EXPORTS
|
||||
#define ASMJIT_EXPORTS_X86_REGS
|
||||
|
||||
// [Guard]
|
||||
#include "../build.h"
|
||||
#if defined(ASMJIT_BUILD_X86) || defined(ASMJIT_BUILD_X64)
|
||||
|
||||
// [Dependencies]
|
||||
#include "../x86/x86operand.h"
|
||||
|
||||
// [Api-Begin]
|
||||
#include "../apibegin.h"
|
||||
|
||||
namespace asmjit {
|
||||
|
||||
#define REG(type, index, size) {{{ \
|
||||
Operand::kTypeReg, size, { ((type) << 8) + index }, kInvalidValue, {{ kInvalidVar, 0 }} \
|
||||
}}}
|
||||
|
||||
#define REG_LIST_04(type, start, size) \
|
||||
REG(type, start + 0, size), \
|
||||
REG(type, start + 1, size), \
|
||||
REG(type, start + 2, size), \
|
||||
REG(type, start + 3, size)
|
||||
|
||||
#define REG_LIST_08(type, start, size) \
|
||||
REG_LIST_04(type, start + 0, size), \
|
||||
REG_LIST_04(type, start + 4, size)
|
||||
|
||||
#define REG_LIST_16(type, start, size) \
|
||||
REG_LIST_08(type, start + 0, size), \
|
||||
REG_LIST_08(type, start + 8, size)
|
||||
|
||||
#define REG_LIST_32(type, start, size) \
|
||||
REG_LIST_16(type, start + 0, size), \
|
||||
REG_LIST_16(type, start + 16, size)
|
||||
|
||||
const X86RegData x86RegData = {
|
||||
{ REG_LIST_16(kX86RegTypeGpd , 0, 4) },
|
||||
{ REG_LIST_16(kX86RegTypeGpq , 0, 8) },
|
||||
{ REG_LIST_16(kX86RegTypeGpbLo, 0, 1) },
|
||||
{ REG_LIST_04(kX86RegTypeGpbHi, 0, 1) },
|
||||
{ REG_LIST_16(kX86RegTypeGpw , 0, 2) },
|
||||
{ REG_LIST_32(kX86RegTypeXmm , 0, 16) },
|
||||
{ REG_LIST_32(kX86RegTypeYmm , 0, 32) },
|
||||
{ REG_LIST_32(kX86RegTypeZmm , 0, 64) },
|
||||
{ REG_LIST_08(kX86RegTypeK , 0, 8) },
|
||||
{ REG_LIST_08(kX86RegTypeFp , 0, 10) },
|
||||
{ REG_LIST_08(kX86RegTypeMm , 0, 8) },
|
||||
|
||||
{
|
||||
REG(kX86RegTypeSeg, 0, 2), // Default.
|
||||
REG(kX86RegTypeSeg, 1, 2), // ES.
|
||||
REG(kX86RegTypeSeg, 2, 2), // CS.
|
||||
REG(kX86RegTypeSeg, 3, 2), // SS.
|
||||
REG(kX86RegTypeSeg, 4, 2), // DS.
|
||||
REG(kX86RegTypeSeg, 5, 2), // FS.
|
||||
REG(kX86RegTypeSeg, 6, 2) // GS.
|
||||
},
|
||||
|
||||
REG(kInvalidReg, kInvalidReg, 0), // NoGp.
|
||||
REG(kX86RegTypeRip, 0, 0), // RIP.
|
||||
};
|
||||
|
||||
#undef REG_LIST_32
|
||||
#undef REG_LIST_16
|
||||
#undef REG_LIST_08
|
||||
#undef REG_LIST_04
|
||||
#undef REG
|
||||
|
||||
} // asmjit namespace
|
||||
|
||||
// [Api-End]
|
||||
#include "../apiend.h"
|
||||
|
||||
// [Guard]
|
||||
#endif // ASMJIT_BUILD_X86 || ASMJIT_BUILD_X64
|
||||
Reference in New Issue
Block a user