mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-17 20:44:37 +03:00
- Moved to cxxtool to generate build.h compiler and platform based definitions.
- Compiler no longer works on its own, it requires Assembler. - Labels created by Assembler and Compiler now share their IDs, so they can be used nearly interchangeably without weird side-effects and hacks. - Renamed getError() and setError() to getLastError() and setLastError(). - Renamed compiler nodes to "HL" nodes (preparation for HLStream). - Renamed FuncConv to CallConv. - Function calling convention is now part of FuncPrototype. - Added a possibility to align by inserting zeros (kAlignZero) - Fixed assertion in X86Compiler that didn't like unhandled function argument(s). - Added Compiler::embedConstPool() helper, which can be handy if you use your own ConstPool. - Code refactorization and other minor changes. - CpuTicks::now() renamed to Utils::getTickCount(). - error.h merged with globals.h - Documentation updates related to recent API changes.
This commit is contained in:
584
src/asmjit/base/compilercontext.cpp
Normal file
584
src/asmjit/base/compilercontext.cpp
Normal file
@@ -0,0 +1,584 @@
|
||||
// [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 - AsmJit]
|
||||
#include "../base/compilercontext_p.h"
|
||||
#include "../base/utils.h"
|
||||
|
||||
// [Api-Begin]
|
||||
#include "../apibegin.h"
|
||||
|
||||
namespace asmjit {
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Context - Construction / Destruction]
|
||||
// ============================================================================
|
||||
|
||||
Context::Context(Compiler* compiler) :
|
||||
_compiler(compiler),
|
||||
_zoneAllocator(8192 - Zone::kZoneOverhead),
|
||||
_varMapToVaListOffset(0) {
|
||||
|
||||
Context::reset();
|
||||
}
|
||||
|
||||
Context::~Context() {}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Context - Reset]
|
||||
// ============================================================================
|
||||
|
||||
void Context::reset(bool releaseMemory) {
|
||||
_zoneAllocator.reset(releaseMemory);
|
||||
|
||||
_func = NULL;
|
||||
_start = NULL;
|
||||
_end = NULL;
|
||||
_extraBlock = NULL;
|
||||
_stop = NULL;
|
||||
|
||||
_unreachableList.reset();
|
||||
_returningList.reset();
|
||||
_jccList.reset();
|
||||
_contextVd.reset(releaseMemory);
|
||||
|
||||
_memVarCells = NULL;
|
||||
_memStackCells = NULL;
|
||||
|
||||
_mem1ByteVarsUsed = 0;
|
||||
_mem2ByteVarsUsed = 0;
|
||||
_mem4ByteVarsUsed = 0;
|
||||
_mem8ByteVarsUsed = 0;
|
||||
_mem16ByteVarsUsed = 0;
|
||||
_mem32ByteVarsUsed = 0;
|
||||
_mem64ByteVarsUsed = 0;
|
||||
_memStackCellsUsed = 0;
|
||||
|
||||
_memMaxAlign = 0;
|
||||
_memVarTotal = 0;
|
||||
_memStackTotal = 0;
|
||||
_memAllTotal = 0;
|
||||
_annotationLength = 12;
|
||||
|
||||
_state = NULL;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Context - Mem]
|
||||
// ============================================================================
|
||||
|
||||
static ASMJIT_INLINE uint32_t BaseContext_getDefaultAlignment(uint32_t size) {
|
||||
if (size > 32)
|
||||
return 64;
|
||||
else if (size > 16)
|
||||
return 32;
|
||||
else if (size > 8)
|
||||
return 16;
|
||||
else if (size > 4)
|
||||
return 8;
|
||||
else if (size > 2)
|
||||
return 4;
|
||||
else if (size > 1)
|
||||
return 2;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
VarCell* Context::_newVarCell(VarData* vd) {
|
||||
ASMJIT_ASSERT(vd->_memCell == NULL);
|
||||
|
||||
VarCell* cell;
|
||||
uint32_t size = vd->getSize();
|
||||
|
||||
if (vd->isStack()) {
|
||||
cell = _newStackCell(size, vd->getAlignment());
|
||||
|
||||
if (cell == NULL)
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
cell = static_cast<VarCell*>(_zoneAllocator.alloc(sizeof(VarCell)));
|
||||
if (cell == NULL)
|
||||
goto _NoMemory;
|
||||
|
||||
cell->_next = _memVarCells;
|
||||
_memVarCells = cell;
|
||||
|
||||
cell->_offset = 0;
|
||||
cell->_size = size;
|
||||
cell->_alignment = size;
|
||||
|
||||
_memMaxAlign = Utils::iMax<uint32_t>(_memMaxAlign, size);
|
||||
_memVarTotal += size;
|
||||
|
||||
switch (size) {
|
||||
case 1: _mem1ByteVarsUsed++ ; break;
|
||||
case 2: _mem2ByteVarsUsed++ ; break;
|
||||
case 4: _mem4ByteVarsUsed++ ; break;
|
||||
case 8: _mem8ByteVarsUsed++ ; break;
|
||||
case 16: _mem16ByteVarsUsed++; break;
|
||||
case 32: _mem32ByteVarsUsed++; break;
|
||||
case 64: _mem64ByteVarsUsed++; break;
|
||||
default: ASMJIT_ASSERT(!"Reached");
|
||||
}
|
||||
}
|
||||
|
||||
vd->_memCell = cell;
|
||||
return cell;
|
||||
|
||||
_NoMemory:
|
||||
_compiler->setLastError(kErrorNoHeapMemory);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VarCell* Context::_newStackCell(uint32_t size, uint32_t alignment) {
|
||||
VarCell* cell = static_cast<VarCell*>(_zoneAllocator.alloc(sizeof(VarCell)));
|
||||
if (cell == NULL)
|
||||
goto _NoMemory;
|
||||
|
||||
if (alignment == 0)
|
||||
alignment = BaseContext_getDefaultAlignment(size);
|
||||
|
||||
if (alignment > 64)
|
||||
alignment = 64;
|
||||
|
||||
ASMJIT_ASSERT(Utils::isPowerOf2(alignment));
|
||||
size = Utils::alignTo<uint32_t>(size, alignment);
|
||||
|
||||
// Insert it sorted according to the alignment and size.
|
||||
{
|
||||
VarCell** pPrev = &_memStackCells;
|
||||
VarCell* cur = *pPrev;
|
||||
|
||||
for (cur = *pPrev; cur != NULL; cur = cur->_next) {
|
||||
if (cur->getAlignment() > alignment)
|
||||
continue;
|
||||
if (cur->getAlignment() == alignment && cur->getSize() > size)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
cell->_next = cur;
|
||||
cell->_offset = 0;
|
||||
cell->_size = size;
|
||||
cell->_alignment = alignment;
|
||||
|
||||
*pPrev = cell;
|
||||
_memStackCellsUsed++;
|
||||
|
||||
_memMaxAlign = Utils::iMax<uint32_t>(_memMaxAlign, alignment);
|
||||
_memStackTotal += size;
|
||||
}
|
||||
|
||||
return cell;
|
||||
|
||||
_NoMemory:
|
||||
_compiler->setLastError(kErrorNoHeapMemory);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Error Context::resolveCellOffsets() {
|
||||
VarCell* varCell = _memVarCells;
|
||||
VarCell* stackCell = _memStackCells;
|
||||
|
||||
uint32_t stackAlignment = 0;
|
||||
if (stackCell != NULL)
|
||||
stackAlignment = stackCell->getAlignment();
|
||||
|
||||
uint32_t pos64 = 0;
|
||||
uint32_t pos32 = pos64 + _mem64ByteVarsUsed * 64;
|
||||
uint32_t pos16 = pos32 + _mem32ByteVarsUsed * 32;
|
||||
uint32_t pos8 = pos16 + _mem16ByteVarsUsed * 16;
|
||||
uint32_t pos4 = pos8 + _mem8ByteVarsUsed * 8 ;
|
||||
uint32_t pos2 = pos4 + _mem4ByteVarsUsed * 4 ;
|
||||
uint32_t pos1 = pos2 + _mem2ByteVarsUsed * 2 ;
|
||||
|
||||
uint32_t stackPos = pos1 + _mem1ByteVarsUsed;
|
||||
|
||||
uint32_t gapAlignment = stackAlignment;
|
||||
uint32_t gapSize = 0;
|
||||
|
||||
// TODO: Not used!
|
||||
if (gapAlignment)
|
||||
Utils::alignDiff(stackPos, gapAlignment);
|
||||
stackPos += gapSize;
|
||||
|
||||
uint32_t gapPos = stackPos;
|
||||
uint32_t allTotal = stackPos;
|
||||
|
||||
// Vars - Allocated according to alignment/width.
|
||||
while (varCell != NULL) {
|
||||
uint32_t size = varCell->getSize();
|
||||
uint32_t offset = 0;
|
||||
|
||||
switch (size) {
|
||||
case 1: offset = pos1 ; pos1 += 1 ; break;
|
||||
case 2: offset = pos2 ; pos2 += 2 ; break;
|
||||
case 4: offset = pos4 ; pos4 += 4 ; break;
|
||||
case 8: offset = pos8 ; pos8 += 8 ; break;
|
||||
case 16: offset = pos16; pos16 += 16; break;
|
||||
case 32: offset = pos32; pos32 += 32; break;
|
||||
case 64: offset = pos64; pos64 += 64; break;
|
||||
default: ASMJIT_ASSERT(!"Reached");
|
||||
}
|
||||
|
||||
varCell->setOffset(static_cast<int32_t>(offset));
|
||||
varCell = varCell->_next;
|
||||
}
|
||||
|
||||
// Stack - Allocated according to alignment/width.
|
||||
while (stackCell != NULL) {
|
||||
uint32_t size = stackCell->getSize();
|
||||
uint32_t alignment = stackCell->getAlignment();
|
||||
uint32_t offset;
|
||||
|
||||
// Try to fill the gap between variables/stack first.
|
||||
if (size <= gapSize && alignment <= gapAlignment) {
|
||||
offset = gapPos;
|
||||
|
||||
gapSize -= size;
|
||||
gapPos -= size;
|
||||
|
||||
if (alignment < gapAlignment)
|
||||
gapAlignment = alignment;
|
||||
}
|
||||
else {
|
||||
offset = stackPos;
|
||||
|
||||
stackPos += size;
|
||||
allTotal += size;
|
||||
}
|
||||
|
||||
stackCell->setOffset(offset);
|
||||
stackCell = stackCell->_next;
|
||||
}
|
||||
|
||||
_memAllTotal = allTotal;
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Context - RemoveUnreachableCode]
|
||||
// ============================================================================
|
||||
|
||||
Error Context::removeUnreachableCode() {
|
||||
Compiler* compiler = getCompiler();
|
||||
|
||||
PodList<HLNode*>::Link* link = _unreachableList.getFirst();
|
||||
HLNode* stop = getStop();
|
||||
|
||||
while (link != NULL) {
|
||||
HLNode* node = link->getValue();
|
||||
if (node != NULL && node->getPrev() != NULL && node != stop) {
|
||||
// Locate all unreachable nodes.
|
||||
HLNode* first = node;
|
||||
do {
|
||||
if (node->isFetched())
|
||||
break;
|
||||
node = node->getNext();
|
||||
} while (node != stop);
|
||||
|
||||
// Remove unreachable nodes that are neither informative nor directives.
|
||||
if (node != first) {
|
||||
HLNode* end = node;
|
||||
node = first;
|
||||
do {
|
||||
HLNode* next = node->getNext();
|
||||
if (!node->isInformative() && node->getType() != kHLNodeTypeAlign) {
|
||||
ASMJIT_TLOG("[%05d] Unreachable\n", node->getFlowId());
|
||||
compiler->removeNode(node);
|
||||
}
|
||||
node = next;
|
||||
} while (node != end);
|
||||
}
|
||||
}
|
||||
|
||||
link = link->getNext();
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Context - Liveness Analysis]
|
||||
// ============================================================================
|
||||
|
||||
//! \internal
|
||||
struct LivenessTarget {
|
||||
//! Previous target.
|
||||
LivenessTarget* prev;
|
||||
|
||||
//! Target node.
|
||||
HLLabel* node;
|
||||
//! Jumped from.
|
||||
HLJump* from;
|
||||
};
|
||||
|
||||
Error Context::livenessAnalysis() {
|
||||
uint32_t bLen = static_cast<uint32_t>(
|
||||
((_contextVd.getLength() + BitArray::kEntityBits - 1) / BitArray::kEntityBits));
|
||||
|
||||
// No variables.
|
||||
if (bLen == 0)
|
||||
return kErrorOk;
|
||||
|
||||
HLFunc* func = getFunc();
|
||||
HLJump* from = NULL;
|
||||
|
||||
LivenessTarget* ltCur = NULL;
|
||||
LivenessTarget* ltUnused = NULL;
|
||||
|
||||
PodList<HLNode*>::Link* retPtr = _returningList.getFirst();
|
||||
ASMJIT_ASSERT(retPtr != NULL);
|
||||
|
||||
HLNode* node = retPtr->getValue();
|
||||
|
||||
size_t varMapToVaListOffset = _varMapToVaListOffset;
|
||||
BitArray* bCur = newBits(bLen);
|
||||
|
||||
if (bCur == NULL)
|
||||
goto _NoMemory;
|
||||
|
||||
// Allocate bits for code visited first time.
|
||||
_OnVisit:
|
||||
for (;;) {
|
||||
if (node->hasLiveness()) {
|
||||
if (bCur->_addBitsDelSource(node->getLiveness(), bCur, bLen))
|
||||
goto _OnPatch;
|
||||
else
|
||||
goto _OnDone;
|
||||
}
|
||||
|
||||
BitArray* bTmp = copyBits(bCur, bLen);
|
||||
if (bTmp == NULL)
|
||||
goto _NoMemory;
|
||||
|
||||
node->setLiveness(bTmp);
|
||||
VarMap* map = node->getMap();
|
||||
|
||||
if (map != NULL) {
|
||||
uint32_t vaCount = map->getVaCount();
|
||||
VarAttr* vaList = reinterpret_cast<VarAttr*>(((uint8_t*)map) + varMapToVaListOffset);
|
||||
|
||||
for (uint32_t i = 0; i < vaCount; i++) {
|
||||
VarAttr* va = &vaList[i];
|
||||
VarData* vd = va->getVd();
|
||||
|
||||
uint32_t flags = va->getFlags();
|
||||
uint32_t ctxId = vd->getLocalId();
|
||||
|
||||
if ((flags & kVarAttrWAll) && !(flags & kVarAttrRAll)) {
|
||||
// Write-Only.
|
||||
bTmp->setBit(ctxId);
|
||||
bCur->delBit(ctxId);
|
||||
}
|
||||
else {
|
||||
// Read-Only or Read/Write.
|
||||
bTmp->setBit(ctxId);
|
||||
bCur->setBit(ctxId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node->getType() == kHLNodeTypeLabel)
|
||||
goto _OnTarget;
|
||||
|
||||
if (node == func)
|
||||
goto _OnDone;
|
||||
|
||||
ASMJIT_ASSERT(node->getPrev());
|
||||
node = node->getPrev();
|
||||
}
|
||||
|
||||
// Patch already generated liveness bits.
|
||||
_OnPatch:
|
||||
for (;;) {
|
||||
ASMJIT_ASSERT(node->hasLiveness());
|
||||
BitArray* bNode = node->getLiveness();
|
||||
|
||||
if (!bNode->_addBitsDelSource(bCur, bLen))
|
||||
goto _OnDone;
|
||||
|
||||
if (node->getType() == kHLNodeTypeLabel)
|
||||
goto _OnTarget;
|
||||
|
||||
if (node == func)
|
||||
goto _OnDone;
|
||||
|
||||
node = node->getPrev();
|
||||
}
|
||||
|
||||
_OnTarget:
|
||||
if (static_cast<HLLabel*>(node)->getNumRefs() != 0) {
|
||||
// Push a new LivenessTarget onto the stack if needed.
|
||||
if (ltCur == NULL || ltCur->node != node) {
|
||||
// Allocate a new LivenessTarget object (from pool or zone).
|
||||
LivenessTarget* ltTmp = ltUnused;
|
||||
|
||||
if (ltTmp != NULL) {
|
||||
ltUnused = ltUnused->prev;
|
||||
}
|
||||
else {
|
||||
ltTmp = _zoneAllocator.allocT<LivenessTarget>(
|
||||
sizeof(LivenessTarget) - sizeof(BitArray) + bLen * sizeof(uintptr_t));
|
||||
|
||||
if (ltTmp == NULL)
|
||||
goto _NoMemory;
|
||||
}
|
||||
|
||||
// Initialize and make current - ltTmp->from will be set later on.
|
||||
ltTmp->prev = ltCur;
|
||||
ltTmp->node = static_cast<HLLabel*>(node);
|
||||
ltCur = ltTmp;
|
||||
|
||||
from = static_cast<HLLabel*>(node)->getFrom();
|
||||
ASMJIT_ASSERT(from != NULL);
|
||||
}
|
||||
else {
|
||||
from = ltCur->from;
|
||||
goto _OnJumpNext;
|
||||
}
|
||||
|
||||
// Visit/Patch.
|
||||
do {
|
||||
ltCur->from = from;
|
||||
bCur->copyBits(node->getLiveness(), bLen);
|
||||
|
||||
if (!from->hasLiveness()) {
|
||||
node = from;
|
||||
goto _OnVisit;
|
||||
}
|
||||
|
||||
// Issue #25: Moved '_OnJumpNext' here since it's important to patch
|
||||
// code again if there are more live variables than before.
|
||||
_OnJumpNext:
|
||||
if (bCur->delBits(from->getLiveness(), bLen)) {
|
||||
node = from;
|
||||
goto _OnPatch;
|
||||
}
|
||||
|
||||
from = from->getJumpNext();
|
||||
} while (from != NULL);
|
||||
|
||||
// Pop the current LivenessTarget from the stack.
|
||||
{
|
||||
LivenessTarget* ltTmp = ltCur;
|
||||
|
||||
ltCur = ltCur->prev;
|
||||
ltTmp->prev = ltUnused;
|
||||
ltUnused = ltTmp;
|
||||
}
|
||||
}
|
||||
|
||||
bCur->copyBits(node->getLiveness(), bLen);
|
||||
node = node->getPrev();
|
||||
|
||||
if (node->isJmp() || !node->isFetched())
|
||||
goto _OnDone;
|
||||
|
||||
if (!node->hasLiveness())
|
||||
goto _OnVisit;
|
||||
|
||||
if (bCur->delBits(node->getLiveness(), bLen))
|
||||
goto _OnPatch;
|
||||
|
||||
_OnDone:
|
||||
if (ltCur != NULL) {
|
||||
node = ltCur->node;
|
||||
from = ltCur->from;
|
||||
|
||||
goto _OnJumpNext;
|
||||
}
|
||||
|
||||
retPtr = retPtr->getNext();
|
||||
if (retPtr != NULL) {
|
||||
node = retPtr->getValue();
|
||||
goto _OnVisit;
|
||||
}
|
||||
|
||||
return kErrorOk;
|
||||
|
||||
_NoMemory:
|
||||
return setLastError(kErrorNoHeapMemory);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Context - Schedule]
|
||||
// ============================================================================
|
||||
|
||||
Error Context::schedule() {
|
||||
// By default there is no instruction scheduler implemented.
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Context - Cleanup]
|
||||
// ============================================================================
|
||||
|
||||
void Context::cleanup() {
|
||||
VarData** array = _contextVd.getData();
|
||||
size_t length = _contextVd.getLength();
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
VarData* vd = array[i];
|
||||
vd->resetLocalId();
|
||||
vd->resetRegIndex();
|
||||
}
|
||||
|
||||
_contextVd.reset(false);
|
||||
_extraBlock = NULL;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// [asmjit::Context - CompileFunc]
|
||||
// ============================================================================
|
||||
|
||||
Error Context::compile(HLFunc* func) {
|
||||
HLNode* end = func->getEnd();
|
||||
HLNode* stop = end->getNext();
|
||||
|
||||
_func = func;
|
||||
_stop = stop;
|
||||
_extraBlock = end;
|
||||
|
||||
ASMJIT_PROPAGATE_ERROR(fetch());
|
||||
ASMJIT_PROPAGATE_ERROR(removeUnreachableCode());
|
||||
ASMJIT_PROPAGATE_ERROR(livenessAnalysis());
|
||||
|
||||
Compiler* compiler = getCompiler();
|
||||
|
||||
#if !defined(ASMJIT_DISABLE_LOGGER)
|
||||
if (compiler->getAssembler()->hasLogger())
|
||||
ASMJIT_PROPAGATE_ERROR(annotate());
|
||||
#endif // !ASMJIT_DISABLE_LOGGER
|
||||
|
||||
ASMJIT_PROPAGATE_ERROR(translate());
|
||||
|
||||
if (compiler->hasFeature(kCompilerFeatureEnableScheduler))
|
||||
ASMJIT_PROPAGATE_ERROR(schedule());
|
||||
|
||||
// We alter the compiler cursor, because it doesn't make sense to reference
|
||||
// it after compilation - some nodes may disappear and it's forbidden to add
|
||||
// new code after the compilation is done.
|
||||
compiler->_setCursor(NULL);
|
||||
|
||||
return kErrorOk;
|
||||
}
|
||||
|
||||
} // asmjit namespace
|
||||
|
||||
// [Api-End]
|
||||
#include "../apiend.h"
|
||||
|
||||
// [Guard]
|
||||
#endif // !ASMJIT_DISABLE_COMPILER
|
||||
Reference in New Issue
Block a user