LLVM RAGreedy Register Allocator Internals: Allocation, Eviction, Splitting, and Spilling Mechanics

Core Core Data Structures

Structure Purpose
LiveIntervals Stores live ranges for every virtual register
LiveRegMatrix Tracks physical-to-virtual mappings and interference
PriorityQueue Heap-based queue ordered by Priority
VirtRegMap Final virtual → physical assignment
EvictAdvisor Decides whether evicting an existing allocation is profitable
SpillerInstance Generates spill/reload code

1. Priortiy Queue Population

1.1 Prioriyt Formula

float computePriority(const LiveInterval &LI) {
  float span = LI.getSize();
  float freq = MBFI->getBlockFreq(LI.begin()->valno->def->getParent());
  float weight = span * freq;

  if (TRI->isHinted(LI.reg))
    weight += HINT_BONUS;

  if (GreedyRegClassPriorityTrumpsGlobalness)
    weight += TRI->getRegClassWeight(LI.reg);

  if (GreedyReverseLocalAssignment && !LI.isCrossBB())
    weight = 1.0f / weight;

  return weight;
}

1.2 Queue Initialization

void populateQueue() {
  for (unsigned vReg : LIS->getVirtRegs()) {
    LiveInterval &LI = LIS->getInterval(vReg);
    float prio = computePriority(LI);
    Queue.push(vReg, prio);
  }
}

2. Physical Register Assignment

2.1 Allocation Order Construction

AllocationOrder buildOrder(unsigned vReg) {
  AllocationOrder AO(vReg, TRI, Hints);
  if (SplitThresholdForRegWithHint && hasHint(vReg))
    AO.preferHints();
  return AO;
}

2.2 Interference Check & Costing

unsigned tryAllocate(unsigned vReg) {
  AllocationOrder AO = buildOrder(vReg);
  while (unsigned pReg = AO.next()) {
    switch (Matrix->checkInterference(vReg, pReg)) {
    case IK_Free:
      if (isAcceptable(pReg, vReg)) {
        Matrix->assign(vReg, pReg);
        VRM->setPhys(vReg, pReg);
        return pReg;
      }
      break;
    case IK_VirtReg:
      if (tryEvict(vReg, pReg))
        return pReg;
      break;
    case IK_PhysReg:
      continue; // fixed use, skip
    }
  }
  return ~0u;
}

3. Failure Recovery Pipeline

3.1 Eviction

bool tryEvict(unsigned vReg, unsigned pReg) {
  SmallVector<unsigned, 8> evictees;
  Matrix->collectInterferences(vReg, pReg, evictees);
  if (!EvictAdvisor->profitable(vReg, pReg, evictees))
    return false;

  for (unsigned e : evictees) {
    Matrix->unassign(e);
    VRM->clearPhys(e);
    Queue.push(e, computePriority(LIS->getInterval(e)));
  }
  ++NumEvictions;
  return true;
}

3.2 Live-Range Splitting

3.2.1 Local Split

bool localSplit(unsigned vReg) {
  if (!LIS->getInterval(vReg).isSingleBlock())
    return false;
  float bestGap = findMinGapWeight(vReg);
  unsigned newVReg = splitAtGap(vReg, bestGap);
  Queue.push(newVReg, computePriority(LIS->getInterval(newVReg)));
  return true;
}

3.2.2 Region Split

bool regionSplit(unsigned vReg) {
  SpillPlacement->analyze(vReg);
  SmallVector<BasicBlock*, 4> coldBlocks;
  float splitCost = computeRegionSplitCost(vReg, coldBlocks);
  if (splitCost >= spillCost(vReg))
    return false;
  unsigned newVReg = performRegionSplit(vReg, coldBlocks);
  Queue.push(newVReg, computePriority(LIS->getInterval(newVReg)));
  return true;
}

3.2.3 Block-Level Split

void blockSplit(unsigned vReg) {
  SmallVector<unsigned, 4> fragments;
  splitPerBlock(vReg, fragments);
  for (unsigned f : fragments)
    Queue.push(f, computePriority(LIS->getInterval(f)));
}

3.3 Spilling

unsigned spill(unsigned vReg) {
  if (!LIS->getInterval(vReg).isSpillable())
    return ~0u;

  if (EnableDeferredSpilling) {
    markDeferred(vReg);
    return 0;
  }

  SpillerInstance->spill(vReg);
  ++NumSpills;
  return 0;
}

3.4 Last-Chance Recoloring

unsigned recolor(unsigned vReg, AllocationOrder &AO) {
  if (RecolorDepth >= LastChanceRecoloringMaxDepth)
    return ~0u;
  ++RecolorDepth;
  SmallVector<unsigned, 4> recolored;
  if (tryRecolorInterferences(vReg, AO, recolored)) {
    unsigned pReg = AO.current();
    Matrix->assign(vReg, pReg);
    VRM->setPhys(vReg, pReg);
    --RecolorDepth;
    return pReg;
  }
  --RecolorDepth;
  return ~0u;
}

3.5 CSR First-Time Assignment

unsigned assignCSR(unsigned vReg) {
  float csrCost = computeCSRCost(vReg);
  if (csrCost >= spillCost(vReg) || csrCost >= splitCost(vReg))
    return ~0u;
  unsigned pReg = AO.getUnusedCSR();
  Matrix->assign(vReg, pReg);
  VRM->setPhys(vReg, pReg);
  CostPerUseLimit = 1;
  return pReg;
}

4. Hint-Driven Recoloring Pass

void repairBrokenHints() {
  for (unsigned vReg : BrokenHints) {
    SmallVector<MachineInstr*, 4> copies;
    collectCopies(vReg, copies);
    if (getBrokenHintCost(copies) > 0) {
      tryHintRecolor(vReg);
      ++NumHintRecolorings;
    }
  }
}

5. Post-Allcoation Cleanup

void finalize() {
  removeRedundantCopies();
  SpillerInstance->finalize();
  LiveDebugVariables->emitDebugValues();
  printStatistics();
  releaseTransientData();
}

Debugging Knobs

Flag Effect
-debug-only=regalloc Verbose per-step logs
-stats Emits NumSpills, NumEvictions, etc.
-verify-regalloc Runs MachineVerifier after each phase

Tags: llvm regalloc greedy Backend Optimization

Posted on Wed, 13 May 2026 03:59:11 +0000 by dnice