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 }