kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

pass_lower.c (74481B)


      1 #include <stdint.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 
      5 #include "core/arena.h"
      6 #include "core/core.h"
      7 #include "core/metrics.h"
      8 #include "core/pool.h"
      9 #include "core/slice.h"
     10 #include "core/strbuf.h"
     11 #include "opt/opt_internal.h"
     12 
     13 static int direct_builtin_from_type(KitCgTypeId t, KitCgBuiltinType* out) {
     14   if (t == KIT_CG_TYPE_NONE || t > (KitCgTypeId)KIT_CG_BUILTIN_COUNT) return 0;
     15   *out = (KitCgBuiltinType)(t - 1u);
     16   return 1;
     17 }
     18 
     19 static u32 type_size_fallback(const Func* f, KitCgTypeId t) {
     20   KitCgBuiltinType b;
     21   if (!t) return f->opt_target.ptr_size ? f->opt_target.ptr_size : 8u;
     22   if (f && f->c) {
     23     uint64_t size = kit_cg_type_size((KitCompiler*)f->c, t);
     24     if (size && size <= UINT32_MAX) return (u32)size;
     25   }
     26   if (!direct_builtin_from_type(t, &b)) {
     27     return f->opt_target.ptr_size ? f->opt_target.ptr_size : 8u;
     28   }
     29   switch (b) {
     30     case KIT_CG_BUILTIN_BOOL:
     31     case KIT_CG_BUILTIN_I8:
     32       return 1;
     33     case KIT_CG_BUILTIN_I16:
     34       return 2;
     35     case KIT_CG_BUILTIN_I32:
     36     case KIT_CG_BUILTIN_F32:
     37       return 4;
     38     case KIT_CG_BUILTIN_I64:
     39     case KIT_CG_BUILTIN_F64:
     40       return 8;
     41     case KIT_CG_BUILTIN_I128:
     42       return 16;
     43     case KIT_CG_BUILTIN_VOID:
     44       return 0;
     45     case KIT_CG_BUILTIN_VARARG_STATE:
     46     case KIT_CG_BUILTIN_COUNT:
     47     default:
     48       return f->opt_target.ptr_size ? f->opt_target.ptr_size : 8u;
     49   }
     50 }
     51 
     52 static u32 bit_words(u32 npregs) { return (npregs + 63u) / 64u; }
     53 
     54 static void bit_set(u64* bits, PReg v) {
     55   u32 w = v / 64u;
     56   u64 mask = 1ull << (v % 64u);
     57   u64 old = bits[w];
     58   bits[w] = old | mask;
     59 }
     60 static void bit_clear(u64* bits, PReg v) {
     61   u32 w = v / 64u;
     62   u64 mask = 1ull << (v % 64u);
     63   u64 old = bits[w];
     64   bits[w] = old & ~mask;
     65 }
     66 static int bit_has(const u64* bits, PReg v) {
     67   u32 w = v / 64u;
     68   u64 mask = 1ull << (v % 64u);
     69   u64 old = bits[w];
     70   return (old & mask) != 0;
     71 }
     72 
     73 typedef struct BitsCtx {
     74   u64* use;
     75   u64* def;
     76 } BitsCtx;
     77 
     78 typedef struct InstRefs {
     79   PReg* uses;
     80   PReg* defs;
     81   u32 nuses;
     82   u32 ndefs;
     83   u32 use_cap;
     84   u32 def_cap;
     85 } InstRefs;
     86 
     87 static void collect_bits(Func* f, Inst* in, Operand* op, int is_def,
     88                          void* arg) {
     89   (void)in;
     90   BitsCtx* c = (BitsCtx*)arg;
     91   if (op->kind != OPK_REG) return;
     92   PReg v = (PReg)op->v.reg;
     93   if (v == PREG_NONE || v == 0 || v >= opt_reg_count(f)) return;
     94   if (is_def)
     95     bit_set(c->def, v);
     96   else
     97     bit_set(c->use, v);
     98 }
     99 
    100 static void refs_reset(InstRefs* refs) {
    101   refs->nuses = 0;
    102   refs->ndefs = 0;
    103 }
    104 
    105 static void refs_push(Func* f, PReg** pregs, u32* npregs, u32* cap, PReg v) {
    106   for (u32 i = 0; i < *npregs; ++i)
    107     if ((*pregs)[i] == v) return;
    108   if (*npregs == *cap) {
    109     u32 ncap = *cap ? *cap * 2u : 8u;
    110     PReg* nv = arena_array(f->arena, PReg, ncap);
    111     if (*pregs) memcpy(nv, *pregs, sizeof((*pregs)[0]) * *npregs);
    112     *pregs = nv;
    113     *cap = ncap;
    114   }
    115   (*pregs)[(*npregs)++] = v;
    116 }
    117 
    118 static void refs_collect(Func* f, Inst* in, Operand* op, int is_def,
    119                          void* arg) {
    120   (void)in;
    121   InstRefs* refs = (InstRefs*)arg;
    122   if (op->kind != OPK_REG) return;
    123   PReg v = (PReg)op->v.reg;
    124   if (v == PREG_NONE || v == 0 || v >= opt_reg_count(f)) return;
    125   if (is_def)
    126     refs_push(f, &refs->defs, &refs->ndefs, &refs->def_cap, v);
    127   else
    128     refs_push(f, &refs->uses, &refs->nuses, &refs->use_cap, v);
    129 }
    130 
    131 static int refs_has_def(const InstRefs* refs, PReg v) {
    132   for (u32 i = 0; i < refs->ndefs; ++i)
    133     if (refs->defs[i] == v) return 1;
    134   return 0;
    135 }
    136 
    137 static void live_update_refs_before(u64* live, const InstRefs* refs) {
    138   for (u32 i = 0; i < refs->ndefs; ++i) bit_clear(live, refs->defs[i]);
    139   for (u32 i = 0; i < refs->nuses; ++i) bit_set(live, refs->uses[i]);
    140 }
    141 
    142 static u32 live_update_refs_before_active(u64* live, u32 active_words,
    143                                           u32 nwords, const InstRefs* refs) {
    144   for (u32 i = 0; i < refs->ndefs; ++i) {
    145     PReg v = refs->defs[i];
    146     if (v == PREG_NONE || v == 0) continue;
    147     u32 w = v / 64u;
    148     if (w < active_words) live[w] &= ~(1ull << (v % 64u));
    149   }
    150   while (active_words && live[active_words - 1u] == 0) --active_words;
    151   for (u32 i = 0; i < refs->nuses; ++i) {
    152     PReg v = refs->uses[i];
    153     if (v == PREG_NONE || v == 0) continue;
    154     u32 w = v / 64u;
    155     if (w >= nwords) continue;
    156     live[w] |= 1ull << (v % 64u);
    157     if (active_words <= w) active_words = w + 1u;
    158   }
    159   return active_words;
    160 }
    161 
    162 static void forbid_preg_reg(Func* f, PReg v, u8 cls, Reg r) {
    163   if (v == PREG_NONE || v == 0 || v >= opt_reg_count(f) ||
    164       cls >= OPT_REG_CLASSES || r >= OPT_MAX_HARD_REGS)
    165     return;
    166   if (opt_reg_cls(f, v) != cls) return;
    167   f->preg_info[v].forbidden_hard_regs |= 1u << r;
    168 }
    169 
    170 static void reserve_asm_requirement(Func* f,
    171                                     const IRAsmRegRequirement* req, u64* use,
    172                                     u64* def, u64* live_after) {
    173   u32 mask;
    174   if (!f || !f->preg_info || !req || !req->present) return;
    175   if (req->cls >= OPT_REG_CLASSES)
    176     compiler_panic(f->c, (SrcLoc){0, 0, 0},
    177                    "opt asm: invalid register requirement class");
    178   if (req->fixed_reg >= 0) {
    179     if ((u32)req->fixed_reg >= OPT_MAX_HARD_REGS)
    180       compiler_panic(f->c, (SrcLoc){0, 0, 0},
    181                      "opt asm: invalid fixed register requirement");
    182     mask = 1u << (u32)req->fixed_reg;
    183   } else {
    184     mask = req->allowed_mask;
    185   }
    186   /* Generic whole-class constraints need no reservation: ordinary allocated
    187    * operands already satisfy them, while spills use the emitter's private temp
    188    * bank. Fixed/subset constraints reserve their candidate registers only at
    189    * this instruction. With an unsplit allocator the implementation is a
    190    * conservative per-value forbid, but placement ownership remains local: the
    191    * operand itself is staged rather than pinned for its whole live range. */
    192   if (!mask) return;
    193   for (PReg v = 1; v < opt_reg_count(f); ++v) {
    194     if (opt_reg_cls(f, v) != req->cls ||
    195         (!bit_has(use, v) && !bit_has(def, v) &&
    196          !(live_after && bit_has(live_after, v))))
    197       continue;
    198     f->preg_info[v].forbidden_hard_regs |= mask;
    199   }
    200 }
    201 
    202 static void apply_asm_register_constraints(Func* f, Inst* in, u64* use,
    203                                            u64* def, u64* live_after) {
    204   IRAsmAux* aux = (IRAsmAux*)in->extra.aux;
    205   if (!aux || !f->preg_info) return;
    206 
    207   for (u32 i = 0; i < aux->nout; ++i)
    208     reserve_asm_requirement(f, aux->out_reg_reqs ? &aux->out_reg_reqs[i] : NULL,
    209                             use, def, live_after);
    210   for (u32 i = 0; i < aux->nin; ++i)
    211     reserve_asm_requirement(f, aux->in_reg_reqs ? &aux->in_reg_reqs[i] : NULL,
    212                             use, def, live_after);
    213 
    214   for (u32 cls = 0; cls < OPT_REG_CLASSES; ++cls) {
    215     u32 mask = aux->clobber_mask[cls];
    216     if (!mask) continue;
    217     for (Reg r = 0; r < OPT_MAX_HARD_REGS; ++r) {
    218       if ((mask & (1u << r)) == 0) continue;
    219       for (PReg v = 1; v < opt_reg_count(f); ++v) {
    220         if (!bit_has(use, v) && !bit_has(def, v) &&
    221             !(live_after && bit_has(live_after, v)))
    222           continue;
    223         forbid_preg_reg(f, v, (u8)cls, r);
    224       }
    225     }
    226   }
    227 }
    228 
    229 /* Apply the per-instruction fixed-register clobbers recorded in machinization
    230  * (Func.inst_clobbers). A register the instruction's encoding destroys must not
    231  * hold any value live AFTER the instruction unless that value is (re)defined
    232  * here — so forbid each clobbered register for every live-after, non-def value.
    233  * Values that merely die at the instruction (dying uses) need no constraint:
    234  * the backend stages them into/out of the fixed registers itself. */
    235 static void apply_machine_reg_clobbers(Func* f, Inst* in, u64* def,
    236                                        u64* live_after) {
    237   const u32* clobbers = opt_inst_machine_clobber_masks(f, in);
    238   if (!f->preg_info || !clobbers) return;
    239   for (u32 cls = 0; cls < OPT_REG_CLASSES; ++cls) {
    240     u32 mask = clobbers[cls];
    241     if (!mask) continue;
    242     for (Reg r = 0; r < OPT_MAX_HARD_REGS; ++r) {
    243       if ((mask & (1u << r)) == 0) continue;
    244       for (PReg v = 1; v < opt_reg_count(f); ++v) {
    245         if (!(live_after && bit_has(live_after, v)) || bit_has(def, v))
    246           continue;
    247         if ((u8)opt_reg_cls(f, v) != (u8)cls) continue;
    248         f->preg_info[v].forbidden_hard_regs |= 1u << r;
    249       }
    250     }
    251   }
    252 }
    253 
    254 static int phys_arg_reg_for_index(Func* f, u8 cls, u32 abi_index, Reg* out) {
    255   if (!f || cls >= OPT_REG_CLASSES) return 0;
    256   for (u32 i = 0; i < f->opt_phys_reg_count[cls]; ++i) {
    257     const CGPhysRegInfo* pi = &f->opt_phys_regs[cls][i];
    258     if ((pi->flags & CG_REG_ARG) && pi->abi_index == abi_index) {
    259       if (out) *out = pi->reg;
    260       return 1;
    261     }
    262   }
    263   return 0;
    264 }
    265 
    266 static int is_caller_saved(Func* f, u8 cls, Reg r) {
    267   if (cls >= OPT_REG_CLASSES || r >= OPT_MAX_HARD_REGS) return 0;
    268   return (f->opt_caller_saved[cls] & (1u << r)) != 0;
    269 }
    270 
    271 static int is_emit_temp(Func* f, u8 cls, Reg r) {
    272   if (!f || cls >= OPT_REG_CLASSES || r >= OPT_MAX_HARD_REGS) return 0;
    273   for (u32 i = 0; i < f->emit_temp_reg_count[cls]; ++i)
    274     if (f->emit_temp_regs[cls][i] == r) return 1;
    275   return 0;
    276 }
    277 
    278 static Reg first_ret_reg(Func* f, u8 cls) {
    279   if (!f || cls >= OPT_REG_CLASSES) return REG_NONE;
    280   u32 mask = f->opt_ret_regs[cls];
    281   for (Reg r = 0; r < OPT_MAX_HARD_REGS; ++r)
    282     if (mask & (1u << r)) return r;
    283   return REG_NONE;
    284 }
    285 
    286 static void set_preg_pref_to_ret_reg(Func* f, const Operand* op) {
    287   if (!op || op->kind != OPK_REG) return;
    288   PReg v = (PReg)op->v.reg;
    289   if (v == PREG_NONE || v == 0 || v >= opt_reg_count(f)) return;
    290   u8 cls = f->preg_info[v].cls;
    291   if (cls >= OPT_REG_CLASSES) return;
    292   Reg hint = first_ret_reg(f, cls);
    293   if (hint == REG_NONE || hint >= OPT_MAX_HARD_REGS ||
    294       is_emit_temp(f, cls, hint))
    295     return;
    296   /* A value live across an instruction that clobbers the ret reg cannot live
    297    * there; skip the hint so the allocator places it elsewhere and the return
    298    * copy moves it into the ret reg (e.g. an accumulator returned past a va_arg,
    299    * which uses rax). */
    300   if (f->preg_info[v].forbidden_hard_regs & (1u << hint)) return;
    301   /* The hint reg may not be in opt_hard_regs (e.g. x0 on aa64 is reserved as
    302    * the ABI return register, outside the NATIVE_REG_ALLOCABLE bank); the
    303    * allocator's preferred-reg branch can still consider it. A hard forbid
    304    * above always wins. */
    305   f->preg_info[v].preferred_hard_reg = (i8)hint;
    306 }
    307 
    308 static void set_preg_pref_for_abivalue(Func* f, const CGABIValue* v) {
    309   if (!v) return;
    310   set_preg_pref_to_ret_reg(f, &v->storage);
    311   for (u32 i = 0; i < v->nparts; ++i)
    312     set_preg_pref_to_ret_reg(f, &v->parts[i].op);
    313 }
    314 
    315 /* Soft hint: prefer a specific ABI register for `op`'s PReg. Symmetric to
    316  * set_preg_pref_to_ret_reg but takes an arbitrary hint reg (the matching
    317  * arg reg for the i-th call argument). */
    318 static void set_preg_pref_to_arg_reg(Func* f, const Operand* op, Reg hint) {
    319   if (!op || op->kind != OPK_REG) return;
    320   if (hint == REG_NONE || hint >= OPT_MAX_HARD_REGS) return;
    321   PReg v = (PReg)op->v.reg;
    322   if (v == PREG_NONE || v == 0 || v >= opt_reg_count(f)) return;
    323   u8 cls = f->preg_info[v].cls;
    324   if (cls >= OPT_REG_CLASSES) return;
    325   if (is_emit_temp(f, cls, hint)) return;
    326   if (f->preg_info[v].preferred_hard_reg >= 0) return;
    327   if (f->preg_info[v].forbidden_hard_regs & (1u << hint)) return;
    328   f->preg_info[v].preferred_hard_reg = (i8)hint;
    329 }
    330 
    331 /* Hint each single-PReg-stored param toward its own incoming ABI reg. When
    332  * the allocator picks the incoming reg, bind_param sees src==dst and emits
    333  * no entry move (aa_bind_native_param checks at native.c:3227). Live-range
    334  * conflicts at body use sites still go through the normal allocator check,
    335  * so cross-call params that need a callee-save get one. */
    336 /* True iff `f` contains any IR_CALL flagged as a tail call. Tail-call arg
    337  * routing goes through the backend shuffle which can permute the caller's
    338  * incoming arg regs into different positions for the callee — pinning each
    339  * param PReg to its own incoming reg turns those permutations into multi-reg
    340  * cycles the shuffle can't break. Symmetric to the per-call tail skip in
    341  * set_preg_pref_for_call_args. */
    342 static int func_has_tail_call(const Func* f) {
    343   if (!f) return 0;
    344   for (u32 b = 0; b < f->nblocks; ++b) {
    345     const Block* bl = &f->blocks[b];
    346     for (u32 i = 0; i < bl->ninsts; ++i) {
    347       const Inst* in = &bl->insts[i];
    348       if ((IROp)in->op != IR_CALL) continue;
    349       const IRCallAux* aux = (const IRCallAux*)in->extra.aux;
    350       if (aux && (aux->desc.flags & CG_CALL_TAIL)) return 1;
    351     }
    352   }
    353   return 0;
    354 }
    355 
    356 static void set_preg_pref_for_params(Func* f) {
    357   if (!f || !f->preg_info || !f->nparams) return;
    358   if (func_has_tail_call(f)) return;
    359   /* Per-class ABI arg cursors. Drives from per-param ABI info rather than
    360    * f->desc.abi so this fires on paths where only f->params[i].abi is set. */
    361   u32 next_int = 0;
    362   u32 next_fp = 0;
    363   /* An sret pointer passed in the first integer argument register consumes
    364    * that slot (SysV-x64 rdi, Win64 rcx, RISC-V a0); ABIs that return it in a
    365    * dedicated register (AArch64 x8) do not. Driven by the ABI descriptor so no
    366    * arch identity is needed here. */
    367   if (f->desc.abi && f->desc.abi->sret_consumes_int_arg) next_int = 1;
    368   for (u32 i = 0; i < f->nparams; ++i) {
    369     IRParam* p = &f->params[i];
    370     const ABIArgInfo* ai = p->abi;
    371     if (!ai || ai->kind == ABI_ARG_IGNORE) continue;
    372     if (ai->kind == ABI_ARG_INDIRECT) {
    373       ++next_int;
    374       continue;
    375     }
    376     if (ai->kind != ABI_ARG_DIRECT) continue;
    377     /* Only hint single-part DIRECT params whose home is a single PReg.
    378      * Aggregate / split params take the bind_param frame-store path. */
    379     int single_part_to_preg =
    380         (ai->nparts == 1) && (p->storage.kind == CG_LOCAL_STORAGE_REG);
    381     if (single_part_to_preg) {
    382       const ABIArgPart* part = &ai->parts[0];
    383       u8 cls = (part->cls == ABI_CLASS_FP) ? RC_FP : RC_INT;
    384       u32* counter = (cls == RC_FP) ? &next_fp : &next_int;
    385       Reg hint = REG_NONE;
    386       if (*counter < 8u && phys_arg_reg_for_index(f, cls, *counter, &hint)) {
    387         PReg v = (PReg)p->storage.v.reg;
    388         if (v != PREG_NONE && v != 0 && v < opt_reg_count(f) &&
    389             f->preg_info[v].cls == cls &&
    390             f->preg_info[v].preferred_hard_reg < 0 && hint != REG_NONE &&
    391             hint < OPT_MAX_HARD_REGS && !is_emit_temp(f, cls, hint) &&
    392             !(f->preg_info[v].forbidden_hard_regs & (1u << hint))) {
    393           f->preg_info[v].preferred_hard_reg = (i8)hint;
    394         }
    395       }
    396     }
    397     /* Advance the ABI cursors for every part of this param's home, regardless
    398      * of whether we hinted, so subsequent params see the right slot. */
    399     for (u32 j = 0; j < ai->nparts; ++j) {
    400       u32* c = (ai->parts[j].cls == ABI_CLASS_FP) ? &next_fp : &next_int;
    401       *c += 1u;
    402     }
    403   }
    404 }
    405 
    406 /* For each IR_CALL arg whose source storage is a single OPK_REG, hint that
    407  * PReg to the matching ABI arg register. Sequential int/fp counters mirror
    408  * the per-class arg slot assignment used by set_preg_pref_for_params. Skips
    409  * variadic, has_sret, and indirect/aggregate args: they need per-target
    410  * counter logic that hasn't been factored out of marshal_call. */
    411 static void set_preg_pref_for_call_args(Func* f, const CGCallDesc* desc) {
    412   if (!f || !desc) return;
    413   /* Tail calls handle arg routing through the tail-call shuffle in the
    414    * backend, which can resolve permutations (e.g. swap of caller's incoming
    415    * x0/x1 into tail-call x1/x0). Hinting the arg source PRegs across that
    416    * shuffle creates cycles bind_param / the entry-bind moves can't unbreak,
    417    * miscompiling permute / cycle cases (toy 24, 27, 28, ...). Skip. */
    418   if (desc->flags & CG_CALL_TAIL) return;
    419   const ABIFuncInfo* abi = desc->abi;
    420   if (!abi && f->c && f->c->abi)
    421     abi = abi_cg_func_info(f->c->abi, desc->fn_type);
    422   if (!abi || abi->variadic || abi->has_sret) return;
    423   u32 next_int = 0, next_fp = 0;
    424   for (u32 i = 0; i < desc->nargs; ++i) {
    425     if (i >= abi->nparams) break;
    426     const CGABIValue* a = &desc->args[i];
    427     const ABIArgInfo* ai = &abi->params[i];
    428     if (ai->kind == ABI_ARG_IGNORE) continue;
    429     if (ai->kind == ABI_ARG_INDIRECT) {
    430       ++next_int;
    431       continue;
    432     }
    433     if (ai->kind != ABI_ARG_DIRECT || ai->nparts == 0) continue;
    434     const ABIArgPart* part0 = &ai->parts[0];
    435     u8 cls = part0->cls == ABI_CLASS_FP ? RC_FP : RC_INT;
    436     u32* counter = cls == RC_FP ? &next_fp : &next_int;
    437     if (*counter < 8u && ai->nparts == 1) {
    438       Reg hint = REG_NONE;
    439       if (phys_arg_reg_for_index(f, cls, *counter, &hint))
    440         set_preg_pref_to_arg_reg(f, &a->storage, hint);
    441     }
    442     for (u32 p = 0; p < ai->nparts; ++p) {
    443       u32* c = (ai->parts[p].cls == ABI_CLASS_FP) ? &next_fp : &next_int;
    444       *c += 1u;
    445     }
    446   }
    447 }
    448 
    449 /* Propagate preferred_hard_reg backward across IR_COPY chains. When the
    450  * frontend emits `copy def=v_arg opnds=[v_arg, v_value]` to carry a value
    451  * into a call's arg slot, set_preg_pref_for_call_args hints v_arg to the
    452  * ABI dest (e.g. x0). But the underlying producer (v_value, e.g. the load
    453  * result) is unhinted and lands in a generic caller-save (e.g. x8); the
    454  * copy then emits `mov x0, x8`. Propagating the hint from v_arg to v_value
    455  * lets both land at x0 and turns the copy into an identity move that
    456  * combine elides. Walks insts; for each IR_COPY whose def has a hint and
    457  * whose single OPK_REG source operand has none, propagate when that placement
    458  * remains legal. Safe because the copy itself dies once both sides share the
    459  * register. */
    460 static void propagate_hint_through_copies(Func* f) {
    461   if (!f || !f->preg_info) return;
    462   for (u32 b = 0; b < f->nblocks; ++b) {
    463     Block* bl = &f->blocks[b];
    464     for (u32 i = 0; i < bl->ninsts; ++i) {
    465       Inst* in = &bl->insts[i];
    466       if ((IROp)in->op != IR_COPY) continue;
    467       if (in->nopnds < 2 || in->opnds[0].kind != OPK_REG ||
    468           in->opnds[1].kind != OPK_REG)
    469         continue;
    470       PReg dst = (PReg)in->opnds[0].v.reg;
    471       PReg src = (PReg)in->opnds[1].v.reg;
    472       if (dst == PREG_NONE || dst == 0 || dst >= opt_reg_count(f)) continue;
    473       if (src == PREG_NONE || src == 0 || src >= opt_reg_count(f)) continue;
    474       i8 dst_pref = f->preg_info[dst].preferred_hard_reg;
    475       if (dst_pref < 0) continue;
    476       if (f->preg_info[src].preferred_hard_reg >= 0) continue;
    477       if (f->preg_info[dst].cls != f->preg_info[src].cls) continue;
    478       /* A boundary forbid always outranks the copy-placement preference (for
    479        * example a loop accumulator returned past va_arg/idiv). */
    480       if (f->preg_info[src].forbidden_hard_regs & (1u << (Reg)dst_pref))
    481         continue;
    482       f->preg_info[src].preferred_hard_reg = dst_pref;
    483     }
    484   }
    485 }
    486 
    487 /* Set a soft "prefer the ABI return reg" hint on:
    488  *   - IR_CALL result PRegs (so emit_call's `mov result, x0` is elided)
    489  *   - IR_RET value PRegs   (so emit_ret's `mov x0, value` is elided)
    490  *   - IR_CALL arg source PRegs (so emit_call's `mov x0, src` is elided);
    491  *     hints are propagated backward through IR_COPY so the actual producer
    492  *     of the value also prefers the ABI dest reg.
    493  *
    494  * The hint is a tie-breaker only — see hard_reg_alloc_score. The allocator's
    495  * existing conflict checks already exclude regs with real interference (e.g.
    496  * a result PReg live across another call cannot pick x0). */
    497 static void apply_abi_aliasing_hints(Func* f) {
    498   if (!f || !f->preg_info) return;
    499   set_preg_pref_for_params(f);
    500   for (u32 b = 0; b < f->nblocks; ++b) {
    501     Block* bl = &f->blocks[b];
    502     for (u32 i = 0; i < bl->ninsts; ++i) {
    503       Inst* in = &bl->insts[i];
    504       if ((IROp)in->op == IR_CALL) {
    505         IRCallAux* aux = (IRCallAux*)in->extra.aux;
    506         if (aux) {
    507           set_preg_pref_for_abivalue(f, &aux->desc.ret);
    508           set_preg_pref_for_call_args(f, &aux->desc);
    509         }
    510       } else if ((IROp)in->op == IR_RET) {
    511         IRRetAux* aux = (IRRetAux*)in->extra.aux;
    512         if (aux && aux->present) set_preg_pref_for_abivalue(f, &aux->val);
    513       }
    514     }
    515   }
    516   propagate_hint_through_copies(f);
    517 }
    518 
    519 /* ---------------------------------------------------------------------------
    520  * Register allocator, MIR-shaped.
    521  *
    522  * Data structures and assignment algorithm mirror MIR's reg_alloc/assign
    523  * (mir-gen.c:7551-7728, simplified_p branch). Conflict detection uses a
    524  * point-indexed bitmap of locations (hard regs + stack slots) instead of
    525  * a sorted interval vector per hard register.
    526  *
    527  *   used_locs[p * loc_words .. p * loc_words + loc_words)  -- one row per
    528  *                                                             compressed
    529  *                                                             program point
    530  *
    531  *   Bit indices:
    532  *     0 .. hard_loc_bits-1   -> hard registers (hard_loc_bit(cls, r))
    533  *     hard_loc_bits + k      -> stack slot index k (k < stack_slot_count)
    534  *
    535  * Live-range splitting (`get_hard_reg_with_split`, `lr_gap_t`, `split()`)
    536  * is deferred per doc/plan/OPTIMIZER.md.
    537  * ------------------------------------------------------------------------- */
    538 
    539 typedef struct OptAllocator OptAllocator;
    540 
    541 static const CGPhysRegInfo* phys_info_for(Func* f, u8 cls, Reg r) {
    542   if (cls >= OPT_REG_CLASSES) return NULL;
    543   for (u32 i = 0; i < f->opt_phys_reg_count[cls]; ++i)
    544     if (f->opt_phys_regs[cls][i].reg == r) return &f->opt_phys_regs[cls][i];
    545   return NULL;
    546 }
    547 
    548 static FrameSlot spill_slot_for(Func* f, PReg v) {
    549   FrameSlot existing = opt_preg_spill_slot(f, v);
    550   if (existing != FRAME_SLOT_NONE) return existing;
    551   FrameSlotDesc d;
    552   memset(&d, 0, sizeof d);
    553   d.type = opt_reg_type(f, v);
    554   d.size = type_size_fallback(f, opt_reg_type(f, v));
    555   d.align = d.size >= 8 ? 8 : d.size;
    556   d.kind = FS_SPILL;
    557   f->preg_info[v].spill_slot = ir_frame_slot_new(f, &d);
    558   return f->preg_info[v].spill_slot;
    559 }
    560 
    561 /* Aggregate one coalesce group's spill-traffic cost onto its final spill slot's
    562  * IRFrameSlot.priority (O1.md W1.0). Called from the allocator's final
    563  * assignment, including the slot-reuse path, so a slot shared by several
    564  * non-overlapping groups accumulates the traffic of all of them. Saturating sum:
    565  * priority is purely an ordering key for the known-frame emitter (hot slot ->
    566  * smallest displacement), so wrap-free saturation at u32 max is sufficient and
    567  * keeps the hottest slots ordered ahead of cold ones. Slots outside [1,
    568  * nframe_slots] (none today) are ignored defensively. */
    569 static void spill_slot_add_priority(Func* f, FrameSlot slot, u32 cost) {
    570   if (slot == FRAME_SLOT_NONE || slot > f->nframe_slots || cost == 0) return;
    571   IRFrameSlot* s = &f->frame_slots[slot - 1u];
    572   u32 sum = s->priority + cost;
    573   s->priority = sum < s->priority ? 0xffffffffu : sum; /* saturate on overflow */
    574 }
    575 
    576 static u32 hard_loc_bit(u8 cls, Reg r) {
    577   return ((u32)cls * OPT_MAX_HARD_REGS) + (u32)r;
    578 }
    579 
    580 typedef struct OptAllocGroupInfo {
    581   PReg root;
    582   u32 spill_cost;
    583   u32 live_length;
    584   u32 live_across_call_freq; /* sum over group members (W3-coalesce aware) */
    585   u32 first;
    586   u32 last;
    587   u32 forbidden_hard_regs;
    588   u8 cls;
    589   u8 pad[3];
    590 } OptAllocGroupInfo;
    591 
    592 typedef struct OptAllocCandidate {
    593   PReg v; /* coalesce-root PReg with live ranges */
    594   /* Group info computed once during candidate collection and reused by the
    595    * assignment loop, which would otherwise recompute it per candidate. */
    596   OptAllocGroupInfo gi;
    597 } OptAllocCandidate;
    598 
    599 typedef struct OptAllocator {
    600   OptLoc* locs; /* per-PReg result (cls, hard_reg, spill_slot) */
    601 
    602   /* Per-point bitmap of locations. used_locs[p * loc_words + w] is word w
    603    * of the bitmap for compressed program point p. Bit indices:
    604    *   0 .. hard_loc_bits - 1            -> hard regs (hard_loc_bit)
    605    *   hard_loc_bits + stack_idx         -> stack slot indices */
    606   u64* used_locs;
    607   u32 point_count;
    608   u32 loc_words;     /* width of one row, in u64 words */
    609   u32 hard_loc_bits; /* OPT_REG_CLASSES * OPT_MAX_HARD_REGS */
    610 
    611   /* Stack slot table (parallel arrays). */
    612   FrameSlot* stack_slots;
    613   u32 stack_slot_count;
    614   u32 stack_slot_cap;
    615 
    616   /* hard_open[hard_loc_bit] is 1 if at least one PReg has been assigned to
    617    * this hard reg in the current function. Drives `hard_reg_alloc_score`'s
    618    * callee-save bias. */
    619   u8* hard_open;
    620 
    621   /* Scratch bitmap of loc_words. Reused per candidate. */
    622   u64* conflict_locs;
    623 
    624   /* Per-coalesce-root member lists, built once (O(nregs)) so the hint-path
    625    * precise interference check can enumerate a candidate group's members in
    626    * O(group size) instead of rescanning all PRegs. member_head[root] is the
    627    * first member (or 0), member_next[m] links the rest (0 terminates). Indexed
    628    * by PReg. NULL when there is no coalescing (single-member groups). */
    629   u32* member_head;
    630   u32* member_next;
    631 
    632   /* Metrics. */
    633   u64 hard_point_visits;  /* points scanned during hard-reg conflict probe */
    634   u64 stack_point_visits; /* points scanned during stack-slot probe */
    635   u64 hard_word_ors;      /* word-OR operations into conflict_locs */
    636   u64 stack_word_ors;
    637   u64 hard_mark_points; /* points marked when assigning a hard reg */
    638   u64 stack_mark_points;
    639 } OptAllocator;
    640 
    641 /* Bitmap helpers over loc_words-wide rows of used_locs. */
    642 static u64* used_locs_row(OptAllocator* a, u32 p) {
    643   return &a->used_locs[(u64)p * a->loc_words];
    644 }
    645 
    646 static int loc_bit_in_conflict(const u64* conflict_locs, u32 bit) {
    647   return (conflict_locs[bit / 64u] & (1ull << (bit % 64u))) != 0;
    648 }
    649 
    650 static u32 alloc_loc_words_for_bits(u32 bits) { return (bits + 63u) / 64u; }
    651 
    652 static void alloc_grow_loc_words(Func* f, OptAllocator* a, u32 need_words) {
    653   if (need_words <= a->loc_words) return;
    654   u32 new_words = a->loc_words ? a->loc_words : 1u;
    655   while (new_words < need_words) new_words *= 2u;
    656   u64* nb = arena_zarray(f->arena, u64, (u64)a->point_count * new_words);
    657   if (a->used_locs && a->loc_words) {
    658     for (u32 p = 0; p < a->point_count; ++p)
    659       memcpy(&nb[(u64)p * new_words], &a->used_locs[(u64)p * a->loc_words],
    660              sizeof(u64) * a->loc_words);
    661   }
    662   a->used_locs = nb;
    663   a->loc_words = new_words;
    664   u64* nc = arena_zarray(f->arena, u64, new_words);
    665   a->conflict_locs = nc;
    666 }
    667 
    668 static u32 alloc_alloc_stack_slot(Func* f, OptAllocator* a, FrameSlot fs) {
    669   if (a->stack_slot_count == a->stack_slot_cap) {
    670     u32 ncap = a->stack_slot_cap ? a->stack_slot_cap * 2u : 16u;
    671     FrameSlot* ns = arena_array(f->arena, FrameSlot, ncap);
    672     if (a->stack_slots)
    673       memcpy(ns, a->stack_slots,
    674              sizeof(a->stack_slots[0]) * a->stack_slot_count);
    675     a->stack_slots = ns;
    676     a->stack_slot_cap = ncap;
    677   }
    678   u32 idx = a->stack_slot_count++;
    679   a->stack_slots[idx] = fs;
    680   u32 needed_bits = a->hard_loc_bits + a->stack_slot_count;
    681   u32 needed_words = alloc_loc_words_for_bits(needed_bits);
    682   alloc_grow_loc_words(f, a, needed_words);
    683   return idx;
    684 }
    685 
    686 static u32 hard_reg_alloc_score(Func* f, const OptAllocator* a,
    687                                 const OptPRegInfo* vi, Reg hr) {
    688   const CGPhysRegInfo* pi = phys_info_for(f, vi->cls, hr);
    689   u32 score = pi ? pi->spill_cost : 0;
    690   if (vi->live_across_call_freq) {
    691     if (is_caller_saved(f, vi->cls, hr))
    692       score += 1000u + vi->live_across_call_freq;
    693     else
    694       score += 20u;
    695   } else if (!is_caller_saved(f, vi->cls, hr)) {
    696     u32 bit = hard_loc_bit(vi->cls, hr);
    697     int already_open =
    698         a->hard_open && bit < a->hard_loc_bits && a->hard_open[bit];
    699     if (!already_open) score += pi ? pi->copy_cost : 50u;
    700   }
    701   /* Soft hint: tie-break toward a preferred hard reg by adding +1 to every
    702    * non-hinted choice. Used by apply_abi_aliasing_hints to put IR_CALL
    703    * results / IR_RET values into the ABI return register so emit_call /
    704    * emit_ret can elide the materialization move. Stays well below the
    705    * live-across-call (+1000) penalty and the callee-save (+20) gap so it
    706    * never overrides real costs. */
    707   if (vi->preferred_hard_reg >= 0 && (Reg)vi->preferred_hard_reg != hr)
    708     score += 1u;
    709   return score;
    710 }
    711 
    712 static int alloc_candidate_higher(const OptAllocCandidate* a,
    713                                   const OptAllocCandidate* b) {
    714   if (a->gi.spill_cost != b->gi.spill_cost)
    715     return a->gi.spill_cost > b->gi.spill_cost;
    716   if (a->gi.live_length != b->gi.live_length)
    717     return a->gi.live_length < b->gi.live_length;
    718   return a->v < b->v;
    719 }
    720 
    721 static int alloc_candidate_cmp(const void* va, const void* vb) {
    722   const OptAllocCandidate* a = (const OptAllocCandidate*)va;
    723   const OptAllocCandidate* b = (const OptAllocCandidate*)vb;
    724   if (alloc_candidate_higher(a, b)) return -1;
    725   if (alloc_candidate_higher(b, a)) return 1;
    726   return 0;
    727 }
    728 
    729 static void alloc_sort_candidates(OptAllocCandidate* cands, u32 n) {
    730   if (n > 1) qsort(cands, n, sizeof(cands[0]), alloc_candidate_cmp);
    731 }
    732 
    733 static PReg alloc_coalesce_root(Func* f, PReg v) {
    734   if (!f->opt_coalesce_parent || v == PREG_NONE || v >= opt_reg_count(f))
    735     return v;
    736   PReg p = (PReg)f->opt_coalesce_parent[v];
    737   while (p != f->opt_coalesce_parent[p]) p = (PReg)f->opt_coalesce_parent[p];
    738   while (v != p) {
    739     PReg n = (PReg)f->opt_coalesce_parent[v];
    740     f->opt_coalesce_parent[v] = p;
    741     v = n;
    742   }
    743   return p;
    744 }
    745 
    746 static int alloc_group_member(Func* f, PReg root, PReg v) {
    747   return alloc_coalesce_root(f, v) == root;
    748 }
    749 
    750 static void alloc_build_member_lists(Func* f, OptAllocator* a,
    751                                      const OptLiveRangeSet* ranges) {
    752   if (!f->opt_coalesce_parent) return;
    753   u32 nr = opt_reg_count(f);
    754   a->member_head = arena_zarray(f->arena, u32, nr ? nr : 1u);
    755   a->member_next = arena_zarray(f->arena, u32, nr ? nr : 1u);
    756   for (PReg v = 1; v < nr; ++v) {
    757     if (ranges->first_range_by_preg[v] == OPT_RANGE_NONE) continue;
    758     PReg root = alloc_coalesce_root(f, v);
    759     a->member_next[v] = a->member_head[root];
    760     a->member_head[root] = v;
    761   }
    762 }
    763 
    764 static PReg alloc_group_first_member(const OptAllocator* a, PReg root) {
    765   return a->member_head ? (PReg)a->member_head[root] : root;
    766 }
    767 
    768 static PReg alloc_group_next_member(const OptAllocator* a, PReg root,
    769                                     PReg member) {
    770   (void)root;
    771   if (!a->member_head) return 0;
    772   return (PReg)a->member_next[member];
    773 }
    774 
    775 static void alloc_group_info(Func* f, const OptAllocator* a,
    776                              const OptLiveRangeSet* ranges, PReg root,
    777                              OptAllocGroupInfo* out) {
    778   memset(out, 0, sizeof *out);
    779   out->root = root;
    780   out->first = (u32)~0u;
    781   out->cls = f->preg_info[root].cls;
    782   for (PReg v = alloc_group_first_member(a, root); v != 0;
    783        v = alloc_group_next_member(a, root, v)) {
    784     if (ranges->first_range_by_preg[v] == OPT_RANGE_NONE) continue;
    785     OptPRegInfo* vi = &f->preg_info[v];
    786     out->spill_cost += vi->frequency ? vi->frequency : vi->spill_cost;
    787     out->live_length += vi->live_length;
    788     out->live_across_call_freq += vi->live_across_call_freq;
    789     u32 first = vi->first_pos ? vi->first_pos - 1u : 0;
    790     if (first < out->first) out->first = first;
    791     if (vi->last_pos > out->last) out->last = vi->last_pos;
    792     out->forbidden_hard_regs |= vi->forbidden_hard_regs;
    793   }
    794   if (out->first == (u32)~0u) out->first = 0;
    795 }
    796 
    797 /* Rebuild allocation metadata exclusively from the current HIR and its live
    798  * ranges. OptPRegInfo and OptLoc are products of this pass, never inputs: all
    799  * hard constraints and ABI preferences are derived immediately after this
    800  * reset. Carrying either table across a rerun makes removed clobbers,
    801  * constraints, hints, and spill locations silently authoritative. */
    802 static void opt_rebuild_preg_info_from_ranges(Func* f,
    803                                               const OptLiveRangeSet* ranges) {
    804   OptPRegInfo* info = arena_zarray(f->arena, OptPRegInfo,
    805                                    opt_reg_count(f) ? opt_reg_count(f) : 1u);
    806   f->preg_locs = NULL;
    807   for (PReg v = 0; v < opt_reg_count(f); ++v) {
    808     OptPRegInfo* vi = &info[v];
    809     vi->preferred_hard_reg = (i8)-1;
    810     vi->hard_reg = REG_NONE;
    811     vi->spill_slot = FRAME_SLOT_NONE;
    812     vi->alloc_kind = OPT_ALLOC_NONE;
    813     vi->cls = opt_reg_cls(f, v);
    814     if (!ranges || v == PREG_NONE || v == 0 ||
    815         ranges->first_range_by_preg[v] == OPT_RANGE_NONE) {
    816       continue;
    817     }
    818     u32 first = (u32)~0u;
    819     u32 last = 0;
    820     for (u32 r = ranges->first_range_by_preg[v]; r != OPT_RANGE_NONE;
    821          r = ranges->ranges[r].next) {
    822       const OptLiveRange* lr = &ranges->ranges[r];
    823       if (lr->start < first) first = lr->start;
    824       if (lr->end > last) last = lr->end;
    825     }
    826     vi->first_pos = first == (u32)~0u ? 0 : first + 1u;
    827     vi->last_pos = last;
    828     vi->live_length = ranges->live_length_by_preg[v];
    829     vi->use_freq = ranges->use_freq_by_preg[v];
    830     vi->def_freq = ranges->def_freq_by_preg[v];
    831     vi->live_block_freq = ranges->live_block_freq_by_preg[v];
    832     vi->live_across_call_freq = ranges->live_across_call_freq_by_preg[v];
    833     vi->spill_cost = ranges->spill_cost_by_preg[v];
    834     vi->frequency = vi->spill_cost;
    835   }
    836   f->preg_info = info;
    837 }
    838 
    839 static void bits_clear(u64* bits, u32 words) {
    840   for (u32 i = 0; i < words; ++i) bits[i] = 0;
    841 }
    842 
    843 static void live_update_before(u64* live, const u64* use, const u64* def,
    844                                u32 words) {
    845   for (u32 w = 0; w < words; ++w) live[w] = (live[w] & ~def[w]) | use[w];
    846 }
    847 
    848 static void live_copy_block_out(Func* f, const OptLiveInfo* live_info, u32 b,
    849                                 u64* live, u32 words) {
    850   (void)f;
    851   bits_clear(live, words);
    852   if (live_info) {
    853     const OptBitset* out = &live_info->blocks[b].live_out;
    854     for (u32 w = 0; w < words && w < out->nwords; ++w) live[w] = out->words[w];
    855   }
    856 }
    857 
    858 static u32 live_copy_block_out_active(const OptLiveInfo* live_info, u32 b,
    859                                       u64* live, u32 words,
    860                                       u32 old_active_words) {
    861   for (u32 w = 0; w < old_active_words; ++w) live[w] = 0;
    862   if (!live_info) return 0;
    863   const OptBitset* out = &live_info->blocks[b].live_out;
    864   u32 active = words < out->active_words ? words : out->active_words;
    865   for (u32 w = 0; w < active; ++w) live[w] = out->words[w];
    866   return active;
    867 }
    868 
    869 static void opt_apply_asm_constraints_from_live(Func* f,
    870                                                 const OptLiveInfo* live_info) {
    871   int has_asm = 0;
    872   for (u32 b = 0; b < f->nblocks && !has_asm; ++b) {
    873     Block* bl = &f->blocks[b];
    874     for (u32 i = 0; i < bl->ninsts; ++i) {
    875       if ((IROp)bl->insts[i].op == IR_ASM_BLOCK) {
    876         has_asm = 1;
    877         break;
    878       }
    879     }
    880   }
    881   /* The live walk drives both inline-asm operand constraints and
    882    * per-instruction fixed-register clobbers (Func.inst_clobbers); run it if
    883    * either is present. */
    884   if (!has_asm && !f->inst_clobbers) return;
    885 
    886   u32 words = live_info ? live_info->words : f->opt_live_words;
    887   if (!words) words = bit_words(opt_reg_count(f));
    888   f->opt_live_words = (u16)words;
    889 
    890   u64* live = arena_zarray(f->arena, u64, words ? words : 1u);
    891   u64* use = arena_zarray(f->arena, u64, words ? words : 1u);
    892   u64* def = arena_zarray(f->arena, u64, words ? words : 1u);
    893   for (u32 b = 0; b < f->nblocks; ++b) {
    894     Block* bl = &f->blocks[b];
    895     live_copy_block_out(f, live_info, b, live, words);
    896     for (u32 ri = bl->ninsts; ri > 0; --ri) {
    897       u32 i = ri - 1u;
    898       Inst* in = &bl->insts[i];
    899       bits_clear(use, words);
    900       bits_clear(def, words);
    901       BitsCtx bc = {use, def};
    902       opt_walk_inst_operands(f, in, collect_bits, &bc);
    903       if ((IROp)in->op == IR_ASM_BLOCK)
    904         apply_asm_register_constraints(f, in, use, def, live);
    905       apply_machine_reg_clobbers(f, in, def, live);
    906       live_update_before(live, use, def, words);
    907     }
    908   }
    909 }
    910 
    911 static int spill_slot_compatible(Func* f, FrameSlot fs, PReg v) {
    912   if (fs == FRAME_SLOT_NONE || fs > f->nframe_slots) return 0;
    913   IRFrameSlot* s = &f->frame_slots[fs - 1u];
    914   u32 size = type_size_fallback(f, opt_reg_type(f, v));
    915   u32 align = size >= 8 ? 8 : size;
    916   if (s->kind != FS_SPILL) return 0;
    917   if (s->size < size) return 0;
    918   if (s->align < align) return 0;
    919   return 1;
    920 }
    921 
    922 /* Compute conflict_locs = union of used_locs[j] for j in every live range
    923  * point of every PReg in `root`'s coalesce group. */
    924 static void alloc_compute_group_conflicts(Func* f, OptAllocator* a,
    925                                           const OptLiveRangeSet* ranges,
    926                                           PReg root) {
    927   (void)f;
    928   for (u32 w = 0; w < a->loc_words; ++w) a->conflict_locs[w] = 0;
    929   for (PReg v = alloc_group_first_member(a, root); v != 0;
    930        v = alloc_group_next_member(a, root, v)) {
    931     if (ranges->first_range_by_preg[v] == OPT_RANGE_NONE) continue;
    932     for (u32 ri = ranges->first_range_by_preg[v]; ri != OPT_RANGE_NONE;
    933          ri = ranges->ranges[ri].next) {
    934       const OptLiveRange* lr = &ranges->ranges[ri];
    935       u32 end = lr->end < a->point_count ? lr->end : a->point_count;
    936       for (u32 j = lr->start; j < end; ++j) {
    937         ++a->hard_point_visits;
    938         const u64* row = used_locs_row(a, j);
    939         for (u32 w = 0; w < a->loc_words; ++w) {
    940           a->conflict_locs[w] |= row[w];
    941           ++a->hard_word_ors;
    942         }
    943       }
    944     }
    945   }
    946 }
    947 
    948 /* Mark `loc_bit` as occupied at every point covered by `root`'s group's
    949  * live ranges. */
    950 static void alloc_mark_group_loc(Func* f, OptAllocator* a,
    951                                  const OptLiveRangeSet* ranges, PReg root,
    952                                  u32 loc_bit) {
    953   (void)f;
    954   u32 w = loc_bit / 64u;
    955   u64 mask = 1ull << (loc_bit % 64u);
    956   for (PReg v = alloc_group_first_member(a, root); v != 0;
    957        v = alloc_group_next_member(a, root, v)) {
    958     if (ranges->first_range_by_preg[v] == OPT_RANGE_NONE) continue;
    959     for (u32 ri = ranges->first_range_by_preg[v]; ri != OPT_RANGE_NONE;
    960          ri = ranges->ranges[ri].next) {
    961       const OptLiveRange* lr = &ranges->ranges[ri];
    962       u32 end = lr->end < a->point_count ? lr->end : a->point_count;
    963       for (u32 j = lr->start; j < end; ++j) {
    964         ++a->hard_mark_points;
    965         used_locs_row(a, j)[w] |= mask;
    966       }
    967     }
    968   }
    969 }
    970 
    971 static void alloc_assign_group_hard(Func* f, OptAllocator* a,
    972                                     const OptLiveRangeSet* ranges, PReg root,
    973                                     Reg r) {
    974   u8 cls = f->preg_info[root].cls;
    975   u32 bit = hard_loc_bit(cls, r);
    976   for (PReg v = alloc_group_first_member(a, root); v != 0;
    977        v = alloc_group_next_member(a, root, v)) {
    978     if (ranges->first_range_by_preg[v] == OPT_RANGE_NONE) continue;
    979     OptPRegInfo* vi = &f->preg_info[v];
    980     vi->alloc_kind = OPT_ALLOC_HARD;
    981     vi->hard_reg = r;
    982     a->locs[v].kind = OPT_LOC_HARD;
    983     a->locs[v].cls = vi->cls;
    984     a->locs[v].hard_reg = r;
    985     a->locs[v].spill_slot = FRAME_SLOT_NONE;
    986   }
    987   alloc_mark_group_loc(f, a, ranges, root, bit);
    988   if (bit < a->hard_loc_bits) a->hard_open[bit] = 1;
    989 }
    990 
    991 static void alloc_assign_group_stack(Func* f, OptAllocator* a,
    992                                      const OptLiveRangeSet* ranges, PReg root,
    993                                      u32 group_priority) {
    994   /* Try to reuse an existing stack slot whose bit is clear in conflict_locs
    995    * and whose frame slot is compatible. The conflict_locs scratch must
    996    * already be populated for `root` by the caller. */
    997   u32 stack_idx = (u32)~0u;
    998   for (u32 k = 0; k < a->stack_slot_count; ++k) {
    999     u32 bit = a->hard_loc_bits + k;
   1000     if (loc_bit_in_conflict(a->conflict_locs, bit)) continue;
   1001     if (!spill_slot_compatible(f, a->stack_slots[k], root)) continue;
   1002     stack_idx = k;
   1003     break;
   1004   }
   1005   if (stack_idx == (u32)~0u) {
   1006     FrameSlot fs = spill_slot_for(f, root);
   1007     stack_idx = alloc_alloc_stack_slot(f, a, fs);
   1008     /* alloc_alloc_stack_slot may have widened a->loc_words: refresh
   1009      * conflict_locs (callers don't reuse it after this). */
   1010   }
   1011   FrameSlot slot = a->stack_slots[stack_idx];
   1012   /* W1.0: accumulate this group's spill-traffic onto the slot's priority. This
   1013    * is the FINAL assignment, and a slot reused for several non-overlapping
   1014    * groups sums all of their traffic — so the emitter's hot-slot-low ordering
   1015    * sees the slot's total demand, not just the first group that created it. */
   1016   spill_slot_add_priority(f, slot, group_priority);
   1017   for (PReg v = alloc_group_first_member(a, root); v != 0;
   1018        v = alloc_group_next_member(a, root, v)) {
   1019     if (ranges->first_range_by_preg[v] == OPT_RANGE_NONE) continue;
   1020     OptPRegInfo* vi = &f->preg_info[v];
   1021     vi->spill_slot = slot;
   1022     vi->alloc_kind = OPT_ALLOC_SPILL;
   1023     a->locs[v].kind = OPT_LOC_STACK;
   1024     a->locs[v].cls = vi->cls;
   1025     a->locs[v].hard_reg = REG_NONE;
   1026     a->locs[v].spill_slot = slot;
   1027   }
   1028   u32 bit = a->hard_loc_bits + stack_idx;
   1029   u32 w = bit / 64u;
   1030   u64 mask = 1ull << (bit % 64u);
   1031   for (PReg v = alloc_group_first_member(a, root); v != 0;
   1032        v = alloc_group_next_member(a, root, v)) {
   1033     if (ranges->first_range_by_preg[v] == OPT_RANGE_NONE) continue;
   1034     for (u32 ri = ranges->first_range_by_preg[v]; ri != OPT_RANGE_NONE;
   1035          ri = ranges->ranges[ri].next) {
   1036       const OptLiveRange* lr = &ranges->ranges[ri];
   1037       u32 end = lr->end < a->point_count ? lr->end : a->point_count;
   1038       for (u32 j = lr->start; j < end; ++j) {
   1039         ++a->stack_mark_points;
   1040         used_locs_row(a, j)[w] |= mask;
   1041       }
   1042     }
   1043   }
   1044 }
   1045 
   1046 static int alloc_group_conflicts_bit(const OptAllocator* a, u32 bit) {
   1047   if (bit / 64u >= a->loc_words) return 1;
   1048   return loc_bit_in_conflict(a->conflict_locs, bit);
   1049 }
   1050 
   1051 static void opt_assign_ranges(Func* f, const OptLiveRangeSet* ranges,
   1052                               OptAllocator* a) {
   1053   memset(a, 0, sizeof *a);
   1054   a->point_count = ranges->point_count ? ranges->point_count : 1u;
   1055   a->hard_loc_bits = OPT_REG_CLASSES * OPT_MAX_HARD_REGS;
   1056   a->loc_words = alloc_loc_words_for_bits(a->hard_loc_bits);
   1057   a->used_locs =
   1058       arena_zarray(f->arena, u64, (u64)a->point_count * a->loc_words);
   1059   a->conflict_locs = arena_zarray(f->arena, u64, a->loc_words);
   1060   a->locs =
   1061       arena_zarray(f->arena, OptLoc, opt_reg_count(f) ? opt_reg_count(f) : 1u);
   1062   a->hard_open = arena_zarray(f->arena, u8, a->hard_loc_bits);
   1063   a->stack_slots = NULL;
   1064   a->stack_slot_count = 0;
   1065   a->stack_slot_cap = 0;
   1066   alloc_build_member_lists(f, a, ranges);
   1067 
   1068   /* Build candidate list: every coalesce-root PReg that has live ranges. */
   1069   u32 ncands = 0;
   1070   for (PReg v = 1; v < opt_reg_count(f); ++v) {
   1071     if (ranges->first_range_by_preg[v] == OPT_RANGE_NONE) continue;
   1072     if (alloc_coalesce_root(f, v) != v) continue;
   1073     ++ncands;
   1074   }
   1075   OptAllocCandidate* cands =
   1076       arena_array(f->arena, OptAllocCandidate, ncands ? ncands : 1u);
   1077   u32 n = 0;
   1078   for (PReg v = 1; v < opt_reg_count(f); ++v) {
   1079     if (ranges->first_range_by_preg[v] == OPT_RANGE_NONE) continue;
   1080     PReg root = alloc_coalesce_root(f, v);
   1081     if (root != v) continue;
   1082     cands[n].v = v;
   1083     alloc_group_info(f, a, ranges, root, &cands[n].gi);
   1084     ++n;
   1085   }
   1086   alloc_sort_candidates(cands, n);
   1087 
   1088   for (u32 i = 0; i < n; ++i) {
   1089     PReg v = cands[i].v;
   1090     OptAllocGroupInfo gi = cands[i].gi;
   1091     OptPRegInfo* vi = &f->preg_info[v];
   1092     u8 cls = gi.cls;
   1093     alloc_compute_group_conflicts(f, a, ranges, v);
   1094 
   1095     int found = 0;
   1096     Reg best = REG_NONE;
   1097     u32 best_score = 0xffffffffu;
   1098     for (u32 r = 0; r < f->opt_hard_reg_count[cls]; ++r) {
   1099       Reg hr = f->opt_hard_regs[cls][r];
   1100       if (hr >= OPT_MAX_HARD_REGS) continue;
   1101       if (gi.forbidden_hard_regs & (1u << hr)) continue;
   1102       u32 bit = hard_loc_bit(cls, hr);
   1103       if (alloc_group_conflicts_bit(a, bit)) continue;
   1104       u32 score = hard_reg_alloc_score(f, a, vi, hr);
   1105       if (!found || score < best_score) {
   1106         found = 1;
   1107         best = hr;
   1108         best_score = score;
   1109       }
   1110     }
   1111     /* Also consider the preferred hard reg if it's outside the standard O1
   1112      * allocation bank (e.g. x0 on aa64: reserved as the ABI return register,
   1113      * without NATIVE_REG_ALLOCABLE). Used by apply_abi_aliasing_hints to let
   1114      * an IR_CALL result PReg or IR_RET value PReg land directly in x0, eliding the
   1115      * materialization move emit_call/emit_ret would otherwise emit. Only
   1116      * short-lived PRegs benefit: a value live across a call cannot survive in
   1117      * a caller-saved hint reg, and this is the *only* path that can reach an
   1118      * out-of-allocable-set reg, so guard it explicitly. The +1000 caller-save
   1119      * penalty in hard_reg_alloc_score only deflects the hint when a cheaper
   1120      * reg is found; under high register pressure (found == 0) the fallback
   1121      * below would otherwise take the hint reg regardless of score, parking a
   1122      * cross-call value in x0 where it collides with the next call's result. */
   1123     if (vi->preferred_hard_reg >= 0 &&
   1124         !is_emit_temp(f, cls, (Reg)vi->preferred_hard_reg) &&
   1125         !(gi.live_across_call_freq &&
   1126           is_caller_saved(f, cls, (Reg)vi->preferred_hard_reg))) {
   1127       Reg hint = (Reg)vi->preferred_hard_reg;
   1128       int already_tried = 0;
   1129       if (hint < OPT_MAX_HARD_REGS) {
   1130         for (u32 r = 0; r < f->opt_hard_reg_count[cls]; ++r) {
   1131           if (f->opt_hard_regs[cls][r] == hint) {
   1132             already_tried = 1;
   1133             break;
   1134           }
   1135         }
   1136         if (!already_tried && !(gi.forbidden_hard_regs & (1u << hint))) {
   1137           u32 bit = hard_loc_bit(cls, hint);
   1138           int hint_safe = !alloc_group_conflicts_bit(a, bit);
   1139           /* The bitmap conflict can be falsely positive when an
   1140            * already-assigned PReg ends exactly where v begins — the
   1141            * swap-friendly pattern like `sub x0, x21, x0`, where the previous
   1142            * call's result occupies x0 and the sub both reads it and writes
   1143            * the new value. Fall back to a precise per-PReg interference check
   1144            * that allows the unit-length overlap (same rule used by
   1145            * opt_coalesce_ranges for moves). The check must be group-aware on
   1146            * both sides: with W3 coalescing the candidate root v stands for a
   1147            * whole coalesce group, and the conflict may involve a non-root
   1148            * member m (a value coalesced into v) overlapping an already-assigned
   1149            * PReg u. Checking only v would miss it and place two raw-overlapping
   1150            * groups in the same hint register. */
   1151           if (!hint_safe) {
   1152             int real_conflict = 0;
   1153             for (PReg u = 1; u < opt_reg_count(f) && !real_conflict; ++u) {
   1154               const OptPRegInfo* ui = &f->preg_info[u];
   1155               if (ui->alloc_kind != OPT_ALLOC_HARD) continue;
   1156               if (ui->hard_reg != hint) continue;
   1157               if (alloc_group_member(f, v, u)) continue; /* same group: benign */
   1158               /* Group-aware: the conflict may be between u and a non-root
   1159                * member m coalesced into v (W3). Enumerate v's members via the
   1160                * precomputed list (O(group size)); fall back to the root alone
   1161                * when there is no coalescing. */
   1162               if (a->member_head) {
   1163                 for (u32 m = a->member_head[v]; m; m = a->member_next[m]) {
   1164                   if (opt_ranges_overlap_kind(ranges, u, (PReg)m) >= 2) {
   1165                     real_conflict = 1;
   1166                     break;
   1167                   }
   1168                 }
   1169               } else if (opt_ranges_overlap_kind(ranges, u, v) >= 2) {
   1170                 real_conflict = 1;
   1171               }
   1172             }
   1173             if (!real_conflict) hint_safe = 1;
   1174           }
   1175           if (hint_safe) {
   1176             u32 score = hard_reg_alloc_score(f, a, vi, hint);
   1177             if (!found || score < best_score) {
   1178               found = 1;
   1179               best = hint;
   1180               best_score = score;
   1181             }
   1182           }
   1183         }
   1184       }
   1185     }
   1186     if (found) {
   1187       alloc_assign_group_hard(f, a, ranges, v, best);
   1188     } else {
   1189       /* gi.spill_cost is the group's aggregated spill-traffic metric
   1190        * (alloc_group_info sums each member's frequency/spill_cost). Thread it to
   1191        * the slot so the known-frame emitter can order hot slots low (W1.0). */
   1192       alloc_assign_group_stack(f, a, ranges, v, gi.spill_cost);
   1193     }
   1194   }
   1195 
   1196   /* Report storage metrics in u64 words (used_locs is the only bitmap). */
   1197   u32 total_words = a->point_count * a->loc_words;
   1198   u32 hard_words = alloc_loc_words_for_bits(a->hard_loc_bits) * a->point_count;
   1199   if (hard_words > total_words) hard_words = total_words;
   1200   f->opt_alloc_hard_loc_words = hard_words;
   1201   f->opt_alloc_stack_loc_words = total_words - hard_words;
   1202   f->opt_alloc_stack_slots = a->stack_slot_count;
   1203   f->opt_used_loc_words = total_words;
   1204   f->opt_alloc_hard_point_visits = a->hard_point_visits;
   1205   f->opt_alloc_stack_point_visits = a->stack_point_visits;
   1206   f->opt_alloc_hard_word_ors = a->hard_word_ors;
   1207   f->opt_alloc_stack_word_ors = a->stack_word_ors;
   1208   f->opt_alloc_hard_mark_points = a->hard_mark_points;
   1209   f->opt_alloc_stack_mark_points = a->stack_mark_points;
   1210   f->preg_locs = a->locs;
   1211 }
   1212 
   1213 typedef struct RewriteList {
   1214   Inst* data;
   1215   u32 n;
   1216   u32 cap;
   1217 } RewriteList;
   1218 
   1219 typedef struct RewriteOut {
   1220   Inst* data;
   1221   u32 cap;
   1222   u32 start;
   1223 } RewriteOut;
   1224 
   1225 static Inst* list_push(Func* f, RewriteList* l, IROp op) {
   1226   if (l->n == l->cap) {
   1227     u32 ncap = l->cap ? l->cap * 2u : 16u;
   1228     Inst* nb = arena_zarray(f->arena, Inst, ncap);
   1229     if (l->data) memcpy(nb, l->data, sizeof(Inst) * l->n);
   1230     l->data = nb;
   1231     l->cap = ncap;
   1232   }
   1233   Inst* in = &l->data[l->n++];
   1234   memset(in, 0, sizeof *in);
   1235   in->op = (u16)op;
   1236   ir_assign_inst_id(f, in);
   1237   ++f->opt_rewrite_inserted_insts;
   1238   return in;
   1239 }
   1240 
   1241 static void list_reset(RewriteList* l) {
   1242   if (l) l->n = 0;
   1243 }
   1244 
   1245 static void out_init(Func* f, RewriteOut* out, u32 cap) {
   1246   out->cap = cap ? cap : 16u;
   1247   out->data = arena_zarray(f->arena, Inst, out->cap);
   1248   out->start = out->cap;
   1249 }
   1250 
   1251 static Inst* out_push_front(Func* f, RewriteOut* out, IROp op) {
   1252   if (out->start == 0) {
   1253     u32 n = out->cap;
   1254     u32 ncap = out->cap ? out->cap * 2u : 16u;
   1255     Inst* nb = arena_zarray(f->arena, Inst, ncap);
   1256     if (out->data && n)
   1257       memcpy(nb + (ncap - n), out->data + out->start, sizeof(Inst) * n);
   1258     out->data = nb;
   1259     out->cap = ncap;
   1260     out->start = ncap - n;
   1261   }
   1262   Inst* in = &out->data[--out->start];
   1263   memset(in, 0, sizeof *in);
   1264   in->op = (u16)op;
   1265   ir_assign_inst_id(f, in);
   1266   return in;
   1267 }
   1268 
   1269 static void out_prepend_list_reverse(Func* f, RewriteOut* out,
   1270                                      const RewriteList* list) {
   1271   for (u32 i = list->n; i > 0; --i) {
   1272     Inst* dst = out_push_front(f, out, (IROp)list->data[i - 1u].op);
   1273     *dst = list->data[i - 1u];
   1274   }
   1275 }
   1276 
   1277 static void out_prepend_inst(Func* f, RewriteOut* out, const Inst* in) {
   1278   Inst* dst = out_push_front(f, out, (IROp)in->op);
   1279   *dst = *in;
   1280 }
   1281 
   1282 static Operand spill_addr(Func* f, PReg v) {
   1283   Operand o;
   1284   memset(&o, 0, sizeof o);
   1285   o.kind = OPK_STACK;
   1286   o.cls = opt_preg_loc_cls(f, v);
   1287   o.type = opt_reg_type(f, v);
   1288   o.v.frame_slot = spill_slot_for(f, v);
   1289   return o;
   1290 }
   1291 
   1292 static Operand hard_operand(Func* f, PReg v) {
   1293   Operand o;
   1294   memset(&o, 0, sizeof o);
   1295   o.kind = OPK_REG;
   1296   o.cls = opt_preg_loc_cls(f, v);
   1297   o.type = opt_reg_type(f, v);
   1298   o.v.reg = opt_preg_hard_reg(f, v);
   1299   return o;
   1300 }
   1301 
   1302 static void append_store_preg(Func* f, RewriteList* out, PReg v) {
   1303   Inst* st = list_push(f, out, IR_COPY);
   1304   st->opnds = arena_array(f->arena, Operand, 2);
   1305   st->opnds[0] = spill_addr(f, v);
   1306   st->opnds[1] = hard_operand(f, v);
   1307   st->nopnds = 2;
   1308 }
   1309 
   1310 static void append_load_preg(Func* f, RewriteList* out, PReg v) {
   1311   Inst* ld = list_push(f, out, IR_COPY);
   1312   ld->opnds = arena_array(f->arena, Operand, 2);
   1313   ld->opnds[0] = hard_operand(f, v);
   1314   ld->opnds[1] = spill_addr(f, v);
   1315   ld->nopnds = 2;
   1316 }
   1317 
   1318 /* ---------------------------------------------------------------------------
   1319  * Rematerialization (doc/plan/O1.md W2).
   1320  *
   1321  * A spilled value whose single def is *cheaper to recompute than to reload* is
   1322  * carried as a location recipe at each use instead of occupying a spill home;
   1323  * the original producer is omitted from MIR. Native emission realizes the
   1324  * recipe inside the consuming instruction's temporary scope. The v1 set is
   1325  * restricted to input-less producers whose recompute is
   1326  * <= reload cost, so the recompute is valid at any use site:
   1327  *   - IR_LOAD_IMM whose immediate materializes in <= 2 instructions
   1328  *     (movz, or movz;movk -- i.e. <= 2 nonzero 16-bit halfwords);
   1329  *   - IR_ADDR_OF of an OPK_LOCAL (one add off the stable frame base).
   1330  * IR_ADDR_OF(global) (hoisted by addr_of_global_cse) and large IR_LOAD_CONST
   1331  * (4-insn materialize, worse than a 1-insn reload) are deliberately excluded.
   1332  *
   1333  * The classification is a single linear pre-pass (opt_mark_remat) over all
   1334  * instructions; the choice at each spill site is local. No global analysis.
   1335  * ------------------------------------------------------------------------- */
   1336 
   1337 /* Cost gate: how many machine instructions an immediate of `type` takes to
   1338  * materialize, using the movz/movk model (count of nonzero 16-bit halfwords).
   1339  * This matches aa64's aa_load_imm_words exactly and is conservative for the
   1340  * other backends (a 2-halfword imm is at most a 2-3 insn rebuild everywhere),
   1341  * so a constant that costs more than its slot reload never rematerializes. */
   1342 static u32 remat_imm_insn_count(const Func* f, KitCgTypeId type, i64 imm) {
   1343   u32 width = type_size_fallback(f, type);
   1344   u32 halfwords = (width > 4u) ? 4u : 2u;
   1345   u64 v = (u64)imm;
   1346   u32 n = 0;
   1347   for (u32 i = 0; i < halfwords; ++i)
   1348     if (((v >> (i * 16u)) & 0xffffu) != 0u) ++n;
   1349   return n ? n : 1u; /* zero is one movz */
   1350 }
   1351 
   1352 /* True iff `in` is in the conservative v1 rematerialization set. */
   1353 static int remat_inst_is_candidate(const Func* f, const Inst* in) {
   1354   switch ((IROp)in->op) {
   1355     case IR_LOAD_IMM:
   1356       return remat_imm_insn_count(f, in->type, in->extra.imm) <= 2u;
   1357     case IR_ADDR_OF:
   1358       return in->nopnds >= 2 && in->opnds[1].kind == OPK_LOCAL;
   1359     default:
   1360       return 0;
   1361   }
   1362 }
   1363 
   1364 /* Per-function rematerialization table, computed once by opt_mark_remat and
   1365  * read at every spill site. remat_def[v] is an arena snapshot of v's single
   1366  * defining inst when v is a remat candidate, else NULL. */
   1367 typedef struct RematInfo {
   1368   Inst** remat_def; /* indexed by PReg; NULL entry = not a remat candidate */
   1369   u32 nregs;
   1370 } RematInfo;
   1371 
   1372 typedef struct RematCountCtx {
   1373   u8* def_count; /* saturating at 2 per PReg */
   1374 } RematCountCtx;
   1375 
   1376 static void remat_count_def(Func* f, Inst* in, Operand* op, int is_def,
   1377                             void* arg) {
   1378   (void)in;
   1379   if (!is_def || op->kind != OPK_REG) return;
   1380   PReg v = (PReg)op->v.reg;
   1381   if (v == PREG_NONE || v == 0 || v >= opt_reg_count(f)) return;
   1382   RematCountCtx* c = (RematCountCtx*)arg;
   1383   if (c->def_count[v] < 2u) ++c->def_count[v];
   1384 }
   1385 
   1386 /* Linear pre-pass: record remat_def[v] iff v has exactly one def and that def
   1387  * is in the v1 set. One walk to count defs + snapshot candidate defs, one PReg
   1388  * sweep to drop multiply-defined entries. */
   1389 /* Drop `op`'s PReg from the remat set. */
   1390 static void remat_exclude_operand(RematInfo* ri, const Operand* op) {
   1391   if (!op || op->kind != OPK_REG) return;
   1392   PReg v = (PReg)op->v.reg;
   1393   if (v != PREG_NONE && v != 0 && v < ri->nregs) ri->remat_def[v] = NULL;
   1394 }
   1395 
   1396 /* Effective-address components have a deliberately compact reg/frame encoding,
   1397  * not the full scalar recipe union. Keep an immediate/frame-address producer
   1398  * in its stable spill home when it is used as an embedded base or index. The
   1399  * walker marks these transient component operands explicitly. */
   1400 static void remat_exclude_indirect_use(Func* f, Inst* in, Operand* op,
   1401                                        int is_def, void* arg) {
   1402   (void)f;
   1403   (void)in;
   1404   if (is_def || !op || op->kind != OPK_REG ||
   1405       !(op->flags & OPT_OPERAND_WALK_INDIRECT_PART))
   1406     return;
   1407   remat_exclude_operand((RematInfo*)arg, op);
   1408 }
   1409 
   1410 static void opt_mark_remat(Func* f, RematInfo* ri) {
   1411   u32 nregs = opt_reg_count(f);
   1412   memset(ri, 0, sizeof *ri);
   1413   ri->nregs = nregs;
   1414   if (nregs == 0) return;
   1415   ri->remat_def = arena_zarray(f->arena, Inst*, nregs);
   1416   u8* def_count = arena_zarray(f->arena, u8, nregs);
   1417   RematCountCtx cc = {def_count};
   1418   for (u32 b = 0; b < f->nblocks; ++b) {
   1419     Block* bl = &f->blocks[b];
   1420     for (u32 i = 0; i < bl->ninsts; ++i) {
   1421       Inst* in = &bl->insts[i];
   1422       opt_walk_inst_operands(f, in, remat_count_def, &cc);
   1423       if (!remat_inst_is_candidate(f, in)) continue;
   1424       PReg dv = (PReg)in->def;
   1425       if (dv == PREG_NONE || dv == 0 || dv >= nregs) continue;
   1426       /* Only the single-def case is sound; the count sweep below nullifies any
   1427        * PReg that ends up with more than one def. Snapshot the inst (and its
   1428        * operand array) so later block mutation cannot disturb the recipe. */
   1429       Inst* snap = arena_znew(f->arena, Inst);
   1430       *snap = *in;
   1431       if (in->nopnds) {
   1432         snap->opnds = arena_array(f->arena, Operand, in->nopnds);
   1433         memcpy(snap->opnds, in->opnds, sizeof(Operand) * in->nopnds);
   1434       }
   1435       ri->remat_def[dv] = snap;
   1436     }
   1437   }
   1438   for (u32 v = 0; v < nregs; ++v)
   1439     if (def_count[v] != 1u) ri->remat_def[v] = NULL;
   1440   for (u32 b = 0; b < f->nblocks; ++b) {
   1441     Block* bl = &f->blocks[b];
   1442     for (u32 i = 0; i < bl->ninsts; ++i)
   1443       opt_walk_inst_operands(f, &bl->insts[i], remat_exclude_indirect_use, ri);
   1444   }
   1445 }
   1446 
   1447 static Inst* remat_def_for(const RematInfo* ri, PReg v) {
   1448   if (!ri || !ri->remat_def || v == PREG_NONE || v == 0 || v >= ri->nregs)
   1449     return NULL;
   1450   return ri->remat_def[v];
   1451 }
   1452 
   1453 typedef struct RewriteCtx {
   1454   const RematInfo* remat;
   1455 } RewriteCtx;
   1456 
   1457 static int remat_operand_for(const RematInfo* ri, PReg v, Operand* out) {
   1458   const Inst* def = remat_def_for(ri, v);
   1459   if (!def || !out) return 0;
   1460   memset(out, 0, sizeof *out);
   1461   switch ((IROp)def->op) {
   1462     case IR_LOAD_IMM:
   1463       out->kind = OPK_IMM;
   1464       out->cls = def->nopnds ? def->opnds[0].cls : RC_INT;
   1465       out->type = def->type ? def->type
   1466                             : (def->nopnds ? def->opnds[0].type : 0);
   1467       out->v.imm = def->extra.imm;
   1468       return 1;
   1469     case IR_ADDR_OF:
   1470       if (def->nopnds < 2 || def->opnds[1].kind != OPK_LOCAL) return 0;
   1471       out->kind = OPK_FRAME_ADDR;
   1472       out->cls = RC_INT;
   1473       out->type = def->type ? def->type : def->opnds[0].type;
   1474       out->v.frame_slot = def->opnds[1].v.frame_slot;
   1475       return 1;
   1476     default:
   1477       return 0;
   1478   }
   1479 }
   1480 
   1481 /* Translate one virtual value to its stable allocated location. A spill is a
   1482  * frame operand, not a synthetic reload into an emitter-reserved register.
   1483  * Cheap input-less values remain recipes and are materialized inside the
   1484  * consumer's native-emission temp scope. */
   1485 static void rewrite_one_operand(Func* f, Inst* owner, Operand* op, int is_def,
   1486                                 void* arg) {
   1487   RewriteCtx* c = (RewriteCtx*)arg;
   1488   (void)owner;
   1489   if (op->kind != OPK_REG) return;
   1490   PReg v = (PReg)op->v.reg;
   1491   if (v == PREG_NONE || v == 0 || v >= opt_reg_count(f)) return;
   1492   u8 alloc_kind = opt_preg_alloc_kind(f, v);
   1493   if (alloc_kind == OPT_ALLOC_HARD) {
   1494     op->v.reg = opt_preg_hard_reg(f, v);
   1495     return;
   1496   }
   1497   if (alloc_kind != OPT_ALLOC_SPILL) return;
   1498   if (!is_def && !(op->flags & OPT_OPERAND_WALK_INDIRECT_PART) &&
   1499       remat_operand_for(c->remat, v, op))
   1500     return;
   1501   *op = spill_addr(f, v);
   1502 }
   1503 
   1504 typedef struct RewriteCallSaveCtx {
   1505   Func* f;
   1506   RewriteList* out;
   1507   const InstRefs* refs;
   1508   const Inst* call;
   1509   int emit_restore;
   1510 } RewriteCallSaveCtx;
   1511 
   1512 static void rewrite_call_save_one(PReg v, void* arg) {
   1513   RewriteCallSaveCtx* c = (RewriteCallSaveCtx*)arg;
   1514   Func* f = c->f;
   1515   if (v == PREG_NONE || v == 0 || v >= opt_reg_count(f)) return;
   1516   if (c->refs && refs_has_def(c->refs, v)) return;
   1517   if (opt_preg_alloc_kind(f, v) != OPT_ALLOC_HARD) return;
   1518   u8 cls = opt_preg_loc_cls(f, v);
   1519   Reg hr = opt_preg_hard_reg(f, v);
   1520   if (cls >= OPT_REG_CLASSES || hr >= OPT_MAX_HARD_REGS) return;
   1521   if ((opt_call_clobber_mask_for(f, c->call, cls) & (1u << hr)) == 0) return;
   1522   if (c->emit_restore)
   1523     append_load_preg(f, c->out, v);
   1524   else
   1525     append_store_preg(f, c->out, v);
   1526 }
   1527 
   1528 static void append_live_call_saves(Func* f, RewriteList* out, const Inst* call,
   1529                                    const u64* live_after, u32 live_active_words,
   1530                                    const InstRefs* refs,
   1531                                    const PReg* call_save_pregs,
   1532                                    u32 ncall_save_pregs, int emit_restore) {
   1533   RewriteCallSaveCtx ctx = {f, out, refs, call, emit_restore};
   1534   f->opt_rewrite_live_words_touched += ncall_save_pregs;
   1535   for (u32 i = 0; i < ncall_save_pregs; ++i) {
   1536     PReg v = call_save_pregs[i];
   1537     u32 w = v / 64u;
   1538     if (w >= live_active_words) continue;
   1539     if (!bit_has(live_after, v)) continue;
   1540     rewrite_call_save_one(v, &ctx);
   1541   }
   1542 }
   1543 
   1544 static PReg* rewrite_collect_call_save_pregs(Func* f, u32* count_out) {
   1545   u32 n = 0;
   1546   for (PReg v = 1; v < opt_reg_count(f); ++v) {
   1547     OptPRegInfo* vi = &f->preg_info[v];
   1548     if (opt_preg_alloc_kind(f, v) != OPT_ALLOC_HARD) continue;
   1549     if (!vi->live_across_call_freq) continue;
   1550     ++n;
   1551   }
   1552   PReg* pregs = arena_array(f->arena, PReg, n ? n : 1u);
   1553   u32 w = 0;
   1554   for (PReg v = 1; v < opt_reg_count(f); ++v) {
   1555     OptPRegInfo* vi = &f->preg_info[v];
   1556     if (opt_preg_alloc_kind(f, v) != OPT_ALLOC_HARD) continue;
   1557     if (!vi->live_across_call_freq) continue;
   1558     pregs[w++] = v;
   1559   }
   1560   *count_out = n;
   1561   return pregs;
   1562 }
   1563 
   1564 static int rewrite_elides_remat_producer(Func* f, const Inst* in,
   1565                                          const RematInfo* remat) {
   1566   PReg v;
   1567   if (!in || !remat_inst_is_candidate(f, in)) return 0;
   1568   v = (PReg)in->def;
   1569   if (v == PREG_NONE || v == 0 || v >= opt_reg_count(f)) return 0;
   1570   return opt_preg_alloc_kind(f, v) == OPT_ALLOC_SPILL &&
   1571          remat_def_for(remat, v) != NULL;
   1572 }
   1573 
   1574 static void rewrite_func(Func* f, const OptLiveInfo* live_info) {
   1575   u32 words = live_info ? live_info->words : f->opt_live_words;
   1576   if (!words) words = bit_words(opt_reg_count(f));
   1577   f->opt_live_words = (u16)words;
   1578   f->opt_rewrite_inserted_insts = 0;
   1579   f->opt_rewrite_live_words_touched = 0;
   1580 
   1581   u64* live = arena_zarray(f->arena, u64, words ? words : 1u);
   1582   u32 ncall_save_pregs = 0;
   1583   PReg* call_save_pregs = rewrite_collect_call_save_pregs(f, &ncall_save_pregs);
   1584   RematInfo remat;
   1585   opt_mark_remat(f, &remat);
   1586   InstRefs refs;
   1587   memset(&refs, 0, sizeof refs);
   1588   u32 live_active_words = 0;
   1589   for (u32 b = 0; b < f->nblocks; ++b) {
   1590     Block* bl = &f->blocks[b];
   1591     RewriteOut out;
   1592     out_init(f, &out, bl->ninsts + 16u);
   1593     RewriteList call_saves, call_restores;
   1594     memset(&call_saves, 0, sizeof call_saves);
   1595     memset(&call_restores, 0, sizeof call_restores);
   1596     live_active_words = live_copy_block_out_active(live_info, b, live, words,
   1597                                                    live_active_words);
   1598     f->opt_rewrite_live_words_touched += live_active_words;
   1599 
   1600     for (u32 ri = bl->ninsts; ri > 0; --ri) {
   1601       u32 i = ri - 1u;
   1602       Inst in;
   1603       opt_mir_clone_inst(f->arena, &in, &bl->insts[i]);
   1604       if ((IROp)in.op == IR_PARAM_DECL) {
   1605         out_prepend_inst(f, &out, &in);
   1606         continue;
   1607       }
   1608       refs_reset(&refs);
   1609       opt_walk_inst_operands(f, &in, refs_collect, &refs);
   1610       list_reset(&call_saves);
   1611       list_reset(&call_restores);
   1612       RewriteCtx ctx = {&remat};
   1613       int elide = rewrite_elides_remat_producer(f, &in, &remat);
   1614       opt_walk_inst_operands(f, &in, rewrite_one_operand, &ctx);
   1615       if ((IROp)in.op == IR_CALL) {
   1616         append_live_call_saves(f, &call_saves, &in, live, live_active_words,
   1617                                &refs, call_save_pregs, ncall_save_pregs, 0);
   1618         append_live_call_saves(f, &call_restores, &in, live, live_active_words,
   1619                                &refs, call_save_pregs, ncall_save_pregs, 1);
   1620       }
   1621       out_prepend_list_reverse(f, &out, &call_restores);
   1622       if (!elide) out_prepend_inst(f, &out, &in);
   1623       out_prepend_list_reverse(f, &out, &call_saves);
   1624       live_active_words =
   1625           live_update_refs_before_active(live, live_active_words, words, &refs);
   1626       f->opt_rewrite_live_words_touched += refs.nuses + refs.ndefs;
   1627     }
   1628     bl->insts = out.data + out.start;
   1629     bl->ninsts = out.cap - out.start;
   1630     bl->cap = bl->ninsts;
   1631   }
   1632 
   1633   /* MIR operands are locations, not SSA values.  Keep the HIR definition
   1634    * metadata on the untouched source graph and make the representation
   1635    * boundary explicit here: post-allocation passes derive register effects
   1636    * solely from instruction semantics and location operands. */
   1637   for (u32 b = 0; b < f->nblocks; ++b) {
   1638     Block* bl = &f->blocks[b];
   1639     for (u32 i = 0; i < bl->ninsts; ++i) {
   1640       bl->insts[i].def = VAL_NONE;
   1641       bl->insts[i].ndefs = 0;
   1642       bl->insts[i].defs = NULL;
   1643     }
   1644   }
   1645   f->opt_rewritten = 1;
   1646 }
   1647 
   1648 void opt_lower_to_mir(Func* f, const OptLiveInfo* live_info) {
   1649   if (!f) return;
   1650   Func phys;
   1651   opt_mir_prepare_rewrite(&phys, f);
   1652 
   1653   rewrite_func(&phys, live_info);
   1654 
   1655   MFunc* m = arena_zarray(f->arena, MFunc, 1);
   1656   m->blocks = phys.blocks;
   1657   m->nblocks = phys.nblocks;
   1658   m->blocks_cap = phys.blocks_cap;
   1659   m->entry = phys.entry;
   1660   m->emit_order_n = phys.emit_order_n;
   1661   m->emit_order_cap = phys.emit_order_cap;
   1662   m->emit_order = phys.emit_order;
   1663 
   1664   f->mir = m;
   1665   /* frame slots and instruction ids are function-wide emission metadata.  The
   1666    * HIR graph itself remains untouched and virtual; MIR owns phys.blocks. */
   1667   f->frame_slots = phys.frame_slots;
   1668   f->nframe_slots = phys.nframe_slots;
   1669   f->frame_slots_cap = phys.frame_slots_cap;
   1670   f->next_inst_id = phys.next_inst_id;
   1671   f->opt_rewrite_inserted_insts = phys.opt_rewrite_inserted_insts;
   1672   f->opt_rewrite_live_words_touched = phys.opt_rewrite_live_words_touched;
   1673 }
   1674 
   1675 static void rewrite_dump_sb(Writer* w, const StrBuf* sb) {
   1676   kit_writer_write(w, strbuf_cstr(sb), strbuf_len(sb));
   1677 }
   1678 
   1679 void opt_rewrite_dump(Func* f, Writer* w) {
   1680   if (!f || !w) return;
   1681   char buf[96];
   1682   StrBuf sb;
   1683   strbuf_init(&sb, buf, sizeof buf);
   1684   strbuf_puts(&sb, "rewrite blocks=");
   1685   strbuf_put_u64(&sb, (u64)(unsigned)f->nblocks);
   1686   strbuf_puts(&sb, " pregs=");
   1687   strbuf_put_u64(&sb, (u64)(unsigned)opt_reg_count(f));
   1688   strbuf_puts(&sb, " rewritten=");
   1689   strbuf_put_u64(&sb, (u64)(unsigned)f->opt_rewritten);
   1690   strbuf_putc(&sb, '\n');
   1691   rewrite_dump_sb(w, &sb);
   1692   for (u32 b = 0; b < f->nblocks; ++b) {
   1693     Block* bl = &f->blocks[b];
   1694     strbuf_reset(&sb);
   1695     strbuf_puts(&sb, "block ");
   1696     strbuf_put_u64(&sb, (u64)(unsigned)b);
   1697     strbuf_puts(&sb, " insts=");
   1698     strbuf_put_u64(&sb, (u64)(unsigned)bl->ninsts);
   1699     strbuf_putc(&sb, '\n');
   1700     rewrite_dump_sb(w, &sb);
   1701     for (u32 i = 0; i < bl->ninsts; ++i) {
   1702       Inst* in = &bl->insts[i];
   1703       strbuf_reset(&sb);
   1704       strbuf_puts(&sb, "  ");
   1705       strbuf_put_u64(&sb, (u64)(unsigned)i);
   1706       strbuf_puts(&sb, " op=");
   1707       strbuf_put_u64(&sb, (u64)(unsigned)in->op);
   1708       strbuf_puts(&sb, " operands=");
   1709       strbuf_put_u64(&sb, (u64)(unsigned)in->nopnds);
   1710       strbuf_putc(&sb, '\n');
   1711       rewrite_dump_sb(w, &sb);
   1712     }
   1713   }
   1714 }
   1715 
   1716 static int all_defs_dead(Func* f, Inst* in, u64* live) {
   1717   if (in->def != 0 && in->def < opt_reg_count(f) && bit_has(live, in->def))
   1718     return 0;
   1719   for (u32 i = 0; i < in->ndefs; ++i) {
   1720     PReg r = (PReg)in->defs[i];
   1721     if (r != 0 && r < opt_reg_count(f) && bit_has(live, r)) return 0;
   1722   }
   1723   return 1;
   1724 }
   1725 
   1726 void opt_dead_def_elim_with_live(Func* f, const OptLiveInfo* live_info) {
   1727   u32 words = live_info ? live_info->words : f->opt_live_words;
   1728   if (!words) words = bit_words(opt_reg_count(f));
   1729   f->opt_dde_live_words_touched = 0;
   1730   InstRefs refs;
   1731   memset(&refs, 0, sizeof refs);
   1732   for (u32 b = 0; b < f->nblocks; ++b) {
   1733     Block* bl = &f->blocks[b];
   1734     u64* live = arena_zarray(f->arena, u64, words ? words : 1u);
   1735     f->opt_dde_live_words_touched += words;
   1736     live_copy_block_out(f, live_info, b, live, words);
   1737 
   1738     Inst* new_insts = arena_array(f->arena, Inst, bl->ninsts);
   1739     u32 w = 0;
   1740     for (u32 ri = bl->ninsts; ri > 0; --ri) {
   1741       u32 i = ri - 1u;
   1742       Inst* in = &bl->insts[i];
   1743       if (!opt_inst_has_side_effect(f, in) && all_defs_dead(f, in, live)) {
   1744         continue;
   1745       }
   1746       new_insts[w++] = *in;
   1747 
   1748       refs_reset(&refs);
   1749       opt_walk_inst_operands(f, in, refs_collect, &refs);
   1750       live_update_refs_before(live, &refs);
   1751       f->opt_dde_live_words_touched += refs.nuses + refs.ndefs;
   1752     }
   1753 
   1754     for (u32 i = 0; i < w / 2; ++i) {
   1755       Inst tmp = new_insts[i];
   1756       new_insts[i] = new_insts[w - 1 - i];
   1757       new_insts[w - 1 - i] = tmp;
   1758     }
   1759 
   1760     bl->insts = new_insts;
   1761     bl->ninsts = w;
   1762     bl->cap = w;
   1763   }
   1764 }
   1765 
   1766 void opt_dead_def_elim(Func* f) {
   1767   OptLiveInfo live;
   1768   opt_live_blocks(f, &live);
   1769   opt_dead_def_elim_with_live(f, &live);
   1770 }
   1771 
   1772 /* Collect the def and use PRegs of one instruction (operands + aux fan-out). */
   1773 typedef struct AllocVerifyRefs {
   1774   PReg defs[256];
   1775   u32 ndefs;
   1776   PReg uses[256];
   1777   u32 nuses;
   1778 } AllocVerifyRefs;
   1779 
   1780 static void alloc_verify_collect(Func* f, Inst* in, Operand* op, int is_def,
   1781                                  void* ctx) {
   1782   (void)in;
   1783   AllocVerifyRefs* r = (AllocVerifyRefs*)ctx;
   1784   PReg p;
   1785   if (!op || op->kind != OPK_REG) return;
   1786   p = (PReg)op->v.reg;
   1787   if (p == 0 || p >= opt_reg_count(f)) return;
   1788   if (is_def) {
   1789     if (r->ndefs < 256u) r->defs[r->ndefs++] = p;
   1790   } else {
   1791     if (r->nuses < 256u) r->uses[r->nuses++] = p;
   1792   }
   1793 }
   1794 
   1795 /* Post-allocation interference verifier for the O1 (no-coalesce) path. Since
   1796  * O1 never coalesces, two *distinct* PRegs that are live simultaneously must
   1797  * never share a hard register. Re-derive per-instruction liveness from the
   1798  * block live-out sets and panic if any definition collides with a different
   1799  * live value in the same hard reg — turning a silent miscompile into a hard
   1800  * error. */
   1801 static void opt_verify_alloc(Func* f, const OptLiveInfo* live) {
   1802   u32 nregs = opt_reg_count(f);
   1803   u8* cur;
   1804   if (nregs <= 1u || !live) return;
   1805   /* No "left in incoming reg" pre-check: the hint path's
   1806    * opt_ranges_overlap_kind precision check already permits the unit-overlap
   1807    * between a param PReg and its own incoming reg (= "no entry move"), and
   1808    * the standard allocator's bitmap rejects every other overlap. The
   1809    * per-instruction interference scan below is the residual safety net. */
   1810   cur = arena_array(f->arena, u8, nregs);
   1811   for (u32 b = 0; b < f->nblocks; ++b) {
   1812     Block* bl = &f->blocks[b];
   1813     const OptBlockLive* lb = &live->blocks[b];
   1814     memset(cur, 0, nregs);
   1815     for (PReg p = 1; p < nregs; ++p)
   1816       if (opt_bitset_has(&lb->live_out, p)) cur[p] = 1u;
   1817     for (u32 ri = bl->ninsts; ri > 0; --ri) {
   1818       Inst* in = &bl->insts[ri - 1u];
   1819       AllocVerifyRefs refs;
   1820       refs.ndefs = 0;
   1821       refs.nuses = 0;
   1822       opt_walk_inst_operands(f, in, alloc_verify_collect, &refs);
   1823       for (u32 k = 0; k < refs.ndefs; ++k) {
   1824         PReg d = refs.defs[k];
   1825         u8 d_kind = opt_preg_alloc_kind(f, d);
   1826         if (d_kind != OPT_ALLOC_HARD && d_kind != OPT_ALLOC_SPILL) continue;
   1827         for (PReg p = 1; p < nregs; ++p) {
   1828           u8 p_kind;
   1829           if (!cur[p] || p == d) continue;
   1830           /* W3: PRegs in the same coalesce root deliberately share one location
   1831            * (the allocator assigns per root). They are one value, not an
   1832            * interference — the move-coalescer only merges roots whose live
   1833            * ranges do not truly overlap, so the shared location is sound. */
   1834           if (alloc_coalesce_root(f, p) == alloc_coalesce_root(f, d)) continue;
   1835           p_kind = opt_preg_alloc_kind(f, p);
   1836           if (p_kind == OPT_ALLOC_HARD && d_kind == OPT_ALLOC_HARD &&
   1837               opt_preg_loc_cls(f, p) == opt_preg_loc_cls(f, d) &&
   1838               opt_preg_hard_reg(f, p) == opt_preg_hard_reg(f, d)) {
   1839             SrcLoc loc = {0, 0, 0};
   1840             compiler_panic(f->c, loc,
   1841                            "opt regalloc: O1 interference — pregs %u and %u "
   1842                            "share cls%u reg%u, both live at block %u inst %u "
   1843                            "(op %u)",
   1844                            (unsigned)d, (unsigned)p,
   1845                            (unsigned)opt_preg_loc_cls(f, d),
   1846                            (unsigned)opt_preg_hard_reg(f, d), b, ri - 1u,
   1847                            (unsigned)in->op);
   1848           }
   1849           if (p_kind == OPT_ALLOC_SPILL && d_kind == OPT_ALLOC_SPILL &&
   1850               opt_preg_spill_slot(f, p) == opt_preg_spill_slot(f, d)) {
   1851             SrcLoc loc = {0, 0, 0};
   1852             compiler_panic(f->c, loc,
   1853                            "opt regalloc: O1 interference — pregs %u and %u "
   1854                            "share spill slot %u, both live at block %u inst %u "
   1855                            "(op %u)",
   1856                            (unsigned)d, (unsigned)p,
   1857                            (unsigned)opt_preg_spill_slot(f, d), b, ri - 1u,
   1858                            (unsigned)in->op);
   1859           }
   1860         }
   1861       }
   1862       for (u32 k = 0; k < refs.ndefs; ++k) cur[refs.defs[k]] = 0u;
   1863       for (u32 k = 0; k < refs.nuses; ++k) cur[refs.uses[k]] = 1u;
   1864     }
   1865   }
   1866 }
   1867 
   1868 static void opt_regalloc_place(Func* f, OptLiveInfo* live_out) {
   1869   metrics_scope_begin(f->c, "opt.live_ranges.regalloc");
   1870   OptLiveInfo live;
   1871   opt_live_blocks(f, &live);
   1872   OptLiveRangeSet ranges;
   1873   opt_live_ranges_build(f, &live, &ranges);
   1874   opt_rebuild_preg_info_from_ranges(f, &ranges);
   1875   opt_apply_asm_constraints_from_live(f, &live);
   1876   apply_abi_aliasing_hints(f);
   1877   /* Linear move coalescing (O1.md W3): populate the union-find that
   1878    * alloc_coalesce_root/alloc_group_member consult so the allocator merges
   1879    * copy-related values onto one location. No O(n^2) conflict matrix — see
   1880    * opt_coalesce_linear. opt_verify_alloc treats same-root PRegs as one value. */
   1881   opt_coalesce_linear(f, &ranges);
   1882   metrics_count(f->c, "opt.live_words", f->opt_live_words);
   1883   metrics_count(f->c, "opt.ranges", ranges.nranges);
   1884   metrics_count(f->c, "opt.range_points", ranges.point_count);
   1885   metrics_count(f->c, "opt.range_raw_points", ranges.raw_point_count);
   1886   metrics_count(f->c, "opt.range_max_per_preg", ranges.max_ranges_per_preg);
   1887   metrics_count(f->c, "opt.range_max_length", ranges.max_live_length);
   1888   metrics_count(f->c, "opt.range_whole_block_spans", ranges.whole_block_spans);
   1889   metrics_count(f->c, "opt.live.bitset_words_touched",
   1890                 live.bitset_words_touched);
   1891   metrics_count(f->c, "opt.live.dataflow_iterations", live.dataflow_iterations);
   1892   metrics_count(f->c, "opt.live.dataflow_block_visits",
   1893                 live.dataflow_block_visits);
   1894   metrics_count(f->c, "opt.range.point_visits", ranges.range_point_visits);
   1895   metrics_count(f->c, "opt.range.preg_scans", ranges.preg_scans);
   1896   metrics_count(f->c, "opt.range.live_words_touched",
   1897                 ranges.live_words_touched);
   1898   metrics_count(f->c, "opt.conflict_bytes", 0);
   1899   metrics_scope_end(f->c, "opt.live_ranges.regalloc");
   1900 
   1901   OptAllocator alloc;
   1902   opt_assign_ranges(f, &ranges, &alloc);
   1903   opt_verify_alloc(f, &live);
   1904   if (live_out) *live_out = live;
   1905 }
   1906 
   1907 void opt_regalloc_locations(Func* f, OptLiveInfo* live_out) {
   1908   opt_regalloc_place(f, live_out);
   1909 }