Files
asmjit/src/asmjit/base/compiler.h
kobalicekp fa528f4741 Added gcc visibility push/pop to ApiBegin/ApiEnd.
ApiBegin/ApiEnd moved from asmjit/base to asmjit.
2014-04-01 21:16:31 +02:00

1983 lines
71 KiB
C++

// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.
// [Guard]
#ifndef _ASMJIT_BASE_COMPILER_H
#define _ASMJIT_BASE_COMPILER_H
// [Dependencies - AsmJit]
#include "../base/assembler.h"
#include "../base/codegen.h"
#include "../base/defs.h"
#include "../base/error.h"
#include "../base/func.h"
#include "../base/intutil.h"
#include "../base/podlist.h"
#include "../base/podvector.h"
#include "../base/runtime.h"
// [Api-Begin]
#include "../apibegin.h"
namespace asmjit {
// ============================================================================
// [Forward Declarations]
// ============================================================================
struct BaseCompiler;
struct VarAttr;
struct VarData;
struct BaseVarInst;
struct BaseVarState;
struct BaseNode;
struct EndNode;
struct InstNode;
struct JumpNode;
// ============================================================================
// [asmjit::kVarAttrFlags]
// ============================================================================
//! @brief Variable attribute flags.
ASMJIT_ENUM(kVarAttrFlags) {
//! @brief Variable is accessed through register on input.
kVarAttrInReg = 0x00000001,
//! @brief Variable is accessed through register on output.
kVarAttrOutReg = 0x00000002,
//! @brief Variable is accessed through register on input & output.
kVarAttrInOutReg = 0x00000003,
//! @brief Variable is accessed through memory on input.
kVarAttrInMem = 0x00000004,
//! @brief Variable is accessed through memory on output.
kVarAttrOutMem = 0x00000008,
//! @brief Variable is accessed through memory on input & output.
kVarAttrInOutMem = 0x0000000C,
//! @brief It can be decided whether it's better to alloc variable to register
//! or memory on the input.
kVarAttrInDecide = 0x00000010,
//! @brief It can be decided whether it's better to alloc variable to register
//! or memory on the output.
kVarAttrOutDecide = 0x00000020,
//! @brief It can be decided whether it's better to alloc variable to register
//! or memory on the input & output.
kVarAttrInOutDecide = 0x00000030,
//! @brief Variable is converted to other type/class on the input.
kVarAttrInConv = 0x00000040,
//! @brief Variable is converted from other type/class on the output.
kVarAttrOutConv = 0x00000080,
//! @brief Combination of @ref kVarAttrInConv and @ref kVarAttrOutConv.
kVarAttrInOutConv = 0x000000C0,
//! @brief Variable is a function call operand.
kVarAttrInCall = 0x00000100,
//! @brief Variable is a function argument passed in register.
kVarAttrInArg = 0x00000200,
//! @brief Variable is a function return value passed in register.
kVarAttrOutRet = 0x00000400,
//! @brief Variable should be unused at the end of the instruction/node.
kVarAttrUnuse = 0x00000800,
kVarAttrInAll = kVarAttrInReg | kVarAttrInMem | kVarAttrInDecide | kVarAttrInCall | kVarAttrInArg,
kVarAttrOutAll = kVarAttrOutReg | kVarAttrOutMem | kVarAttrOutDecide | kVarAttrOutRet,
//! @brief Variable is already allocated on the input.
kVarAttrAllocInDone = 0x00400000,
//! @brief Variable is already allocated on the output.
kVarAttrAllocOutDone = 0x00800000
};
// ============================================================================
// [asmjit::kVarHint]
// ============================================================================
//! @brief Variable hint (used by @ref BaseCompiler).
//!
//! @sa @ref BaseCompiler.
ASMJIT_ENUM(kVarHint) {
//! @brief Alloc variable.
kVarHintAlloc = 0,
//! @brief Spill variable.
kVarHintSpill = 1,
//! @brief Save variable if modified.
kVarHintSave = 2,
//! @brief Save variable if modified and mark it as unused.
kVarHintSaveAndUnuse = 3,
//! @brief Mark variable as unused.
kVarHintUnuse = 4
};
// ============================================================================
// [asmjit::kVarState]
// ============================================================================
//! @brief State of variable.
//!
//! @note State of variable is used only during make process and it's not
//! visible to the developer.
ASMJIT_ENUM(kVarState) {
//! @brief Variable is currently not used.
kVarStateUnused = 0,
//! @brief Variable is in register.
//!
//! Variable is currently allocated in register.
kVarStateReg = 1,
//! @brief Variable is in memory location or spilled.
//!
//! Variable was spilled from register to memory or variable is used for
//! memory only storage.
kVarStateMem = 2
};
// ============================================================================
// [asmjit::kNodeType]
// ============================================================================
//! @brief Type of node (see @ref BaseNode).
ASMJIT_ENUM(kNodeType) {
//! @brief Invalid node (internal, can't be used).
kNodeTypeNone = 0,
//! @brief Node is an .align directive, see @ref AlignNode.
kNodeTypeAlign,
//! @brief Node is an embedded data, see @ref EmbedNode.
kNodeTypeEmbed,
//! @brief Node is a comment, see @ref CommentNode.
kNodeTypeComment,
//! @brief Node is a variable hint (alloc, spill, use, unuse), see @ref HintNode.
kNodeTypeHint,
//! @brief Node is a label, see @ref TargetNode.
kNodeTypeTarget,
//! @brief Node is an instruction, see @ref InstNode.
kNodeTypeInst,
//! @brief Node is a function declaration, see @ref FuncNode.
kNodeTypeFunc,
//! @brief Node is an end of the function, see @ref EndNode.
kNodeTypeEnd,
//! @brief Node is a return, see @ref RetNode.
kNodeTypeRet,
//! @brief Node is a function call, see @ref CallNode.
kNodeTypeCall,
//! @brief Node is a function call argument moved on stack, see @ref SArgNode.
kNodeTypeSArg
};
// ============================================================================
// [asmjit::kNodeFlag]
// ============================================================================
ASMJIT_ENUM(kNodeFlag) {
//! @brief Whether the node was translated by @ref BaseContext.
kNodeFlagIsTranslated = 0x0001,
//! @Brief Whether the @ref InstNode is a jump.
kNodeFlagIsJmp = 0x0002,
//! @Brief Whether the @ref InstNode is a conditional jump.
kNodeFlagIsJcc = 0x0004,
//! @brief Whether the @ref InstNode is an unconditinal jump or conditional
//! jump that is likely to be taken.
kNodeFlagIsTaken = 0x0008,
//! @brief Whether the @ref Node will return from a function.
//!
//! This flag is used by both @ref EndNode and @ref RetNode.
kNodeFlagIsRet = 0x0010,
//! @brief Whether the instruction is special.
kNodeFlagIsSpecial = 0x0020,
//! @brief Whether the instruction is an FPU instruction.
kNodeFlagIsFp = 0x0040
};
// ============================================================================
// [asmjit::MemCell]
// ============================================================================
struct MemCell {
ASMJIT_NO_COPY(MemCell)
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get cell offset.
ASMJIT_INLINE int32_t getOffset() const { return _offset; }
//! @brief Set cell offset.
ASMJIT_INLINE void setOffset(int32_t offset) { _offset = offset; }
//! @brief Get cell size.
ASMJIT_INLINE uint32_t getSize() const { return _size; }
//! @brief Set cell size.
ASMJIT_INLINE void setSize(uint32_t size) { _size = size; }
//! @brief Get cell alignment.
ASMJIT_INLINE uint32_t getAlignment() const { return _alignment; }
//! @brief Set cell alignment.
ASMJIT_INLINE void setAlignment(uint32_t alignment) { _alignment = alignment; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Next active cell.
MemCell* _next;
//! @brief Offset, relative to base-offset.
int32_t _offset;
//! @brief Size.
uint32_t _size;
//! @brief Alignment.
uint32_t _alignment;
};
// ============================================================================
// [asmjit::VarBits]
// ============================================================================
//! @brief Bit-array used by variable-liveness analysis.
struct VarBits {
// --------------------------------------------------------------------------
// [Enums]
// --------------------------------------------------------------------------
enum {
kEntitySize = static_cast<int>(sizeof(uintptr_t)),
kEntityBits = kEntitySize * 8
};
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE uintptr_t getBit(uint32_t index) const {
return (data[index / kEntityBits] >> (index % kEntityBits)) & 1;
}
ASMJIT_INLINE void setBit(uint32_t index) {
data[index / kEntityBits] |= static_cast<uintptr_t>(1) << (index % kEntityBits);
}
ASMJIT_INLINE void delBit(uint32_t index) {
data[index / kEntityBits] &= ~(static_cast<uintptr_t>(1) << (index % kEntityBits));
}
// --------------------------------------------------------------------------
// [Ops]
// --------------------------------------------------------------------------
ASMJIT_INLINE bool copyBits(const VarBits* s0, uint32_t len) {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool addBits(const VarBits* s0, uint32_t len) {
return addBits(this, s0, len);
}
ASMJIT_INLINE bool addBits(const VarBits* s0, const VarBits* s1, uint32_t len) {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i] | s1->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool andBits(const VarBits* s1, uint32_t len) {
return andBits(this, s1, len);
}
ASMJIT_INLINE bool andBits(const VarBits* s0, const VarBits* s1, uint32_t len) {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i] & s1->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool delBits(const VarBits* s1, uint32_t len) {
return delBits(this, s1, len);
}
ASMJIT_INLINE bool delBits(const VarBits* s0, const VarBits* s1, uint32_t len) {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t t = s0->data[i] & ~s1->data[i];
data[i] = t;
r |= t;
}
return r != 0;
}
ASMJIT_INLINE bool _addBitsDelSource(VarBits* s1, uint32_t len) {
return _addBitsDelSource(this, s1, len);
}
ASMJIT_INLINE bool _addBitsDelSource(const VarBits* s0, VarBits* s1, uint32_t len) {
uintptr_t r = 0;
for (uint32_t i = 0; i < len; i++) {
uintptr_t a = s0->data[i];
uintptr_t b = s1->data[i];
this->data[i] = a | b;
b &= ~a;
s1->data[i] = b;
r |= b;
}
return r != 0;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
uintptr_t data[1];
};
// ============================================================================
// [asmjit::VarData]
// ============================================================================
//! @brief Base variable data.
struct VarData {
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get variable name.
ASMJIT_INLINE const char* getName() const { return _name; }
//! @brief Get variable id.
ASMJIT_INLINE uint32_t getId() const { return _id; }
//! @brief Get whether the variable has context id.
ASMJIT_INLINE bool hasContextId() const { return _contextId != kInvalidValue; }
//! @brief Get context variable id (used only by @ref Context).
ASMJIT_INLINE uint32_t getContextId() const { return _contextId; }
//! @brief Set context variable id (used only by @ref Context).
ASMJIT_INLINE void setContextId(uint32_t contextId) { _contextId = contextId; }
//! @brief Reset context variable id (used only by @ref Context).
ASMJIT_INLINE void resetContextId() { _contextId = kInvalidValue; }
//! @brief Get variable type.
ASMJIT_INLINE uint32_t getType() const { return _type; }
//! @brief Get variable class.
ASMJIT_INLINE uint32_t getClass() const { return _class; }
//! @brief Get variable flags.
ASMJIT_INLINE uint32_t getFlags() const { return _flags; }
//! @brief Get variable priority.
ASMJIT_INLINE uint32_t getPriority() const { return _priority; }
//! @brief Get variable state (only used by @ref Context).
ASMJIT_INLINE uint32_t getState() const { return _state; }
//! @brief Set variable state (only used by @ref Context).
ASMJIT_INLINE void setState(uint32_t state) { _state = static_cast<uint8_t>(state); }
//! @brief Get register index.
ASMJIT_INLINE uint32_t getRegIndex() const { return _regIndex; }
//! @brief Set register index.
ASMJIT_INLINE void setRegIndex(uint32_t regIndex) { _regIndex = static_cast<uint8_t>(regIndex); }
//! @brief Reset register index.
ASMJIT_INLINE void resetRegIndex() { _regIndex = static_cast<uint8_t>(kInvalidReg); }
//! @brief Get whether the VarData is only memory allocated on the stack.
ASMJIT_INLINE bool isStack() const { return static_cast<bool>(_isStack); }
//! @brief Get whether the variable is a function argument passed through memory.
ASMJIT_INLINE bool isMemArg() const { return static_cast<bool>(_isMemArg); }
//! @brief Get variable content can be calculated by a simple instruction.
ASMJIT_INLINE bool isCalculated() const { return static_cast<bool>(_isCalculated); }
//! @brief Get whether to save variable when it's unused (spill).
ASMJIT_INLINE bool saveOnUnuse() const { return static_cast<bool>(_saveOnUnuse); }
//! @brief Get whether the variable was changed.
ASMJIT_INLINE bool isModified() const { return static_cast<bool>(_modified); }
//! @brief Set whether the variable was changed.
ASMJIT_INLINE void setModified(bool modified) { _modified = modified; }
//! @brief Get variable alignment.
ASMJIT_INLINE uint32_t getAlignment() const { return _alignment; }
//! @brief Get variable size.
ASMJIT_INLINE uint32_t getSize() const { return _size; }
//! @brief Get home memory offset.
ASMJIT_INLINE int32_t getMemOffset() const { return _memOffset; }
//! @brief Set home memory offset.
ASMJIT_INLINE void setMemOffset(int32_t offset) { _memOffset = offset; }
//! @brief Get home memory cell.
ASMJIT_INLINE MemCell* getMemCell() const { return _memCell; }
//! @brief Set home memory cell.
ASMJIT_INLINE void setMemCell(MemCell* cell) { _memCell = cell; }
// --------------------------------------------------------------------------
// [Accessors - Temporary Usage]
// --------------------------------------------------------------------------
//! @brief Get temporary VarAttr.
ASMJIT_INLINE VarAttr* getVa() const { return _va; }
//! @brief Set temporary VarAttr.
ASMJIT_INLINE void setVa(VarAttr* va) { _va = va; }
//! @brief Reset temporary VarAttr.
ASMJIT_INLINE void resetVa() { _va = NULL; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Variable name.
const char* _name;
//! @brief Variable id.
uint32_t _id;
//! @brief Context variable id (used by @ref Context only, initially set to
//! @c kInvalidValue).
uint32_t _contextId;
//! @brief Variable type.
uint8_t _type;
//! @brief Variable class.
uint8_t _class;
//! @brief Variable flags.
uint8_t _flags;
//! @brief Variable priority.
uint8_t _priority;
//! @brief Variable state (connected with actual @ref BaseVarState).
uint8_t _state;
//! @brief Actual register index (only used by @ref Context), during translate.
uint8_t _regIndex;
//! @brief Whether the variable is only used as memory allocated on the stack.
uint8_t _isStack : 1;
//! @brief Whether the variable is a function argument passed through memory.
uint8_t _isMemArg : 1;
//! @brief Whether variable content can be calculated by a simple instruction.
//!
//! This is used mainly by MMX and SSE2 code. This flag indicates that
//! register allocator should never reserve memory for this variable, because
//! the content can be generated by a single instruction (for example PXOR).
uint8_t _isCalculated : 1;
//! @brief Save on unuse (at end of the variable scope).
uint8_t _saveOnUnuse : 1;
//! @brief Whether variable was changed (connected with actual @ref BaseVarState).
uint8_t _modified : 1;
//! @internal
uint8_t _reserved0 : 3;
//! @brief Varialbe natural alignment.
uint8_t _alignment;
//! @brief Variable size.
uint32_t _size;
//! @brief Home memory offset.
int32_t _memOffset;
//! @brief Home memory cell, used by @c Context (initially NULL).
MemCell* _memCell;
//! @brief Register read access statistics.
uint32_t rReadCount;
//! @brief Register write access statistics.
uint32_t rWriteCount;
//! @brief Memory read statistics.
uint32_t mReadCount;
//! @brief Memory write statistics.
uint32_t mWriteCount;
// --------------------------------------------------------------------------
// [Members - Temporary Usage]
// --------------------------------------------------------------------------
// These variables are only used during register allocation. They are
// initialized by init() phase and cleared by cleanup() phase.
union {
//! @brief Temporary link to VarAttr* used by the @ref Context used in
//! various phases, but always set back to NULL when finished.
//!
//! This temporary data is designed to be used by algorithms that need to
//! store some data into variables themselves during compilation. But it's
//! expected that after variable is compiled & translated the data is set
//! back to zero/null. Initial value is NULL.
VarAttr* _va;
//! @internal
uintptr_t _vaUInt;
};
};
// ============================================================================
// [asmjit::VarAttr]
// ============================================================================
struct VarAttr {
// --------------------------------------------------------------------------
// [Setup]
// --------------------------------------------------------------------------
ASMJIT_INLINE void setup(VarData* vd, uint32_t flags = 0, uint32_t inRegs = 0, uint32_t allocableRegs = 0) {
_vd = vd;
_flags = flags;
_varCount = 0;
_inRegIndex = kInvalidReg;
_outRegIndex = kInvalidReg;
_reserved = 0;
_inRegs = inRegs;
_allocableRegs = allocableRegs;
}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get VarData.
ASMJIT_INLINE VarData* getVd() const { return _vd; }
//! @brief Set VarData.
ASMJIT_INLINE void setVd(VarData* vd) { _vd = vd; }
//! @brief Get flags.
ASMJIT_INLINE uint32_t getFlags() const { return _flags; }
//! @brief Set flags.
ASMJIT_INLINE void setFlags(uint32_t flags) { _flags = flags; }
//! @brief Get whether @a flag is on.
ASMJIT_INLINE bool hasFlag(uint32_t flag) { return (_flags & flag) != 0; }
//! @brief Add @a flags.
ASMJIT_INLINE void addFlags(uint32_t flags) { _flags |= flags; }
//! @brief Mask @a flags.
ASMJIT_INLINE void andFlags(uint32_t flags) { _flags &= flags; }
//! @brief Clear @a flags.
ASMJIT_INLINE void delFlags(uint32_t flags) { _flags &= ~flags; }
//! @brief Get how many times the variable is used by the instruction/node.
ASMJIT_INLINE uint32_t getVarCount() const { return _varCount; }
//! @brief Set how many times the variable is used by the instruction/node.
ASMJIT_INLINE void setVarCount(uint32_t count) { _varCount = static_cast<uint8_t>(count); }
//! @brief Add how many times the variable is used by the instruction/node.
ASMJIT_INLINE void addVarCount(uint32_t count = 1) { _varCount += static_cast<uint8_t>(count); }
//! @brief Get whether the variable has to be allocated in a specific input register.
ASMJIT_INLINE uint32_t hasInRegIndex() const { return _inRegIndex != kInvalidReg; }
//! @brief Get the input register index or @ref kInvalidReg.
ASMJIT_INLINE uint32_t getInRegIndex() const { return _inRegIndex; }
//! @brief Set the input register index.
ASMJIT_INLINE void setInRegIndex(uint32_t index) { _inRegIndex = static_cast<uint8_t>(index); }
//! @brief Reset the input register index.
ASMJIT_INLINE void resetInRegIndex() { _inRegIndex = kInvalidReg; }
//! @brief Get whether the variable has to be allocated in a specific output register.
ASMJIT_INLINE uint32_t hasOutRegIndex() const { return _outRegIndex != kInvalidReg; }
//! @brief Get the output register index or @ref kInvalidReg.
ASMJIT_INLINE uint32_t getOutRegIndex() const { return _outRegIndex; }
//! @brief Set the output register index.
ASMJIT_INLINE void setOutRegIndex(uint32_t index) { _outRegIndex = static_cast<uint8_t>(index); }
//! @brief Reset the output register index.
ASMJIT_INLINE void resetOutRegIndex() { _outRegIndex = kInvalidReg; }
//! @brief Get whether the mandatory input registers are in used.
ASMJIT_INLINE bool hasInRegs() const { return _inRegs != 0; }
//! @brief Get mandatory input registers (mask).
ASMJIT_INLINE uint32_t getInRegs() const { return _inRegs; }
//! @brief Set mandatory input registers (mask).
ASMJIT_INLINE void setInRegs(uint32_t mask) { _inRegs = mask; }
//! @brief Add mandatory input registers (mask).
ASMJIT_INLINE void addInRegs(uint32_t mask) { _inRegs |= mask; }
//! @brief And mandatory input registers (mask).
ASMJIT_INLINE void andInRegs(uint32_t mask) { _inRegs &= mask; }
//! @brief Clear mandatory input registers (mask).
ASMJIT_INLINE void delInRegs(uint32_t mask) { _inRegs &= ~mask; }
//! @brief Get allocable input registers (mask).
ASMJIT_INLINE uint32_t getAllocableRegs() const { return _allocableRegs; }
//! @brief Set allocable input registers (mask).
ASMJIT_INLINE void setAllocableRegs(uint32_t mask) { _allocableRegs = mask; }
//! @brief Add allocable input registers (mask).
ASMJIT_INLINE void addAllocableRegs(uint32_t mask) { _allocableRegs |= mask; }
//! @brief And allocable input registers (mask).
ASMJIT_INLINE void andAllocableRegs(uint32_t mask) { _allocableRegs &= mask; }
//! @brief Clear allocable input registers (mask).
ASMJIT_INLINE void delAllocableRegs(uint32_t mask) { _allocableRegs &= ~mask; }
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
ASMJIT_INLINE VarAttr& operator=(const VarAttr& other) {
::memcpy(this, &other, sizeof(VarAttr));
return *this;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
VarData* _vd;
//! @brief Flags.
uint32_t _flags;
union {
struct {
//! @brief How many times the variable is used by the instruction/node.
uint8_t _varCount;
//! @brief Input register index or @ref kInvalidReg if it's not given.
//!
//! Even if the input register index is not given (i.e. it may by any
//! register), register allocator should assign an index that will be
//! used to persist a variable into this specific index. It's helpful
//! in situations where one variable has to be allocated in multiple
//! registers to determine the register which will be persistent.
uint8_t _inRegIndex;
//! @brief Output register index or @ref kInvalidReg if it's not given.
//!
//! Typically @ref kInvalidReg if variable is only used on input.
uint8_t _outRegIndex;
//! @internal
uint8_t _reserved;
};
//! @internal
//!
//! @brief Packed data #0.
uint32_t _packed;
};
//! @brief Mandatory input registers.
//!
//! Mandatory input registers are required by the instruction even if
//! there are duplicates. This schema allows us to allocate one variable
//! in one or more register when needed. Required mostly by instructions
//! that have implicit register operands (imul, cpuid, ...) and function
//! call.
uint32_t _inRegs;
//! @brief Allocable input registers.
//!
//! Optional input registers is a mask of all allocable registers for a given
//! variable where we have to pick one of them. This mask is usually not used
//! when _inRegs is set. If both masks are used then the register
//! allocator tries first to find an intersection between these and allocates
//! an extra slot if not found.
uint32_t _allocableRegs;
};
// ============================================================================
// [asmjit::BaseVarInst]
// ============================================================================
//! @brief Variable allocation instructions.
struct BaseVarInst {};
// ============================================================================
// [asmjit::BaseVarState]
// ============================================================================
//! @brief Variable(s) state.
struct BaseVarState {};
// ============================================================================
// [asmjit::BaseNode]
// ============================================================================
//! @brief Base node.
//!
//! @ref Every node represents an abstract instruction, directive, label, or
//! macro-instruction generated by compiler.
struct BaseNode {
ASMJIT_NO_COPY(BaseNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create new @ref BaseNode.
//!
//! @note Always use compiler to create nodes.
ASMJIT_INLINE BaseNode(BaseCompiler* compiler, uint32_t type); // Defined-Later.
//! @brief Destroy @ref BaseNode.
ASMJIT_INLINE ~BaseNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get previous node in the compiler stream.
ASMJIT_INLINE BaseNode* getPrev() const { return _prev; }
//! @brief Get next node in the compiler stream.
ASMJIT_INLINE BaseNode* getNext() const { return _next; }
//! @brief Get comment string.
ASMJIT_INLINE const char* getComment() const { return _comment; }
//! @brief Set comment string to @a str.
ASMJIT_INLINE void setComment(const char* comment) { _comment = comment; }
//! @brief Get type of node, see @ref kNodeType.
ASMJIT_INLINE uint32_t getType() const { return _type; }
//! @brief Get node flags.
ASMJIT_INLINE uint32_t getFlags() const { return _flags; }
//! @brief Set node flags to @a flags.
ASMJIT_INLINE void setFlags(uint32_t flags) { _flags = static_cast<uint16_t>(flags); }
//! @brief Get whether the instruction has flag @a flag.
ASMJIT_INLINE bool hasFlag(uint32_t flag) const { return (static_cast<uint32_t>(_flags) & flag) != 0; }
//! @brief Add instruction @a flags.
ASMJIT_INLINE void addFlags(uint32_t flags) { _flags |= static_cast<uint16_t>(flags); }
//! @brief Clear instruction @a flags.
ASMJIT_INLINE void delFlags(uint32_t flags) { _flags &= static_cast<uint16_t>(~flags); }
//! @brief Get whether the node has beed fetched.
ASMJIT_INLINE bool isFetched() const { return _flowId != 0; }
//! @brief Get whether the node has been translated.
ASMJIT_INLINE bool isTranslated() const { return hasFlag(kNodeFlagIsTranslated); }
//! @brief Whether the instruction is an unconditional jump.
ASMJIT_INLINE bool isJmp() const { return hasFlag(kNodeFlagIsJmp); }
//! @brief Whether the instruction is a conditional jump.
ASMJIT_INLINE bool isJcc() const { return hasFlag(kNodeFlagIsJcc); }
//! @brief Whether the instruction is an unconditional or conditional jump.
ASMJIT_INLINE bool isJmpOrJcc() const { return hasFlag(kNodeFlagIsJmp | kNodeFlagIsJcc); }
//! @brief Whether the instruction is a return.
ASMJIT_INLINE bool isRet() const { return hasFlag(kNodeFlagIsRet); }
//! @brief Get whether the instruction is special.
ASMJIT_INLINE bool isSpecial() const { return hasFlag(kNodeFlagIsSpecial); }
//! @brief Get whether the instruction accesses FPU.
ASMJIT_INLINE bool isFp() const { return hasFlag(kNodeFlagIsFp); }
//! @brief Get flow index.
ASMJIT_INLINE uint32_t getFlowId() const { return _flowId; }
//! @brief Set flow index.
ASMJIT_INLINE void setFlowId(uint32_t flowId) { _flowId = flowId; }
//! @brief Get whether node contains variable allocation instructions.
ASMJIT_INLINE bool hasVarInst() const { return _varInst != NULL; }
//! @brief Get variable allocation instructions.
ASMJIT_INLINE BaseVarInst* getVarInst() const { return _varInst; }
//! @brief Get variable allocation instructions <T>.
template<typename T>
ASMJIT_INLINE T* getVarInst() const { return static_cast<T*>(_varInst); }
//! @brief Set variable allocation instructions.
ASMJIT_INLINE void setVarInst(BaseVarInst* vi) { _varInst = vi; }
//! @brief Get node state.
ASMJIT_INLINE BaseVarState* getState() const { return _state; }
//! @brief Get node state <T>.
template<typename T>
ASMJIT_INLINE T* getState() const { return static_cast<BaseVarState*>(_state); }
//! @brief Set node state.
ASMJIT_INLINE void setState(BaseVarState* state) { _state = state; }
//! @brief Get whether the node has variable liveness bits.
ASMJIT_INLINE bool hasLiveness() const { return _liveness != NULL; }
//! @brief Get variable liveness bits.
ASMJIT_INLINE VarBits* getLiveness() const { return _liveness; }
//! @brief Set variable liveness bits.
ASMJIT_INLINE void setLiveness(VarBits* liveness) { _liveness = liveness; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Previous node.
BaseNode* _prev;
//! @brief Next node.
BaseNode* _next;
//! @brief Node type, see @ref kNodeType.
uint8_t _type;
//! @brief Operands count (if the node has operands, otherwise zero).
uint8_t _opCount;
//! @brief Node flags, different meaning for every node type.
uint16_t _flags;
//! @brief Flow index.
uint32_t _flowId;
//! @brief Inline comment string, initially set to NULL.
const char* _comment;
//! @brief Variable allocation instructions (initially NULL, filled by prepare
//! phase).
BaseVarInst* _varInst;
//! @brief Variable liveness bits (initially NULL, filled by analysis phase).
VarBits* _liveness;
//! @brief Saved state.
//!
//! Initially NULL, not all nodes have saved state, only branch/flow control
//! nodes.
BaseVarState* _state;
};
// ============================================================================
// [asmjit::AlignNode]
// ============================================================================
//! @brief Align node.
struct AlignNode : public BaseNode {
ASMJIT_NO_COPY(AlignNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @ref AlignNode instance.
ASMJIT_INLINE AlignNode(BaseCompiler* compiler, uint32_t size) : BaseNode(compiler, kNodeTypeAlign) {
_size = size;
}
//! @brief Destroy the @ref AlignNode instance.
ASMJIT_INLINE ~AlignNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get align size in bytes.
ASMJIT_INLINE uint32_t getSize() const { return _size; }
//! @brief Set align size in bytes to @a size.
ASMJIT_INLINE void setSize(uint32_t size) { _size = size; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Size of the alignment.
uint32_t _size;
};
// ============================================================================
// [asmjit::EmbedNode]
// ============================================================================
//! @brief Embed node.
//!
//! Embed node is used to embed data into final assembler stream. The data is
//! considered to be RAW; No analysis is performed on RAW data.
struct EmbedNode : public BaseNode {
ASMJIT_NO_COPY(EmbedNode)
// --------------------------------------------------------------------------
// [Enums]
// --------------------------------------------------------------------------
enum { kInlineBufferSize = 8 };
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @ref EmbedNode instance.
ASMJIT_INLINE EmbedNode(BaseCompiler* compiler, void* data, uint32_t size) : BaseNode(compiler, kNodeTypeEmbed) {
_size = size;
if (size <= kInlineBufferSize)
::memcpy(_data.buf, data, size);
else
_data.ptr = static_cast<uint8_t*>(data);
}
//! @brief Destroy the @ref EmbedNode instance.
ASMJIT_INLINE ~EmbedNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get pointer to data.
uint8_t* getData() { return getSize() <= kInlineBufferSize ? const_cast<uint8_t*>(_data.buf) : _data.ptr; }
//! @brief Get size of data.
uint32_t getSize() const { return _size; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Size of the embedded data.
uint32_t _size;
union {
//! @brief data buffer.
uint8_t buf[kInlineBufferSize];
//! @brief Data buffer.
uint8_t* ptr;
} _data;
};
// ============================================================================
// [asmjit::CommentNode]
// ============================================================================
//! @brief Comment node.
//!
//! Comments allows to comment your assembler stream for better debugging
//! and visualization. Comments are usually ignored in release builds unless
//! the logger is present.
struct CommentNode : public BaseNode {
ASMJIT_NO_COPY(CommentNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @ref CommentNode instance.
ASMJIT_INLINE CommentNode(BaseCompiler* compiler, const char* comment) : BaseNode(compiler, kNodeTypeComment) {
_comment = comment;
}
//! @brief Destroy the @ref CommentNode instance.
ASMJIT_INLINE ~CommentNode() {}
};
// ============================================================================
// [asmjit::HintNode]
// ============================================================================
//! @brief Hint node.
struct HintNode : public BaseNode {
ASMJIT_NO_COPY(HintNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @ref HintNode instance.
ASMJIT_INLINE HintNode(BaseCompiler* compiler, VarData* vd, uint32_t hint, uint32_t value) : BaseNode(compiler, kNodeTypeHint) {
_vd = vd;
_hint = hint;
_value = value;
}
//! @brief Destroy the @ref HintNode instance.
ASMJIT_INLINE ~HintNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get variable.
ASMJIT_INLINE VarData* getVd() const { return _vd; }
//! @brief Get hint it (see @ref kVarHint).
ASMJIT_INLINE uint32_t getHint() const{ return _hint; }
//! @brief Set hint it (see @ref kVarHint).
ASMJIT_INLINE void setHint(uint32_t hint) { _hint = hint; }
//! @brief Get hint value.
ASMJIT_INLINE uint32_t getValue() const { return _value; }
//! @brief Set hint value.
ASMJIT_INLINE void setValue(uint32_t value) { _value = value; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Variable.
VarData* _vd;
//! @brief Hint id.
uint32_t _hint;
//! @brief Value.
uint32_t _value;
};
// ============================================================================
// [asmjit::TargetNode]
// ============================================================================
//! @brief label node.
struct TargetNode : public BaseNode {
ASMJIT_NO_COPY(TargetNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @ref TargetNode instance.
ASMJIT_INLINE TargetNode(BaseCompiler* compiler, uint32_t labelId) : BaseNode(compiler, kNodeTypeTarget) {
_id = labelId;
_numRefs = 0;
_from = NULL;
}
//! @brief Destroy the @ref TargetNode instance.
ASMJIT_INLINE ~TargetNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get target label.
ASMJIT_INLINE Label getLabel() const { return Label(_id); }
//! @brief Get target label id.
ASMJIT_INLINE uint32_t getLabelId() const { return _id; }
//! @brief Get first jmp instruction.
ASMJIT_INLINE JumpNode* getFrom() const { return _from; }
//! @brief Get whether the node has assigned state.
ASMJIT_INLINE bool hasState() const { return _state != NULL; }
//! @brief Get state for this target.
ASMJIT_INLINE BaseVarState* getState() const { return _state; }
//! @brief Set state for this target.
ASMJIT_INLINE void setState(BaseVarState* state) { _state = state; }
//! @brief Get number of jumps to this target.
ASMJIT_INLINE uint32_t getNumRefs() const { return _numRefs; }
//! @brief Set number of jumps to this target.
ASMJIT_INLINE void setNumRefs(uint32_t i) { _numRefs = i; }
//! @brief Add number of jumps to this target.
ASMJIT_INLINE void addNumRefs(uint32_t i = 1) { _numRefs += i; }
//! @brief Subtract number of jumps to this target.
ASMJIT_INLINE void subNumRefs(uint32_t i = 1) { _numRefs -= i; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Label id.
uint32_t _id;
//! @brief Count of jumps here.
uint32_t _numRefs;
//! @brief First jump instruction that points to this target (label).
JumpNode* _from;
};
// ============================================================================
// [asmjit::InstNode]
// ============================================================================
//! @brief Instruction node.
struct InstNode : public BaseNode {
ASMJIT_NO_COPY(InstNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @ref InstNode instance.
ASMJIT_INLINE InstNode(BaseCompiler* compiler, uint32_t code, uint32_t options, Operand* opList, uint32_t opCount) : BaseNode(compiler, kNodeTypeInst) {
_code = static_cast<uint16_t>(code);
_options = static_cast<uint8_t>(options);
_opCount = static_cast<uint8_t>(opCount);
_opList = opList;
_updateMemOp();
}
//! @brief Destroy the @ref InstNode instance.
ASMJIT_INLINE ~InstNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get instruction code, see @c kInstCode.
ASMJIT_INLINE uint32_t getCode() const
{ return _code; }
//! @brief Set instruction code to @a code.
//!
//! Please do not modify instruction code if you are not know what you are
//! doing. Incorrect instruction code or operands can raise assertion() at
//! runtime.
ASMJIT_INLINE void setCode(uint32_t code)
{ _code = static_cast<uint16_t>(code); }
//! @brief Whether the instruction is an unconditional jump or whether the
//! instruction is a conditional jump which is likely to be taken.
ASMJIT_INLINE bool isTaken() const { return hasFlag(kNodeFlagIsTaken); }
//! @brief Get emit options.
ASMJIT_INLINE uint32_t getOptions() const { return _options; }
//! @brief Set emit options.
ASMJIT_INLINE void setOptions(uint32_t options) { _options = static_cast<uint8_t>(options); }
//! @brief Add emit options.
ASMJIT_INLINE void addOptions(uint32_t options) { _options |= static_cast<uint8_t>(options); }
//! @brief Mask emit options.
ASMJIT_INLINE void andOptions(uint32_t options) { _options &= static_cast<uint8_t>(options); }
//! @brief Clear emit options.
ASMJIT_INLINE void delOptions(uint32_t options) { _options &= static_cast<uint8_t>(~options); }
//! @brief Get operands list.
ASMJIT_INLINE Operand* getOpList() { return _opList; }
//! @overload
ASMJIT_INLINE const Operand* getOpList() const { return _opList; }
//! @brief Get operands count.
ASMJIT_INLINE uint32_t getOpCount() const { return _opCount; }
//! @brief Get whether the instruction contains a memory operand.
ASMJIT_INLINE bool hasMemOp() const { return _memOpIndex != 0xFF; }
//! @brief Set memory operand index (in opList), 0xFF means that instruction
//! doesn't have a memory operand.
ASMJIT_INLINE void setMemOpIndex(uint32_t index) { _memOpIndex = static_cast<uint8_t>(index); }
//! @brief Reset memory operand index, setting it to 0xFF.
ASMJIT_INLINE void resetMemOpIndex() { _memOpIndex = 0xFF; }
//! @brief Get memory operand.
//!
//! @note Can only be called if the instruction has such operand, see @ref
//! hasMemOp().
ASMJIT_INLINE BaseMem* getMemOp() const {
ASMJIT_ASSERT(hasMemOp());
return static_cast<BaseMem*>(&_opList[_memOpIndex]);
}
//! @overload
template<typename T>
ASMJIT_INLINE T* getMemOp() const {
ASMJIT_ASSERT(hasMemOp());
return static_cast<T*>(&_opList[_memOpIndex]);
}
// --------------------------------------------------------------------------
// [Utils]
// --------------------------------------------------------------------------
ASMJIT_INLINE void _updateMemOp() {
Operand* opList = getOpList();
uint32_t opCount = getOpCount();
uint32_t i;
for (i = 0; i < opCount; i++)
if (opList[i].isMem())
goto _Update;
i = 0xFF;
_Update:
setMemOpIndex(i);
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Instruction code, see @c kInstCode.
uint16_t _code;
//! @brief Instruction options, see @c kInstOptions.
uint8_t _options;
//! @internal
uint8_t _memOpIndex;
//! @brief Operands list.
Operand* _opList;
};
// ============================================================================
// [asmjit::JumpNode]
// ============================================================================
//! @brief Jump node.
struct JumpNode : public InstNode {
ASMJIT_NO_COPY(JumpNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
ASMJIT_INLINE JumpNode(BaseCompiler* compiler, uint32_t code, uint32_t options, Operand* opList, uint32_t opCount) :
InstNode(compiler, code, options, opList, opCount) {}
ASMJIT_INLINE ~JumpNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
ASMJIT_INLINE TargetNode* getTarget() const { return _target; }
ASMJIT_INLINE JumpNode* getJumpNext() const { return _jumpNext; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Target node.
TargetNode* _target;
//! @brief Next jump to the same target in a single linked-list.
JumpNode *_jumpNext;
};
// ============================================================================
// [asmjit::FuncNode]
// ============================================================================
//! @brief Function declaration node.
//!
//! Functions are base blocks for generating assembler output. Each generated
//! assembler stream needs standard entry and leave sequences which are compatible
//! with the operating system ABI.
//!
//! @ref FuncNode can be used to generate function prolog and epilog which are
//! compatible with a given function calling convention and to allocate and
//! manage variables that can be allocated/spilled during compilation phase.
struct FuncNode : public BaseNode {
ASMJIT_NO_COPY(FuncNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @c FuncNode instance.
//!
//! @note Always use @ref asmjit::BaseCompiler::addFunc() to create a
//! @ref FuncNode instance.
ASMJIT_INLINE FuncNode(BaseCompiler* compiler) :
BaseNode(compiler, kNodeTypeFunc),
_entryNode(NULL),
_exitNode(NULL),
_decl(NULL),
_end(NULL),
_argList(NULL),
_funcHints(IntUtil::mask(kFuncHintNaked)),
_funcFlags(0),
_expectedStackAlignment(0),
_requiredStackAlignment(0),
_redZoneSize(0),
_spillZoneSize(0),
_argStackSize(0),
_memStackSize(0),
_callStackSize(0) {}
//! @brief Destroy the @c FuncNode instance.
ASMJIT_INLINE ~FuncNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get function entry @ref TargetNode.
ASMJIT_INLINE TargetNode* getEntryNode() const { return _entryNode; }
//! @brief Get function exit @ref TargetNode.
ASMJIT_INLINE TargetNode* getExitNode() const { return _exitNode; }
//! @brief Get function entry label.
ASMJIT_INLINE Label getEntryLabel() const { return _entryNode->getLabel(); }
//! @brief Get function exit label.
ASMJIT_INLINE Label getExitLabel() const { return _exitNode->getLabel(); }
//! @brief Get function @ref EndNode.
ASMJIT_INLINE EndNode* getEnd() const { return _end; }
//! @brief Get function declaration.
ASMJIT_INLINE FuncDecl* getDecl() const { return _decl; }
//! @brief Get arguments list.
ASMJIT_INLINE VarData** getArgList() const { return _argList; }
//! @brief Get arguments count.
ASMJIT_INLINE uint32_t getArgCount() const { return _decl->getArgCount(); }
//! @brief Get argument at @a i.
ASMJIT_INLINE VarData* getArg(uint32_t i) const {
ASMJIT_ASSERT(i < getArgCount());
return _argList[i];
}
//! @brief Set argument at @a i.
ASMJIT_INLINE void setArg(uint32_t i, VarData* vd) {
ASMJIT_ASSERT(i < getArgCount());
_argList[i] = vd;
}
//! @brief Reset argument at @a i.
ASMJIT_INLINE void resetArg(uint32_t i) {
ASMJIT_ASSERT(i < getArgCount());
_argList[i] = NULL;
}
//! @brief Get function hints.
ASMJIT_INLINE uint32_t getFuncHints() const { return _funcHints; }
//! @brief Get function flags.
ASMJIT_INLINE uint32_t getFuncFlags() const { return _funcFlags; }
//! @brief Get whether the _funcFlags has @a flag
ASMJIT_INLINE bool hasFuncFlag(uint32_t flag) const { return (_funcFlags & flag) != 0; }
//! @brief Set function @a flag.
ASMJIT_INLINE void addFuncFlags(uint32_t flags) { _funcFlags |= flags; }
//! @brief Clear function @a flag.
ASMJIT_INLINE void clearFuncFlags(uint32_t flags) { _funcFlags &= ~flags; }
//! @brief Get whether the function is naked.
ASMJIT_INLINE bool isNaked() const { return hasFuncFlag(kFuncFlagIsNaked); }
//! @brief Get whether the function is also a caller.
ASMJIT_INLINE bool isCaller() const { return hasFuncFlag(kFuncFlagIsCaller); }
//! @brief Get whether the required stack alignment is lower than expected one,
//! thus it has to be aligned manually.
ASMJIT_INLINE bool isStackMisaligned() const { return hasFuncFlag(kFuncFlagIsStackMisaligned); }
//! @brief Get whether the stack pointer is adjusted inside function prolog/epilog.
ASMJIT_INLINE bool isStackAdjusted() const { return hasFuncFlag(kFuncFlagIsStackAdjusted); }
//! @brief Get whether the function is finished.
ASMJIT_INLINE bool isFinished() const { return hasFuncFlag(kFuncFlagIsFinished); }
//! @brief Get expected stack alignment.
ASMJIT_INLINE uint32_t getExpectedStackAlignment() const { return _expectedStackAlignment; }
//! @brief Set expected stack alignment.
ASMJIT_INLINE void setExpectedStackAlignment(uint32_t alignment) { _expectedStackAlignment = alignment; }
//! @brief Get required stack alignment.
ASMJIT_INLINE uint32_t getRequiredStackAlignment() const { return _requiredStackAlignment; }
//! @brief Set required stack alignment.
ASMJIT_INLINE void setRequiredStackAlignment(uint32_t alignment) { _requiredStackAlignment = alignment; }
//! @brief Update required stack alignment so it's not lower than expected
//! stack alignment.
ASMJIT_INLINE void updateRequiredStackAlignment() {
if (_requiredStackAlignment <= _expectedStackAlignment) {
_requiredStackAlignment = _expectedStackAlignment;
clearFuncFlags(kFuncFlagIsStackMisaligned);
}
else {
addFuncFlags(kFuncFlagIsStackMisaligned);
}
}
//! @brief Set stack "Red Zone" size.
ASMJIT_INLINE uint32_t getRedZoneSize() const { return _redZoneSize; }
//! @brief Get stack "Red Zone" size.
ASMJIT_INLINE void setRedZoneSize(uint32_t s) { _redZoneSize = static_cast<uint16_t>(s); }
//! @brief Set stack "Spill Zone" size.
ASMJIT_INLINE uint32_t getSpillZoneSize() const { return _spillZoneSize; }
//! @brief Get stack "Spill Zone" size.
ASMJIT_INLINE void setSpillZoneSize(uint32_t s) { _spillZoneSize = static_cast<uint16_t>(s); }
//! @brief Get stack size used by function arguments.
ASMJIT_INLINE uint32_t getArgStackSize() const { return _argStackSize; }
//! @brief Get stack size used by variables and memory allocated on the stack.
ASMJIT_INLINE uint32_t getMemStackSize() const { return _memStackSize; }
//! @brief Get stack size used by function calls.
ASMJIT_INLINE uint32_t getCallStackSize() const { return _callStackSize; }
//! @brief Merge stack size used by function call with @a s.
ASMJIT_INLINE void mergeCallStackSize(uint32_t s) { if (_callStackSize < s) _callStackSize = s; }
// --------------------------------------------------------------------------
// [Hints]
// --------------------------------------------------------------------------
//! @brief Set function hint.
ASMJIT_INLINE void setHint(uint32_t hint, uint32_t value) {
ASMJIT_ASSERT(hint <= 31);
ASMJIT_ASSERT(value <= 1);
_funcHints &= ~(1 << hint);
_funcHints |= (value << hint);
}
//! @brief Get function hint.
ASMJIT_INLINE uint32_t getHint(uint32_t hint) const {
ASMJIT_ASSERT(hint <= 31);
return (_funcHints >> hint) & 0x1;
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Function entry.
TargetNode* _entryNode;
//! @brief Function exit.
TargetNode* _exitNode;
//! @brief Function declaration.
FuncDecl* _decl;
//! @brief Function end.
EndNode* _end;
//! @brief Arguments list as @ref VarData.
VarData** _argList;
//! @brief Function hints;
uint32_t _funcHints;
//! @brief Function flags.
uint32_t _funcFlags;
//! @brief Expected stack alignment (we depend on this value).
//!
//! @note It can be global alignment given by the OS or described by an
//! target platform ABI.
uint32_t _expectedStackAlignment;
//! @brief Required stack alignment (usually for multimedia instructions).
uint32_t _requiredStackAlignment;
//! @brief The "Red Zone" suze - count of bytes which might be accessed
//! without adjusting the stack pointer.
uint16_t _redZoneSize;
//! @brief Spill zone size (zone used by WIN64ABI).
uint16_t _spillZoneSize;
//! @brief Stack size needed for function arguments.
uint32_t _argStackSize;
//! @brief Stack size needed for all variables and memory allocated on
//! the stack.
uint32_t _memStackSize;
//! @brief Stack size needed to call other functions.
uint32_t _callStackSize;
};
// ============================================================================
// [asmjit::EndNode]
// ============================================================================
//! @brief End of function/block node.
struct EndNode : public BaseNode {
ASMJIT_NO_COPY(EndNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @ref EndNode instance.
ASMJIT_INLINE EndNode(BaseCompiler* compiler) : BaseNode(compiler, kNodeTypeEnd) {
_flags |= kNodeFlagIsRet;
}
//! @brief Destroy the @ref EndNode instance.
ASMJIT_INLINE ~EndNode() {}
};
// ============================================================================
// [asmjit::RetNode]
// ============================================================================
//! @brief Function return node.
struct RetNode : public BaseNode {
ASMJIT_NO_COPY(RetNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @ref RetNode instance.
ASMJIT_INLINE RetNode(BaseCompiler* compiler, const Operand& o0, const Operand& o1) : BaseNode(compiler, kNodeTypeRet) {
_flags |= kNodeFlagIsRet;
_ret[0] = o0;
_ret[1] = o1;
}
//! @brief Destroy the @ref RetNode instance.
ASMJIT_INLINE ~RetNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get the first return operand.
ASMJIT_INLINE Operand& getFirst() { return _ret[0]; }
//! @overload
ASMJIT_INLINE const Operand& getFirst() const { return _ret[0]; }
//! @brief Get the second return operand.
ASMJIT_INLINE Operand& getSecond() { return _ret[1]; }
//! @overload
ASMJIT_INLINE const Operand& getSecond() const { return _ret[1]; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Ret operand(s).
Operand _ret[2];
};
// ============================================================================
// [asmjit::CallNode]
// ============================================================================
//! @brief Function-call node.
struct CallNode : public BaseNode {
ASMJIT_NO_COPY(CallNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @ref CallNode instance.
ASMJIT_INLINE CallNode(BaseCompiler* compiler, const Operand& target) :
BaseNode(compiler, kNodeTypeCall),
_decl(NULL),
_target(target),
_args(NULL) {}
//! @brief Destroy the @ref CallNode instance.
ASMJIT_INLINE ~CallNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get function declaration.
ASMJIT_INLINE FuncDecl* getDecl() const { return _decl; }
//! @brief Get target operand.
ASMJIT_INLINE Operand& getTarget() { return _target; }
//! @overload
ASMJIT_INLINE const Operand& getTarget() const { return _target; }
//! @brief Get return at @a i.
ASMJIT_INLINE Operand& getRet(uint32_t i = 0) {
ASMJIT_ASSERT(i < 2);
return _ret[i];
}
//! @overload
ASMJIT_INLINE const Operand& getRet(uint32_t i = 0) const {
ASMJIT_ASSERT(i < 2);
return _ret[i];
}
//! @brief Get argument at @a i.
ASMJIT_INLINE Operand& getArg(uint32_t i) {
ASMJIT_ASSERT(i < kFuncArgCountLoHi);
return _args[i];
}
//! @overload
ASMJIT_INLINE const Operand& getArg(uint32_t i) const {
ASMJIT_ASSERT(i < kFuncArgCountLoHi);
return _args[i];
}
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Function declaration.
FuncDecl* _decl;
//! @brief Target (address of function, register, label, ...).
Operand _target;
//! @brief Return.
Operand _ret[2];
//! @brief Arguments.
Operand* _args;
};
// ============================================================================
// [asmjit::SArgNode]
// ============================================================================
//! @brief Function-call 'argument on the stack' node.
struct SArgNode : public BaseNode {
ASMJIT_NO_COPY(SArgNode)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @ref SArgNode instance.
ASMJIT_INLINE SArgNode(BaseCompiler* compiler, CallNode* call, VarData* sVd, VarData* cVd) :
BaseNode(compiler, kNodeTypeSArg),
_call(call),
_sVd(sVd),
_cVd(cVd),
_args(0) {}
//! @brief Destroy the @ref SArgNode instance.
ASMJIT_INLINE ~SArgNode() {}
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! @brief Get the associated function-call.
ASMJIT_INLINE CallNode* getCall() const { return _call; }
//! @brief Get source variable.
ASMJIT_INLINE VarData* getSVd() const { return _sVd; }
//! @brief Get conversion variable.
ASMJIT_INLINE VarData* getCVd() const { return _cVd; }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Associated @ref CallNode.
CallNode* _call;
//! @brief Source variable.
VarData* _sVd;
//! @brief Temporary variable used for conversion (or NULL).
VarData* _cVd;
//! @brief Affected arguments bit-array.
uint32_t _args;
};
// ============================================================================
// [asmjit::BaseCompiler]
// ============================================================================
//! @brief Base compiler.
//!
//! @sa BaseAssembler.
struct BaseCompiler : public CodeGen {
ASMJIT_NO_COPY(BaseCompiler)
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! @brief Create a new @ref BaseCompiler instance.
ASMJIT_API BaseCompiler(BaseRuntime* runtime);
//! @brief Destroy the @ref BaseCompiler instance.
ASMJIT_API virtual ~BaseCompiler();
// --------------------------------------------------------------------------
// [LookAhead]
// --------------------------------------------------------------------------
//! @brief Get maximum look ahead.
ASMJIT_INLINE uint32_t getMaxLookAhead() const { return _maxLookAhead; }
//! @brief Set maximum look ahead to @a val.
ASMJIT_INLINE void setMaxLookAhead(uint32_t val) { _maxLookAhead = val; }
// --------------------------------------------------------------------------
// [Clear / Reset]
// --------------------------------------------------------------------------
//! @brief Clear everything, but keep buffers allocated.
//!
//! @note This method will destroy your code.
ASMJIT_API void clear();
//! @brief Clear everything and reset all buffers.
//!
//! @note This method will destroy your code.
ASMJIT_API void reset();
//! @brief Called by clear() and reset() to clear all data related to derived
//! class implementation.
ASMJIT_API virtual void _purge();
// --------------------------------------------------------------------------
// [Nodes]
// --------------------------------------------------------------------------
template<typename T>
ASMJIT_INLINE T* newNode() {
void* p = _zoneAllocator.alloc(sizeof(T));
return new(p) T(this);
}
template<typename T, typename P0>
ASMJIT_INLINE T* newNode(P0 p0) {
void* p = _zoneAllocator.alloc(sizeof(T));
return new(p) T(this, p0);
}
template<typename T, typename P0, typename P1>
ASMJIT_INLINE T* newNode(P0 p0, P1 p1) {
void* p = _zoneAllocator.alloc(sizeof(T));
return new(p) T(this, p0, p1);
}
template<typename T, typename P0, typename P1, typename P2>
ASMJIT_INLINE T* newNode(P0 p0, P1 p1, P2 p2) {
void* p = _zoneAllocator.alloc(sizeof(T));
return new(p) T(this, p0, p1, p2);
}
//! @brief Get first node.
ASMJIT_INLINE BaseNode* getFirstNode() const { return _firstNode; }
//! @brief Get last node.
ASMJIT_INLINE BaseNode* getLastNode() const { return _lastNode; }
//! @brief Get current node.
//!
//! @note If this method returns @c NULL it means that nothing has been emitted
//! yet.
ASMJIT_INLINE BaseNode* getCursor() const { return _cursor; }
//! @brief Set the current node without returning the previous node (private).
ASMJIT_INLINE void _setCursor(BaseNode* node) { _cursor = node; }
//! @brief Set the current node to @a node and return the previous one.
ASMJIT_API BaseNode* setCursor(BaseNode* node);
//! @brief Add node @a node after current and set current to @a node.
ASMJIT_API BaseNode* addNode(BaseNode* node);
//! @brief Add node before @a ref.
ASMJIT_API BaseNode* addNodeBefore(BaseNode* node, BaseNode* ref);
//! @brief Add node after @a ref.
ASMJIT_API BaseNode* addNodeAfter(BaseNode* node, BaseNode* ref);
//! @brief Remove node @a node.
ASMJIT_API BaseNode* removeNode(BaseNode* node);
//! @brief Remove multiple nodes.
ASMJIT_API void removeNodes(BaseNode* first, BaseNode* last);
// --------------------------------------------------------------------------
// [Func]
// --------------------------------------------------------------------------
//! @brief Get current function.
ASMJIT_INLINE FuncNode* getFunc() const { return _func; }
// --------------------------------------------------------------------------
// [Align]
// --------------------------------------------------------------------------
//! @brief Create a new @ref AlignNode.
ASMJIT_API AlignNode* newAlign(uint32_t m);
//! @brief Add a new @ref AlignNode.
ASMJIT_API AlignNode* addAlign(uint32_t m);
//! @brief Align target buffer to @a m bytes.
//!
//! Typical usage of this is to align labels at start of the inner loops.
//!
//! Inserts @c nop() instructions or CPU optimized NOPs.
ASMJIT_INLINE AlignNode* align(uint32_t m) { return addAlign(m); }
// --------------------------------------------------------------------------
// [Target]
// --------------------------------------------------------------------------
//! @brief Create a new @ref TargetNode.
ASMJIT_API TargetNode* newTarget();
//! @brief Add a new @ref TargetNode.
ASMJIT_API TargetNode* addTarget();
//! @brief Get @ref TargetNode by @a id.
ASMJIT_INLINE TargetNode* getTargetById(uint32_t id) {
ASMJIT_ASSERT(OperandUtil::isLabelId(id));
ASMJIT_ASSERT(id < _targets.getLength());
return _targets[id];
}
//! @brief Get @ref TargetNode by @a label.
ASMJIT_INLINE TargetNode* getTarget(const Label& label) {
return getTargetById(label.getId());
}
// --------------------------------------------------------------------------
// [Label]
// --------------------------------------------------------------------------
//! @brief Get count of created labels.
ASMJIT_INLINE size_t getLabelsCount() const
{ return _targets.getLength(); }
//! @brief Get whether @a label is created.
ASMJIT_INLINE bool isLabelCreated(const Label& label) const
{ return static_cast<size_t>(label.getId()) < _targets.getLength(); }
//! @internal
//!
//! @brief Create and initialize a new label.
ASMJIT_API Error _newLabel(Label* dst);
//! @brief Create and return new label.
ASMJIT_INLINE Label newLabel() {
Label result(NoInit);
_newLabel(&result);
return result;
}
//! @brief Bind label to the current offset.
//!
//! @note Label can be bound only once!
ASMJIT_API void bind(const Label& label);
// --------------------------------------------------------------------------
// [Embed]
// --------------------------------------------------------------------------
//! @brief Create a new @ref EmbedNode.
ASMJIT_API EmbedNode* newEmbed(const void* data, uint32_t size);
//! @brief Add a new @ref EmbedNode.
ASMJIT_API EmbedNode* addEmbed(const void* data, uint32_t size);
//! @brief Embed data.
ASMJIT_INLINE EmbedNode* embed(const void* data, uint32_t size) { return addEmbed(data, size); }
// --------------------------------------------------------------------------
// [Comment]
// --------------------------------------------------------------------------
//! @brief Create a new @ref CommentNode.
ASMJIT_API CommentNode* newComment(const char* str);
//! @brief Add a new @ref CommentNode.
ASMJIT_API CommentNode* addComment(const char* str);
//! @brief Emit a single comment line.
ASMJIT_API CommentNode* comment(const char* fmt, ...);
// --------------------------------------------------------------------------
// [Hint]
// --------------------------------------------------------------------------
//! @brief Create a new @ref HintNode.
ASMJIT_API HintNode* newHint(BaseVar& var, uint32_t hint, uint32_t value);
//! @brief Add a new @ref HintNode.
ASMJIT_API HintNode* addHint(BaseVar& var, uint32_t hint, uint32_t value);
// --------------------------------------------------------------------------
// [Vars]
// --------------------------------------------------------------------------
//! @brief Get whether variable @a var is created.
ASMJIT_INLINE bool isVarCreated(const BaseVar& var) const {
return static_cast<size_t>(var.getId() & kOperandIdNum) < _vars.getLength();
}
//! @internal
//!
//! @brief Get @ref VarData by @a var.
ASMJIT_INLINE VarData* getVd(const BaseVar& var) const {
return getVdById(var.getId());
}
//! @internal
//!
//! @brief Get @ref VarData by @a id.
ASMJIT_INLINE VarData* getVdById(uint32_t id) const {
ASMJIT_ASSERT(id != kInvalidValue);
ASMJIT_ASSERT(static_cast<size_t>(id & kOperandIdNum) < _vars.getLength());
return _vars[id & kOperandIdNum];
}
//! @internal
//!
//! @brief Get an array of 'VarData*'.
ASMJIT_INLINE VarData** _getVdArray() const {
return const_cast<VarData**>(_vars.getData());
}
//! @internal
//!
//! @brief Create a new @ref VarData.
ASMJIT_API VarData* _newVd(uint32_t type, uint32_t size, uint32_t c, const char* name);
//! @brief Create a new @ref BaseVar.
virtual Error _newVar(BaseVar* var, uint32_t type, const char* name) = 0;
//! @brief Alloc variable @a var.
ASMJIT_API void alloc(BaseVar& var);
//! @brief Alloc variable @a var using @a regIndex as a register index.
ASMJIT_API void alloc(BaseVar& var, uint32_t regIndex);
//! @brief Alloc variable @a var using @a reg as a demanded register.
ASMJIT_API void alloc(BaseVar& var, const BaseReg& reg);
//! @brief Spill variable @a var.
ASMJIT_API void spill(BaseVar& var);
//! @brief Save variable @a var if modified.
ASMJIT_API void save(BaseVar& var);
//! @brief Unuse variable @a var.
ASMJIT_API void unuse(BaseVar& var);
//! @brief Get priority of variable @a var.
ASMJIT_API uint32_t getPriority(BaseVar& var) const;
//! @brief Set priority of variable @a var to @a priority.
ASMJIT_API void setPriority(BaseVar& var, uint32_t priority);
//! @brief Get save-on-unuse @a var property.
ASMJIT_API bool getSaveOnUnuse(BaseVar& var) const;
//! @brief Set save-on-unuse @a var property to @a value.
ASMJIT_API void setSaveOnUnuse(BaseVar& var, bool value);
//! @brief Rename variable @a var to @a name.
//!
//! @note Only new name will appear in the logger.
ASMJIT_API void rename(BaseVar& var, const char* name);
// --------------------------------------------------------------------------
// [Stack]
// --------------------------------------------------------------------------
//! @brief Create a new @ref BaseMem.
virtual Error _newStack(BaseMem* mem, uint32_t size, uint32_t alignment, const char* name) = 0;
// --------------------------------------------------------------------------
// [Serialize]
// --------------------------------------------------------------------------
//! @brief Send assembled code to @a assembler.
virtual Error serialize(BaseAssembler& assembler) = 0;
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
//! @brief Flow id added to each node created (used only by @ref Context).
uint32_t _nodeFlowId;
//! @brief Flags added to each node created (used only by @ref Context).
uint32_t _nodeFlags;
//! @brief Maximum count of nodes to look ahead when allocating/spilling
//! registers.
uint32_t _maxLookAhead;
//! @brief Variable mapping (translates incoming kVarType into target).
const uint8_t* _targetVarMapping;
//! @brief First node.
BaseNode* _firstNode;
//! @brief Last node.
BaseNode* _lastNode;
//! @brief Current node.
BaseNode* _cursor;
//! @brief Current function.
FuncNode* _func;
//! @brief Variable allocator.
Zone _varAllocator;
//! @brief String/data allocator.
Zone _stringAllocator;
//! @brief Targets.
PodVector<TargetNode*> _targets;
//! @brief Variables.
PodVector<VarData*> _vars;
};
// ============================================================================
// [Defined-Later]
// ============================================================================
ASMJIT_INLINE Label::Label(BaseCompiler& c) : Operand(NoInit) {
c._newLabel(this);
}
ASMJIT_INLINE BaseNode::BaseNode(BaseCompiler* compiler, uint32_t type) {
_prev = NULL;
_next = NULL;
_type = static_cast<uint8_t>(type);
_opCount = 0;
_flags = static_cast<uint16_t>(compiler->_nodeFlags);
_flowId = compiler->_nodeFlowId;
_comment = NULL;
_varInst = NULL;
_liveness = NULL;
_state = NULL;
}
} // asmjit namespace
// [Api-End]
#include "../apiend.h"
// [Guard]
#endif // _ASMJIT_BASE_COMPILER_H