LCOV - code coverage report
Current view: top level - asmjit - ralocal.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 156 346 45.1 %
Date: 2026-11-05 10:19:06 Functions: 8 12 66.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             : #include "api-build_p.h"
       8             : #ifndef ASMJIT_NO_COMPILER
       9             : 
      10             : #include "ralocal_p.h"
      11             : #include "support.h"
      12             : 
      13             : ASMJIT_BEGIN_NAMESPACE
      14             : 
      15             : // RALocalAllocator - Utilities
      16             : // ============================
      17             : 
      18             : static ASMJIT_FORCE_INLINE RATiedReg* RALocal_findTiedRegByWorkId(RATiedReg* tiedRegs, size_t count, uint32_t workId) noexcept {
      19           0 :   for (size_t i = 0; i < count; i++)
      20           0 :     if (tiedRegs[i].workId() == workId)
      21             :       return &tiedRegs[i];
      22             :   return nullptr;
      23             : }
      24             : 
      25             : // RALocalAllocator - Init & Reset
      26             : // ===============================
      27             : 
      28       27492 : Error RALocalAllocator::init() noexcept {
      29             :   PhysToWorkMap* physToWorkMap;
      30             :   WorkToPhysMap* workToPhysMap;
      31             : 
      32       27492 :   physToWorkMap = _pass->newPhysToWorkMap();
      33       27492 :   workToPhysMap = _pass->newWorkToPhysMap();
      34       27492 :   if (!physToWorkMap || !workToPhysMap)
      35             :     return DebugUtils::errored(kErrorOutOfMemory);
      36             : 
      37       27492 :   _curAssignment.initLayout(_pass->_physRegCount, _pass->workRegs());
      38             :   _curAssignment.initMaps(physToWorkMap, workToPhysMap);
      39             : 
      40       27492 :   physToWorkMap = _pass->newPhysToWorkMap();
      41       27492 :   workToPhysMap = _pass->newWorkToPhysMap();
      42       27492 :   _tmpWorkToPhysMap = _pass->newWorkToPhysMap();
      43             : 
      44       27492 :   if (!physToWorkMap || !workToPhysMap || !_tmpWorkToPhysMap)
      45             :     return DebugUtils::errored(kErrorOutOfMemory);
      46             : 
      47       27492 :   _tmpAssignment.initLayout(_pass->_physRegCount, _pass->workRegs());
      48             :   _tmpAssignment.initMaps(physToWorkMap, workToPhysMap);
      49             : 
      50       27492 :   return kErrorOk;
      51             : }
      52             : 
      53             : // RALocalAllocator - Assignment
      54             : // =============================
      55             : 
      56       27492 : Error RALocalAllocator::makeInitialAssignment() noexcept {
      57       27492 :   FuncNode* func = _pass->func();
      58             :   RABlock* entry = _pass->entryBlock();
      59             : 
      60             :   ZoneBitVector& liveIn = entry->liveIn();
      61             :   uint32_t argCount = func->argCount();
      62             :   uint32_t numIter = 1;
      63             : 
      64       54984 :   for (uint32_t iter = 0; iter < numIter; iter++) {
      65       27492 :     for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
      66           0 :       for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
      67             :         // Unassigned argument.
      68           0 :         const RegOnly& regArg = func->argPack(argIndex)[valueIndex];
      69           0 :         if (!regArg.isReg() || !_cc->isVirtIdValid(regArg.id()))
      70           0 :           continue;
      71             : 
      72             :         VirtReg* virtReg = _cc->virtRegById(regArg.id());
      73             : 
      74             :         // Unreferenced argument.
      75             :         RAWorkReg* workReg = virtReg->workReg();
      76           0 :         if (!workReg)
      77           0 :           continue;
      78             : 
      79             :         // Overwritten argument.
      80             :         uint32_t workId = workReg->workId();
      81           0 :         if (!liveIn.bitAt(workId))
      82           0 :           continue;
      83             : 
      84             :         RegGroup group = workReg->group();
      85           0 :         if (_curAssignment.workToPhysId(group, workId) != RAAssignment::kPhysNone)
      86           0 :           continue;
      87             : 
      88           0 :         RegMask allocableRegs = _availableRegs[group] & ~_curAssignment.assigned(group);
      89           0 :         if (iter == 0) {
      90             :           // First iteration: Try to allocate to home RegId.
      91           0 :           if (workReg->hasHomeRegId()) {
      92             :             uint32_t physId = workReg->homeRegId();
      93           0 :             if (Support::bitTest(allocableRegs, physId)) {
      94           0 :               _curAssignment.assign(group, workId, physId, true);
      95           0 :               _pass->_argsAssignment.assignRegInPack(argIndex, valueIndex, workReg->type(), physId, workReg->typeId());
      96           0 :               continue;
      97             :             }
      98             :           }
      99             : 
     100             :           numIter = 2;
     101             :         }
     102             :         else {
     103             :           // Second iteration: Pick any other register if the is an unassigned one or assign to stack.
     104           0 :           if (allocableRegs) {
     105             :             uint32_t physId = Support::ctz(allocableRegs);
     106           0 :             _curAssignment.assign(group, workId, physId, true);
     107           0 :             _pass->_argsAssignment.assignRegInPack(argIndex, valueIndex, workReg->type(), physId, workReg->typeId());
     108             :           }
     109             :           else {
     110             :             // This register will definitely need stack, create the slot now and assign also `argIndex`
     111             :             // to it. We will patch `_argsAssignment` later after RAStackAllocator finishes.
     112           0 :             RAStackSlot* slot = _pass->getOrCreateStackSlot(workReg);
     113           0 :             if (ASMJIT_UNLIKELY(!slot))
     114             :               return DebugUtils::errored(kErrorOutOfMemory);
     115             : 
     116             :             // This means STACK_ARG may be moved to STACK.
     117             :             workReg->addFlags(RAWorkRegFlags::kStackArgToStack);
     118           0 :             _pass->_numStackArgsToStackSlots++;
     119             :           }
     120             :         }
     121             :       }
     122             :     }
     123             :   }
     124             : 
     125             :   return kErrorOk;
     126             : }
     127             : 
     128           0 : Error RALocalAllocator::replaceAssignment(const PhysToWorkMap* physToWorkMap) noexcept {
     129           0 :   _curAssignment.copyFrom(physToWorkMap);
     130           0 :   return kErrorOk;
     131             : }
     132             : 
     133           0 : Error RALocalAllocator::switchToAssignment(PhysToWorkMap* dstPhysToWorkMap, const ZoneBitVector& liveIn, bool dstReadOnly, bool tryMode) noexcept {
     134           0 :   RAAssignment dst;
     135             :   RAAssignment& cur = _curAssignment;
     136             : 
     137           0 :   dst.initLayout(_pass->_physRegCount, _pass->workRegs());
     138           0 :   dst.initMaps(dstPhysToWorkMap, _tmpWorkToPhysMap);
     139           0 :   dst.assignWorkIdsFromPhysIds();
     140             : 
     141           0 :   if (tryMode)
     142             :     return kErrorOk;
     143             : 
     144           0 :   for (RegGroup group : RegGroupVirtValues{}) {
     145             :     // STEP 1
     146             :     // ------
     147             :     //
     148             :     //   - KILL all registers that are not live at `dst`,
     149             :     //   - SPILL all registers that are not assigned at `dst`.
     150             : 
     151             :     if (!tryMode) {
     152             :       Support::BitWordIterator<RegMask> it(cur.assigned(group));
     153           0 :       while (it.hasNext()) {
     154             :         uint32_t physId = it.next();
     155             :         uint32_t workId = cur.physToWorkId(group, physId);
     156             : 
     157             :         // Must be true as we iterate over assigned registers.
     158             :         ASMJIT_ASSERT(workId != RAAssignment::kWorkNone);
     159             : 
     160             :         // KILL if it's not live on entry.
     161           0 :         if (!liveIn.bitAt(workId)) {
     162             :           onKillReg(group, workId, physId);
     163           0 :           continue;
     164             :         }
     165             : 
     166             :         // SPILL if it's not assigned on entry.
     167             :         uint32_t altId = dst.workToPhysId(group, workId);
     168           0 :         if (altId == RAAssignment::kPhysNone) {
     169           0 :           ASMJIT_PROPAGATE(onSpillReg(group, workId, physId));
     170             :         }
     171             :       }
     172             :     }
     173             : 
     174             :     // STEP 2
     175             :     // ------
     176             :     //
     177             :     //   - MOVE and SWAP registers from their current assignments into their DST assignments.
     178             :     //   - Build `willLoadRegs` mask of registers scheduled for `onLoadReg()`.
     179             : 
     180             :     // Current run-id (1 means more aggressive decisions).
     181             :     int32_t runId = -1;
     182             :     // Remaining registers scheduled for `onLoadReg()`.
     183             :     RegMask willLoadRegs = 0;
     184             :     // Remaining registers to be allocated in this loop.
     185             :     RegMask affectedRegs = dst.assigned(group);
     186             : 
     187           0 :     while (affectedRegs) {
     188           0 :       if (++runId == 2) {
     189             :         if (!tryMode)
     190             :           return DebugUtils::errored(kErrorInvalidState);
     191             : 
     192             :         // Stop in `tryMode` if we haven't done anything in past two rounds.
     193             :         break;
     194             :       }
     195             : 
     196             :       Support::BitWordIterator<RegMask> it(affectedRegs);
     197           0 :       while (it.hasNext()) {
     198             :         uint32_t physId = it.next();
     199             :         RegMask physMask = Support::bitMask<RegMask>(physId);
     200             : 
     201             :         uint32_t curWorkId = cur.physToWorkId(group, physId);
     202             :         uint32_t dstWorkId = dst.physToWorkId(group, physId);
     203             : 
     204             :         // The register must have assigned `dstWorkId` as we only iterate over assigned regs.
     205             :         ASMJIT_ASSERT(dstWorkId != RAAssignment::kWorkNone);
     206             : 
     207           0 :         if (curWorkId != RAAssignment::kWorkNone) {
     208             :           // Both assigned.
     209           0 :           if (curWorkId != dstWorkId) {
     210             :             // Wait a bit if this is the first run, we may avoid this if `curWorkId` moves out.
     211           0 :             if (runId <= 0)
     212           0 :               continue;
     213             : 
     214             :             uint32_t altPhysId = cur.workToPhysId(group, dstWorkId);
     215           0 :             if (altPhysId == RAAssignment::kPhysNone)
     216           0 :               continue;
     217             : 
     218             :             // Reset as we will do some changes to the current assignment.
     219             :             runId = -1;
     220             : 
     221           0 :             if (_archTraits->hasInstRegSwap(group)) {
     222           0 :               ASMJIT_PROPAGATE(onSwapReg(group, curWorkId, physId, dstWorkId, altPhysId));
     223             :             }
     224             :             else {
     225             :               // SPILL the reg if it's not dirty in DST, otherwise try to MOVE.
     226           0 :               if (!cur.isPhysDirty(group, physId)) {
     227             :                 ASMJIT_PROPAGATE(onKillReg(group, curWorkId, physId));
     228             :               }
     229             :               else {
     230           0 :                 RegMask allocableRegs = _pass->_availableRegs[group] & ~cur.assigned(group);
     231             : 
     232             :                 // If possible don't conflict with assigned regs at DST.
     233           0 :                 if (allocableRegs & ~dst.assigned(group))
     234             :                   allocableRegs &= ~dst.assigned(group);
     235             : 
     236           0 :                 if (allocableRegs) {
     237             :                   // MOVE is possible, thus preferred.
     238             :                   uint32_t tmpPhysId = Support::ctz(allocableRegs);
     239             : 
     240           0 :                   ASMJIT_PROPAGATE(onMoveReg(group, curWorkId, tmpPhysId, physId));
     241           0 :                   _pass->_clobberedRegs[group] |= Support::bitMask(tmpPhysId);
     242             :                 }
     243             :                 else {
     244             :                   // MOVE is impossible, must SPILL.
     245           0 :                   ASMJIT_PROPAGATE(onSpillReg(group, curWorkId, physId));
     246             :                 }
     247             :               }
     248             : 
     249           0 :               goto Cleared;
     250             :             }
     251             :           }
     252             :         }
     253             :         else {
     254           0 : Cleared:
     255             :           // DST assigned, CUR unassigned.
     256             :           uint32_t altPhysId = cur.workToPhysId(group, dstWorkId);
     257           0 :           if (altPhysId == RAAssignment::kPhysNone) {
     258           0 :             if (liveIn.bitAt(dstWorkId))
     259           0 :               willLoadRegs |= physMask; // Scheduled for `onLoadReg()`.
     260           0 :             affectedRegs &= ~physMask;  // Unaffected from now.
     261           0 :             continue;
     262             :           }
     263           0 :           ASMJIT_PROPAGATE(onMoveReg(group, dstWorkId, physId, altPhysId));
     264             :         }
     265             : 
     266             :         // Both DST and CUR assigned to the same reg or CUR just moved to DST.
     267           0 :         if ((dst.dirty(group) & physMask) != (cur.dirty(group) & physMask)) {
     268           0 :           if ((dst.dirty(group) & physMask) == 0) {
     269             :             // CUR dirty, DST not dirty (the assert is just to visualize the condition).
     270             :             ASMJIT_ASSERT(!dst.isPhysDirty(group, physId) && cur.isPhysDirty(group, physId));
     271             : 
     272             :             // If `dstReadOnly` is true it means that that block was already processed and we cannot change from
     273             :             // CLEAN to DIRTY. In that case the register has to be saved as it cannot enter the block DIRTY.
     274           0 :             if (dstReadOnly)
     275           0 :               ASMJIT_PROPAGATE(onSaveReg(group, dstWorkId, physId));
     276             :             else
     277             :               dst.makeDirty(group, dstWorkId, physId);
     278             :           }
     279             :           else {
     280             :             // DST dirty, CUR not dirty (the assert is just to visualize the condition).
     281             :             ASMJIT_ASSERT(dst.isPhysDirty(group, physId) && !cur.isPhysDirty(group, physId));
     282             : 
     283             :             cur.makeDirty(group, dstWorkId, physId);
     284             :           }
     285             :         }
     286             : 
     287             :         // Must match now...
     288             :         ASMJIT_ASSERT(dst.physToWorkId(group, physId) == cur.physToWorkId(group, physId));
     289             :         ASMJIT_ASSERT(dst.isPhysDirty(group, physId) == cur.isPhysDirty(group, physId));
     290             : 
     291             :         runId = -1;
     292           0 :         affectedRegs &= ~physMask;
     293             :       }
     294             :     }
     295             : 
     296             :     // STEP 3
     297             :     // ------
     298             :     //
     299             :     //   - Load registers specified by `willLoadRegs`.
     300             : 
     301             :     {
     302             :       Support::BitWordIterator<RegMask> it(willLoadRegs);
     303           0 :       while (it.hasNext()) {
     304             :         uint32_t physId = it.next();
     305             : 
     306           0 :         if (!cur.isPhysAssigned(group, physId)) {
     307             :           uint32_t workId = dst.physToWorkId(group, physId);
     308             : 
     309             :           // The algorithm is broken if it tries to load a register that is not in LIVE-IN.
     310             :           ASMJIT_ASSERT(liveIn.bitAt(workId) == true);
     311             : 
     312           0 :           ASMJIT_PROPAGATE(onLoadReg(group, workId, physId));
     313           0 :           if (dst.isPhysDirty(group, physId))
     314             :             cur.makeDirty(group, workId, physId);
     315             :           ASMJIT_ASSERT(dst.isPhysDirty(group, physId) == cur.isPhysDirty(group, physId));
     316             :         }
     317             :         else {
     318             :           // Not possible otherwise.
     319             :           ASMJIT_ASSERT(tryMode == true);
     320             :         }
     321             :       }
     322             :     }
     323             :   }
     324             : 
     325             :   if (!tryMode) {
     326             :     // Here is a code that dumps the conflicting part if something fails here:
     327             :     // if (!dst.equals(cur)) {
     328             :     //   uint32_t physTotal = dst._layout.physTotal;
     329             :     //   uint32_t workCount = dst._layout.workCount;
     330             :     //
     331             :     //   fprintf(stderr, "Dirty    DST=0x%08X CUR=0x%08X\n", dst.dirty(RegGroup::kGp), cur.dirty(RegGroup::kGp));
     332             :     //   fprintf(stderr, "Assigned DST=0x%08X CUR=0x%08X\n", dst.assigned(RegGroup::kGp), cur.assigned(RegGroup::kGp));
     333             :     //
     334             :     //   for (uint32_t physId = 0; physId < physTotal; physId++) {
     335             :     //     uint32_t dstWorkId = dst._physToWorkMap->workIds[physId];
     336             :     //     uint32_t curWorkId = cur._physToWorkMap->workIds[physId];
     337             :     //     if (dstWorkId != curWorkId)
     338             :     //       fprintf(stderr, "[PhysIdWork] PhysId=%u WorkId[DST(%u) != CUR(%u)]\n", physId, dstWorkId, curWorkId);
     339             :     //   }
     340             :     //
     341             :     //   for (uint32_t workId = 0; workId < workCount; workId++) {
     342             :     //     uint32_t dstPhysId = dst._workToPhysMap->physIds[workId];
     343             :     //     uint32_t curPhysId = cur._workToPhysMap->physIds[workId];
     344             :     //     if (dstPhysId != curPhysId)
     345             :     //       fprintf(stderr, "[WorkToPhys] WorkId=%u PhysId[DST(%u) != CUR(%u)]\n", workId, dstPhysId, curPhysId);
     346             :     //   }
     347             :     // }
     348             :     ASMJIT_ASSERT(dst.equals(cur));
     349             :   }
     350             : 
     351             :   return kErrorOk;
     352             : }
     353             : 
     354       27492 : Error RALocalAllocator::spillScratchGpRegsBeforeEntry(RegMask scratchRegs) noexcept {
     355             :   RegGroup group = RegGroup::kGp;
     356             :   Support::BitWordIterator<RegMask> it(scratchRegs);
     357             : 
     358       27492 :   while (it.hasNext()) {
     359             :     uint32_t physId = it.next();
     360           0 :     if (_curAssignment.isPhysAssigned(group, physId)) {
     361             :       uint32_t workId = _curAssignment.physToWorkId(group, physId);
     362           0 :       ASMJIT_PROPAGATE(onSpillReg(group, workId, physId));
     363             :     }
     364             :   }
     365             : 
     366             :   return kErrorOk;
     367             : }
     368             : 
     369             : // RALocalAllocator - Allocation
     370             : // =============================
     371             : 
     372      331808 : Error RALocalAllocator::allocInst(InstNode* node) noexcept {
     373             :   RAInst* raInst = node->passData<RAInst>();
     374             : 
     375             :   RATiedReg* outTiedRegs[Globals::kMaxPhysRegs];
     376             :   RATiedReg* dupTiedRegs[Globals::kMaxPhysRegs];
     377             :   RATiedReg* consecutiveRegs[kMaxConsecutiveRegs];
     378             : 
     379             :   // The cursor must point to the previous instruction for a possible instruction insertion.
     380      331808 :   _cc->_setCursor(node->prev());
     381             : 
     382      331808 :   _node = node;
     383      331808 :   _raInst = raInst;
     384      331808 :   _tiedTotal = raInst->_tiedTotal;
     385      331808 :   _tiedCount = raInst->_tiedCount;
     386             : 
     387             :   // Whether we already replaced register operand with memory operand.
     388             :   bool rmAllocated = false;
     389             : 
     390     1659040 :   for (RegGroup group : RegGroupVirtValues{}) {
     391             :     uint32_t i, count = this->tiedCount(group);
     392             :     RATiedReg* tiedRegs = this->tiedRegs(group);
     393             : 
     394     1327232 :     RegMask willUse = _raInst->_usedRegs[group];
     395     1327232 :     RegMask willOut = _raInst->_clobberedRegs[group];
     396             :     RegMask willFree = 0;
     397             : 
     398             :     uint32_t usePending = count;
     399             :     uint32_t outTiedCount = 0;
     400             :     uint32_t dupTiedCount = 0;
     401             :     uint32_t consecutiveMask = 0;
     402             : 
     403             :     // STEP 1
     404             :     // ------
     405             :     //
     406             :     // Calculate `willUse` and `willFree` masks based on tied registers we have. In addition, aggregate information
     407             :     // regarding consecutive registers used by this instruction. We need that to make USE/OUT assignments.
     408             :     //
     409             :     // We don't do any assignment decisions at this stage as we just need to collect some information first. Then,
     410             :     // after we populate all masks needed we can finally make some decisions in the second loop. The main reason
     411             :     // for this is that we really need `willFree` to make assignment decisions for `willUse`, because if we mark
     412             :     // some registers that will be freed, we can consider them in decision making afterwards.
     413             : 
     414     1941686 :     for (i = 0; i < count; i++) {
     415      614454 :       RATiedReg* tiedReg = &tiedRegs[i];
     416             : 
     417      614454 :       if (tiedReg->hasAnyConsecutiveFlag()) {
     418           0 :         uint32_t consecutiveOffset = tiedReg->isLeadConsecutive() ? uint32_t(0) : tiedReg->consecutiveData();
     419             : 
     420           0 :         if (ASMJIT_UNLIKELY(Support::bitTest(consecutiveMask, consecutiveOffset)))
     421             :           return DebugUtils::errored(kErrorInvalidState);
     422             : 
     423           0 :         consecutiveMask |= Support::bitMask(consecutiveOffset);
     424           0 :         consecutiveRegs[consecutiveOffset] = tiedReg;
     425             :       }
     426             : 
     427             :       // Add OUT and KILL to `outPending` for CLOBBERing and/or OUT assignment.
     428      614454 :       if (tiedReg->isOutOrKill())
     429      559912 :         outTiedRegs[outTiedCount++] = tiedReg;
     430             : 
     431      614454 :       if (tiedReg->isDuplicate())
     432           0 :         dupTiedRegs[dupTiedCount++] = tiedReg;
     433             : 
     434      614454 :       if (!tiedReg->isUse()) {
     435             :         tiedReg->markUseDone();
     436      283174 :         usePending--;
     437      283174 :         continue;
     438             :       }
     439             : 
     440             :       // Don't assign anything here if this is a consecutive USE - we will handle this in STEP 2 instead.
     441      331280 :       if (tiedReg->isUseConsecutive())
     442           0 :         continue;
     443             : 
     444             :       uint32_t workId = tiedReg->workId();
     445             :       uint32_t assignedId = _curAssignment.workToPhysId(group, workId);
     446             : 
     447      331280 :       if (tiedReg->hasUseId()) {
     448             :         // If the register has `useId` it means it can only be allocated in that register.
     449             :         RegMask useMask = Support::bitMask(tiedReg->useId());
     450             : 
     451             :         // RAInstBuilder must have collected `usedRegs` on-the-fly.
     452             :         ASMJIT_ASSERT((willUse & useMask) != 0);
     453             : 
     454       37365 :         if (assignedId == tiedReg->useId()) {
     455             :           // If the register is already allocated in this one, mark it done and continue.
     456             :           tiedReg->markUseDone();
     457       35117 :           if (tiedReg->isWrite())
     458             :             _curAssignment.makeDirty(group, workId, assignedId);
     459       35117 :           usePending--;
     460       35117 :           willUse |= useMask;
     461             :         }
     462             :         else {
     463        2248 :           willFree |= useMask & _curAssignment.assigned(group);
     464             :         }
     465             :       }
     466             :       else {
     467             :         // Check if the register must be moved to `allocableRegs`.
     468             :         RegMask allocableRegs = tiedReg->useRegMask();
     469      293915 :         if (assignedId != RAAssignment::kPhysNone) {
     470             :           RegMask assignedMask = Support::bitMask(assignedId);
     471      254524 :           if ((allocableRegs & ~willUse) & assignedMask) {
     472             :             tiedReg->setUseId(assignedId);
     473             :             tiedReg->markUseDone();
     474      254524 :             if (tiedReg->isWrite())
     475             :               _curAssignment.makeDirty(group, workId, assignedId);
     476      254524 :             usePending--;
     477      254524 :             willUse |= assignedMask;
     478             :           }
     479             :           else {
     480           0 :             willFree |= assignedMask;
     481             :           }
     482             :         }
     483             :       }
     484             :     }
     485             : 
     486             :     // STEP 2
     487             :     // ------
     488             :     //
     489             :     // Verify that all the consecutive registers are really consecutive. Terminate if there is a gap. In addition,
     490             :     // decide which USE ids will be used in case that this consecutive sequence is USE (OUT registers are allocated
     491             :     // in a different step).
     492             :     uint32_t consecutiveCount = 0;
     493             : 
     494     1327232 :     if (consecutiveMask) {
     495           0 :       if ((consecutiveMask & (consecutiveMask + 1u)) != 0)
     496             :         return DebugUtils::errored(kErrorInvalidState);
     497             : 
     498             :       // Count of trailing ones is the count of consecutive registers. There cannot be gap.
     499           0 :       consecutiveCount = Support::ctz(~consecutiveMask);
     500             : 
     501             :       // Prioritize allocation that would result in least moves even when moving registers away from their homes.
     502           0 :       RATiedReg* lead = consecutiveRegs[0];
     503             : 
     504             :       // Assign the best possible USE Ids to all consecutives.
     505           0 :       if (lead->isUseConsecutive()) {
     506             :         uint32_t bestScore = 0;
     507             :         uint32_t bestLeadReg = 0xFFFFFFFF;
     508           0 :         RegMask allocableRegs = (_availableRegs[group] | willFree) & ~willUse;
     509             : 
     510             :         uint32_t assignments[kMaxConsecutiveRegs];
     511             : 
     512           0 :         for (i = 0; i < consecutiveCount; i++)
     513           0 :           assignments[i] = _curAssignment.workToPhysId(group, consecutiveRegs[i]->workId());
     514             : 
     515             :         Support::BitWordIterator<uint32_t> it(lead->useRegMask());
     516           0 :         while (it.hasNext()) {
     517             :           uint32_t regIndex = it.next();
     518           0 :           if (Support::bitTest(lead->useRegMask(), regIndex)) {
     519             :             uint32_t score = 15;
     520             : 
     521           0 :             for (i = 0; i < consecutiveCount; i++) {
     522           0 :               uint32_t consecutiveIndex = regIndex + i;
     523           0 :               if (!Support::bitTest(allocableRegs, consecutiveIndex)) {
     524             :                 score = 0;
     525             :                 break;
     526             :               }
     527             : 
     528           0 :               RAWorkReg* workReg = workRegById(consecutiveRegs[i]->workId());
     529           0 :               score += uint32_t(workReg->homeRegId() == consecutiveIndex);
     530           0 :               score += uint32_t(assignments[i] == consecutiveIndex) * 2;
     531             :             }
     532             : 
     533           0 :             if (score > bestScore) {
     534             :               bestScore = score;
     535             :               bestLeadReg = regIndex;
     536             :             }
     537             :           }
     538             :         }
     539             : 
     540           0 :         if (bestLeadReg == 0xFFFFFFFF)
     541           0 :           return DebugUtils::errored(kErrorConsecutiveRegsAllocation);
     542             : 
     543           0 :         for (i = 0; i < consecutiveCount; i++) {
     544           0 :           uint32_t consecutiveIndex = bestLeadReg + i;
     545             : 
     546           0 :           RATiedReg* tiedReg = consecutiveRegs[i];
     547             :           RegMask useMask = Support::bitMask(consecutiveIndex);
     548             : 
     549             :           uint32_t workId = tiedReg->workId();
     550             :           uint32_t assignedId = _curAssignment.workToPhysId(group, workId);
     551             : 
     552             :           tiedReg->setUseId(consecutiveIndex);
     553             : 
     554           0 :           if (assignedId == consecutiveIndex) {
     555             :             // If the register is already allocated in this one, mark it done and continue.
     556             :             tiedReg->markUseDone();
     557           0 :             if (tiedReg->isWrite())
     558             :               _curAssignment.makeDirty(group, workId, assignedId);
     559           0 :             usePending--;
     560           0 :             willUse |= useMask;
     561             :           }
     562             :           else {
     563           0 :             willUse |= useMask;
     564           0 :             willFree |= useMask & _curAssignment.assigned(group);
     565             :           }
     566             :         }
     567             :       }
     568             :     }
     569             : 
     570             :     // STEP 3
     571             :     // ------
     572             :     //
     573             :     // Do some decision making to find the best candidates of registers that need to be assigned, moved, and/or
     574             :     // spilled. Only USE registers are considered here, OUT will be decided later after all CLOBBERed and OUT
     575             :     // registers are unassigned.
     576             : 
     577     1327232 :     if (usePending) {
     578             :       // TODO: Not sure `liveRegs` should be used, maybe willUse and willFree would be enough and much more clear.
     579             : 
     580             :       // All registers that are currently alive without registers that will be freed.
     581       33640 :       RegMask liveRegs = _curAssignment.assigned(group) & ~willFree;
     582             : 
     583      130709 :       for (i = 0; i < count; i++) {
     584       97069 :         RATiedReg* tiedReg = &tiedRegs[i];
     585       97069 :         if (tiedReg->isUseDone())
     586       55430 :           continue;
     587             : 
     588             :         uint32_t workId = tiedReg->workId();
     589             :         uint32_t assignedId = _curAssignment.workToPhysId(group, workId);
     590             : 
     591             :         // REG/MEM: Patch register operand to memory operand if not allocated.
     592       41639 :         if (!rmAllocated && tiedReg->hasUseRM()) {
     593       23166 :           if (assignedId == RAAssignment::kPhysNone && Support::isPowerOf2(tiedReg->useRewriteMask())) {
     594             :             RAWorkReg* workReg = workRegById(tiedReg->workId());
     595       23106 :             uint32_t opIndex = Support::ctz(tiedReg->useRewriteMask()) / uint32_t(sizeof(Operand) / sizeof(uint32_t));
     596             :             uint32_t rmSize = tiedReg->rmSize();
     597             : 
     598       23106 :             if (rmSize <= workReg->virtReg()->virtSize()) {
     599       22695 :               Operand& op = node->operands()[opIndex];
     600       22695 :               op = _pass->workRegAsMem(workReg);
     601             :               op.as<BaseMem>().setSize(rmSize);
     602       22695 :               tiedReg->_useRewriteMask = 0;
     603             : 
     604             :               tiedReg->markUseDone();
     605       22695 :               usePending--;
     606             : 
     607             :               rmAllocated = true;
     608       22695 :               continue;
     609       22695 :             }
     610             :           }
     611             :         }
     612             : 
     613       18944 :         if (!tiedReg->hasUseId()) {
     614             :           // DECIDE where to assign the USE register.
     615       16696 :           RegMask allocableRegs = tiedReg->useRegMask() & ~(willFree | willUse);
     616       16696 :           uint32_t useId = decideOnAssignment(group, workId, assignedId, allocableRegs);
     617             : 
     618             :           RegMask useMask = Support::bitMask(useId);
     619       16696 :           willUse |= useMask;
     620       16696 :           willFree |= useMask & liveRegs;
     621             :           tiedReg->setUseId(useId);
     622             : 
     623       16696 :           if (assignedId != RAAssignment::kPhysNone) {
     624             :             RegMask assignedMask = Support::bitMask(assignedId);
     625             : 
     626           0 :             willFree |= assignedMask;
     627           0 :             liveRegs &= ~assignedMask;
     628             : 
     629             :             // OPTIMIZATION: Assign the USE register here if it's possible.
     630           0 :             if (!(liveRegs & useMask)) {
     631           0 :               ASMJIT_PROPAGATE(onMoveReg(group, workId, useId, assignedId));
     632             :               tiedReg->markUseDone();
     633           0 :               if (tiedReg->isWrite())
     634             :                 _curAssignment.makeDirty(group, workId, useId);
     635           0 :               usePending--;
     636             :             }
     637             :           }
     638             :           else {
     639             :             // OPTIMIZATION: Assign the USE register here if it's possible.
     640       16696 :             if (!(liveRegs & useMask)) {
     641       15728 :               ASMJIT_PROPAGATE(onLoadReg(group, workId, useId));
     642             :               tiedReg->markUseDone();
     643       15728 :               if (tiedReg->isWrite())
     644             :                 _curAssignment.makeDirty(group, workId, useId);
     645       15728 :               usePending--;
     646             :             }
     647             :           }
     648             : 
     649       16696 :           liveRegs |= useMask;
     650             :         }
     651             :       }
     652             :     }
     653             : 
     654             :     // Initially all used regs will be marked as clobbered.
     655     1327232 :     RegMask clobberedByInst = willUse | willOut;
     656             : 
     657             :     // STEP 4
     658             :     // ------
     659             :     //
     660             :     // Free all registers that we marked as `willFree`. Only registers that are not USEd by the instruction are
     661             :     // considered as we don't want to free regs we need.
     662             : 
     663     1327232 :     if (willFree) {
     664        2883 :       RegMask allocableRegs = _availableRegs[group] & ~(_curAssignment.assigned(group) | willFree | willUse | willOut);
     665             :       Support::BitWordIterator<RegMask> it(willFree);
     666             : 
     667             :       do {
     668             :         uint32_t assignedId = it.next();
     669        2892 :         if (_curAssignment.isPhysAssigned(group, assignedId)) {
     670             :           uint32_t workId = _curAssignment.physToWorkId(group, assignedId);
     671             : 
     672             :           // DECIDE whether to MOVE or SPILL.
     673        2892 :           if (allocableRegs) {
     674         968 :             uint32_t reassignedId = decideOnReassignment(group, workId, assignedId, allocableRegs);
     675         968 :             if (reassignedId != RAAssignment::kPhysNone) {
     676         479 :               ASMJIT_PROPAGATE(onMoveReg(group, workId, reassignedId, assignedId));
     677         479 :               allocableRegs ^= Support::bitMask(reassignedId);
     678             :               continue;
     679         479 :             }
     680             :           }
     681             : 
     682        2413 :           ASMJIT_PROPAGATE(onSpillReg(group, workId, assignedId));
     683             :         }
     684        2892 :       } while (it.hasNext());
     685             :     }
     686             : 
     687             :     // STEP 5
     688             :     // ------
     689             :     //
     690             :     // ALLOCATE / SHUFFLE all registers that we marked as `willUse` and weren't allocated yet. This is a bit
     691             :     // complicated as the allocation is iterative. In some cases we have to wait before allocating a particual
     692             :     // physical register as it's still occupied by some other one, which we need to move before we can use it.
     693             :     // In this case we skip it and allocate another some other instead (making it free for another iteration).
     694             :     //
     695             :     // NOTE: Iterations are mostly important for complicated allocations like function calls, where there can
     696             :     // be up to N registers used at once. Asm instructions won't run the loop more than once in 99.9% of cases
     697             :     // as they use 2..3 registers in average.
     698             : 
     699     1327232 :     if (usePending) {
     700             :       bool mustSwap = false;
     701             :       do {
     702             :         uint32_t oldPending = usePending;
     703             : 
     704       10428 :         for (i = 0; i < count; i++) {
     705        7227 :           RATiedReg* thisTiedReg = &tiedRegs[i];
     706        7227 :           if (thisTiedReg->isUseDone())
     707        4011 :             continue;
     708             : 
     709             :           uint32_t thisWorkId = thisTiedReg->workId();
     710             :           uint32_t thisPhysId = _curAssignment.workToPhysId(group, thisWorkId);
     711             : 
     712             :           // This would be a bug, fatal one!
     713             :           uint32_t targetPhysId = thisTiedReg->useId();
     714             :           ASMJIT_ASSERT(targetPhysId != thisPhysId);
     715             : 
     716             :           uint32_t targetWorkId = _curAssignment.physToWorkId(group, targetPhysId);
     717        3216 :           if (targetWorkId != RAAssignment::kWorkNone) {
     718             :             RAWorkReg* targetWorkReg = workRegById(targetWorkId);
     719             : 
     720             :             // Swapping two registers can solve two allocation tasks by emitting just a single instruction. However,
     721             :             // swap is only available on few architectures and it's definitely not available for each register group.
     722             :             // Calling `onSwapReg()` before checking these would be fatal.
     723           0 :             if (_archTraits->hasInstRegSwap(group) && thisPhysId != RAAssignment::kPhysNone) {
     724           0 :               ASMJIT_PROPAGATE(onSwapReg(group, thisWorkId, thisPhysId, targetWorkId, targetPhysId));
     725             : 
     726             :               thisTiedReg->markUseDone();
     727           0 :               if (thisTiedReg->isWrite())
     728             :                 _curAssignment.makeDirty(group, thisWorkId, targetPhysId);
     729           0 :               usePending--;
     730             : 
     731             :               // Double-hit.
     732           0 :               RATiedReg* targetTiedReg = RALocal_findTiedRegByWorkId(tiedRegs, count, targetWorkReg->workId());
     733           0 :               if (targetTiedReg && targetTiedReg->useId() == thisPhysId) {
     734             :                 targetTiedReg->markUseDone();
     735           0 :                 if (targetTiedReg->isWrite())
     736             :                   _curAssignment.makeDirty(group, targetWorkId, thisPhysId);
     737           0 :                 usePending--;
     738             :               }
     739           0 :               continue;
     740           0 :             }
     741             : 
     742           0 :             if (!mustSwap)
     743           0 :               continue;
     744             : 
     745             :             // Only branched here if the previous iteration did nothing. This is essentially a SWAP operation without
     746             :             // having a dedicated instruction for that purpose (vector registers, etc). The simplest way to handle
     747             :             // such case is to SPILL the target register.
     748           0 :             ASMJIT_PROPAGATE(onSpillReg(group, targetWorkId, targetPhysId));
     749             :           }
     750             : 
     751        3216 :           if (thisPhysId != RAAssignment::kPhysNone) {
     752        1306 :             ASMJIT_PROPAGATE(onMoveReg(group, thisWorkId, targetPhysId, thisPhysId));
     753             : 
     754             :             thisTiedReg->markUseDone();
     755        1306 :             if (thisTiedReg->isWrite())
     756             :               _curAssignment.makeDirty(group, thisWorkId, targetPhysId);
     757        1306 :             usePending--;
     758             :           }
     759             :           else {
     760        1910 :             ASMJIT_PROPAGATE(onLoadReg(group, thisWorkId, targetPhysId));
     761             : 
     762             :             thisTiedReg->markUseDone();
     763        1910 :             if (thisTiedReg->isWrite())
     764             :               _curAssignment.makeDirty(group, thisWorkId, targetPhysId);
     765        1910 :             usePending--;
     766             :           }
     767             :         }
     768             : 
     769        3201 :         mustSwap = (oldPending == usePending);
     770        3201 :       } while (usePending);
     771             :     }
     772             : 
     773             :     // STEP 6
     774             :     // ------
     775             :     //
     776             :     // KILL registers marked as KILL/OUT.
     777             : 
     778             :     uint32_t outPending = outTiedCount;
     779     1327232 :     if (outTiedCount) {
     780      952355 :       for (i = 0; i < outTiedCount; i++) {
     781      559912 :         RATiedReg* tiedReg = outTiedRegs[i];
     782             : 
     783             :         uint32_t workId = tiedReg->workId();
     784             :         uint32_t physId = _curAssignment.workToPhysId(group, workId);
     785             : 
     786             :         // Must check if it's allocated as KILL can be related to OUT (like KILL immediately after OUT, which could
     787             :         // mean the register is not assigned).
     788      559912 :         if (physId != RAAssignment::kPhysNone) {
     789             :           ASMJIT_PROPAGATE(onKillReg(group, workId, physId));
     790      258530 :           willOut &= ~Support::bitMask(physId);
     791             :         }
     792             : 
     793             :         // We still maintain number of pending registers for OUT assignment. So, if this is only KILL, not OUT, we
     794             :         // can safely decrement it.
     795      559912 :         outPending -= !tiedReg->isOut();
     796             :       }
     797             :     }
     798             : 
     799             :     // STEP 7
     800             :     // ------
     801             :     //
     802             :     // SPILL registers that will be CLOBBERed. Since OUT and KILL were already processed this is used mostly to
     803             :     // handle function CALLs.
     804             : 
     805     1327232 :     if (willOut) {
     806             :       Support::BitWordIterator<RegMask> it(willOut);
     807             :       do {
     808             :         uint32_t physId = it.next();
     809             :         uint32_t workId = _curAssignment.physToWorkId(group, physId);
     810             : 
     811      376714 :         if (workId == RAAssignment::kWorkNone)
     812      350627 :           continue;
     813             : 
     814       26087 :         ASMJIT_PROPAGATE(onSpillReg(group, workId, physId));
     815      376714 :       } while (it.hasNext());
     816             :     }
     817             : 
     818             :     // STEP 8
     819             :     // ------
     820             :     //
     821             :     // Duplication.
     822             : 
     823     1327232 :     for (i = 0; i < dupTiedCount; i++) {
     824           0 :       RATiedReg* tiedReg = dupTiedRegs[i];
     825             :       uint32_t workId = tiedReg->workId();
     826             :       uint32_t srcId = tiedReg->useId();
     827             : 
     828             :       Support::BitWordIterator<RegMask> it(tiedReg->useRegMask());
     829           0 :       while (it.hasNext()) {
     830             :         uint32_t dstId = it.next();
     831           0 :         if (dstId == srcId)
     832           0 :           continue;
     833           0 :         _pass->emitMove(workId, dstId, srcId);
     834             :       }
     835             :     }
     836             : 
     837             :     // STEP 9
     838             :     // ------
     839             :     //
     840             :     // Vector registers can be cloberred partially by invoke - find if that's the case and clobber when necessary.
     841             : 
     842     1327232 :     if (node->isInvoke() && group == RegGroup::kVec) {
     843             :       const InvokeNode* invokeNode = node->as<InvokeNode>();
     844             : 
     845        9639 :       RegMask maybeClobberedRegs = invokeNode->detail().callConv().preservedRegs(group) & _curAssignment.assigned(group);
     846        9639 :       if (maybeClobberedRegs) {
     847             :         uint32_t saveRestoreVecSize = invokeNode->detail().callConv().saveRestoreRegSize(group);
     848             :         Support::BitWordIterator<RegMask> it(maybeClobberedRegs);
     849             : 
     850             :         do {
     851             :           uint32_t physId = it.next();
     852             :           uint32_t workId = _curAssignment.physToWorkId(group, physId);
     853             : 
     854             :           RAWorkReg* workReg = workRegById(workId);
     855             :           uint32_t virtSize = workReg->virtReg()->virtSize();
     856             : 
     857           0 :           if (virtSize > saveRestoreVecSize) {
     858           0 :             ASMJIT_PROPAGATE(onSpillReg(group, workId, physId));
     859             :           }
     860             : 
     861           0 :         } while (it.hasNext());
     862             :       }
     863             :     }
     864             : 
     865             :     // STEP 10
     866             :     // -------
     867             :     //
     868             :     // Assign OUT registers.
     869             : 
     870     1327232 :     if (outPending) {
     871             :       // Live registers, we need a separate register (outside of `_curAssignment) to hold these because of KILLed
     872             :       // registers. If we KILL a register here it will go out from `_curAssignment`, but we cannot assign to it in
     873             :       // here.
     874             :       RegMask liveRegs = _curAssignment.assigned(group);
     875             : 
     876             :       // Must avoid as they have been already OUTed (added during the loop).
     877             :       RegMask outRegs = 0;
     878             : 
     879             :       // Must avoid as they collide with already allocated ones.
     880      304222 :       RegMask avoidRegs = willUse & ~clobberedByInst;
     881             : 
     882             :       // Assign the best possible OUT ids of all consecutives.
     883      304222 :       if (consecutiveCount) {
     884           0 :         RATiedReg* lead = consecutiveRegs[0];
     885           0 :         if (lead->isOutConsecutive()) {
     886             :           uint32_t bestScore = 0;
     887             :           uint32_t bestLeadReg = 0xFFFFFFFF;
     888           0 :           RegMask allocableRegs = _availableRegs[group] & ~(outRegs | avoidRegs);
     889             : 
     890             :           Support::BitWordIterator<uint32_t> it(lead->outRegMask());
     891           0 :           while (it.hasNext()) {
     892             :             uint32_t regIndex = it.next();
     893           0 :             if (Support::bitTest(lead->outRegMask(), regIndex)) {
     894             :               uint32_t score = 15;
     895             : 
     896           0 :               for (i = 0; i < consecutiveCount; i++) {
     897           0 :                 uint32_t consecutiveIndex = regIndex + i;
     898           0 :                 if (!Support::bitTest(allocableRegs, consecutiveIndex)) {
     899             :                   score = 0;
     900             :                   break;
     901             :                 }
     902             : 
     903           0 :                 RAWorkReg* workReg = workRegById(consecutiveRegs[i]->workId());
     904           0 :                 score += uint32_t(workReg->homeRegId() == consecutiveIndex);
     905             :               }
     906             : 
     907           0 :               if (score > bestScore) {
     908             :                 bestScore = score;
     909             :                 bestLeadReg = regIndex;
     910             :               }
     911             :             }
     912             :           }
     913             : 
     914           0 :           if (bestLeadReg == 0xFFFFFFFF)
     915             :             return DebugUtils::errored(kErrorConsecutiveRegsAllocation);
     916             : 
     917           0 :           for (i = 0; i < consecutiveCount; i++) {
     918           0 :             uint32_t consecutiveIndex = bestLeadReg + i;
     919           0 :             RATiedReg* tiedReg = consecutiveRegs[i];
     920             :             tiedReg->setOutId(consecutiveIndex);
     921             :           }
     922             :         }
     923             :       }
     924             : 
     925             :       // Allocate OUT registers.
     926      775725 :       for (i = 0; i < outTiedCount; i++) {
     927      471503 :         RATiedReg* tiedReg = outTiedRegs[i];
     928      471503 :         if (!tiedReg->isOut())
     929             :           continue;
     930             : 
     931             :         uint32_t workId = tiedReg->workId();
     932             :         uint32_t assignedId = _curAssignment.workToPhysId(group, workId);
     933             : 
     934      304222 :         if (assignedId != RAAssignment::kPhysNone)
     935             :           ASMJIT_PROPAGATE(onKillReg(group, workId, assignedId));
     936             : 
     937             :         uint32_t physId = tiedReg->outId();
     938      304222 :         if (physId == RAAssignment::kPhysNone) {
     939      294583 :           RegMask allocableRegs = tiedReg->outRegMask() & ~(outRegs | avoidRegs);
     940             : 
     941      294583 :           if (!(allocableRegs & ~liveRegs)) {
     942             :             // There are no more registers, decide which one to spill.
     943             :             uint32_t spillWorkId;
     944        7346 :             physId = decideOnSpillFor(group, workId, allocableRegs & liveRegs, &spillWorkId);
     945        7346 :             ASMJIT_PROPAGATE(onSpillReg(group, spillWorkId, physId));
     946             :           }
     947             :           else {
     948      287237 :             physId = decideOnAssignment(group, workId, RAAssignment::kPhysNone, allocableRegs & ~liveRegs);
     949             :           }
     950             :         }
     951             : 
     952             :         // OUTs are CLOBBERed thus cannot be ASSIGNed right now.
     953             :         ASMJIT_ASSERT(!_curAssignment.isPhysAssigned(group, physId));
     954             : 
     955      304222 :         if (!tiedReg->isKill())
     956             :           ASMJIT_PROPAGATE(onAssignReg(group, workId, physId, true));
     957             : 
     958             :         tiedReg->setOutId(physId);
     959             :         tiedReg->markOutDone();
     960             : 
     961      304222 :         outRegs |= Support::bitMask(physId);
     962      304222 :         liveRegs &= ~Support::bitMask(physId);
     963             :         outPending--;
     964             :       }
     965             : 
     966      304222 :       clobberedByInst |= outRegs;
     967             :       ASMJIT_ASSERT(outPending == 0);
     968             :     }
     969             : 
     970     1327232 :     _clobberedRegs[group] |= clobberedByInst;
     971             :   }
     972             : 
     973             :   return kErrorOk;
     974             : }
     975             : 
     976      322169 : Error RALocalAllocator::spillAfterAllocation(InstNode* node) noexcept {
     977             :   // This is experimental feature that would spill registers that don't have home-id and are last in this basic block.
     978             :   // This prevents saving these regs in other basic blocks and then restoring them (mostly relevant for loops).
     979             :   RAInst* raInst = node->passData<RAInst>();
     980             :   uint32_t count = raInst->tiedCount();
     981             : 
     982      907472 :   for (uint32_t i = 0; i < count; i++) {
     983             :     RATiedReg* tiedReg = raInst->tiedAt(i);
     984      585303 :     if (tiedReg->isLast()) {
     985             :       uint32_t workId = tiedReg->workId();
     986             :       RAWorkReg* workReg = workRegById(workId);
     987      252829 :       if (!workReg->hasHomeRegId()) {
     988             :         RegGroup group = workReg->group();
     989             :         uint32_t assignedId = _curAssignment.workToPhysId(group, workId);
     990        8983 :         if (assignedId != RAAssignment::kPhysNone) {
     991           0 :           _cc->_setCursor(node);
     992           0 :           ASMJIT_PROPAGATE(onSpillReg(group, workId, assignedId));
     993             :         }
     994             :       }
     995             :     }
     996             :   }
     997             : 
     998             :   return kErrorOk;
     999             : }
    1000             : 
    1001           0 : Error RALocalAllocator::allocBranch(InstNode* node, RABlock* target, RABlock* cont) noexcept {
    1002             :   // TODO: This should be used to make the branch allocation better.
    1003             :   DebugUtils::unused(cont);
    1004             : 
    1005             :   // The cursor must point to the previous instruction for a possible instruction insertion.
    1006           0 :   _cc->_setCursor(node->prev());
    1007             : 
    1008             :   // Use TryMode of `switchToAssignment()` if possible.
    1009           0 :   if (target->hasEntryAssignment()) {
    1010           0 :     ASMJIT_PROPAGATE(switchToAssignment(target->entryPhysToWorkMap(), target->liveIn(), target->isAllocated(), true));
    1011             :   }
    1012             : 
    1013           0 :   ASMJIT_PROPAGATE(allocInst(node));
    1014           0 :   ASMJIT_PROPAGATE(spillRegsBeforeEntry(target));
    1015             : 
    1016           0 :   if (target->hasEntryAssignment()) {
    1017           0 :     BaseNode* injectionPoint = _pass->extraBlock()->prev();
    1018           0 :     BaseNode* prevCursor = _cc->setCursor(injectionPoint);
    1019             : 
    1020           0 :     _tmpAssignment.copyFrom(_curAssignment);
    1021           0 :     ASMJIT_PROPAGATE(switchToAssignment(target->entryPhysToWorkMap(), target->liveIn(), target->isAllocated(), false));
    1022             : 
    1023           0 :     BaseNode* curCursor = _cc->cursor();
    1024           0 :     if (curCursor != injectionPoint) {
    1025             :       // Additional instructions emitted to switch from the current state to the `target` state. This means
    1026             :       // that we have to move these instructions into an independent code block and patch the jump location.
    1027           0 :       Operand& targetOp = node->op(node->opCount() - 1);
    1028           0 :       if (ASMJIT_UNLIKELY(!targetOp.isLabel()))
    1029           0 :         return DebugUtils::errored(kErrorInvalidState);
    1030             : 
    1031           0 :       Label trampoline = _cc->newLabel();
    1032             :       Label savedTarget = targetOp.as<Label>();
    1033             : 
    1034             :       // Patch `target` to point to the `trampoline` we just created.
    1035           0 :       targetOp = trampoline;
    1036             : 
    1037             :       // Clear a possible SHORT form as we have no clue now if the SHORT form would be encodable after patching
    1038             :       // the target to `trampoline` (X86 specific).
    1039             :       node->clearOptions(InstOptions::kShortForm);
    1040             : 
    1041             :       // Finalize the switch assignment sequence.
    1042           0 :       ASMJIT_PROPAGATE(_pass->emitJump(savedTarget));
    1043           0 :       _cc->_setCursor(injectionPoint);
    1044           0 :       _cc->bind(trampoline);
    1045             :     }
    1046             : 
    1047           0 :     _cc->_setCursor(prevCursor);
    1048             :     _curAssignment.swap(_tmpAssignment);
    1049             :   }
    1050             :   else {
    1051           0 :     ASMJIT_PROPAGATE(_pass->setBlockEntryAssignment(target, block(), _curAssignment));
    1052             :   }
    1053             : 
    1054             :   return kErrorOk;
    1055             : }
    1056             : 
    1057           0 : Error RALocalAllocator::allocJumpTable(InstNode* node, const RABlocks& targets, RABlock* cont) noexcept {
    1058             :   // TODO: Do we really need to use `cont`?
    1059             :   DebugUtils::unused(cont);
    1060             : 
    1061           0 :   if (targets.empty())
    1062             :     return DebugUtils::errored(kErrorInvalidState);
    1063             : 
    1064             :   // The cursor must point to the previous instruction for a possible instruction insertion.
    1065           0 :   _cc->_setCursor(node->prev());
    1066             : 
    1067             :   // All `targets` should have the same sharedAssignmentId, we just read the first.
    1068           0 :   RABlock* anyTarget = targets[0];
    1069           0 :   if (!anyTarget->hasSharedAssignmentId())
    1070             :     return DebugUtils::errored(kErrorInvalidState);
    1071             : 
    1072           0 :   RASharedAssignment& sharedAssignment = _pass->_sharedAssignments[anyTarget->sharedAssignmentId()];
    1073             : 
    1074           0 :   ASMJIT_PROPAGATE(allocInst(node));
    1075             : 
    1076           0 :   if (!sharedAssignment.empty()) {
    1077           0 :     ASMJIT_PROPAGATE(switchToAssignment(
    1078             :       sharedAssignment.physToWorkMap(),
    1079             :       sharedAssignment.liveIn(),
    1080             :       true,  // Read-only.
    1081             :       false  // Try-mode.
    1082             :     ));
    1083             :   }
    1084             : 
    1085           0 :   ASMJIT_PROPAGATE(spillRegsBeforeEntry(anyTarget));
    1086             : 
    1087           0 :   if (sharedAssignment.empty()) {
    1088           0 :     ASMJIT_PROPAGATE(_pass->setBlockEntryAssignment(anyTarget, block(), _curAssignment));
    1089             :   }
    1090             : 
    1091             :   return kErrorOk;
    1092             : }
    1093             : 
    1094             : // RALocalAllocator - Decision Making
    1095             : // ==================================
    1096             : 
    1097      303933 : uint32_t RALocalAllocator::decideOnAssignment(RegGroup group, uint32_t workId, uint32_t physId, RegMask allocableRegs) const noexcept {
    1098             :   ASMJIT_ASSERT(allocableRegs != 0);
    1099             :   DebugUtils::unused(group, physId);
    1100             : 
    1101             :   RAWorkReg* workReg = workRegById(workId);
    1102             : 
    1103             :   // Prefer home register id, if possible.
    1104      303933 :   if (workReg->hasHomeRegId()) {
    1105             :     uint32_t homeId = workReg->homeRegId();
    1106      299176 :     if (Support::bitTest(allocableRegs, homeId))
    1107             :       return homeId;
    1108             :   }
    1109             : 
    1110             :   // Prefer registers used upon block entries.
    1111             :   RegMask previouslyAssignedRegs = workReg->allocatedMask();
    1112       26179 :   if (allocableRegs & previouslyAssignedRegs)
    1113             :     allocableRegs &= previouslyAssignedRegs;
    1114             : 
    1115       26179 :   return Support::ctz(allocableRegs);
    1116             : }
    1117             : 
    1118         968 : uint32_t RALocalAllocator::decideOnReassignment(RegGroup group, uint32_t workId, uint32_t physId, RegMask allocableRegs) const noexcept {
    1119             :   ASMJIT_ASSERT(allocableRegs != 0);
    1120             :   DebugUtils::unused(group, physId);
    1121             : 
    1122             :   RAWorkReg* workReg = workRegById(workId);
    1123             : 
    1124             :   // Prefer allocating back to HomeId, if possible.
    1125         968 :   if (workReg->hasHomeRegId()) {
    1126         707 :     if (Support::bitTest(allocableRegs, workReg->homeRegId()))
    1127             :       return workReg->homeRegId();
    1128             :   }
    1129             : 
    1130             :   // TODO: [Register Allocator] This could be improved.
    1131             : 
    1132             :   // Decided to SPILL.
    1133             :   return RAAssignment::kPhysNone;
    1134             : }
    1135             : 
    1136        7346 : uint32_t RALocalAllocator::decideOnSpillFor(RegGroup group, uint32_t workId, RegMask spillableRegs, uint32_t* spillWorkId) const noexcept {
    1137             :   // May be used in the future to decide which register would be best to spill so `workId` can be assigned.
    1138             :   DebugUtils::unused(workId);
    1139             :   ASMJIT_ASSERT(spillableRegs != 0);
    1140             : 
    1141             :   Support::BitWordIterator<RegMask> it(spillableRegs);
    1142             :   uint32_t bestPhysId = it.next();
    1143             :   uint32_t bestWorkId = _curAssignment.physToWorkId(group, bestPhysId);
    1144             : 
    1145             :   // Avoid calculating the cost model if there is only one spillable register.
    1146        7346 :   if (it.hasNext()) {
    1147        7346 :     uint32_t bestCost = calculateSpillCost(group, bestWorkId, bestPhysId);
    1148             :     do {
    1149             :       uint32_t localPhysId = it.next();
    1150             :       uint32_t localWorkId = _curAssignment.physToWorkId(group, localPhysId);
    1151      110190 :       uint32_t localCost = calculateSpillCost(group, localWorkId, localPhysId);
    1152             : 
    1153      110190 :       if (localCost < bestCost) {
    1154             :         bestCost = localCost;
    1155             :         bestPhysId = localPhysId;
    1156             :         bestWorkId = localWorkId;
    1157             :       }
    1158      110190 :     } while (it.hasNext());
    1159             :   }
    1160             : 
    1161        7346 :   *spillWorkId = bestWorkId;
    1162        7346 :   return bestPhysId;
    1163             : }
    1164             : 
    1165             : ASMJIT_END_NAMESPACE
    1166             : 
    1167             : #endif // !ASMJIT_NO_COMPILER
    1168             : 
    1169             : #endif // __PLUMED_HAS_ASMJIT

Generated by: LCOV version 1.16