727 lines
22 KiB
C++
727 lines
22 KiB
C++
// [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
|