// 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::RACFGBuilderT] // ============================================================================ template class RACFGBuilderT { public: BaseRAPass* _pass = nullptr; BaseCompiler* _cc = nullptr; RABlock* _curBlock = nullptr; RABlock* _retBlock = nullptr; FuncNode* _funcNode = nullptr; RARegsStats _blockRegStats {}; uint32_t _exitLabelId = Globals::kInvalidId; ZoneVector _sharedAssignmentsMap {}; // Only used by logging, it's fine to be here to prevent more #ifdefs... bool _hasCode = false; RABlock* _lastLoggedBlock = nullptr; #ifndef ASMJIT_NO_LOGGING Logger* _logger = nullptr; uint32_t _logFlags = FormatOptions::kFlagPositions; 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 RACFGBuilderT(BaseRAPass* pass) noexcept : _pass(pass), _cc(pass->cc()) { #ifndef ASMJIT_NO_LOGGING _logger = _pass->debugLogger(); 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); RABlock* entryBlock = _curBlock; BaseNode* node = _funcNode->next(); if (ASMJIT_UNLIKELY(!node)) return DebugUtils::errored(kErrorInvalidState); _curBlock->setFirst(_funcNode); _curBlock->setLast(_funcNode); RAInstBuilder ib; ZoneVector 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)->onBeforeInvoke(node->as())); else ASMJIT_PROPAGATE(static_cast(this)->onBeforeRet(node->as())); 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(); logNode(inst, kCodeIndentation); uint32_t controlType = BaseInst::kControlNone; ib.reset(); ASMJIT_PROPAGATE(static_cast(this)->onInst(inst, controlType, ib)); if (node->isInvoke()) { ASMJIT_PROPAGATE(static_cast(this)->onInvoke(inst->as(), ib)); } if (node->isFuncRet()) { ASMJIT_PROPAGATE(static_cast(this)->onRet(inst->as(), 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(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