LCOV - code coverage report
Current view: top level - asmjit - rabuilders_p.h (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 78 199 39.2 %
Date: 2026-11-05 10:19:06 Functions: 3 18 16.7 %

          Line data    Source code
       1             : #ifdef __PLUMED_HAS_ASMJIT
       2             : // This file is part of AsmJit project <https://asmjit.com>
       3             : //
       4             : // See asmjit.h or LICENSE.md for license and copyright information
       5             : // SPDX-License-Identifier: Zlib
       6             : 
       7             : #ifndef ASMJIT_CORE_RABUILDERS_P_H_INCLUDED
       8             : #define ASMJIT_CORE_RABUILDERS_P_H_INCLUDED
       9             : 
      10             : #include "api-config.h"
      11             : #ifndef ASMJIT_NO_COMPILER
      12             : 
      13             : #include "formatter.h"
      14             : #include "rapass_p.h"
      15             : 
      16             : ASMJIT_BEGIN_NAMESPACE
      17             : 
      18             : //! \cond INTERNAL
      19             : //! \addtogroup asmjit_ra
      20             : //! \{
      21             : 
      22             : template<typename This>
      23             : class RACFGBuilderT {
      24             : public:
      25             :   enum : uint32_t {
      26             :     kRootIndentation = 2,
      27             :     kCodeIndentation = 4,
      28             : 
      29             :     // NOTE: This is a bit hacky. There are some nodes which are processed twice (see `onBeforeInvoke()` and
      30             :     // `onBeforeRet()`) as they can insert some nodes around them. Since we don't have any flags to mark these
      31             :     // we just use their position that is [at that time] unassigned.
      32             :     kNodePositionDidOnBefore = 0xFFFFFFFFu
      33             :   };
      34             : 
      35             :   //! \name Members
      36             :   //! \{
      37             : 
      38             :   BaseRAPass* _pass = nullptr;
      39             :   BaseCompiler* _cc = nullptr;
      40             :   RABlock* _curBlock = nullptr;
      41             :   RABlock* _retBlock = nullptr;
      42             :   FuncNode* _funcNode = nullptr;
      43             :   RARegsStats _blockRegStats {};
      44             :   uint32_t _exitLabelId = Globals::kInvalidId;
      45             :   ZoneVector<uint32_t> _sharedAssignmentsMap {};
      46             : 
      47             :   // Only used by logging, it's fine to be here to prevent more #ifdefs...
      48             :   bool _hasCode = false;
      49             :   RABlock* _lastLoggedBlock = nullptr;
      50             : 
      51             : #ifndef ASMJIT_NO_LOGGING
      52             :   Logger* _logger = nullptr;
      53             :   FormatOptions _formatOptions {};
      54             :   StringTmp<512> _sb;
      55             : #endif
      56             : 
      57             :   //! \}
      58             : 
      59       27492 :   inline RACFGBuilderT(BaseRAPass* pass) noexcept
      60       27492 :     : _pass(pass),
      61       27492 :       _cc(pass->cc()) {
      62             : #ifndef ASMJIT_NO_LOGGING
      63       27492 :     _logger = _pass->hasDiagnosticOption(DiagnosticOptions::kRADebugCFG) ? _pass->logger() : nullptr;
      64       27492 :     if (_logger)
      65           0 :       _formatOptions = _logger->options();
      66             : #endif
      67       27492 :   }
      68             : 
      69           0 :   inline BaseCompiler* cc() const noexcept { return _cc; }
      70             : 
      71             :   //! \name Run
      72             :   //! \{
      73             : 
      74             :   //! Called per function by an architecture-specific CFG builder.
      75       27492 :   Error run() noexcept {
      76             :     log("[BuildCFG]\n");
      77       27492 :     ASMJIT_PROPAGATE(prepare());
      78             : 
      79       27492 :     logNode(_funcNode, kRootIndentation);
      80       27492 :     logBlock(_curBlock, kRootIndentation);
      81             : 
      82       27492 :     RABlock* entryBlock = _curBlock;
      83       27492 :     BaseNode* node = _funcNode->next();
      84       27492 :     if (ASMJIT_UNLIKELY(!node))
      85             :       return DebugUtils::errored(kErrorInvalidState);
      86             : 
      87             :     _curBlock->setFirst(_funcNode);
      88       27492 :     _curBlock->setLast(_funcNode);
      89             : 
      90             :     RAInstBuilder ib;
      91             :     ZoneVector<RABlock*> blocksWithUnknownJumps;
      92             : 
      93             :     for (;;) {
      94             :       BaseNode* next = node->next();
      95             :       ASMJIT_ASSERT(node->position() == 0 || node->position() == kNodePositionDidOnBefore);
      96             : 
      97      386792 :       if (node->isInst()) {
      98             :         // Instruction | Jump | Invoke | Return
      99             :         // ------------------------------------
     100             : 
     101             :         // Handle `InstNode`, `InvokeNode`, and `FuncRetNode`. All of them share the same interface that provides
     102             :         // operands that have read/write semantics.
     103      331808 :         if (ASMJIT_UNLIKELY(!_curBlock)) {
     104             :           // Unreachable code has to be removed, we cannot allocate registers in such code as we cannot do proper
     105             :           // liveness analysis in such case.
     106           0 :           removeNode(node);
     107             :           node = next;
     108           0 :           continue;
     109             :         }
     110             : 
     111      331808 :         _hasCode = true;
     112             : 
     113      331808 :         if (node->isInvoke() || node->isFuncRet()) {
     114       37225 :           if (node->position() != kNodePositionDidOnBefore) {
     115             :             // Call and Reg are complicated as they may insert some surrounding code around them. The simplest
     116             :             // approach is to get the previous node, call the `onBefore()` handlers and then check whether
     117             :             // anything changed and restart if so. By restart we mean that the current `node` would go back to
     118             :             // the first possible inserted node by `onBeforeInvoke()` or `onBeforeRet()`.
     119             :             BaseNode* prev = node->prev();
     120             : 
     121       37131 :             if (node->type() == NodeType::kInvoke)
     122        9639 :               ASMJIT_PROPAGATE(static_cast<This*>(this)->onBeforeInvoke(node->as<InvokeNode>()));
     123             :             else
     124       27492 :               ASMJIT_PROPAGATE(static_cast<This*>(this)->onBeforeRet(node->as<FuncRetNode>()));
     125             : 
     126       37131 :             if (prev != node->prev()) {
     127             :               // If this was the first node in the block and something was
     128             :               // inserted before it then we have to update the first block.
     129          94 :               if (_curBlock->first() == node)
     130             :                 _curBlock->setFirst(prev->next());
     131             : 
     132             :               node->setPosition(kNodePositionDidOnBefore);
     133             :               node = prev->next();
     134             : 
     135             :               // `onBeforeInvoke()` and `onBeforeRet()` can only insert instructions.
     136             :               ASMJIT_ASSERT(node->isInst());
     137             :             }
     138             : 
     139             :             // Necessary if something was inserted after `node`, but nothing before.
     140             :             next = node->next();
     141             :           }
     142             :           else {
     143             :             // Change the position back to its original value.
     144             :             node->setPosition(0);
     145             :           }
     146             :         }
     147             : 
     148             :         InstNode* inst = node->as<InstNode>();
     149             :         logNode(inst, kCodeIndentation);
     150             : 
     151      331808 :         InstControlFlow cf = InstControlFlow::kRegular;
     152      331808 :         ib.reset();
     153      331808 :         ASMJIT_PROPAGATE(static_cast<This*>(this)->onInst(inst, cf, ib));
     154             : 
     155      331808 :         if (node->isInvoke()) {
     156        9639 :           ASMJIT_PROPAGATE(static_cast<This*>(this)->onInvoke(inst->as<InvokeNode>(), ib));
     157             :         }
     158             : 
     159      331808 :         if (node->isFuncRet()) {
     160       27492 :           ASMJIT_PROPAGATE(static_cast<This*>(this)->onRet(inst->as<FuncRetNode>(), ib));
     161       27492 :           cf = InstControlFlow::kReturn;
     162             :         }
     163             : 
     164      331808 :         if (cf == InstControlFlow::kJump) {
     165             :           uint32_t fixedRegCount = 0;
     166           0 :           for (RATiedReg& tiedReg : ib) {
     167           0 :             RAWorkReg* workReg = _pass->workRegById(tiedReg.workId());
     168           0 :             if (workReg->group() == RegGroup::kGp) {
     169             :               uint32_t useId = tiedReg.useId();
     170           0 :               if (useId == BaseReg::kIdBad) {
     171           0 :                 useId = _pass->_scratchRegIndexes[fixedRegCount++];
     172             :                 tiedReg.setUseId(useId);
     173             :               }
     174           0 :               _curBlock->addExitScratchGpRegs(Support::bitMask(useId));
     175             :             }
     176             :           }
     177             :         }
     178             : 
     179      663616 :         ASMJIT_PROPAGATE(_pass->assignRAInst(inst, _curBlock, ib));
     180             :         _blockRegStats.combineWith(ib._stats);
     181             : 
     182      331808 :         if (cf != InstControlFlow::kRegular) {
     183             :           // Support for conditional and unconditional jumps.
     184       37131 :           if (cf == InstControlFlow::kJump || cf == InstControlFlow::kBranch) {
     185           0 :             _curBlock->setLast(node);
     186           0 :             _curBlock->addFlags(RABlockFlags::kHasTerminator);
     187           0 :             _curBlock->makeConstructed(_blockRegStats);
     188             : 
     189           0 :             if (!inst->hasOption(InstOptions::kUnfollow)) {
     190             :               // Jmp/Jcc/Call/Loop/etc...
     191             :               uint32_t opCount = inst->opCount();
     192             :               const Operand* opArray = inst->operands();
     193             : 
     194             :               // Cannot jump anywhere without operands.
     195           0 :               if (ASMJIT_UNLIKELY(!opCount))
     196             :                 return DebugUtils::errored(kErrorInvalidState);
     197             : 
     198           0 :               if (opArray[opCount - 1].isLabel()) {
     199             :                 // Labels are easy for constructing the control flow.
     200             :                 LabelNode* labelNode;
     201           0 :                 ASMJIT_PROPAGATE(cc()->labelNodeOf(&labelNode, opArray[opCount - 1].as<Label>()));
     202             : 
     203           0 :                 RABlock* targetBlock = _pass->newBlockOrExistingAt(labelNode);
     204           0 :                 if (ASMJIT_UNLIKELY(!targetBlock))
     205             :                   return DebugUtils::errored(kErrorOutOfMemory);
     206             : 
     207             :                 targetBlock->makeTargetable();
     208           0 :                 ASMJIT_PROPAGATE(_curBlock->appendSuccessor(targetBlock));
     209             :               }
     210             :               else {
     211             :                 // Not a label - could be jump with reg/mem operand, which means that it can go anywhere. Such jumps
     212             :                 // must either be annotated so the CFG can be properly constructed, otherwise we assume the worst case
     213             :                 // - can jump to any basic block.
     214             :                 JumpAnnotation* jumpAnnotation = nullptr;
     215           0 :                 _curBlock->addFlags(RABlockFlags::kHasJumpTable);
     216             : 
     217           0 :                 if (inst->type() == NodeType::kJump)
     218             :                   jumpAnnotation = inst->as<JumpNode>()->annotation();
     219             : 
     220           0 :                 if (jumpAnnotation) {
     221           0 :                   uint64_t timestamp = _pass->nextTimestamp();
     222           0 :                   for (uint32_t id : jumpAnnotation->labelIds()) {
     223             :                     LabelNode* labelNode;
     224           0 :                     ASMJIT_PROPAGATE(cc()->labelNodeOf(&labelNode, id));
     225             : 
     226           0 :                     RABlock* targetBlock = _pass->newBlockOrExistingAt(labelNode);
     227           0 :                     if (ASMJIT_UNLIKELY(!targetBlock))
     228             :                       return DebugUtils::errored(kErrorOutOfMemory);
     229             : 
     230             :                     // Prevents adding basic-block successors multiple times.
     231           0 :                     if (!targetBlock->hasTimestamp(timestamp)) {
     232             :                       targetBlock->setTimestamp(timestamp);
     233             :                       targetBlock->makeTargetable();
     234           0 :                       ASMJIT_PROPAGATE(_curBlock->appendSuccessor(targetBlock));
     235             :                     }
     236             :                   }
     237           0 :                   ASMJIT_PROPAGATE(shareAssignmentAcrossSuccessors(_curBlock));
     238             :                 }
     239             :                 else {
     240           0 :                   ASMJIT_PROPAGATE(blocksWithUnknownJumps.append(_pass->allocator(), _curBlock));
     241             :                 }
     242             :               }
     243             :             }
     244             : 
     245           0 :             if (cf == InstControlFlow::kJump) {
     246             :               // Unconditional jump makes the code after the jump unreachable, which will be removed instantly during
     247             :               // the CFG construction; as we cannot allocate registers for instructions that are not part of any block.
     248             :               // Of course we can leave these instructions as they are, however, that would only postpone the problem
     249             :               // as assemblers can't encode instructions that use virtual registers.
     250           0 :               _curBlock = nullptr;
     251             :             }
     252             :             else {
     253             :               node = next;
     254           0 :               if (ASMJIT_UNLIKELY(!node))
     255             :                 return DebugUtils::errored(kErrorInvalidState);
     256             : 
     257             :               RABlock* consecutiveBlock;
     258           0 :               if (node->type() == NodeType::kLabel) {
     259           0 :                 if (node->hasPassData()) {
     260             :                   consecutiveBlock = node->passData<RABlock>();
     261             :                 }
     262             :                 else {
     263           0 :                   consecutiveBlock = _pass->newBlock(node);
     264           0 :                   if (ASMJIT_UNLIKELY(!consecutiveBlock))
     265             :                     return DebugUtils::errored(kErrorOutOfMemory);
     266             :                   node->setPassData<RABlock>(consecutiveBlock);
     267             :                 }
     268             :               }
     269             :               else {
     270           0 :                 consecutiveBlock = _pass->newBlock(node);
     271           0 :                 if (ASMJIT_UNLIKELY(!consecutiveBlock))
     272             :                   return DebugUtils::errored(kErrorOutOfMemory);
     273             :               }
     274             : 
     275           0 :               _curBlock->addFlags(RABlockFlags::kHasConsecutive);
     276           0 :               ASMJIT_PROPAGATE(_curBlock->prependSuccessor(consecutiveBlock));
     277             : 
     278           0 :               _curBlock = consecutiveBlock;
     279           0 :               _hasCode = false;
     280             :               _blockRegStats.reset();
     281             : 
     282           0 :               if (_curBlock->isConstructed())
     283             :                 break;
     284           0 :               ASMJIT_PROPAGATE(_pass->addBlock(consecutiveBlock));
     285             : 
     286           0 :               logBlock(_curBlock, kRootIndentation);
     287           0 :               continue;
     288           0 :             }
     289             :           }
     290             : 
     291       37131 :           if (cf == InstControlFlow::kReturn) {
     292       27492 :             _curBlock->setLast(node);
     293       27492 :             _curBlock->makeConstructed(_blockRegStats);
     294       27492 :             ASMJIT_PROPAGATE(_curBlock->appendSuccessor(_retBlock));
     295             : 
     296       27492 :             _curBlock = nullptr;
     297             :           }
     298             :         }
     299             :       }
     300       54984 :       else if (node->type() == NodeType::kLabel) {
     301             :         // Label - Basic-Block Management
     302             :         // ------------------------------
     303             : 
     304       27492 :         if (!_curBlock) {
     305             :           // If the current code is unreachable the label makes it reachable again. We may remove the whole block in
     306             :           // the future if it's not referenced though.
     307       27492 :           _curBlock = node->passData<RABlock>();
     308             : 
     309       27492 :           if (_curBlock) {
     310             :             // If the label has a block assigned we can either continue with it or skip it if the block has been
     311             :             // constructed already.
     312       27492 :             if (_curBlock->isConstructed())
     313             :               break;
     314             :           }
     315             :           else {
     316             :             // No block assigned - create a new one and assign it.
     317           0 :             _curBlock = _pass->newBlock(node);
     318           0 :             if (ASMJIT_UNLIKELY(!_curBlock))
     319             :               return DebugUtils::errored(kErrorOutOfMemory);
     320             :             node->setPassData<RABlock>(_curBlock);
     321             :           }
     322             : 
     323       27492 :           _curBlock->makeTargetable();
     324       27492 :           _hasCode = false;
     325             :           _blockRegStats.reset();
     326       27492 :           ASMJIT_PROPAGATE(_pass->addBlock(_curBlock));
     327             :         }
     328             :         else {
     329           0 :           if (node->hasPassData()) {
     330             :             RABlock* consecutive = node->passData<RABlock>();
     331             :             consecutive->makeTargetable();
     332             : 
     333           0 :             if (_curBlock == consecutive) {
     334             :               // The label currently processed is part of the current block. This is only possible for multiple labels
     335             :               // that are right next to each other or labels that are separated by non-code nodes like directives and
     336             :               // comments.
     337           0 :               if (ASMJIT_UNLIKELY(_hasCode))
     338             :                 return DebugUtils::errored(kErrorInvalidState);
     339             :             }
     340             :             else {
     341             :               // Label makes the current block constructed. There is a chance that the Label is not used, but we don't
     342             :               // know that at this point. In the worst case there would be two blocks next to each other, it's just fine.
     343             :               ASMJIT_ASSERT(_curBlock->last() != node);
     344             :               _curBlock->setLast(node->prev());
     345           0 :               _curBlock->addFlags(RABlockFlags::kHasConsecutive);
     346           0 :               _curBlock->makeConstructed(_blockRegStats);
     347             : 
     348           0 :               ASMJIT_PROPAGATE(_curBlock->appendSuccessor(consecutive));
     349           0 :               ASMJIT_PROPAGATE(_pass->addBlock(consecutive));
     350             : 
     351           0 :               _curBlock = consecutive;
     352           0 :               _hasCode = false;
     353             :               _blockRegStats.reset();
     354             :             }
     355             :           }
     356             :           else {
     357             :             // First time we see this label.
     358           0 :             if (_hasCode || _curBlock == entryBlock) {
     359             :               // Cannot continue the current block if it already contains some code or it's a block entry. We need to
     360             :               // create a new block and make it a successor.
     361             :               ASMJIT_ASSERT(_curBlock->last() != node);
     362             :               _curBlock->setLast(node->prev());
     363           0 :               _curBlock->addFlags(RABlockFlags::kHasConsecutive);
     364           0 :               _curBlock->makeConstructed(_blockRegStats);
     365             : 
     366           0 :               RABlock* consecutive = _pass->newBlock(node);
     367           0 :               if (ASMJIT_UNLIKELY(!consecutive))
     368             :                 return DebugUtils::errored(kErrorOutOfMemory);
     369             :               consecutive->makeTargetable();
     370             : 
     371           0 :               ASMJIT_PROPAGATE(_curBlock->appendSuccessor(consecutive));
     372           0 :               ASMJIT_PROPAGATE(_pass->addBlock(consecutive));
     373             : 
     374           0 :               _curBlock = consecutive;
     375           0 :               _hasCode = false;
     376             :               _blockRegStats.reset();
     377             :             }
     378             : 
     379           0 :             node->setPassData<RABlock>(_curBlock);
     380             :           }
     381             :         }
     382             : 
     383       27492 :         if (_curBlock && _curBlock != _lastLoggedBlock)
     384             :           logBlock(_curBlock, kRootIndentation);
     385             :         logNode(node, kRootIndentation);
     386             : 
     387             :         // Unlikely: Assume that the exit label is reached only once per function.
     388       27492 :         if (ASMJIT_UNLIKELY(node->as<LabelNode>()->labelId() == _exitLabelId)) {
     389       27492 :           _curBlock->setLast(node);
     390       27492 :           _curBlock->makeConstructed(_blockRegStats);
     391       27492 :           ASMJIT_PROPAGATE(_pass->addExitBlock(_curBlock));
     392             : 
     393       27492 :           _curBlock = nullptr;
     394             :         }
     395             :       }
     396             :       else {
     397             :         // Other Nodes | Function Exit
     398             :         // ---------------------------
     399             : 
     400             :         logNode(node, kCodeIndentation);
     401             : 
     402       27492 :         if (node->type() == NodeType::kSentinel) {
     403       27492 :           if (node == _funcNode->endNode()) {
     404             :             // Make sure we didn't flow here if this is the end of the function sentinel.
     405       27492 :             if (ASMJIT_UNLIKELY(_curBlock))
     406             :               return DebugUtils::errored(kErrorInvalidState);
     407             :             break;
     408             :           }
     409             :         }
     410           0 :         else if (node->type() == NodeType::kFunc) {
     411             :           // RAPass can only compile a single function at a time. If we
     412             :           // encountered a function it must be the current one, bail if not.
     413           0 :           if (ASMJIT_UNLIKELY(node != _funcNode))
     414             :             return DebugUtils::errored(kErrorInvalidState);
     415             :           // PASS if this is the first node.
     416             :         }
     417             :         else {
     418             :           // PASS if this is a non-interesting or unknown node.
     419             :         }
     420             :       }
     421             : 
     422             :       // Advance to the next node.
     423             :       node = next;
     424             : 
     425             :       // NOTE: We cannot encounter a NULL node, because every function must be terminated by a sentinel (`stop`)
     426             :       // node. If we encountered a NULL node it means that something went wrong and this node list is corrupted;
     427             :       // bail in such case.
     428      359300 :       if (ASMJIT_UNLIKELY(!node))
     429             :         return DebugUtils::errored(kErrorInvalidState);
     430             :     }
     431             : 
     432       27492 :     if (_pass->hasDanglingBlocks())
     433             :       return DebugUtils::errored(kErrorInvalidState);
     434             : 
     435       27492 :     for (RABlock* block : blocksWithUnknownJumps)
     436           0 :       handleBlockWithUnknownJump(block);
     437             : 
     438       27492 :     return _pass->initSharedAssignments(_sharedAssignmentsMap);
     439             :   }
     440             : 
     441             :   //! \}
     442             : 
     443             :   //! \name Prepare
     444             :   //! \{
     445             : 
     446             :   //! Prepares the CFG builder of the current function.
     447       27492 :   Error prepare() noexcept {
     448       27492 :     FuncNode* func = _pass->func();
     449       27492 :     BaseNode* node = nullptr;
     450             : 
     451             :     // Create entry and exit blocks.
     452       27492 :     _funcNode = func;
     453       27492 :     _retBlock = _pass->newBlockOrExistingAt(func->exitNode(), &node);
     454             : 
     455       27492 :     if (ASMJIT_UNLIKELY(!_retBlock))
     456             :       return DebugUtils::errored(kErrorOutOfMemory);
     457             : 
     458             :     _retBlock->makeTargetable();
     459       27492 :     ASMJIT_PROPAGATE(_pass->addExitBlock(_retBlock));
     460             : 
     461       27492 :     if (node != func) {
     462       27492 :       _curBlock = _pass->newBlock();
     463       27492 :       if (ASMJIT_UNLIKELY(!_curBlock))
     464             :         return DebugUtils::errored(kErrorOutOfMemory);
     465             :     }
     466             :     else {
     467             :       // Function that has no code at all.
     468           0 :       _curBlock = _retBlock;
     469             :     }
     470             : 
     471             :     // Reset everything we may need.
     472             :     _blockRegStats.reset();
     473       27492 :     _exitLabelId = func->exitNode()->labelId();
     474             : 
     475             :     // Initially we assume there is no code in the function body.
     476       27492 :     _hasCode = false;
     477             : 
     478       27492 :     return _pass->addBlock(_curBlock);
     479             :   }
     480             : 
     481             :   //! \}
     482             : 
     483             :   //! \name Utilities
     484             :   //! \{
     485             : 
     486             :   //! Called when a `node` is removed, e.g. because of a dead code elimination.
     487           0 :   void removeNode(BaseNode* node) noexcept {
     488             :     logNode(node, kRootIndentation, "<Removed>");
     489           0 :     cc()->removeNode(node);
     490           0 :   }
     491             : 
     492             :   //! Handles block with unknown jump, which could be a jump to a jump table.
     493             :   //!
     494             :   //! If we encounter such block we basically insert all existing blocks as successors except the function entry
     495             :   //! block and a natural successor, if such block exists.
     496           0 :   Error handleBlockWithUnknownJump(RABlock* block) noexcept {
     497           0 :     RABlocks& blocks = _pass->blocks();
     498           0 :     size_t blockCount = blocks.size();
     499             : 
     500             :     // NOTE: Iterate from `1` as the first block is the entry block, we don't
     501             :     // allow the entry to be a successor of any block.
     502             :     RABlock* consecutive = block->consecutive();
     503           0 :     for (size_t i = 1; i < blockCount; i++) {
     504           0 :       RABlock* candidate = blocks[i];
     505           0 :       if (candidate == consecutive || !candidate->isTargetable())
     506           0 :         continue;
     507           0 :       block->appendSuccessor(candidate);
     508             :     }
     509             : 
     510           0 :     return shareAssignmentAcrossSuccessors(block);
     511             :   }
     512             : 
     513           0 :   Error shareAssignmentAcrossSuccessors(RABlock* block) noexcept {
     514           0 :     if (block->successors().size() <= 1)
     515             :       return kErrorOk;
     516             : 
     517             :     RABlock* consecutive = block->consecutive();
     518           0 :     uint32_t sharedAssignmentId = Globals::kInvalidId;
     519             : 
     520           0 :     for (RABlock* successor : block->successors()) {
     521           0 :       if (successor == consecutive)
     522           0 :         continue;
     523             : 
     524           0 :       if (successor->hasSharedAssignmentId()) {
     525           0 :         if (sharedAssignmentId == Globals::kInvalidId)
     526           0 :           sharedAssignmentId = successor->sharedAssignmentId();
     527             :         else
     528           0 :           _sharedAssignmentsMap[successor->sharedAssignmentId()] = sharedAssignmentId;
     529             :       }
     530             :       else {
     531           0 :         if (sharedAssignmentId == Globals::kInvalidId)
     532           0 :           ASMJIT_PROPAGATE(newSharedAssignmentId(&sharedAssignmentId));
     533           0 :         successor->setSharedAssignmentId(sharedAssignmentId);
     534             :       }
     535             :     }
     536             :     return kErrorOk;
     537             :   }
     538             : 
     539           0 :   Error newSharedAssignmentId(uint32_t* out) noexcept {
     540           0 :     uint32_t id = _sharedAssignmentsMap.size();
     541           0 :     ASMJIT_PROPAGATE(_sharedAssignmentsMap.append(_pass->allocator(), id));
     542             : 
     543           0 :     *out = id;
     544           0 :     return kErrorOk;
     545             :   }
     546             : 
     547             :   //! \}
     548             : 
     549             :   //! \name Logging
     550             :   //! \{
     551             : 
     552             : #ifndef ASMJIT_NO_LOGGING
     553             :   template<typename... Args>
     554             :   inline void log(const char* fmt, Args&&... args) noexcept {
     555       27492 :     if (_logger)
     556           0 :       _logger->logf(fmt, std::forward<Args>(args)...);
     557             :   }
     558             : 
     559             :   inline void logBlock(RABlock* block, uint32_t indentation = 0) noexcept {
     560       54984 :     if (_logger)
     561           0 :       _logBlock(block, indentation);
     562             :   }
     563             : 
     564             :   inline void logNode(BaseNode* node, uint32_t indentation = 0, const char* action = nullptr) noexcept {
     565      414284 :     if (_logger)
     566           0 :       _logNode(node, indentation, action);
     567             :   }
     568             : 
     569           0 :   void _logBlock(RABlock* block, uint32_t indentation) noexcept {
     570           0 :     _sb.clear();
     571           0 :     _sb.appendChars(' ', indentation);
     572             :     _sb.appendFormat("{#%u}\n", block->blockId());
     573           0 :     _logger->log(_sb);
     574           0 :     _lastLoggedBlock = block;
     575           0 :   }
     576             : 
     577           0 :   void _logNode(BaseNode* node, uint32_t indentation, const char* action) noexcept {
     578           0 :     _sb.clear();
     579           0 :     _sb.appendChars(' ', indentation);
     580           0 :     if (action) {
     581             :       _sb.append(action);
     582             :       _sb.append(' ');
     583             :     }
     584           0 :     Formatter::formatNode(_sb, _formatOptions, cc(), node);
     585             :     _sb.append('\n');
     586           0 :     _logger->log(_sb);
     587           0 :   }
     588             : #else
     589             :   template<typename... Args>
     590             :   inline void log(const char* fmt, Args&&... args) noexcept {
     591             :     DebugUtils::unused(fmt);
     592             :     DebugUtils::unused(std::forward<Args>(args)...);
     593             :   }
     594             : 
     595             :   inline void logBlock(RABlock* block, uint32_t indentation = 0) noexcept {
     596             :     DebugUtils::unused(block, indentation);
     597             :   }
     598             : 
     599             :   inline void logNode(BaseNode* node, uint32_t indentation = 0, const char* action = nullptr) noexcept {
     600             :     DebugUtils::unused(node, indentation, action);
     601             :   }
     602             : #endif
     603             : 
     604             :   //! \}
     605             : };
     606             : 
     607             : //! \}
     608             : //! \endcond
     609             : 
     610             : ASMJIT_END_NAMESPACE
     611             : 
     612             : #endif // !ASMJIT_NO_COMPILER
     613             : #endif // ASMJIT_CORE_RABUILDERS_P_H_INCLUDED
     614             : 
     615             : #endif // __PLUMED_HAS_ASMJIT

Generated by: LCOV version 1.16