diff options
Diffstat (limited to 'client/asmjit/core/rabuilders_p.h')
| -rw-r--r-- | client/asmjit/core/rabuilders_p.h | 644 |
1 files changed, 0 insertions, 644 deletions
diff --git a/client/asmjit/core/rabuilders_p.h b/client/asmjit/core/rabuilders_p.h deleted file mode 100644 index e14d47f..0000000 --- a/client/asmjit/core/rabuilders_p.h +++ /dev/null @@ -1,644 +0,0 @@ -// AsmJit - Machine code generation for C++ -// -// * Official AsmJit Home Page: https://asmjit.com -// * Official Github Repository: https://github.com/asmjit/asmjit -// -// Copyright (c) 2008-2020 The AsmJit Authors -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - -#ifndef ASMJIT_CORE_RABUILDERS_P_H_INCLUDED -#define ASMJIT_CORE_RABUILDERS_P_H_INCLUDED - -#include "../core/api-config.h" -#ifndef ASMJIT_NO_COMPILER - -#include "../core/formatter.h" -#include "../core/rapass_p.h" - -ASMJIT_BEGIN_NAMESPACE - -//! \cond INTERNAL -//! \addtogroup asmjit_ra -//! \{ - -// ============================================================================ -// [asmjit::RACFGBuilder] -// ============================================================================ - -template<typename This> -class RACFGBuilder { -public: - RAPass* _pass; - BaseCompiler* _cc; - - RABlock* _curBlock; - RABlock* _retBlock; - FuncNode* _funcNode; - RARegsStats _blockRegStats; - uint32_t _exitLabelId; - ZoneVector<uint32_t> _sharedAssignmentsMap; - - // Only used by logging, it's fine to be here to prevent more #ifdefs... - bool _hasCode; - RABlock* _lastLoggedBlock; - -#ifndef ASMJIT_NO_LOGGING - Logger* _logger; - uint32_t _logFlags; - StringTmp<512> _sb; -#endif - - static constexpr uint32_t kRootIndentation = 2; - static constexpr uint32_t kCodeIndentation = 4; - - // NOTE: This is a bit hacky. There are some nodes which are processed twice - // (see `onBeforeInvoke()` and `onBeforeRet()`) as they can insert some nodes - // around them. Since we don't have any flags to mark these we just use their - // position that is [at that time] unassigned. - static constexpr uint32_t kNodePositionDidOnBefore = 0xFFFFFFFFu; - - inline RACFGBuilder(RAPass* pass) noexcept - : _pass(pass), - _cc(pass->cc()), - _curBlock(nullptr), - _retBlock(nullptr), - _funcNode(nullptr), - _blockRegStats{}, - _exitLabelId(Globals::kInvalidId), - _hasCode(false), - _lastLoggedBlock(nullptr) { -#ifndef ASMJIT_NO_LOGGING - _logger = _pass->debugLogger(); - _logFlags = FormatOptions::kFlagPositions; - - if (_logger) - _logFlags |= _logger->flags(); -#endif - } - - inline BaseCompiler* cc() const noexcept { return _cc; } - - // -------------------------------------------------------------------------- - // [Run] - // -------------------------------------------------------------------------- - - //! Called per function by an architecture-specific CFG builder. - Error run() noexcept { - log("[RAPass::BuildCFG]\n"); - ASMJIT_PROPAGATE(prepare()); - - logNode(_funcNode, kRootIndentation); - logBlock(_curBlock, kRootIndentation); - - BaseNode* node = _funcNode->next(); - if (ASMJIT_UNLIKELY(!node)) - return DebugUtils::errored(kErrorInvalidState); - - _curBlock->setFirst(node); - _curBlock->setLast(node); - - RAInstBuilder ib; - ZoneVector<RABlock*> blocksWithUnknownJumps; - - for (;;) { - BaseNode* next = node->next(); - ASMJIT_ASSERT(node->position() == 0 || node->position() == kNodePositionDidOnBefore); - - if (node->isInst()) { - // Instruction | Jump | Invoke | Return - // ------------------------------------ - - // Handle `InstNode`, `InvokeNode`, and `FuncRetNode`. All of them - // share the same interface that provides operands that have read/write - // semantics. - if (ASMJIT_UNLIKELY(!_curBlock)) { - // Unreachable code has to be removed, we cannot allocate registers - // in such code as we cannot do proper liveness analysis in such case. - removeNode(node); - node = next; - continue; - } - - _hasCode = true; - - if (node->isInvoke() || node->isFuncRet()) { - if (node->position() != kNodePositionDidOnBefore) { - // Call and Reg are complicated as they may insert some surrounding - // code around them. The simplest approach is to get the previous - // node, call the `onBefore()` handlers and then check whether - // anything changed and restart if so. By restart we mean that the - // current `node` would go back to the first possible inserted node - // by `onBeforeInvoke()` or `onBeforeRet()`. - BaseNode* prev = node->prev(); - - if (node->type() == BaseNode::kNodeInvoke) - ASMJIT_PROPAGATE(static_cast<This*>(this)->onBeforeInvoke(node->as<InvokeNode>())); - else - ASMJIT_PROPAGATE(static_cast<This*>(this)->onBeforeRet(node->as<FuncRetNode>())); - - if (prev != node->prev()) { - // If this was the first node in the block and something was - // inserted before it then we have to update the first block. - if (_curBlock->first() == node) - _curBlock->setFirst(prev->next()); - - node->setPosition(kNodePositionDidOnBefore); - node = prev->next(); - - // `onBeforeInvoke()` and `onBeforeRet()` can only insert instructions. - ASMJIT_ASSERT(node->isInst()); - } - - // Necessary if something was inserted after `node`, but nothing before. - next = node->next(); - } - else { - // Change the position back to its original value. - node->setPosition(0); - } - } - - InstNode* inst = node->as<InstNode>(); - logNode(inst, kCodeIndentation); - - uint32_t controlType = BaseInst::kControlNone; - ib.reset(); - ASMJIT_PROPAGATE(static_cast<This*>(this)->onInst(inst, controlType, ib)); - - if (node->isInvoke()) { - ASMJIT_PROPAGATE(static_cast<This*>(this)->onInvoke(inst->as<InvokeNode>(), ib)); - } - - if (node->isFuncRet()) { - ASMJIT_PROPAGATE(static_cast<This*>(this)->onRet(inst->as<FuncRetNode>(), ib)); - controlType = BaseInst::kControlReturn; - } - - if (controlType == BaseInst::kControlJump) { - uint32_t fixedRegCount = 0; - for (RATiedReg& tiedReg : ib) { - RAWorkReg* workReg = _pass->workRegById(tiedReg.workId()); - if (workReg->group() == BaseReg::kGroupGp) { - uint32_t useId = tiedReg.useId(); - if (useId == BaseReg::kIdBad) { - useId = _pass->_scratchRegIndexes[fixedRegCount++]; - tiedReg.setUseId(useId); - } - _curBlock->addExitScratchGpRegs(Support::bitMask<uint32_t>(useId)); - } - } - } - - ASMJIT_PROPAGATE(_pass->assignRAInst(inst, _curBlock, ib)); - _blockRegStats.combineWith(ib._stats); - - if (controlType != BaseInst::kControlNone) { - // Support for conditional and unconditional jumps. - if (controlType == BaseInst::kControlJump || controlType == BaseInst::kControlBranch) { - _curBlock->setLast(node); - _curBlock->addFlags(RABlock::kFlagHasTerminator); - _curBlock->makeConstructed(_blockRegStats); - - if (!(inst->instOptions() & BaseInst::kOptionUnfollow)) { - // Jmp/Jcc/Call/Loop/etc... - uint32_t opCount = inst->opCount(); - const Operand* opArray = inst->operands(); - - // Cannot jump anywhere without operands. - if (ASMJIT_UNLIKELY(!opCount)) - return DebugUtils::errored(kErrorInvalidState); - - if (opArray[opCount - 1].isLabel()) { - // Labels are easy for constructing the control flow. - LabelNode* labelNode; - ASMJIT_PROPAGATE(cc()->labelNodeOf(&labelNode, opArray[opCount - 1].as<Label>())); - - RABlock* targetBlock = _pass->newBlockOrExistingAt(labelNode); - if (ASMJIT_UNLIKELY(!targetBlock)) - return DebugUtils::errored(kErrorOutOfMemory); - - targetBlock->makeTargetable(); - ASMJIT_PROPAGATE(_curBlock->appendSuccessor(targetBlock)); - } - else { - // Not a label - could be jump with reg/mem operand, which - // means that it can go anywhere. Such jumps must either be - // annotated so the CFG can be properly constructed, otherwise - // we assume the worst case - can jump to any basic block. - JumpAnnotation* jumpAnnotation = nullptr; - _curBlock->addFlags(RABlock::kFlagHasJumpTable); - - if (inst->type() == BaseNode::kNodeJump) - jumpAnnotation = inst->as<JumpNode>()->annotation(); - - if (jumpAnnotation) { - uint64_t timestamp = _pass->nextTimestamp(); - for (uint32_t id : jumpAnnotation->labelIds()) { - LabelNode* labelNode; - ASMJIT_PROPAGATE(cc()->labelNodeOf(&labelNode, id)); - - RABlock* targetBlock = _pass->newBlockOrExistingAt(labelNode); - if (ASMJIT_UNLIKELY(!targetBlock)) - return DebugUtils::errored(kErrorOutOfMemory); - - // Prevents adding basic-block successors multiple times. - if (!targetBlock->hasTimestamp(timestamp)) { - targetBlock->setTimestamp(timestamp); - targetBlock->makeTargetable(); - ASMJIT_PROPAGATE(_curBlock->appendSuccessor(targetBlock)); - } - } - ASMJIT_PROPAGATE(shareAssignmentAcrossSuccessors(_curBlock)); - } - else { - ASMJIT_PROPAGATE(blocksWithUnknownJumps.append(_pass->allocator(), _curBlock)); - } - } - } - - if (controlType == BaseInst::kControlJump) { - // Unconditional jump makes the code after the jump unreachable, - // which will be removed instantly during the CFG construction; - // as we cannot allocate registers for instructions that are not - // part of any block. Of course we can leave these instructions - // as they are, however, that would only postpone the problem as - // assemblers can't encode instructions that use virtual registers. - _curBlock = nullptr; - } - else { - node = next; - if (ASMJIT_UNLIKELY(!node)) - return DebugUtils::errored(kErrorInvalidState); - - RABlock* consecutiveBlock; - if (node->type() == BaseNode::kNodeLabel) { - if (node->hasPassData()) { - consecutiveBlock = node->passData<RABlock>(); - } - else { - consecutiveBlock = _pass->newBlock(node); - if (ASMJIT_UNLIKELY(!consecutiveBlock)) - return DebugUtils::errored(kErrorOutOfMemory); - node->setPassData<RABlock>(consecutiveBlock); - } - } - else { - consecutiveBlock = _pass->newBlock(node); - if (ASMJIT_UNLIKELY(!consecutiveBlock)) - return DebugUtils::errored(kErrorOutOfMemory); - } - - _curBlock->addFlags(RABlock::kFlagHasConsecutive); - ASMJIT_PROPAGATE(_curBlock->prependSuccessor(consecutiveBlock)); - - _curBlock = consecutiveBlock; - _hasCode = false; - _blockRegStats.reset(); - - if (_curBlock->isConstructed()) - break; - ASMJIT_PROPAGATE(_pass->addBlock(consecutiveBlock)); - - logBlock(_curBlock, kRootIndentation); - continue; - } - } - - if (controlType == BaseInst::kControlReturn) { - _curBlock->setLast(node); - _curBlock->makeConstructed(_blockRegStats); - ASMJIT_PROPAGATE(_curBlock->appendSuccessor(_retBlock)); - - _curBlock = nullptr; - } - } - } - else if (node->type() == BaseNode::kNodeLabel) { - // Label - Basic-Block Management - // ------------------------------ - - if (!_curBlock) { - // If the current code is unreachable the label makes it reachable - // again. We may remove the whole block in the future if it's not - // referenced though. - _curBlock = node->passData<RABlock>(); - - if (_curBlock) { - // If the label has a block assigned we can either continue with - // it or skip it if the block has been constructed already. - if (_curBlock->isConstructed()) - break; - } - else { - // No block assigned - create a new one and assign it. - _curBlock = _pass->newBlock(node); - if (ASMJIT_UNLIKELY(!_curBlock)) - return DebugUtils::errored(kErrorOutOfMemory); - node->setPassData<RABlock>(_curBlock); - } - - _curBlock->makeTargetable(); - _hasCode = false; - _blockRegStats.reset(); - ASMJIT_PROPAGATE(_pass->addBlock(_curBlock)); - } - else { - if (node->hasPassData()) { - RABlock* consecutive = node->passData<RABlock>(); - consecutive->makeTargetable(); - - if (_curBlock == consecutive) { - // The label currently processed is part of the current block. This - // is only possible for multiple labels that are right next to each - // other or labels that are separated by non-code nodes like directives - // and comments. - if (ASMJIT_UNLIKELY(_hasCode)) - return DebugUtils::errored(kErrorInvalidState); - } - else { - // Label makes the current block constructed. There is a chance that the - // Label is not used, but we don't know that at this point. In the worst - // case there would be two blocks next to each other, it's just fine. - ASMJIT_ASSERT(_curBlock->last() != node); - _curBlock->setLast(node->prev()); - _curBlock->addFlags(RABlock::kFlagHasConsecutive); - _curBlock->makeConstructed(_blockRegStats); - - ASMJIT_PROPAGATE(_curBlock->appendSuccessor(consecutive)); - ASMJIT_PROPAGATE(_pass->addBlock(consecutive)); - - _curBlock = consecutive; - _hasCode = false; - _blockRegStats.reset(); - } - } - else { - // First time we see this label. - if (_hasCode) { - // Cannot continue the current block if it already contains some - // code. We need to create a new block and make it a successor. - ASMJIT_ASSERT(_curBlock->last() != node); - _curBlock->setLast(node->prev()); - _curBlock->addFlags(RABlock::kFlagHasConsecutive); - _curBlock->makeConstructed(_blockRegStats); - - RABlock* consecutive = _pass->newBlock(node); - if (ASMJIT_UNLIKELY(!consecutive)) - return DebugUtils::errored(kErrorOutOfMemory); - consecutive->makeTargetable(); - - ASMJIT_PROPAGATE(_curBlock->appendSuccessor(consecutive)); - ASMJIT_PROPAGATE(_pass->addBlock(consecutive)); - - _curBlock = consecutive; - _hasCode = false; - _blockRegStats.reset(); - } - - node->setPassData<RABlock>(_curBlock); - } - } - - if (_curBlock && _curBlock != _lastLoggedBlock) - logBlock(_curBlock, kRootIndentation); - logNode(node, kRootIndentation); - - // Unlikely: Assume that the exit label is reached only once per function. - if (ASMJIT_UNLIKELY(node->as<LabelNode>()->labelId() == _exitLabelId)) { - _curBlock->setLast(node); - _curBlock->makeConstructed(_blockRegStats); - ASMJIT_PROPAGATE(_pass->addExitBlock(_curBlock)); - - _curBlock = nullptr; - } - } - else { - // Other Nodes | Function Exit - // --------------------------- - - logNode(node, kCodeIndentation); - - if (node->type() == BaseNode::kNodeSentinel) { - if (node == _funcNode->endNode()) { - // Make sure we didn't flow here if this is the end of the function sentinel. - if (ASMJIT_UNLIKELY(_curBlock)) - return DebugUtils::errored(kErrorInvalidState); - break; - } - } - else if (node->type() == BaseNode::kNodeFunc) { - // RAPass can only compile a single function at a time. If we - // encountered a function it must be the current one, bail if not. - if (ASMJIT_UNLIKELY(node != _funcNode)) - return DebugUtils::errored(kErrorInvalidState); - // PASS if this is the first node. - } - else { - // PASS if this is a non-interesting or unknown node. - } - } - - // Advance to the next node. - node = next; - - // NOTE: We cannot encounter a NULL node, because every function must be - // terminated by a sentinel (`stop`) node. If we encountered a NULL node it - // means that something went wrong and this node list is corrupted; bail in - // such case. - if (ASMJIT_UNLIKELY(!node)) - return DebugUtils::errored(kErrorInvalidState); - } - - if (_pass->hasDanglingBlocks()) - return DebugUtils::errored(kErrorInvalidState); - - for (RABlock* block : blocksWithUnknownJumps) - handleBlockWithUnknownJump(block); - - return _pass->initSharedAssignments(_sharedAssignmentsMap); - } - - // -------------------------------------------------------------------------- - // [Prepare] - // -------------------------------------------------------------------------- - - //! Prepares the CFG builder of the current function. - Error prepare() noexcept { - FuncNode* func = _pass->func(); - BaseNode* node = nullptr; - - // Create entry and exit blocks. - _funcNode = func; - _retBlock = _pass->newBlockOrExistingAt(func->exitNode(), &node); - - if (ASMJIT_UNLIKELY(!_retBlock)) - return DebugUtils::errored(kErrorOutOfMemory); - - _retBlock->makeTargetable(); - ASMJIT_PROPAGATE(_pass->addExitBlock(_retBlock)); - - if (node != func) { - _curBlock = _pass->newBlock(); - if (ASMJIT_UNLIKELY(!_curBlock)) - return DebugUtils::errored(kErrorOutOfMemory); - } - else { - // Function that has no code at all. - _curBlock = _retBlock; - } - - // Reset everything we may need. - _blockRegStats.reset(); - _exitLabelId = func->exitNode()->labelId(); - - // Initially we assume there is no code in the function body. - _hasCode = false; - - return _pass->addBlock(_curBlock); - } - - // -------------------------------------------------------------------------- - // [Utilities] - // -------------------------------------------------------------------------- - - //! Called when a `node` is removed, e.g. bacause of a dead code elimination. - void removeNode(BaseNode* node) noexcept { - logNode(node, kRootIndentation, "<Removed>"); - cc()->removeNode(node); - } - - //! Handles block with unknown jump, which could be a jump to a jump table. - //! - //! If we encounter such block we basically insert all existing blocks as - //! successors except the function entry block and a natural successor, if - //! such block exists. - Error handleBlockWithUnknownJump(RABlock* block) noexcept { - RABlocks& blocks = _pass->blocks(); - size_t blockCount = blocks.size(); - - // NOTE: Iterate from `1` as the first block is the entry block, we don't - // allow the entry to be a successor of any block. - RABlock* consecutive = block->consecutive(); - for (size_t i = 1; i < blockCount; i++) { - RABlock* candidate = blocks[i]; - if (candidate == consecutive || !candidate->isTargetable()) - continue; - block->appendSuccessor(candidate); - } - - return shareAssignmentAcrossSuccessors(block); - } - - Error shareAssignmentAcrossSuccessors(RABlock* block) noexcept { - if (block->successors().size() <= 1) - return kErrorOk; - - RABlock* consecutive = block->consecutive(); - uint32_t sharedAssignmentId = Globals::kInvalidId; - - for (RABlock* successor : block->successors()) { - if (successor == consecutive) - continue; - - if (successor->hasSharedAssignmentId()) { - if (sharedAssignmentId == Globals::kInvalidId) - sharedAssignmentId = successor->sharedAssignmentId(); - else - _sharedAssignmentsMap[successor->sharedAssignmentId()] = sharedAssignmentId; - } - else { - if (sharedAssignmentId == Globals::kInvalidId) - ASMJIT_PROPAGATE(newSharedAssignmentId(&sharedAssignmentId)); - successor->setSharedAssignmentId(sharedAssignmentId); - } - } - return kErrorOk; - } - - Error newSharedAssignmentId(uint32_t* out) noexcept { - uint32_t id = _sharedAssignmentsMap.size(); - ASMJIT_PROPAGATE(_sharedAssignmentsMap.append(_pass->allocator(), id)); - - *out = id; - return kErrorOk; - } - - // -------------------------------------------------------------------------- - // [Logging] - // -------------------------------------------------------------------------- - -#ifndef ASMJIT_NO_LOGGING - template<typename... Args> - inline void log(const char* fmt, Args&&... args) noexcept { - if (_logger) - _logger->logf(fmt, std::forward<Args>(args)...); - } - - inline void logBlock(RABlock* block, uint32_t indentation = 0) noexcept { - if (_logger) - _logBlock(block, indentation); - } - - inline void logNode(BaseNode* node, uint32_t indentation = 0, const char* action = nullptr) noexcept { - if (_logger) - _logNode(node, indentation, action); - } - - void _logBlock(RABlock* block, uint32_t indentation) noexcept { - _sb.clear(); - _sb.appendChars(' ', indentation); - _sb.appendFormat("{#%u}\n", block->blockId()); - _logger->log(_sb); - _lastLoggedBlock = block; - } - - void _logNode(BaseNode* node, uint32_t indentation, const char* action) noexcept { - _sb.clear(); - _sb.appendChars(' ', indentation); - if (action) { - _sb.append(action); - _sb.append(' '); - } - Formatter::formatNode(_sb, _logFlags, cc(), node); - _sb.append('\n'); - _logger->log(_sb); - } -#else - template<typename... Args> - inline void log(const char* fmt, Args&&... args) noexcept { - DebugUtils::unused(fmt); - DebugUtils::unused(std::forward<Args>(args)...); - } - - inline void logBlock(RABlock* block, uint32_t indentation = 0) noexcept { - DebugUtils::unused(block, indentation); - } - - inline void logNode(BaseNode* node, uint32_t indentation = 0, const char* action = nullptr) noexcept { - DebugUtils::unused(node, indentation, action); - } -#endif -}; - -//! \} -//! \endcond - -ASMJIT_END_NAMESPACE - -#endif // !ASMJIT_NO_COMPILER -#endif // ASMJIT_CORE_RABUILDERS_P_H_INCLUDED |