Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SBUF Allocator — Liveness, Interference & find-partners (Chaitin-Briggs, 2-D)

All addresses on this page apply to neuronx_cc 2.24.5133.0+58f8de22 (cp310; cp310/11/12 are byte-identical). The code lives in neuronxcc/starfish/lib/libwalrus.so. For .text/.rodata the virtual address equals the file offset; 0x5e90200x62d650 is the .plt JMP-thunk band, so every symbol referenced there has a real high-VA body — the addresses below are the real bodies. The binary is stripped; symbol names come from the IDA sidecars and from embedded source-path / assert strings (__PRETTY_FUNCTION__). Other wheels differ — treat every address as version-pinned.

Abstract

The SBUF allocator (ColoringAllocatorWithLoop::Rep::SB_Allocator) is a Chaitin-Briggs graph colorer, but it colors a register file that no scalar allocator ever sees: SBUF is a 2-D memory — 128 partitions × per-partition bytes (1.05) — and a "virtual register" is therefore a partition×byte rectangle that persists over a time interval, not a scalar that occupies one slot for one live range. This page documents the front half of that colorer: how a live range is built (live_range), how two ranges are tested for interference (build + impact), and how move-related ranges are coalesced before coloring (find_partners), plus the two setup phases that shape the node set — renumber_locations (dense node ids) and memloc_split (live-range splitting). The select/spill back half (geometric placement, the spill fixpoint) is documented separately in the planned SBUF spill/select page.

The bar is reimplementation: after this page a reader can build the liveness pass that emits the firstDefs/lastUses def-use maps and the per-tensor 2-D-plus-time interval list, the two-representation interference graph (triangular bit-matrix + adjacency lists) whose edges are time-overlaps and whose weights are geometric partition-band overlaps, and the conservative Briggs coalescer that merges accumulation groups only while the merged byte-pressure fits the SBUF band budget.

Four facts make this a 2-D-geometric Chaitin-Briggs rather than the textbook scalar version, and they are the deliverable of this page:

  1. A live range is a set of eInterval{start,end,block} records — one [start,end] time interval per partition-block the tensor occupies — keyed by the tensor node. The partition span is derived (a shrink factor), the byte span is bytesPerBlock × liveN, the time span is loop-extended.
  2. An interference edge is a pure time-overlap (co-membership in the per-node co-live set). The partition×byte geometry is not folded into the boolean edge; it is carried separately as impact() — a 9×9 partition-band overlap factor times the combined byte extent.
  3. Each node has its own k (possible_placements), because the number of available "colors" depends on the node's partition-band Height (4 bands, 2 half-bands, or 1 full band).
  4. find_partners is conservative Briggs coalescing, but the "won't make the node uncolorable" test is expressed as a byte-pressure gate (accumulation group is too large for SB), the 2-D analogue of the classic significant-degree count.
Allocator classneuronxcc::backend::ColoringAllocatorWithLoop::Rep::SB_Allocator
Source dir (embedded)walrus_loop_flow/coloring_allocator_with_loop/src/sb_*_with_loop.cpp
DriverSB_Allocator::allocate(LinearizedFunction*) @ 0xa95310 ([8.16] allocator-drivers, planned)
renumber_locations0xaacc10 (sb_renumber_with_loop.cpp) — dense ids + Info[] alloc
memloc_split0xab9610 (sb_memloc_split.cpp) — live-range / web splitting
live_range0xa9dbb0 (sb_live_range_with_loop.cpp) — liveness → 2 DenseMaps
create_eintervals0xac14a0 — folds the maps into DenseMap<MLSet*, vector<eInterval*>>
find_partners0xaa9b60 (sb_partners_with_loop.cpp) — Briggs coalescing
build0xa99410 — interference graph (bit-matrix + adjacency)
impact0xab5000 — 2-D partition-band × byte overlap weight
possible_placements0xab5040 — per-node color count k by Height
Info stride128 bytes (0x80); array indexed Info + (id << 7)
Colored nodebir::MemoryLocationSet* (one per SBUF tensor)

The select/spill half — simplify @ 0xab58c0, select/selectNode, insert_spill_code, and the spill fixpoint that wraps this whole pipeline — is the planned SBUF spill/select page; this page references it but does not link it.

Where the colorer sits — the driver order

The colorer runs once per bir::Function, inside SB_Allocator::allocate @ 0xa95310. The phase order, recovered from the driver disassembly, is two setup phases followed by a spill fixpoint whose body re-runs the front-half passes on the (possibly spill-mutated) function:

// SB_Allocator::allocate(LinearizedFunction* LF)            @0xa95310
renumber_locations(LF);          // dense node ids → MLSet+0x15c; alloc Info[128B]   (this page)
memloc_split(LF, info);          // live-range splitting: clone disjoint segments    (this page)
iter = 0;  heuristic = 0.0;
do {                                          // the spill FIXPOINT (not a randomized best-of-n)
    renumber_locations(LF);                   // re-number (lf may have grown via spill code)
    find_partners(LF, info);                  // Briggs COALESCE — pre-graph                 (this page)
    find_first_defs(LF, info);                // anchor: first writer → Info+32
    find_last_uses (LF, info);                // anchor: last reader  → Info+40
    find_loads(LF, info);                     // reload / remat seeding
    live_range(LF, info, firstDefs, lastUses);// LIVENESS → 2 DenseMaps                      (this page)
    build(LF, info, firstDefs, lastUses);     // INTERFERENCE GRAPH                           (this page)
    find_costs(LF, info, firstDefs, lastUses);// spill cost → Info+0
    simplify(LF, info, /*metric=*/0);         // Chaitin simplify → NodeStack          (spill/select page)
    Loc = select(LF, info, stack, &score);    // geometric placement                   (spill/select page)
    if (Loc->needSpill == 0) break;           // FIXPOINT reached — commit
    create_eintervals(LF, info, firstDefs, lastUses);  // rebuild interval list (spill path)
    insert_spill_code(LF, Loc->spillSet, info, homes); // MUTATES LF
    ++iter;
} while (1);

Two structural notes a reimplementer must not get wrong. First, the loop is a spill fixpoint, not a randomized restart — there is no RNG, no seed, no order shuffle, and no retained "best" coloring; simplify's metric argument is hard-wired to 0 every pass, and the only cross-iteration change is the spill code insert_spill_code writes into the function (CONFIRMED — no rand/srand/mt19937/shuffle/seed symbol in the driver; the iteration counter is never compared to a bound). The string " best-of-n loop, heuristic = " is a historical label for this fixpoint, and the value it prints is the integer iteration counter, not a score. Second, find_partners runs before build, so coalescing operates on the un-built graph and the merged super-nodes are what the interference graph then sees.

Setup phase A — renumber_locations: dense ids and the Info array

renumber_locations @ 0xaacc10 (sb_renumber_with_loop.cpp) assigns each SBUF MemoryLocationSet a contiguous integer node-id and builds the parallel Info[] node array the rest of the pipeline indexes in O(1). The node count is the size of the function's MemoryLocationSet* vector, n = (LF+0x18 − LF+0x10) >> 3, stored at LF+0x2f0 (the numNodes field). The Info[] array is _Znam(n << 7) — a 128-byte stride matching the Info layout below. Then a dense pass id = 0 … n−1:

// per node id:
*(uint32*)(MLSet + 0x15c) = id;          // STAMP dense node-id into the tensor   (= MLSet+348)
Info[id].loc          = MLSet;           // Info+0x10
Info[id].coLive       = {};              // Info+0x30 — empty Rb-tree (co-live / partner set)
Info[id].alignedLen   = aligned_length(MLSet);   // Info+0x50  (call @0xa929c0)
Info[id].bandWidth    = (MLSet[0x100] <= 0x20) ? 32 : (… ? 64 : 128);  // Info+0x70
Info[id].height       = height_class;    // Info+0x74 — partition-band class 0..8

The stamped id is the same MLSet+348 that find_partners reads as getLocation()'s node-id (CONFIRMED cross-anchor). renumber_locations re-runs at the top of every spill iteration because insert_spill_code adds and removes nodes; it is deterministic — the same walk order yields the same ids each pass, which is part of why the fixpoint has no perturbation source. A pre-placed or output MemoryLocationSet (MLSet+0xa8 byte set) takes the precolored branch and is excluded from the colorable worklist.

The driver-visible metric SbRenumberLocationsCount (a backend::BackendMetricType enum member, published via addMetric @ 0x1742020) tracks the renumbered node count; the PSUM and DRAM allocators carry the Psum/Dram twins.

Setup phase B — memloc_split: live-range (web) splitting

memloc_split @ 0xab9610 (sb_memloc_split.cpp) is the SBUF analogue of SSA live-range splitting / web separation, run once in setup, before the coloring loop. A single named tensor whose writes and reads happen at disjoint program points has, as one MemoryLocationSet, a live range that conservatively spans the union of all segments — pinning the storage resident the whole time and manufacturing false interference. memloc_split clones such a tensor into one sub-location per disjoint live segment, so each clone is an independent, shorter-lived graph node, dropping false edges and letting the disjoint segments share physical SBUF.

For each Info node it gates the candidate (CONFIRMED from the disassembly):

if (getPartitionDim(MLSet) == 0)        continue;   // skip scalar / 0-D locs
if (MLSet[0xf8] == 0)                   continue;   // no symbolic/tensor body
if (*(uint32*)(MLSet + 0x390) <= 1)     continue;   // writer/def-AP count must be > 1
if (*(uint32*)(MLSet + 0x368) <= 1)     continue;   // reader/use-AP count must be > 1
// candidate: multiple distinct defs AND multiple distinct uses

MLSet+0x368 / +0x390 are the count slots of the reader / writer access-pattern lists (the same lists at MLSet+864/+904 whose extremes liveness uses; the +N here are the adjacent counts — INFERRED field identity, single-anchored). For a candidate it walks the AP list, reads each instruction's getLoopnest(), computes interval relationships with fmt::bigint big-integer stride/period arithmetic, and clones the MemoryLocationSet per segment (_Znwm + memmove of the AP records; the clone is the cl_mem_loc of the assert "cloned mem loc cannot be nullptr"), splicing the clones back via getMemlocByTensorId. The clone+memmove+getMemlocByTensorId machinery is CONFIRMED; the exact bigint period predicate that decides where to cut is STRONG (mechanism proven; cut boundary not byte-transcribed). The TotalSplitSbNodesCount metric counts the minted clones.

The Info node and the colored tensor

The colorable node is bir::MemoryLocationSet* (the SBUF tensor; D-E13). Each is materialized as one 128-byte Info record, indexed Info + (id << 7) (CONFIRMED byte-exact across build, impact, simplify). The fields this front-half touches:

FieldTypeMeaningSet by
Info+0doublespill cost (+INF = uncolorable / precolored)find_costs
Info+16MLSet*the tensor's bir::MemoryLocationSet (colored node)renumber / live_range
Info+32Instruction*first writer (def anchor)find_first_defs
Info+40Instruction*last reader (use anchor)find_last_uses
Info+48set<uint>co-live node-id set (the edge source)live_range / create_eintervals
Info+56 / +64uint32* / uint32adjacency array / cursor (== degree)build pass 2
Info+68int32degree (interfering-neighbor count)build pass 1
Info+72{int hi, uint lo}packed pair: hi = significant-degree residual, lo = reverse impact sumsimplify
Info+80int32per-partition bytesPerBlock (one shrink-block's byte extent)live_range
Info+84int32liveBytes = bytesPerBlock × liveN (the BYTE dimension)live_range (L995)
Info+88uint32liveN (mirror of MLSet::getLiveN())live_range
Info+96 / +104ptrper-partition-block live tracking arrays (tc_new × liveN)live_range
Info+116int32Height class 0..8 (partition-band occupancy)mark_constraints / renumber
Info+122boolprecolored / pre-placed (skipped in simplify)

The eligibility filter candidate(MemoryLocationSet&) @ 0xaacbe0 admits a set as a graph node iff it is SB-resident and not a function output: firstMemLoc.type == 16 /*SB*/ && !isTensorKindOutput(). Outputs are pre-pinned and never colored here.

live_range — building the 2-D-plus-time interval

live_range @ 0xa9dbb0 is phase 1 of the colorer body. It does not re-scan the IR to find first/last; it trusts the anchors find_first_defs/find_last_uses parked at Info+32/Info+40 and asserts they are non-null ("no first def find" @ cpp:0x14, "no last use find" @ cpp:0x15 — both strings CONFIRMED in the binary). Its def-use substrate is the per-MemoryLocationSet writer/reader access-pattern lists the dependence builder populated:

  • READERS at MLSet+864/+888/+896 (begin / data / count)
  • WRITERS at MLSet+904/+928/+936

Each accessor is classified by its access class at AP+24: == 2 is a write (definition), == 1 is a read (use). live_range walks both lists per tensor, collecting write-APs and read-APs, and sets a flag (v74) when a plain read is present that disables the partition-band shrink path.

The time axis

The interval is [def_idx, lastUse_idx] in linear instruction-point index space. The flat program-order index is bir::Instruction+0x4C (the re-indexed program order; the same index get_live_range_len @ 0x994970 reads to compute the range length max−min). create_eintervals later translates Instruction* → integer time-coordinate through LinearizedFunction+968, a DenseMap<Instruction*, uint> of linear instruction-point indices built by walking the function's basic-block instruction list. This LF+968 map is the bridge from Instruction* to the time coordinate. (The point sequence is linearize's product; ownership of LF+968 is built here — INFERRED, structurally certain.)

The partition footprint — the shrink factor

The partition span is not the tensor's full block count; it is the number of distinct partition-blocks the tensor is actually live in, derived by intersecting the partition-index sets across the access-pattern footprint:

partDim      = loc->getPartitionDim();                       // MLSet+820
shrink_factor = Π over collapsible partition axes of TensorShape[axis];   // (or 1 if a plain read present)
shrinkDim     = collapsed_dim;                               // (or -1 if shrinking disabled)
assert(loc->getNBlocks() % shrink_factor == 0);   // "number of blocks is not a multiple of shrink factor" (cpp:0x88)
loc->setLiveN(loc->getNBlocks() / shrink_factor); // MLSet+? — LIVE partition-block count
loc->setShrinkDim(shrinkDim);                     // MLSet+828

liveN ≤ NBlocks is the partition-axis span of the rectangle. The shrink assert and setShrinkDim re-confirm what the ShrinkDN annotation pass pre-computed; live_range re-derives it for the colorer.

The byte footprint and the SB capacity guard

Info+84 = Info+80 /*bytesPerBlock*/ * liveN;          // total live byte footprint
if (Info+84 > LF+696 /*SBUF per-partition byte BUDGET*/)
    FATAL("<name> is too big for SB, requires <liveN> bytes with SB size <budget>");

The string "is too big for SB" is CONFIRMED in the binary. The 2-D footprint per tensor is therefore (liveN partition-blocks) × (bytesPerBlock); the base-partition offset (MLSet+560) is fixed later by select, not here — liveness fixes the span (how many partitions, how many bytes, how long), placement fixes the offset.

The loop-aware endpoint extension — the _with_loop delta

This is the whole reason the pass is named *_with_loop. A value written in iteration i and read in iteration i+1 is loop-carried; if the colorer treated its interval as the inner def→use span it could alias the storage onto something reused within the loop. So the interval is extended to the loop boundary. live_range walks def->getLoopnest() and use->getLoopnest() in lockstep to find the common-nest prefix depth, then:

commonDepth = longest common prefix of (def->getLoopnest(), use->getLoopnest());
// def-side: if def nests DEEPER than the shared prefix, record at the LOOP HEADER, not the inner def
defAnchor = (len(Ld) > commonDepth) ? loopHeader(Ld[commonDepth]) : def;
// use-side: if use nests deeper, push last-use to the loop TERMINATOR (the back-edge marker)
useAnchor = (len(Lu) > commonDepth) ? loopEnd(Lu[commonDepth]) : use;   // loop end = InstLoop opcode 105
firstDefs[defAnchor].push_back(defOrdinal);   // a4
lastUses [useAnchor].push_back(useOrdinal);   // a5

The loop terminator is found by scanning the loop body for opcode 105 (the Loop op, hard-kept by the don't-touch pass — CONFIRMED cross-anchor). The pre-pass label_possible_loop_carried_dependency_node @ 0x9945d0 (a TBB parallel_for over Info nodes) pre-marks which nodes are loop-carried candidates; live_range performs the actual extension. Net effect: a loop-carried tensor's interval is stretched [loopHeader_idx … loopBackEdge_idx], so it is treated as live across the entire loop body.

The outputs

live_range emits two DenseMap<bir::Instruction*, std::vector<unsigned>>firstDefs (key = def-or-loop-header instruction, value = tensor ordinals first-defined there) and lastUses (key = use-or-loop-end instruction, value = tensor ordinals last-used there). build and find_costs consume these directly. create_eintervals @ 0xac14a0 folds them into the per-tensor 2-D-plus-time interval list:

struct eInterval {        // tc_new(12)
    uint32 start;   // +0  linear-index of the DEF anchor (via LF+968)
    uint32 end;     // +4  linear-index of the USE anchor
    uint32 block;   // +8  the partition-BLOCK ordinal this interval belongs to
};
llvm::DenseMap<bir::MemoryLocationSet*, std::vector<eInterval*>>   // keyed by the tensor node
//   one vector of eIntervals per tensor; one eInterval per live partition-block.

This is the complete 2-D-plus-time live model: a tensor's liveness = a set of [start,end] time intervals (one per partition-block — the partition axis), each carrying the tensor's bytesPerBlock (the byte axis).

build — the interference graph: edges are time-overlaps

build @ 0xa99410 ("build interference graph") is a classic Chaitin two-representation graph: a triangular bit-matrix for O(1) membership plus per-node adjacency lists for iteration. The bit-matrix is lower-triangular, sized (n² + 15) >> 4 bytes (one bit per unordered pair), and an edge (i,j) with lo=min, hi=max maps to bit index lo + ((hi*hi) >> 1) (the standard half-matrix packing, CONFIRMED twice in the body).

The edge source is the per-node co-live set Info+48 — an std::set<uint> of node-ids simultaneously live with this node, filled from the def/use maps. The decisive design choice: two nodes interfere iff their live ranges overlap in TIME — co-membership in Info+48. The 2-D partition×byte geometry is not folded into the boolean edge; it is carried separately as a weight (impact) and applied in simplify. So the edge is "ranges overlap in time"; the spatial dimension sharpens the degree into a byte-pressure, not the adjacency bit.

// build PASS 1 — count degrees:
for each node v:  for each distinct co-live id w in Info[v].coLive:
    lo = min(v,w); hi = max(v,w); idx = lo + (hi*hi >> 1);
    if (!bittest(s, idx)) { setbit(s, idx); ++Info[lo].degree; ++Info[hi].degree; }   // undirected
// build PASS 2 — fill adjacency:
for each node: Info.adj = tc_new(degree); Info.cnt = 0;   // then re-walk, append each fresh edge to BOTH endpoints

Interleaved with edge-building, build also accumulates a per-instruction SB-pressure profile: for each instruction's live-set it sums Info+84 (bytes) bucketed by Info+116 Height class, yielding the per-partition-band high-water demand (logged "SB pressure N bytes", "SB high-water mark = N bytes", "N bytes in partitions [0,31]", …). A band that overflows the arch SBUF capacity raises "bad height". This is the 2-D demand the colorer must satisfy.

The 2-D overlap weight — impact, Height, possible_placements

The geometric interference is quantified by impact @ 0xab5000 — "how much of i's space does j deny":

// impact(Info*, uint i, uint j)   @0xab5000
return vertical_impact_with_loop[ 9*Height(j) + Height(i) ]   // partition-band overlap factor
       * ( bytes(i) + bytes(j) - 1 );                          // combined byte pressure
// Height(x) = Info[x]+116;  bytes(x) = Info[x]+84

vertical_impact_with_loop (symbol confirmed in the binary) is a 9×9 int table indexed by both nodes' Height class: 0 when their partition bands are disjoint, >0 scaled by how the bands overlap. The 9×9 shape is CONFIRMED by the 9*Height(j)+Height(i) index arithmetic; the individual cell values are INFERRED — the table lives in .data behind a relocated pointer, so only the shape and role are proven, not the integers. The second factor bytes(i)+bytes(j)−1 is the byte extent the two tensors jointly consume in a shared band. The product is the SBUF space j removes from i's options — the 2-D interference made numeric.

The Height enum (Info+116, 0..8) is decoded from two .rodata tables. Both were read byte-exact from the binary on this pass:

Height012345678
band-base (int32 @ 0x1DDEDA0)333332221
divisor (int16 @ 0x1DDEDD0)3232323232646464128

The band-base table is 32-bit ({3,3,3,3,3,2,2,2,1}, CONFIRMED dword). The divisor table is 16-bit ({32,32,32,32,32,64,64,64,128}, CONFIRMED int16 — reading it as int32 interleaves adjacent pairs, e.g. 0x200020 = (32,32), which is the trap a reimplementer must avoid). The semantics, on SBUF = 128 partitions:

  • Height 0..4 → divisor 32 → occupies one of four 32-partition bands
  • Height 5..7 → divisor 64 → occupies one of two 64-partition half-bands
  • Height 8 → divisor 128 → spans all 128 partitions (a tall tensor)

possible_placements @ 0xab5040 turns this into the per-node color count k:

// possible_placements(Info*, uint)   @0xab5040
result = (SBUF_rows(this+696) + 1) - rows(Info+80);   // free rows the node fits in
switch (Height) {
    case 0:        return 4 * result;   // 4 bands available
    case 5:        return 2 * result;   // 2 half-bands
    case 6,7,8:    return     result;   // 1 band (tall / fixed-band)
    default:       FATAL("Unexpected value for info[xIndex].height");
}

So the Chaitin "k available colors" is not a single scalar — each node carries its own k determined by its partition-band Height. The colorer colors a non-uniform interference graph. This per-node k is what simplify's significant-degree test (the spill/select page) compares against Σ impact(node, neighbor).

find_partners — conservative Briggs coalescing (2-D)

find_partners @ 0xaa9b60 (sb_partners_with_loop.cpp) runs before build and discovers groups of locations that should share storage, collapsing them to one graph node so a copy is elided or an accumulation chain is honored. It is gated by a "partners enabled" bool (this+1040), cleared on failure. Its logging trace (transcribed): "find partners""found N accumulation groups" → per group "largest = <name>" / "tensors = N" / "requires N bytes/partition" → on overflow "accumulation group is too large for SB""expanding partners".

What it coalesces

  1. Accumulation groups — matmul/PSUM accumulation chains and TensorCopy src/dst whose live ranges are move-related. It walks each basic block, and for opcode-8 (Matmult) arguments collects the AccessPattern → MemoryLocation (node-id at getLocation()+348) into a hashtable MemoryLocation* → std::set<uint> — each entry is one co-allocation group.
  2. Base-partition alignmentisBasePartitionConstrained marks members whose base partition is fixed, then stamps each member's Height (Info+116) from its base partition (ml_base 0 → Height 6; 32 → 2; 64 → 7; 96 → 4; with the assert "tall tensors must be accessed starting at partition 0"), forcing coalesced members onto compatible partition bands so they can share one physical region.

The (int, unsigned) comparator

The exact lambda demangle is in the binary: find_partners(LinearizedFunction*, Info*)::<lambda(const auto:40&, const auto:41&)> [with auto:40 = int; auto:41 = unsigned int]. Given a group base partition base and a member tensor-partition tp, it normalizes the member's partition index to the group base:

assert(tp >= base);        // "tp >= base"  (physical AP) / "tps.first >= base"  (symbolic AP)  — both CONFIRMED
offset = tp - base;        // partition OFFSET of each member relative to the group base

The pair (int, unsigned) is (signed tp, unsigned base) and the comparator computes each member's partition offset, then memcmp's the offset-vectors to test whether two members occupy the same relative partition layout — i.e. whether they are coalescable. The physical-AP path (AP+24 == 1) uses the concrete base; the symbolic-AP path (AP+24 == 2) calls SymbolicAccessPattern::getBasePartitionsInMemoryLocation.

Partner expansion (the transitive closure)

"expanding partners" is the final transitive merge: for every group it iterates members sorted by the base-offset comparator and, for each ordered pair of distinct members (u, v), inserts v into u's partner set (the per-node std::set<uint>). This forms the closure — every member of an accumulation group becomes a mutual partner of every other — so the downstream graph treats the group as one coalesced super-node sharing storage.

The Briggs conservative gate

Classic Briggs coalesces a move only if the merged super-node remains colorable (< k significant-degree neighbors). Here the test is the 2-D analogue: a group is coalesced only while its summed "requires N bytes/partition" stays under the per-band SB cap (this+968). The moment a group's combined byte demand exceeds the band capacity, the pass logs "accumulation group is too large for SB" and disables partners (this+1040 = 0), falling back to the un-coalesced graph. This is the Briggs guarantee — "don't coalesce if it would make the node uncolorable" — expressed in the SBUF byte-pressure metric that impact/possible_placements use. The mechanism is CONFIRMED; the literal "< k significant-degree neighbors" phrasing maps onto the per-band byte-cap and is INFERRED, not a separate scalar count in this allocator.

The retry path selectNodeWithPartnerRetry (spill/select page) re-attempts a node honoring its partner set before declaring a spill, closing the coalesce↔color loop.

End-to-end (front half)

renumber_locations          dense ids → MLSet+0x15c; alloc Info[128B]; cache band-width/Height
memloc_split                clone disjoint-segment memlocs → shorter live ranges, fewer false edges
   │
find_partners               Briggs COALESCE (pre-graph): acc-groups + base-partition align,
   │                        (tp:int, base:uint) offset comparator, transitive partner expansion,
   │                        conservative gate = group byte-demand ≤ SB band cap
live_range                  per tensor: def/use anchors → loop-nest common prefix → EXTEND across
   │                        back-edge (header / opcode-105 terminator); 2-D footprint
   │                        (liveN partition-blocks × bytesPerBlock), SB-budget guard;
   │                        OUTPUT: firstDefs / lastUses DenseMaps
create_eintervals           fold maps → DenseMap<MLSet*, vector<eInterval{start,end,block}>>
   │
build                       interference graph: triangular bit-matrix (idx = min + max²/2) +
   │                        symmetric adjacency lists; EDGE = time-overlap (co-live set);
   │                        per-band SB-pressure high-water
impact / possible_placements  2-D weight = vertical_impact[9*Hj+Hi]×(bytes_i+bytes_j−1); per-node k by Height
   │
   └─→  find_costs → simplify → select   (spill/select half — planned page)

Diagnostic strings (all CONFIRMED present in libwalrus.so)

StringPhaseMeaning
coloring_allocator_with_loop/src/sb_live_range_with_loop.cpplive_rangesource path (and sb_partners_/sb_simplify_/sb_renumber_/sb_memloc_split.cpp)
no first def find / no last use findlive_rangeanchor assert (cpp:0x14/0x15)
number of blocks is not a multiple of shrink factorlive_rangeshrink-factor divisibility assert (cpp:0x88)
shrink factor / tenosrizer (sic)live_rangeper-tensor shrink trace
is too big for SBlive_rangebyte-budget fatal
build interference graph / SB pressure / SB high-water / adjacency vectors requirebuildgraph build + 2-D pressure
Bad Height / Unexpected value for info[xIndex].heightHeight / possible_placementsenum guard
tall tensors must be accessed starting at partition 0find_partnersbase-partition align assert
accumulation group is too large for SB / expanding partnersfind_partnersBriggs gate + transitive merge
tp >= base / tps.first >= basefind_partnerscomparator normalization assert
def cannot be nullptr / cloned mem loc cannot be nullptrmemloc_splitsplit clone asserts
best-of-n loop, heuristic = / SB GCA interation / spilling from SB costdriverspill-fixpoint labels (not a randomized restart)

Function map

Symbol (real body)AddrRole
SB_Allocator::allocate(LinearizedFunction*)0xa95310per-Function driver / spill fixpoint ([8.16], planned)
SB_Allocator::renumber_locations0xaacc10dense node ids + Info[] alloc
SB_Allocator::memloc_split0xab9610live-range / web splitting
SB_Allocator::find_partners0xaa9b60Briggs coalescing
SB_Allocator::find_first_defs0xaa6fc0first-def anchor → Info+32
SB_Allocator::find_last_uses0xaa8e00last-use anchor → Info+40
SB_Allocator::live_range0xa9dbb0liveness → firstDefs/lastUses
SB_Allocator::create_eintervals0xac14a0fold maps → eInterval list
SB_Allocator::build0xa99410interference graph
SB_Allocator::impact0xab50002-D partition-band × byte weight
SB_Allocator::possible_placements0xab5040per-node color count k by Height
SB_Allocator::candidate(MemoryLocationSet&)0xaacbe0node eligibility (SB && !output)
get_live_range_len(Info&)0x994970interval length = max−min of +0x4C
label_possible_loop_carried_dependency_node0x9945d0TBB pre-mark loop-carried nodes
vertical_impact_with_loop0x3ded0409×9 partition-band overlap table (.data)

Confidence

  • CONFIRMED: the source paths (sb_*_with_loop.cpp), all asserts/diagnostic strings above (read directly from the binary this pass), the Info stride 128 and the field offsets, the triangular bit-matrix index min + (max²>>1) and size (n²+15)>>4, the two-pass build, the impact formula and its 9×9 index arithmetic, the Height band-base table {3,3,3,3,3,2,2,2,1} (int32 @ 0x1DDEDA0) and divisor table {32,32,32,32,32,64,64,64,128} (int16 @ 0x1DDEDD0), possible_placements k-by-Height, the (int,unsigned) lambda demangle and its tp>=base asserts, the ml_base→Height stamp, the loop opcode-105 terminator, the dense id at MLSet+348, the spill-fixpoint structure (no RNG / no fixed n / no best-retain).
  • STRONG: the interference edge = co-membership in Info+48 (time-overlap); the band byte-cap as the Briggs-conservative coalesce gate; memloc_split as web-splitting (clone+memmove+getMemlocByTensorId proven, segmentation predicate not byte-transcribed); SbRenumberLocationsCount/TotalSplitSbNodesCount as the driver-visible node/clone counters.
  • INFERRED: the vertical_impact_with_loop numeric cell values (table is .data-relocated — only the 9×9 shape and role are proven); the literal "< k significant-degree neighbors" classic phrasing mapping onto the per-band byte-pressure residual; MLSet+0x368/+0x390 as exactly the reader/writer AP-count slots (single-anchored, consistent with the +864/+904 lists); LF+968 ownership (built here, point sequence is linearize's product).
  • GAP: the numeric SBUF byte budget (LF+696) lives in the per-arch Target/EngineInfo ctor (not JSON), tabulated in 1.05; the memloc_split bigint period cut-boundary; the addMetric increment site for the renumber/split counters.

Cross-references

  • SBUF / PSUM Bank Geometry — the 128-partition × per-partition-byte space these live-ranges occupy, and the per-arch SBUF byte budget (LF+696).
  • [8.16] SBUF allocator drivers (planned, walrus/allocator-drivers.md) — the allocate driver and spill fixpoint that runs this colorer.
  • The SBUF spill/select half (planned, walrus/) — simplify (significant-degree worklists, the cost/degree² spill metric), select/selectNode (geometric 2-D placement), and insert_spill_code — the back half of this colorer.
  • The Dependence Graph — build_fdeps + anti-dependency-analyzer — the writer/reader access-pattern lists (MLSet+864/+904) that supply liveness its def-use substrate.
  • matmul-ordering & accumulation groups — the accumulation chains find_partners coalesces.