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; }
Error X86Compiler::finalize() { X86Assembler* assembler = getAssembler(); if (assembler == NULL) return kErrorOk; // Flush the global constant pool. if (_globalConstPoolLabel.isInitialized()) { embedConstPool(_globalConstPoolLabel, _globalConstPool); _globalConstPoolLabel.reset(); _globalConstPool.reset(); } if (_firstNode == NULL) return kErrorOk; X86Context context(this); Error error = kErrorOk; HLNode* node = _firstNode; HLNode* start; // Find all functions and use the `X86Context` to translate/emit them. do { start = node; _resetTokenGenerator(); if (node->getType() == kHLNodeTypeFunc) { node = static_cast<X86FuncNode*>(start)->getEnd(); error = context.compile(static_cast<X86FuncNode*>(start)); if (error != kErrorOk) break; } do { node = node->getNext(); } while (node != NULL && node->getType() != kHLNodeTypeFunc); error = context.serialize(assembler, start, node); context.cleanup(); if (error != kErrorOk) break; } while (node != NULL); reset(false); return error; }
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; } }
Error Context::removeUnreachableCode() { Compiler* compiler = getCompiler(); PodList<HLNode*>::Link* link = _unreachableList.getFirst(); HLNode* stop = getStop(); while (link != nullptr) { HLNode* node = link->getValue(); if (node != nullptr && node->getPrev() != nullptr && 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; // NOTE: The strategy is as follows: // 1. The algorithm removes everything until it finds a first label. // 2. After the first label is found it removes only removable nodes. bool removeEverything = true; do { HLNode* next = node->getNext(); bool remove = node->isRemovable(); if (!remove) { if (node->isLabel()) removeEverything = false; remove = removeEverything; } if (remove) { ASMJIT_TSEC({ this->_traceNode(this, node, "[REMOVED UNREACHABLE] "); }); compiler->removeNode(node); } node = next; } while (node != end); }
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; }
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); }