631 lines
15 KiB
C++
631 lines
15 KiB
C++
// [AsmJit]
|
|
// Complete x86/x64 JIT and Remote Assembler for C++.
|
|
//
|
|
// [License]
|
|
// Zlib - See LICENSE.md file in the package.
|
|
|
|
// [Export]
|
|
#define ASMJIT_EXPORTS
|
|
|
|
// [Guard]
|
|
#include "../build.h"
|
|
#if !defined(ASMJIT_DISABLE_COMPILER)
|
|
|
|
// [Dependencies]
|
|
#include "../base/assembler.h"
|
|
#include "../base/compiler.h"
|
|
#include "../base/compilercontext_p.h"
|
|
#include "../base/cpuinfo.h"
|
|
#include "../base/logger.h"
|
|
#include "../base/utils.h"
|
|
#include <stdarg.h>
|
|
|
|
// [Api-Begin]
|
|
#include "../apibegin.h"
|
|
|
|
namespace asmjit {
|
|
|
|
// ============================================================================
|
|
// [Constants]
|
|
// ============================================================================
|
|
|
|
static const char noName[1] = { '\0' };
|
|
enum { kCompilerDefaultLookAhead = 64 };
|
|
|
|
// ============================================================================
|
|
// [asmjit::Compiler - Construction / Destruction]
|
|
// ============================================================================
|
|
|
|
Compiler::Compiler() noexcept
|
|
: _features(0),
|
|
_maxLookAhead(kCompilerDefaultLookAhead),
|
|
_instOptions(0),
|
|
_tokenGenerator(0),
|
|
_nodeFlowId(0),
|
|
_nodeFlags(0),
|
|
_targetVarMapping(nullptr),
|
|
_firstNode(nullptr),
|
|
_lastNode(nullptr),
|
|
_cursor(nullptr),
|
|
_func(nullptr),
|
|
_zoneAllocator(8192 - Zone::kZoneOverhead),
|
|
_varAllocator(4096 - Zone::kZoneOverhead),
|
|
_stringAllocator(4096 - Zone::kZoneOverhead),
|
|
_constAllocator(4096 - Zone::kZoneOverhead),
|
|
_localConstPool(&_constAllocator),
|
|
_globalConstPool(&_zoneAllocator) {}
|
|
Compiler::~Compiler() noexcept {}
|
|
|
|
// ============================================================================
|
|
// [asmjit::Compiler - Attach / Reset]
|
|
// ============================================================================
|
|
|
|
void Compiler::reset(bool releaseMemory) noexcept {
|
|
Assembler* assembler = getAssembler();
|
|
if (assembler != nullptr)
|
|
assembler->_detached(this);
|
|
|
|
_arch = kArchNone;
|
|
_regSize = 0;
|
|
_finalized = false;
|
|
_lastError = kErrorNotInitialized;
|
|
|
|
_features = 0;
|
|
_maxLookAhead = kCompilerDefaultLookAhead;
|
|
|
|
_instOptions = 0;
|
|
_tokenGenerator = 0;
|
|
|
|
_nodeFlowId = 0;
|
|
_nodeFlags = 0;
|
|
|
|
_firstNode = nullptr;
|
|
_lastNode = nullptr;
|
|
|
|
_cursor = nullptr;
|
|
_func = nullptr;
|
|
|
|
_localConstPool.reset();
|
|
_globalConstPool.reset();
|
|
|
|
_localConstPoolLabel.reset();
|
|
_globalConstPoolLabel.reset();
|
|
|
|
_zoneAllocator.reset(releaseMemory);
|
|
_varAllocator.reset(releaseMemory);
|
|
_stringAllocator.reset(releaseMemory);
|
|
_constAllocator.reset(releaseMemory);
|
|
|
|
_varList.reset(releaseMemory);
|
|
}
|
|
|
|
// ============================================================================
|
|
// [asmjit::Compiler - Node-Factory]
|
|
// ============================================================================
|
|
|
|
HLData* Compiler::newDataNode(const void* data, uint32_t size) noexcept {
|
|
if (size > HLData::kInlineBufferSize) {
|
|
void* clonedData = _stringAllocator.alloc(size);
|
|
if (clonedData == nullptr)
|
|
return nullptr;
|
|
|
|
if (data != nullptr)
|
|
::memcpy(clonedData, data, size);
|
|
data = clonedData;
|
|
}
|
|
|
|
return newNode<HLData>(const_cast<void*>(data), size);
|
|
}
|
|
|
|
HLAlign* Compiler::newAlignNode(uint32_t alignMode, uint32_t offset) noexcept {
|
|
return newNode<HLAlign>(alignMode, offset);
|
|
}
|
|
|
|
HLLabel* Compiler::newLabelNode() noexcept {
|
|
Assembler* assembler = getAssembler();
|
|
if (assembler == nullptr) return nullptr;
|
|
|
|
uint32_t id = assembler->_newLabelId();
|
|
LabelData* ld = assembler->getLabelData(id);
|
|
|
|
HLLabel* node = newNode<HLLabel>(id);
|
|
if (node == nullptr) return nullptr;
|
|
|
|
// These have to be zero now.
|
|
ASMJIT_ASSERT(ld->exId == 0);
|
|
ASMJIT_ASSERT(ld->exData == nullptr);
|
|
|
|
ld->exId = _exId;
|
|
ld->exData = node;
|
|
|
|
return node;
|
|
}
|
|
|
|
HLComment* Compiler::newCommentNode(const char* str) noexcept {
|
|
if (str != nullptr && str[0]) {
|
|
str = _stringAllocator.sdup(str);
|
|
if (str == nullptr)
|
|
return nullptr;
|
|
}
|
|
|
|
return newNode<HLComment>(str);
|
|
}
|
|
|
|
HLHint* Compiler::newHintNode(Var& var, uint32_t hint, uint32_t value) noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return nullptr;
|
|
|
|
VarData* vd = getVd(var);
|
|
return newNode<HLHint>(vd, hint, value);
|
|
}
|
|
|
|
// ============================================================================
|
|
// [asmjit::Compiler - Code-Stream]
|
|
// ============================================================================
|
|
|
|
HLNode* Compiler::addFunc(HLFunc* func) noexcept {
|
|
ASMJIT_ASSERT(_func == nullptr);
|
|
_func = func;
|
|
|
|
addNode(func); // Add function node.
|
|
addNode(func->getEntryNode()); // Add function entry.
|
|
HLNode* cursor = getCursor();
|
|
|
|
addNode(func->getExitNode()); // Add function exit / epilog marker.
|
|
addNode(func->getEnd()); // Add function end.
|
|
setCursor(cursor);
|
|
|
|
return func;
|
|
}
|
|
|
|
HLNode* Compiler::addNode(HLNode* node) noexcept {
|
|
ASMJIT_ASSERT(node != nullptr);
|
|
ASMJIT_ASSERT(node->_prev == nullptr);
|
|
ASMJIT_ASSERT(node->_next == nullptr);
|
|
|
|
if (_cursor == nullptr) {
|
|
if (_firstNode == nullptr) {
|
|
_firstNode = node;
|
|
_lastNode = node;
|
|
}
|
|
else {
|
|
node->_next = _firstNode;
|
|
_firstNode->_prev = node;
|
|
_firstNode = node;
|
|
}
|
|
}
|
|
else {
|
|
HLNode* prev = _cursor;
|
|
HLNode* next = _cursor->_next;
|
|
|
|
node->_prev = prev;
|
|
node->_next = next;
|
|
|
|
prev->_next = node;
|
|
if (next)
|
|
next->_prev = node;
|
|
else
|
|
_lastNode = node;
|
|
}
|
|
|
|
_cursor = node;
|
|
return node;
|
|
}
|
|
|
|
HLNode* Compiler::addNodeBefore(HLNode* node, HLNode* ref) noexcept {
|
|
ASMJIT_ASSERT(node != nullptr);
|
|
ASMJIT_ASSERT(node->_prev == nullptr);
|
|
ASMJIT_ASSERT(node->_next == nullptr);
|
|
ASMJIT_ASSERT(ref != nullptr);
|
|
|
|
HLNode* prev = ref->_prev;
|
|
HLNode* next = ref;
|
|
|
|
node->_prev = prev;
|
|
node->_next = next;
|
|
|
|
next->_prev = node;
|
|
if (prev)
|
|
prev->_next = node;
|
|
else
|
|
_firstNode = node;
|
|
|
|
return node;
|
|
}
|
|
|
|
HLNode* Compiler::addNodeAfter(HLNode* node, HLNode* ref) noexcept {
|
|
ASMJIT_ASSERT(node != nullptr);
|
|
ASMJIT_ASSERT(node->_prev == nullptr);
|
|
ASMJIT_ASSERT(node->_next == nullptr);
|
|
ASMJIT_ASSERT(ref != nullptr);
|
|
|
|
HLNode* prev = ref;
|
|
HLNode* next = ref->_next;
|
|
|
|
node->_prev = prev;
|
|
node->_next = next;
|
|
|
|
prev->_next = node;
|
|
if (next)
|
|
next->_prev = node;
|
|
else
|
|
_lastNode = node;
|
|
|
|
return node;
|
|
}
|
|
|
|
static ASMJIT_INLINE void Compiler_nodeRemoved(Compiler* self, HLNode* node_) noexcept {
|
|
if (node_->isJmpOrJcc()) {
|
|
HLJump* node = static_cast<HLJump*>(node_);
|
|
HLLabel* label = node->getTarget();
|
|
|
|
if (label != nullptr) {
|
|
// Disconnect.
|
|
HLJump** pPrev = &label->_from;
|
|
for (;;) {
|
|
ASMJIT_ASSERT(*pPrev != nullptr);
|
|
HLJump* current = *pPrev;
|
|
|
|
if (current == nullptr)
|
|
break;
|
|
|
|
if (current == node) {
|
|
*pPrev = node->_jumpNext;
|
|
break;
|
|
}
|
|
|
|
pPrev = ¤t->_jumpNext;
|
|
}
|
|
|
|
label->subNumRefs();
|
|
}
|
|
}
|
|
}
|
|
|
|
HLNode* Compiler::removeNode(HLNode* node) noexcept {
|
|
HLNode* prev = node->_prev;
|
|
HLNode* next = node->_next;
|
|
|
|
if (_firstNode == node)
|
|
_firstNode = next;
|
|
else
|
|
prev->_next = next;
|
|
|
|
if (_lastNode == node)
|
|
_lastNode = prev;
|
|
else
|
|
next->_prev = prev;
|
|
|
|
node->_prev = nullptr;
|
|
node->_next = nullptr;
|
|
|
|
if (_cursor == node)
|
|
_cursor = prev;
|
|
Compiler_nodeRemoved(this, node);
|
|
|
|
return node;
|
|
}
|
|
|
|
void Compiler::removeNodes(HLNode* first, HLNode* last) noexcept {
|
|
if (first == last) {
|
|
removeNode(first);
|
|
return;
|
|
}
|
|
|
|
HLNode* prev = first->_prev;
|
|
HLNode* next = last->_next;
|
|
|
|
if (_firstNode == first)
|
|
_firstNode = next;
|
|
else
|
|
prev->_next = next;
|
|
|
|
if (_lastNode == last)
|
|
_lastNode = prev;
|
|
else
|
|
next->_prev = prev;
|
|
|
|
HLNode* node = first;
|
|
for (;;) {
|
|
HLNode* next = node->getNext();
|
|
ASMJIT_ASSERT(next != nullptr);
|
|
|
|
node->_prev = nullptr;
|
|
node->_next = nullptr;
|
|
|
|
if (_cursor == node)
|
|
_cursor = prev;
|
|
Compiler_nodeRemoved(this, node);
|
|
|
|
if (node == last)
|
|
break;
|
|
node = next;
|
|
}
|
|
}
|
|
|
|
HLNode* Compiler::setCursor(HLNode* node) noexcept {
|
|
HLNode* old = _cursor;
|
|
_cursor = node;
|
|
return old;
|
|
}
|
|
|
|
// ============================================================================
|
|
// [asmjit::Compiler - Align]
|
|
// ============================================================================
|
|
|
|
Error Compiler::align(uint32_t alignMode, uint32_t offset) noexcept {
|
|
HLAlign* node = newAlignNode(alignMode, offset);
|
|
if (node == nullptr)
|
|
return setLastError(kErrorNoHeapMemory);
|
|
|
|
addNode(node);
|
|
return kErrorOk;
|
|
}
|
|
|
|
// ============================================================================
|
|
// [asmjit::Compiler - Label]
|
|
// ============================================================================
|
|
|
|
HLLabel* Compiler::getHLLabel(uint32_t id) const noexcept {
|
|
Assembler* assembler = getAssembler();
|
|
if (assembler == nullptr) return nullptr;
|
|
|
|
LabelData* ld = assembler->getLabelData(id);
|
|
if (ld->exId == _exId)
|
|
return static_cast<HLLabel*>(ld->exData);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
bool Compiler::isLabelValid(uint32_t id) const noexcept {
|
|
Assembler* assembler = getAssembler();
|
|
if (assembler == nullptr) return false;
|
|
|
|
return static_cast<size_t>(id) < assembler->getLabelsCount();
|
|
}
|
|
|
|
uint32_t Compiler::_newLabelId() noexcept {
|
|
HLLabel* node = newLabelNode();
|
|
if (node == nullptr) {
|
|
setLastError(kErrorNoHeapMemory);
|
|
return kInvalidValue;
|
|
}
|
|
|
|
return node->getLabelId();
|
|
}
|
|
|
|
Error Compiler::bind(const Label& label) noexcept {
|
|
HLLabel* node = getHLLabel(label);
|
|
if (node == nullptr)
|
|
return setLastError(kErrorInvalidState);
|
|
addNode(node);
|
|
return kErrorOk;
|
|
}
|
|
|
|
// ============================================================================
|
|
// [asmjit::Compiler - Embed]
|
|
// ============================================================================
|
|
|
|
Error Compiler::embed(const void* data, uint32_t size) noexcept {
|
|
HLData* node = newDataNode(data, size);
|
|
if (node == nullptr)
|
|
return setLastError(kErrorNoHeapMemory);
|
|
|
|
addNode(node);
|
|
return kErrorOk;
|
|
}
|
|
|
|
Error Compiler::embedConstPool(const Label& label, const ConstPool& pool) noexcept {
|
|
if (label.getId() == kInvalidValue)
|
|
return kErrorInvalidState;
|
|
|
|
align(kAlignData, static_cast<uint32_t>(pool.getAlignment()));
|
|
bind(label);
|
|
|
|
HLData* embedNode = newDataNode(nullptr, static_cast<uint32_t>(pool.getSize()));
|
|
if (embedNode == nullptr)
|
|
return kErrorNoHeapMemory;
|
|
|
|
pool.fill(embedNode->getData());
|
|
addNode(embedNode);
|
|
|
|
return kErrorOk;
|
|
}
|
|
|
|
// ============================================================================
|
|
// [asmjit::Compiler - Comment]
|
|
// ============================================================================
|
|
|
|
Error Compiler::comment(const char* fmt, ...) noexcept {
|
|
char buf[256];
|
|
char* p = buf;
|
|
|
|
if (fmt) {
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
p += vsnprintf(p, 254, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
p[0] = '\0';
|
|
|
|
HLComment* node = newCommentNode(buf);
|
|
if (node == nullptr)
|
|
return setLastError(kErrorNoHeapMemory);
|
|
|
|
addNode(node);
|
|
return kErrorOk;
|
|
}
|
|
|
|
// ============================================================================
|
|
// [asmjit::Compiler - Hint]
|
|
// ============================================================================
|
|
|
|
Error Compiler::_hint(Var& var, uint32_t hint, uint32_t value) noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return kErrorOk;
|
|
|
|
HLHint* node = newHintNode(var, hint, value);
|
|
if (node == nullptr)
|
|
return setLastError(kErrorNoHeapMemory);
|
|
|
|
addNode(node);
|
|
return kErrorOk;
|
|
}
|
|
|
|
// ============================================================================
|
|
// [asmjit::Compiler - Vars]
|
|
// ============================================================================
|
|
|
|
VarData* Compiler::_newVd(const VarInfo& vi, const char* name) noexcept {
|
|
VarData* vd = reinterpret_cast<VarData*>(_varAllocator.alloc(sizeof(VarData)));
|
|
if (ASMJIT_UNLIKELY(vd == nullptr))
|
|
goto _NoMemory;
|
|
|
|
vd->_name = noName;
|
|
vd->_id = OperandUtil::makeVarId(static_cast<uint32_t>(_varList.getLength()));
|
|
vd->_localId = kInvalidValue;
|
|
|
|
#if !defined(ASMJIT_DISABLE_LOGGER)
|
|
if (name != nullptr && name[0] != '\0') {
|
|
vd->_name = _stringAllocator.sdup(name);
|
|
}
|
|
#endif // !ASMJIT_DISABLE_LOGGER
|
|
|
|
vd->_type = static_cast<uint8_t>(vi.getTypeId());
|
|
vd->_class = static_cast<uint8_t>(vi.getRegClass());
|
|
vd->_flags = 0;
|
|
vd->_priority = 10;
|
|
|
|
vd->_state = kVarStateNone;
|
|
vd->_regIndex = kInvalidReg;
|
|
vd->_isStack = false;
|
|
vd->_isMemArg = false;
|
|
vd->_isCalculated = false;
|
|
vd->_saveOnUnuse = false;
|
|
vd->_modified = false;
|
|
vd->_reserved0 = 0;
|
|
vd->_alignment = static_cast<uint8_t>(Utils::iMin<uint32_t>(vi.getSize(), 64));
|
|
|
|
vd->_size = vi.getSize();
|
|
vd->_homeMask = 0;
|
|
|
|
vd->_memOffset = 0;
|
|
vd->_memCell = nullptr;
|
|
|
|
vd->rReadCount = 0;
|
|
vd->rWriteCount = 0;
|
|
vd->mReadCount = 0;
|
|
vd->mWriteCount = 0;
|
|
|
|
vd->_va = nullptr;
|
|
|
|
if (ASMJIT_UNLIKELY(_varList.append(vd) != kErrorOk))
|
|
goto _NoMemory;
|
|
return vd;
|
|
|
|
_NoMemory:
|
|
setLastError(kErrorNoHeapMemory);
|
|
return nullptr;
|
|
}
|
|
|
|
Error Compiler::alloc(Var& var) noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return kErrorOk;
|
|
return _hint(var, kVarHintAlloc, kInvalidValue);
|
|
}
|
|
|
|
Error Compiler::alloc(Var& var, uint32_t regIndex) noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return kErrorOk;
|
|
return _hint(var, kVarHintAlloc, regIndex);
|
|
}
|
|
|
|
Error Compiler::alloc(Var& var, const Reg& reg) noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return kErrorOk;
|
|
return _hint(var, kVarHintAlloc, reg.getRegIndex());
|
|
}
|
|
|
|
Error Compiler::save(Var& var) noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return kErrorOk;
|
|
return _hint(var, kVarHintSave, kInvalidValue);
|
|
}
|
|
|
|
Error Compiler::spill(Var& var) noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return kErrorOk;
|
|
return _hint(var, kVarHintSpill, kInvalidValue);
|
|
}
|
|
|
|
Error Compiler::unuse(Var& var) noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return kErrorOk;
|
|
return _hint(var, kVarHintUnuse, kInvalidValue);
|
|
}
|
|
|
|
uint32_t Compiler::getPriority(Var& var) const noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return kInvalidValue;
|
|
|
|
VarData* vd = getVdById(var.getId());
|
|
return vd->getPriority();
|
|
}
|
|
|
|
void Compiler::setPriority(Var& var, uint32_t priority) noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return;
|
|
|
|
if (priority > 255)
|
|
priority = 255;
|
|
|
|
VarData* vd = getVdById(var.getId());
|
|
vd->_priority = static_cast<uint8_t>(priority);
|
|
}
|
|
|
|
bool Compiler::getSaveOnUnuse(Var& var) const noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return false;
|
|
|
|
VarData* vd = getVdById(var.getId());
|
|
return static_cast<bool>(vd->_saveOnUnuse);
|
|
}
|
|
|
|
void Compiler::setSaveOnUnuse(Var& var, bool value) noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return;
|
|
|
|
VarData* vd = getVdById(var.getId());
|
|
vd->_saveOnUnuse = value;
|
|
}
|
|
|
|
void Compiler::rename(Var& var, const char* fmt, ...) noexcept {
|
|
if (var.getId() == kInvalidValue)
|
|
return;
|
|
|
|
VarData* vd = getVdById(var.getId());
|
|
vd->_name = noName;
|
|
|
|
if (fmt != nullptr && fmt[0] != '\0') {
|
|
char buf[64];
|
|
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
|
|
vsnprintf(buf, ASMJIT_ARRAY_SIZE(buf), fmt, ap);
|
|
buf[ASMJIT_ARRAY_SIZE(buf) - 1] = '\0';
|
|
|
|
vd->_name = _stringAllocator.sdup(buf);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
|
|
} // asmjit namespace
|
|
|
|
// [Api-End]
|
|
#include "../apiend.h"
|
|
|
|
// [Guard]
|
|
#endif // !ASMJIT_DISABLE_COMPILER
|