First draft of dynamic detours using Ayuto's DynamicHooks library

https://github.com/Ayuto/DynamicHooks
This commit is contained in:
Peace-Maker
2016-12-11 22:02:10 -07:00
parent fd8866a540
commit 2e52ab24b7
84 changed files with 57658 additions and 114 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+860
View File
@@ -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)
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
+551
View File
@@ -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)
+133
View File
@@ -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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+85
View File
@@ -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
File diff suppressed because it is too large Load Diff
+84
View File
@@ -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