control.c (56488B)
1 #include "cg/internal.h" 2 #include "core/metrics.h" 3 4 KitCgLabel kit_cg_label_new(KitCg* g) { 5 if (!g) return KIT_CG_LABEL_NONE; 6 if (api_unevaluated(g) && g->fn_ret_type == KIT_CG_TYPE_NONE) 7 return KIT_CG_LABEL_NONE; 8 return (KitCgLabel)g->target->label_new(g->target); 9 } 10 11 void kit_cg_label_place(KitCg* g, KitCgLabel label) { 12 if (!g) return; 13 if (api_unevaluated(g)) return; 14 api_local_const_control_boundary(g); 15 g->target->label_place(g->target, (Label)label); 16 } 17 18 void kit_cg_jump(KitCg* g, KitCgLabel label) { 19 if (!g) return; 20 if (api_unevaluated(g)) return; 21 api_local_const_control_boundary(g); 22 g->target->jump(g->target, (Label)label); 23 } 24 25 void api_branch_if(KitCg* g, ApiSValue* v, int branch_when_true, Label label) { 26 CgTarget* T; 27 KitCgTypeId ty; 28 if (!g) return; 29 if (api_unevaluated(g)) { 30 api_release(g, v); 31 return; 32 } 33 api_local_const_control_boundary(g); 34 T = g->target; 35 ty = v->type ? v->type : builtin_id(KIT_CG_BUILTIN_I32); 36 if (v->op.kind == OPK_IMM && api_sv_kind(v) == SV_OPERAND) { 37 if ((v->op.v.imm != 0) == !!branch_when_true) T->jump(T, label); 38 api_release(g, v); 39 return; 40 } 41 if (api_sv_kind(v) == SV_CMP) { 42 CmpOp op = branch_when_true ? v->delayed->cmp.op 43 : api_invert_cmp(v->delayed->cmp.op); 44 /* Flag dead-transient operands so the -O0 backend drops them at the branch 45 * instead of spilling them (eager dead-operand drop). The compare has no 46 * local destination, so pass an immediate as the dst sentinel. */ 47 Operand none = api_op_imm(0, builtin_id(KIT_CG_BUILTIN_I32)); 48 Operand ca = api_op_kill_if_dead(g, v->delayed->cmp.a, none); 49 Operand cb = api_op_kill_if_dead(g, v->delayed->cmp.b, none); 50 T->cmp_branch(T, op, ca, cb, label); 51 api_release(g, v); 52 return; 53 } 54 if (api_is_i128_type(g->c, ty)) { 55 KitCgTypeId i128 = builtin_id(KIT_CG_BUILTIN_I128); 56 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 57 KitCgTypeId ps[2] = {i128, i128}; 58 ApiSValue args[2]; 59 ApiSValue cmp; 60 args[0] = *v; 61 args[1] = api_make_sv(api_op_imm(0, i128), i128); 62 api_runtime_call_values(g, "__kit_ucmpti2", i32, ps, 2, args); 63 cmp = api_pop(g); 64 api_branch_if(g, &cmp, branch_when_true, label); 65 return; 66 } 67 /* Split-lane 8-byte truthiness: branch on (lo | hi) != 0. The value is 68 * memory-resident, so a single-slot CMP_NE-vs-zero would only see the low 69 * word; OR the two lanes into an i32 first. */ 70 if (api_is_wide8_scalar_type(g->c, ty)) { 71 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 72 Operand orl = api_wide8_or_lanes(g, v, ty); 73 Operand zero = api_op_imm(0, i32); 74 T->cmp_branch(T, branch_when_true ? CMP_NE : CMP_EQ, orl, zero, label); 75 api_release(g, v); 76 return; 77 } 78 { 79 Operand a = api_force_local(g, v, ty); 80 Operand zero = api_op_imm(0, ty); 81 /* The truthiness test is `v`'s last use; drop it at the branch if it is a 82 * dead transient (zero is the non-local dst sentinel). */ 83 a = api_op_kill_if_dead(g, a, zero); 84 T->cmp_branch(T, branch_when_true ? CMP_NE : CMP_EQ, a, zero, label); 85 api_release(g, v); 86 } 87 } 88 89 void kit_cg_branch_true(KitCg* g, KitCgLabel label) { 90 ApiSValue v; 91 if (!g) return; 92 v = api_pop(g); 93 api_branch_if(g, &v, 1, (Label)label); 94 } 95 96 void kit_cg_branch_false(KitCg* g, KitCgLabel label) { 97 ApiSValue v; 98 if (!g) return; 99 v = api_pop(g); 100 api_branch_if(g, &v, 0, (Label)label); 101 } 102 103 void cg_lower_switch_default(CgTarget* t, const CGSwitchDesc* d) { 104 /* Cmp-and-branch chain: one cmp_branch per case, then jump to 105 * default (or fall through if LABEL_NONE). The fallback shape; the 106 * frontend-facing kit_cg_switch picks chain vs. jump-table up 107 * front (see cg_plan_switch) and routes here only for the chain 108 * case. Backend overrides (the C target's switch_) and opt's IR 109 * replay both reach this from outside the cg API, so the lowering 110 * stays target-only and uses just cmp_branch + jump. */ 111 for (u32 i = 0; i < d->ncases; ++i) { 112 Operand imm = api_op_imm((i64)d->cases[i].value, d->selector_type); 113 t->cmp_branch(t, CMP_EQ, d->selector, imm, d->cases[i].label); 114 } 115 if (d->default_label != LABEL_NONE) { 116 t->jump(t, d->default_label); 117 } 118 } 119 120 /* Density / sizing thresholds for the O1 jump-table heuristic. At O0 121 * we only honor an explicit KIT_CG_SWITCH_JUMP_TABLE hint — the policy 122 * is single-pass and tied to frontend intent rather than analysis. O1 123 * runs a linear scan over the case list and picks a single global table 124 * when the cases are dense enough; clustering / multi-table layouts are 125 * out of scope here. */ 126 #define CG_SWITCH_TABLE_MIN_CASES_O1 4u 127 #define CG_SWITCH_TABLE_MAX_SPAN_O1 4096u 128 #define CG_SWITCH_TABLE_DENSITY_RECIP_O1 4u /* ncases * recip >= span */ 129 130 typedef enum CGSwitchPlanKind { 131 CG_SWITCH_PLAN_CHAIN, 132 CG_SWITCH_PLAN_TABLE, 133 } CGSwitchPlanKind; 134 135 typedef struct CGSwitchPlan { 136 CGSwitchPlanKind kind; 137 i64 vmin; 138 u64 span; 139 } CGSwitchPlan; 140 141 /* Single pass over the cases array, deriving (vmin, span) for the tightest 142 * dispatch window. Dispatch is `idx = (sel - vmin) mod 2^width` with an 143 * unsigned bounds check, so the cases live on a circle of size 2^width and 144 * the minimal enclosing window is the complement of the largest gap between 145 * them. We don't sort to find that gap; instead we evaluate the two windows 146 * whose seam sits at a fixed point and take the smaller: 147 * 148 * - the *signed* window [smin, smax], seam at the signed midpoint — tight 149 * for values near 0 or near 2^width (e.g. a (T)-1 case folded next to 150 * small positives); 151 * - the *unsigned* window [umin, umax], seam at 0 / 2^width — tight for 152 * values straddling the signed midpoint. 153 * 154 * A table-eligible cluster (span <= a few thousand) is far smaller than 155 * 2^(width-1), so it can cross at most one of those two seams; the smaller 156 * window is therefore the true minimum. Selector signedness isn't even 157 * visible here (CG integer builtins carry only a width), and both windows 158 * dispatch correctly under the modular index, so taking the min is a pure 159 * code-size win. Returns 0 if the selector type is unusable (>64 bits) or 160 * both windows degenerate to the full range. */ 161 static int cg_switch_extents(Compiler* c, const CGSwitchDesc* d, i64* out_vmin, 162 u64* out_span) { 163 u32 width; 164 u64 mask; 165 i64 smin; 166 i64 smax; 167 u64 umin; 168 u64 umax; 169 u32 i; 170 u64 sspan; 171 u64 uspan; 172 width = kit_cg_type_int_width((KitCompiler*)c, d->selector_type); 173 if (!width || width > 64u) return 0; 174 if (d->ncases == 0) return 0; 175 mask = (width >= 64u) ? UINT64_MAX : (((u64)1u << width) - 1u); 176 smin = INT64_MAX; 177 smax = INT64_MIN; 178 umin = UINT64_MAX; 179 umax = 0; 180 for (i = 0; i < d->ncases; ++i) { 181 u64 raw = d->cases[i].value & mask; 182 i64 sv = (width == 64u) ? (i64)d->cases[i].value 183 : api_sign_extend_width(d->cases[i].value, width); 184 if (sv < smin) smin = sv; 185 if (sv > smax) smax = sv; 186 if (raw < umin) umin = raw; 187 if (raw > umax) umax = raw; 188 } 189 /* span = delta + 1; a delta of UINT64_MAX is the degenerate full-range 190 * case the table can't represent. */ 191 { 192 u64 sdelta = (u64)smax - (u64)smin; 193 sspan = sdelta == UINT64_MAX ? 0u : sdelta + 1u; 194 } 195 { 196 u64 udelta = umax - umin; 197 uspan = udelta == UINT64_MAX ? 0u : udelta + 1u; 198 } 199 if (sspan == 0u && uspan == 0u) return 0; 200 if (uspan != 0u && (sspan == 0u || uspan < sspan)) { 201 *out_span = uspan; 202 *out_vmin = (i64)umin; /* a bit pattern; emission indexes modularly */ 203 } else { 204 *out_span = sspan; 205 *out_vmin = smin; 206 } 207 return 1; 208 } 209 210 static CGSwitchPlan cg_plan_switch(KitCg* g, const CGSwitchDesc* d) { 211 CGSwitchPlan plan; 212 plan.kind = CG_SWITCH_PLAN_CHAIN; 213 plan.vmin = 0; 214 plan.span = 0; 215 if (d->ncases == 0) return plan; 216 if (d->default_label == LABEL_NONE) return plan; 217 if (d->hint == KIT_CG_SWITCH_BRANCH_CHAIN) return plan; 218 if (!cg_switch_extents(g->c, d, &plan.vmin, &plan.span)) return plan; 219 if (d->hint == KIT_CG_SWITCH_JUMP_TABLE) { 220 /* Frontend explicitly opted in. Honor unless the span is wildly 221 * out of bounds — a forced hint shouldn't blow up code size on a 222 * misshapen switch. */ 223 if (plan.span > CG_SWITCH_TABLE_MAX_SPAN_O1) return plan; 224 plan.kind = CG_SWITCH_PLAN_TABLE; 225 return plan; 226 } 227 /* TARGET_DEFAULT: O0 keeps the chain; O1+ runs the density check. */ 228 if (d->opt_level == 0) return plan; 229 if (d->ncases < CG_SWITCH_TABLE_MIN_CASES_O1) return plan; 230 if (plan.span > CG_SWITCH_TABLE_MAX_SPAN_O1) return plan; 231 if (plan.span > (u64)d->ncases * CG_SWITCH_TABLE_DENSITY_RECIP_O1) 232 return plan; 233 plan.kind = CG_SWITCH_PLAN_TABLE; 234 return plan; 235 } 236 237 /* Emit a dense jump-table dispatch using cg-API ops. The selector value 238 * is still on the value stack on entry. Routing through the cg API 239 * means the same primitives work for direct CG (lowered to machine 240 * ops) and for the opt wrapper (recorded as IR_BINOP / IR_CMP_BRANCH / 241 * IR_LOAD / IR_INDIRECT_BRANCH, which pass_emit already lowers 242 * natively without ever materializing IR_SWITCH). */ 243 static void cg_emit_switch_table(KitCg* g, const CGSwitchDesc* d, 244 const CGSwitchPlan* plan) { 245 Compiler* c; 246 Heap* h; 247 KitCgTypeId sel_ty; 248 u32 sel_w; 249 KitCgTypeId i64_ty; 250 KitCgTypeId void_ptr_ty; 251 KitCgTypeId arr_ty; 252 Label* labels; 253 KitCgLabel* targets; 254 ObjSymId table_sym; 255 KitCgDecl decl; 256 KitCgMemAccess acc; 257 u64 i; 258 u32 width; 259 c = g->c; 260 h = (Heap*)c->ctx->heap; 261 sel_ty = d->selector_type; 262 sel_w = kit_cg_type_int_width((KitCompiler*)c, sel_ty); 263 i64_ty = builtin_id(KIT_CG_BUILTIN_I64); 264 void_ptr_ty = cg_type_ptr_to(c, builtin_id(KIT_CG_BUILTIN_VOID)); 265 266 /* 1. Keep the original selector, then compute idx = sel - vmin 267 * (selector_type wraparound is what we want; the unsigned bounds 268 * check below catches both negative-underflow and positive-overflow 269 * cases). The selector copy lets us recompute idx after the bounds 270 * branch instead of carrying a duplicated arithmetic value across 271 * control flow. */ 272 kit_cg_dup(g); /* [sel, sel] */ 273 kit_cg_push_int(g, (uint64_t)plan->vmin, sel_ty); 274 kit_cg_int_binop(g, KIT_CG_INT_SUB, 0); /* [sel, idx] */ 275 276 /* 2. Bounds check: branch to default when idx u> span-1. */ 277 kit_cg_dup(g); /* [sel, idx, idx] */ 278 kit_cg_push_int(g, plan->span - 1u, sel_ty); 279 kit_cg_int_cmp(g, KIT_CG_INT_GT_U); /* [sel, idx, cond] */ 280 kit_cg_branch_true(g, (KitCgLabel)d->default_label); /* [sel, idx] */ 281 282 /* 3. Recompute idx from the preserved selector for table addressing. */ 283 kit_cg_drop(g); /* [sel] */ 284 kit_cg_push_int(g, (uint64_t)plan->vmin, sel_ty); 285 kit_cg_int_binop(g, KIT_CG_INT_SUB, 0); /* [idx] */ 286 287 /* 4. Widen idx to i64 so the subsequent index multiply runs at 288 * pointer width regardless of selector signedness. The bounds 289 * check above already established 0 <= idx < span, so zext is 290 * value-preserving. */ 291 if (sel_w < 64u) { 292 kit_cg_zext(g, i64_ty); 293 } 294 295 /* 5. Build the dense label[] table and emit the rodata table. */ 296 labels = 297 (Label*)h->alloc(h, (size_t)plan->span * sizeof *labels, _Alignof(Label)); 298 if (!labels) compiler_panic(c, g->cur_loc, "kit_cg_switch: oom"); 299 for (i = 0; i < plan->span; ++i) labels[i] = d->default_label; 300 width = sel_w; 301 { 302 /* Index modularly: vmin may be the unsigned-window origin (a value with 303 * the sign bit set), so a signed `vi - vmin` could overflow. This mirrors 304 * the runtime `idx = sel - vmin` computed in sel_ty, and reproduces the 305 * old non-wrapping subtraction exactly for the signed-window case. */ 306 u64 mask = (width >= 64u) ? UINT64_MAX : (((u64)1u << width) - 1u); 307 for (i = 0; i < d->ncases; ++i) { 308 u64 table_index = ((u64)d->cases[i].value - (u64)plan->vmin) & mask; 309 labels[table_index] = d->cases[i].label; 310 } 311 } 312 table_sym = api_emit_label_table(g, labels, (u32)plan->span); 313 h->free(h, labels, (size_t)plan->span * sizeof *labels); 314 if (table_sym == OBJ_SYM_NONE) { 315 /* api_emit_label_table panics on real failure; this only fires if 316 * a future caller asks for a 0-entry table (which cg_plan_switch 317 * already rules out). */ 318 compiler_panic(c, g->cur_loc, "kit_cg_switch: table emission failed"); 319 return; 320 } 321 arr_ty = kit_cg_type_array((KitCompiler*)c, void_ptr_ty, plan->span); 322 memset(&decl, 0, sizeof decl); 323 decl.kind = KIT_CG_DECL_OBJECT; 324 decl.sym.bind = KIT_SB_LOCAL; 325 decl.sym.visibility = KIT_CG_VIS_DEFAULT; 326 decl.as.object.flags = KIT_CG_OBJ_READONLY; 327 api_remember_sym(g, table_sym, arr_ty, decl); 328 329 /* 6. Load table[idx]: bitcast the table pointer to a pointer-to-element so 330 * the element place carries the pointer-size scale, put the index on top, 331 * project the element place, then load it. */ 332 kit_cg_push_symbol_addr(g, (KitCgSym)table_sym, 0); /* [idx, &table] */ 333 kit_cg_bitcast(g, cg_type_ptr_to(c, void_ptr_ty)); /* &table : void** */ 334 kit_cg_swap(g); /* [&table, idx] */ 335 kit_cg_elem(g, 0); /* [&table[idx]] */ 336 memset(&acc, 0, sizeof acc); 337 acc.type = void_ptr_ty; 338 acc.align = (uint32_t)c->target.ptr_align; 339 kit_cg_load(g, acc); /* [label_addr] */ 340 341 /* 7. Indirect branch with the full closed target set (every case + 342 * default), so backends doing branch-target hardening (BTI/IBT/CFG) 343 * can stamp landing pads on every reachable label. */ 344 targets = (KitCgLabel*)h->alloc(h, (d->ncases + 1u) * sizeof *targets, 345 _Alignof(KitCgLabel)); 346 if (!targets) compiler_panic(c, g->cur_loc, "kit_cg_switch: oom"); 347 for (i = 0; i < d->ncases; ++i) { 348 targets[i] = (KitCgLabel)d->cases[i].label; 349 } 350 targets[d->ncases] = (KitCgLabel)d->default_label; 351 kit_cg_computed_goto(g, targets, d->ncases + 1u); 352 h->free(h, targets, (d->ncases + 1u) * sizeof *targets); 353 } 354 355 void kit_cg_switch(KitCg* g, KitCgSwitch sw) { 356 ApiSValue selector; 357 CGSwitchDesc desc; 358 Heap* h; 359 CGSwitchCase* cases = NULL; 360 CGSwitchPlan plan; 361 int native_switch_override; 362 if (!g) return; 363 if (g->sp == 0) return; 364 if (api_unevaluated(g)) { 365 selector = api_pop(g); 366 api_release(g, &selector); 367 return; 368 } 369 api_local_const_control_boundary(g); 370 memset(&desc, 0, sizeof desc); 371 desc.selector_type = resolve_type(g->c, sw.selector_type); 372 if (!desc.selector_type) { 373 ApiSValue tmp = g->stack[g->sp - 1u]; 374 desc.selector_type = api_sv_type(&tmp); 375 } 376 desc.default_label = (Label)sw.default_label; 377 desc.ncases = sw.ncases; 378 desc.hint = (u8)sw.hint; 379 desc.opt_level = (u8)g->opt_level; 380 if (sw.ncases) { 381 h = g->c->ctx->heap; 382 cases = (CGSwitchCase*)h->alloc(h, sw.ncases * sizeof(CGSwitchCase), 383 _Alignof(CGSwitchCase)); 384 if (!cases) 385 compiler_panic(g->c, g->cur_loc, "kit_cg_switch: out of memory"); 386 for (u32 i = 0; i < sw.ncases; ++i) { 387 cases[i].value = sw.cases[i].value; 388 cases[i].label = (Label)sw.cases[i].label; 389 } 390 desc.cases = cases; 391 } 392 393 /* Direct O0 targets may override switch_ for a single-pass branch-chain 394 * lowering. Still honor an explicit jump-table hint so tests and frontends 395 * can exercise the semantic label-table path without enabling O1. */ 396 native_switch_override = (g->target->switch_ && g->opt_level == 0 && 397 desc.hint != KIT_CG_SWITCH_JUMP_TABLE); 398 plan = native_switch_override ? (CGSwitchPlan){CG_SWITCH_PLAN_CHAIN, 0, 0} 399 : cg_plan_switch(g, &desc); 400 401 /* The label-table lowering materializes a rodata table of code-label 402 * addresses and an indirect branch. Targets that can't express that (Wasm) 403 * realize dense dispatch through their switch_ hook (br_table) instead, so 404 * hand the plan—hint and all—to switch_ rather than the table path. */ 405 if (plan.kind == CG_SWITCH_PLAN_TABLE && g->target->switch_ && 406 g->target->supports_label_table && 407 !g->target->supports_label_table(g->target)) { 408 plan.kind = CG_SWITCH_PLAN_CHAIN; 409 } 410 411 if (plan.kind == CG_SWITCH_PLAN_TABLE) { 412 /* Selector stays on the value stack; cg_emit_switch_table consumes 413 * it via cg-API ops so the path also records cleanly under opt. */ 414 metrics_count(g->c, "cg.switch.table", 1); 415 cg_emit_switch_table(g, &desc, &plan); 416 } else { 417 metrics_count(g->c, "cg.switch.chain", 1); 418 selector = api_pop(g); 419 desc.selector = 420 api_force_local_unless_imm(g, &selector, desc.selector_type); 421 if (g->target->switch_) { 422 g->target->switch_(g->target, &desc); 423 } else { 424 cg_lower_switch_default(g->target, &desc); 425 } 426 api_release(g, &selector); 427 } 428 if (cases) { 429 h = g->c->ctx->heap; 430 h->free(h, cases, sw.ncases * sizeof(CGSwitchCase)); 431 } 432 } 433 434 void kit_cg_push_label_addr(KitCg* g, KitCgLabel label, KitCgTypeId ptr_type) { 435 KitCgTypeId ty; 436 CGLocal r; 437 Operand dst; 438 if (!g) return; 439 ty = resolve_type(g->c, ptr_type); 440 if (!ty) ty = cg_type_ptr_to(g->c, builtin_id(KIT_CG_BUILTIN_VOID)); 441 if (api_unevaluated(g)) { 442 api_push(g, api_uneval_value(g, ty)); 443 api_const_set_top(g, api_const_unknown(ty)); 444 return; 445 } 446 r = api_alloc_temp_local(g, ty); 447 dst = api_op_local(r, ty); 448 g->target->load_label_addr(g->target, dst, (Label)label); 449 api_push(g, api_make_sv(dst, ty)); 450 } 451 452 void kit_cg_computed_goto(KitCg* g, const KitCgLabel* valid_targets, 453 uint32_t ntargets) { 454 ApiSValue target; 455 KitCgTypeId target_ty; 456 Operand target_op; 457 if (!g) return; 458 if (api_unevaluated(g)) { 459 target = api_pop(g); 460 api_release(g, &target); 461 return; 462 } 463 CG_REQUIRE(g, valid_targets && ntargets != 0, 464 "kit_cg_computed_goto: valid_targets must be non-empty"); 465 api_local_const_control_boundary(g); 466 target = api_pop(g); 467 target_ty = api_sv_type(&target); 468 target_op = api_force_local(g, &target, target_ty); 469 g->target->indirect_branch(g->target, target_op, (const Label*)valid_targets, 470 ntargets); 471 api_release(g, &target); 472 } 473 474 void kit_cg_unreachable(KitCg* g) { 475 if (!g) return; 476 if (api_unevaluated(g)) return; 477 api_local_const_control_boundary(g); 478 g->target->unreachable(g->target); 479 } 480 481 /* ============================================================ 482 * Scopes / structured control flow 483 * ============================================================ */ 484 485 /* Grow g->scopes to hold at least `want` entries (mirrors api_grow_locals). 486 * New slots are zeroed so their `active`/`generation` start clean. */ 487 int api_grow_scopes(KitCg* g, u32 want) { 488 Heap* h = g->c->ctx->heap; 489 ApiCgScope* nb; 490 u32 cap; 491 if (g->scopes_cap >= want) return 1; 492 cap = g->scopes_cap ? g->scopes_cap : 16u; 493 while (cap < want) cap *= 2u; 494 nb = (ApiCgScope*)h->alloc(h, sizeof(*nb) * cap, _Alignof(ApiCgScope)); 495 if (!nb) return 0; 496 memset(nb, 0, sizeof(*nb) * cap); 497 if (g->scopes) { 498 memcpy(nb, g->scopes, sizeof(*nb) * g->nscopes); 499 h->free(h, g->scopes, sizeof(*g->scopes) * g->scopes_cap); 500 } 501 g->scopes = nb; 502 g->scopes_cap = cap; 503 return 1; 504 } 505 506 /* The handle packs the 1-based scope index into the low 16 bits and the 507 * generation into the upper 16 — see api_scope_from_handle. */ 508 KitCgScope api_scope_handle(u32 idx, u32 generation) { 509 return (KitCgScope)((generation << 16) | ((idx + 1u) & 0xffffu)); 510 } 511 512 ApiCgScope* api_scope_from_handle(KitCg* g, KitCgScope scope, int require_top, 513 const char* who) { 514 u32 scope_index; 515 u32 generation; 516 ApiCgScope* s; 517 if (!g || scope == 0) return NULL; 518 scope_index = ((u32)scope & 0xffffu); 519 generation = ((u32)scope >> 16); 520 CG_REQUIRE(g, scope_index != 0 && scope_index <= API_CG_MAX_SCOPES, 521 "%.*s: invalid scope handle", SLICE_ARG(slice_from_cstr(who))); 522 scope_index--; 523 CG_REQUIRE(g, scope_index < g->nscopes, "%.*s: stale scope handle", 524 SLICE_ARG(slice_from_cstr(who))); 525 CG_REQUIRE(g, !require_top || scope_index + 1u == g->nscopes, 526 "%.*s: non-LIFO scope end", SLICE_ARG(slice_from_cstr(who))); 527 s = &g->scopes[scope_index]; 528 CG_REQUIRE(g, s->active && s->generation == generation, 529 "%.*s: stale scope handle", SLICE_ARG(slice_from_cstr(who))); 530 return s; 531 } 532 533 static int api_require_loop_scope(KitCg* g, const ApiCgScope* s, 534 const char* op); 535 536 int api_scope_has_result(const ApiCgScope* s) { return s->nresults != 0; } 537 538 /* Store one already-popped value into a scope carry local. Shared by the result 539 * and loop-param paths; `local`/`type` select which carry slot. */ 540 static void api_scope_store_one(KitCg* g, CGLocal local, KitCgTypeId type, 541 ApiSValue* v) { 542 Operand dst = api_op_local(local, type); 543 Operand src = 544 api_sv_op_is_local_or_imm(v) ? v->op : api_force_local(g, v, type); 545 g->target->store(g->target, dst, src, api_mem_for_lvalue(g, &dst, type)); 546 api_release(g, v); 547 } 548 549 /* Reload one carry local onto the value stack (fresh temp, load, push). */ 550 static void api_scope_reload_one(KitCg* g, CGLocal local, KitCgTypeId type, 551 const void* lang_type, u16 lang_flags) { 552 CGLocal r = api_alloc_temp_local(g, type); 553 Operand dst = api_op_local(r, type); 554 Operand src = api_op_local(local, type); 555 ApiSValue sv; 556 g->target->load(g->target, dst, src, api_mem_for_lvalue(g, &src, type)); 557 sv = api_make_sv(dst, type); 558 sv.lang_type = lang_type; 559 sv.lang_flags = lang_flags; 560 api_push(g, sv); 561 } 562 563 /* Pop the scope's N results into a caller array (out[i] = result i, value-stack 564 * bottom->top). The top of stack — the last result — is popped first. */ 565 static void api_scope_pop_results(KitCg* g, ApiCgScope* s, ApiSValue* out) { 566 u32 k; 567 for (k = 0; k < s->nresults; ++k) out[s->nresults - 1u - k] = api_pop(g); 568 } 569 570 /* Pop N results off the value stack and store each into its carry local. */ 571 void api_scope_store_results(KitCg* g, ApiCgScope* s) { 572 u32 k; 573 for (k = 0; k < s->nresults; ++k) { 574 u32 idx = s->nresults - 1u - k; /* TOS popped first -> highest index */ 575 ApiSValue v = api_pop(g); 576 s->result_lang_types[idx] = v.lang_type; 577 s->result_lang_flags[idx] = v.lang_flags; 578 api_scope_store_one(g, s->result_locals[idx], s->result_types[idx], &v); 579 } 580 } 581 582 /* Reload all N result carry locals onto the value stack (inverse of store). */ 583 void api_scope_push_results(KitCg* g, ApiCgScope* s) { 584 u32 k; 585 for (k = 0; k < s->nresults; ++k) 586 api_scope_reload_one(g, s->result_locals[k], s->result_types[k], 587 s->result_lang_types[k], s->result_lang_flags[k]); 588 } 589 590 /* Pop N params off the value stack into the loop-param carry locals. */ 591 static void api_scope_store_params(KitCg* g, ApiCgScope* s) { 592 u32 k; 593 for (k = 0; k < s->nparams; ++k) { 594 u32 idx = s->nparams - 1u - k; 595 ApiSValue v = api_pop(g); 596 s->param_lang_types[idx] = v.lang_type; 597 s->param_lang_flags[idx] = v.lang_flags; 598 api_scope_store_one(g, s->param_locals[idx], s->param_types[idx], &v); 599 } 600 } 601 602 /* Reload all N loop-param carry locals onto the value stack. */ 603 static void api_scope_reload_params(KitCg* g, ApiCgScope* s) { 604 u32 k; 605 for (k = 0; k < s->nparams; ++k) 606 api_scope_reload_one(g, s->param_locals[k], s->param_types[k], 607 s->param_lang_types[k], s->param_lang_flags[k]); 608 } 609 610 /* Allocate a memory-resident carry local of the given type. `resolved` is the 611 * canonicalized type id (for the local's type); `raw` is the as-supplied id 612 * used for the ABI size/align query, matching the historical single-result 613 * path. */ 614 static CGLocal api_scope_alloc_carry_local(KitCg* g, KitCgTypeId resolved, 615 KitCgTypeId raw) { 616 CGLocalDesc ld; 617 memset(&ld, 0, sizeof ld); 618 ld.type = resolved; 619 ld.size = abi_cg_sizeof(g->c->abi, raw); 620 ld.align = abi_cg_alignof(g->c->abi, raw); 621 ld.flags = CG_LOCAL_MEMORY_REQUIRED; 622 return g->target->local(g->target, &ld); 623 } 624 625 /* Point a scope's carry-local vectors at storage: the inline buffers for the 626 * common small arity (no allocation), or one heap block when either count 627 * exceeds API_CG_SCOPE_SIG_INLINE. Returns 0 only on heap OOM. */ 628 static int api_scope_setup_sig(KitCg* g, ApiCgScope* s, 629 const KitCgScopeSig* sig) { 630 u32 nr = sig ? sig->nresults : 0u; 631 u32 np = sig ? sig->nparams : 0u; 632 s->nresults = nr; 633 s->nparams = np; 634 s->heap_block = NULL; 635 if (nr <= API_CG_SCOPE_SIG_INLINE && np <= API_CG_SCOPE_SIG_INLINE) { 636 s->result_locals = s->result_locals_inl; 637 s->result_types = s->result_types_inl; 638 s->result_lang_types = s->result_lang_types_inl; 639 s->result_lang_flags = s->result_lang_flags_inl; 640 s->param_locals = s->param_locals_inl; 641 s->param_types = s->param_types_inl; 642 s->param_lang_types = s->param_lang_types_inl; 643 s->param_lang_flags = s->param_lang_flags_inl; 644 return 1; 645 } 646 { 647 /* CGLocal and KitCgTypeId are both u32; pack the four vectors into one 648 * block as [result_locals][result_types][param_locals][param_types]. */ 649 Heap* h = g->c->ctx->heap; 650 u32 total = 2u * nr + 2u * np; 651 u32* blk = (u32*)h->alloc(h, sizeof(u32) * total, _Alignof(u32)); 652 if (!blk) return 0; 653 s->heap_block = blk; 654 s->result_locals = blk; 655 s->result_types = blk + nr; 656 s->param_locals = blk + 2u * nr; 657 s->param_types = blk + 2u * nr + np; 658 s->heap_lang_types_block = 659 (const void**)h->alloc(h, sizeof(void*) * (nr + np), _Alignof(void*)); 660 s->heap_lang_flags_block = 661 (u16*)h->alloc(h, sizeof(u16) * (nr + np), _Alignof(u16)); 662 if (!s->heap_lang_types_block || !s->heap_lang_flags_block) { 663 if (s->heap_lang_types_block) 664 h->free(h, s->heap_lang_types_block, sizeof(void*) * (nr + np)); 665 if (s->heap_lang_flags_block) 666 h->free(h, s->heap_lang_flags_block, sizeof(u16) * (nr + np)); 667 h->free(h, blk, sizeof(u32) * total); 668 s->heap_block = NULL; 669 s->heap_lang_types_block = NULL; 670 s->heap_lang_flags_block = NULL; 671 return 0; 672 } 673 s->result_lang_types = s->heap_lang_types_block; 674 s->param_lang_types = s->heap_lang_types_block + nr; 675 s->result_lang_flags = s->heap_lang_flags_block; 676 s->param_lang_flags = s->heap_lang_flags_block + nr; 677 return 1; 678 } 679 } 680 681 static void api_scope_free_sig(KitCg* g, ApiCgScope* s) { 682 Heap* h = g->c->ctx->heap; 683 if (s->heap_block) { 684 u32 total = 2u * s->nresults + 2u * s->nparams; 685 h->free(h, s->heap_block, sizeof(u32) * total); 686 s->heap_block = NULL; 687 } 688 if (s->heap_lang_types_block) { 689 h->free(h, s->heap_lang_types_block, 690 sizeof(void*) * (s->nresults + s->nparams)); 691 s->heap_lang_types_block = NULL; 692 } 693 if (s->heap_lang_flags_block) { 694 h->free(h, s->heap_lang_flags_block, 695 sizeof(u16) * (s->nresults + s->nparams)); 696 s->heap_lang_flags_block = NULL; 697 } 698 } 699 700 static KitCgScope api_scope_begin_sig_kind(KitCg* g, u8 kind, 701 const KitCgScopeSig* sig) { 702 Label break_lbl, cont_lbl; 703 CGScopeDesc d; 704 ApiCgScope* s; 705 CGScope target_scope; 706 u32 idx, k; 707 if (!g) return 0; 708 break_lbl = g->target->label_new(g->target); 709 cont_lbl = 710 (kind == SCOPE_LOOP) ? g->target->label_new(g->target) : LABEL_NONE; 711 712 /* Depth past what the 16-bit handle index can address (see api_scope_handle) 713 * — unreachable for any realistic source. */ 714 CG_REQUIRE(g, g->nscopes < API_CG_MAX_SCOPES, 715 "KitCg: too many nested scopes"); 716 if (!api_grow_scopes(g, g->nscopes + 1u)) { 717 compiler_panic(g->c, g->cur_loc, "KitCg: out of memory growing scopes"); 718 return 0; 719 } 720 idx = g->nscopes; 721 s = &g->scopes[idx]; 722 s->break_lbl = break_lbl; 723 s->continue_lbl = cont_lbl; 724 s->generation = ++g->scope_generation; 725 if (s->generation == 0) s->generation = ++g->scope_generation; 726 s->active = 1; 727 if (!api_scope_setup_sig(g, s, sig)) { 728 compiler_panic(g->c, g->cur_loc, 729 "KitCg: out of memory for scope signature"); 730 return 0; 731 } 732 for (k = 0; k < s->nresults; ++k) 733 s->result_types[k] = resolve_type(g->c, sig->results[k]); 734 for (k = 0; k < s->nparams; ++k) 735 s->param_types[k] = resolve_type(g->c, sig->params[k]); 736 737 /* Loop preheader: snapshot the params off the value stack into their carry 738 * locals BEFORE the loop header, so the pop runs once in the preheader and 739 * every back edge (store_params + jump cont) re-stores into the same locals. 740 * Allocating a local emits nothing; the param stores belong before cont_lbl. 741 */ 742 if (kind == SCOPE_LOOP && s->nparams) { 743 for (k = 0; k < s->nparams; ++k) 744 s->param_locals[k] = 745 api_scope_alloc_carry_local(g, s->param_types[k], sig->params[k]); 746 api_scope_store_params(g, s); 747 } 748 749 api_local_const_control_boundary(g); 750 if (cont_lbl != LABEL_NONE) g->target->label_place(g->target, cont_lbl); 751 g->nscopes++; 752 753 memset(&d, 0, sizeof d); 754 d.kind = kind; 755 d.break_label = break_lbl; 756 d.continue_label = cont_lbl; 757 /* CGScopeDesc.result_type stays a single informational field (backends ignore 758 * it): the single-result type, or NONE for void / multi-value. Multi-value is 759 * realized purely through the CG carry locals above. */ 760 d.result_type = (s->nresults == 1u) ? s->result_types[0] : KIT_CG_TYPE_NONE; 761 target_scope = g->target->scope_begin(g->target, &d); 762 s->target_scope = target_scope; 763 764 /* Allocate the result carry locals after scope_begin, matching the historical 765 * single-result ordering. */ 766 for (k = 0; k < s->nresults; ++k) 767 s->result_locals[k] = 768 api_scope_alloc_carry_local(g, s->result_types[k], sig->results[k]); 769 770 /* The loop body observes the params on the value stack: reload them after the 771 * header so a back edge re-establishes exactly this shape. */ 772 if (kind == SCOPE_LOOP && s->nparams) api_scope_reload_params(g, s); 773 774 return api_scope_handle(idx, s->generation); 775 } 776 777 KitCgScope kit_cg_scope_begin(KitCg* g) { 778 KitCgScopeSig sig; 779 memset(&sig, 0, sizeof sig); 780 return api_scope_begin_sig_kind(g, (u8)SCOPE_LOOP, &sig); 781 } 782 783 KitCgScope kit_cg_scope_begin_value(KitCg* g, KitCgTypeId result_type) { 784 KitCgScopeSig sig; 785 KitCgTypeId resolved; 786 memset(&sig, 0, sizeof sig); 787 if (!g) return 0; 788 resolved = resolve_type(g->c, result_type); 789 CG_REQUIRE(g, 790 resolved != KIT_CG_TYPE_NONE && !cg_type_is_void(g->c, resolved), 791 "KitCg: value scope requires non-void result type"); 792 sig.results = &result_type; 793 sig.nresults = 1u; 794 return api_scope_begin_sig_kind(g, (u8)SCOPE_LOOP, &sig); 795 } 796 797 KitCgScope kit_cg_block_begin(KitCg* g) { 798 KitCgScopeSig sig; 799 memset(&sig, 0, sizeof sig); 800 return api_scope_begin_sig_kind(g, (u8)SCOPE_BLOCK, &sig); 801 } 802 803 KitCgScope kit_cg_block_begin_value(KitCg* g, KitCgTypeId result_type) { 804 KitCgScopeSig sig; 805 KitCgTypeId resolved; 806 memset(&sig, 0, sizeof sig); 807 if (!g) return 0; 808 resolved = resolve_type(g->c, result_type); 809 CG_REQUIRE(g, 810 resolved != KIT_CG_TYPE_NONE && !cg_type_is_void(g->c, resolved), 811 "KitCg: value block requires non-void result type"); 812 sig.results = &result_type; 813 sig.nresults = 1u; 814 return api_scope_begin_sig_kind(g, (u8)SCOPE_BLOCK, &sig); 815 } 816 817 KitCgScope kit_cg_scope_begin_sig(KitCg* g, const KitCgScopeSig* sig) { 818 return api_scope_begin_sig_kind(g, (u8)SCOPE_LOOP, sig); 819 } 820 821 KitCgScope kit_cg_block_begin_sig(KitCg* g, const KitCgScopeSig* sig) { 822 return api_scope_begin_sig_kind(g, (u8)SCOPE_BLOCK, sig); 823 } 824 825 void kit_cg_scope_store_params(KitCg* g, KitCgScope scope) { 826 ApiCgScope* s = 827 api_scope_from_handle(g, scope, 0, "KitCg: scope_store_params"); 828 if (!s) return; 829 if (!api_require_loop_scope(g, s, "scope_store_params")) return; 830 api_scope_store_params(g, s); 831 } 832 833 KitCgLabel kit_cg_scope_break_label(KitCg* g, KitCgScope scope) { 834 ApiCgScope* s = 835 api_scope_from_handle(g, scope, 0, "KitCg: scope_break_label"); 836 return s ? (KitCgLabel)s->break_lbl : (KitCgLabel)0; 837 } 838 839 KitCgLabel kit_cg_scope_continue_label(KitCg* g, KitCgScope scope) { 840 ApiCgScope* s = 841 api_scope_from_handle(g, scope, 0, "KitCg: scope_continue_label"); 842 return s ? (KitCgLabel)s->continue_lbl : (KitCgLabel)0; 843 } 844 845 void kit_cg_scope_end(KitCg* g, KitCgScope scope) { 846 ApiCgScope* s = api_scope_from_handle(g, scope, 1, "KitCg: scope_end"); 847 if (!s) return; 848 /* The fall-through results are on TOS; store them so they join any 849 * branch-supplied copies in the same carry locals, then reload after the 850 * break label as the scope's outputs. */ 851 api_scope_store_results(g, s); 852 api_local_const_control_boundary(g); 853 g->target->label_place(g->target, s->break_lbl); 854 g->target->scope_end(g->target, s->target_scope); 855 api_scope_push_results(g, s); 856 api_scope_free_sig(g, s); 857 s->active = 0; 858 g->nscopes--; 859 } 860 861 void kit_cg_break(KitCg* g, KitCgScope scope) { 862 ApiCgScope* s = api_scope_from_handle(g, scope, 0, "KitCg: break"); 863 if (!s) return; 864 api_scope_store_results(g, s); 865 api_local_const_control_boundary(g); 866 g->target->jump(g->target, s->break_lbl); 867 } 868 869 void kit_cg_scope_end_unreachable(KitCg* g, KitCgScope scope) { 870 /* Close a scope whose fall-through is unreachable (its body terminated via a 871 * branch/return). There are no fall-through results to pop; the carry locals 872 * already hold whatever the branch that exited stored. Mirrors scope_end but 873 * skips the leading store_results. The caller must have the value stack at 874 * the scope's base depth. */ 875 ApiCgScope* s = 876 api_scope_from_handle(g, scope, 1, "KitCg: scope_end_unreachable"); 877 if (!s) return; 878 api_local_const_control_boundary(g); 879 g->target->label_place(g->target, s->break_lbl); 880 g->target->scope_end(g->target, s->target_scope); 881 api_scope_push_results(g, s); 882 api_scope_free_sig(g, s); 883 s->active = 0; 884 g->nscopes--; 885 } 886 887 /* Store the pre-popped result array into the scope's carry locals (forward). */ 888 static void api_scope_store_results_from(KitCg* g, ApiCgScope* s, 889 ApiSValue* rs) { 890 u32 k; 891 for (k = 0; k < s->nresults; ++k) { 892 s->result_lang_types[k] = rs[k].lang_type; 893 s->result_lang_flags[k] = rs[k].lang_flags; 894 api_scope_store_one(g, s->result_locals[k], s->result_types[k], &rs[k]); 895 } 896 } 897 898 /* Shared body of break_true / break_false: pop the condition, then — when the 899 * scope carries results — pop them all (so they are consumed on both the taken 900 * and not-taken paths, per the API contract), and store-then-jump only when the 901 * branch is taken. `break_when` selects the sense. The single-result path is 902 * byte-identical to the historical code. */ 903 static void api_break_cond(KitCg* g, KitCgScope scope, int break_when, 904 const char* who) { 905 ApiCgScope* s; 906 ApiSValue cond; 907 if (!g || scope == 0) return; 908 s = api_scope_from_handle(g, scope, 0, who); 909 if (!s) return; 910 cond = api_pop(g); 911 912 if (!api_scope_has_result(s)) { 913 api_branch_if(g, &cond, break_when, s->break_lbl); 914 return; 915 } 916 { 917 Heap* h = g->c->ctx->heap; 918 ApiSValue* rs = (ApiSValue*)h->alloc(h, sizeof(ApiSValue) * s->nresults, 919 _Alignof(ApiSValue)); 920 if (!rs) compiler_panic(g->c, g->cur_loc, "KitCg: out of memory"); 921 api_scope_pop_results(g, s, rs); 922 if (api_sv_kind(&cond) == SV_OPERAND && cond.op.kind == OPK_IMM) { 923 if ((cond.op.v.imm != 0) == !!break_when) { 924 api_scope_store_results_from(g, s, rs); 925 api_local_const_control_boundary(g); 926 g->target->jump(g->target, s->break_lbl); 927 } else { 928 u32 k; 929 for (k = 0; k < s->nresults; ++k) api_release(g, &rs[k]); 930 } 931 api_release(g, &cond); 932 } else { 933 Label skip = g->target->label_new(g->target); 934 api_branch_if(g, &cond, !break_when, skip); 935 api_scope_store_results_from(g, s, rs); 936 api_local_const_control_boundary(g); 937 g->target->jump(g->target, s->break_lbl); 938 api_local_const_control_boundary(g); 939 g->target->label_place(g->target, skip); 940 } 941 h->free(h, rs, sizeof(ApiSValue) * s->nresults); 942 } 943 } 944 945 void kit_cg_break_true(KitCg* g, KitCgScope scope) { 946 api_break_cond(g, scope, 1, "KitCg: break_true"); 947 } 948 949 void kit_cg_break_false(KitCg* g, KitCgScope scope) { 950 api_break_cond(g, scope, 0, "KitCg: break_false"); 951 } 952 953 /* continue jumps to the loop header, which a forward-only block scope does not 954 * have (its continue_lbl is LABEL_NONE). Reject it with a clean diagnostic 955 * rather than emitting a jump to a nonexistent label. */ 956 static int api_require_loop_scope(KitCg* g, const ApiCgScope* s, 957 const char* op) { 958 CG_REQUIRE(g, s->continue_lbl != LABEL_NONE, 959 "KitCg: %s is not valid on a forward-only block scope", op); 960 return 1; 961 } 962 963 void kit_cg_continue(KitCg* g, KitCgScope scope) { 964 ApiCgScope* s = api_scope_from_handle(g, scope, 0, "KitCg: continue"); 965 if (!s) return; 966 if (!api_require_loop_scope(g, s, "continue")) return; 967 api_local_const_control_boundary(g); 968 g->target->jump(g->target, s->continue_lbl); 969 } 970 971 void kit_cg_continue_true(KitCg* g, KitCgScope scope) { 972 ApiCgScope* s; 973 ApiSValue v; 974 if (!g || scope == 0) return; 975 s = api_scope_from_handle(g, scope, 0, "KitCg: continue_true"); 976 if (!s) return; 977 if (!api_require_loop_scope(g, s, "continue_true")) return; 978 v = api_pop(g); 979 api_branch_if(g, &v, 1, s->continue_lbl); 980 } 981 982 void kit_cg_continue_false(KitCg* g, KitCgScope scope) { 983 ApiCgScope* s; 984 ApiSValue v; 985 if (!g || scope == 0) return; 986 s = api_scope_from_handle(g, scope, 0, "KitCg: continue_false"); 987 if (!s) return; 988 if (!api_require_loop_scope(g, s, "continue_false")) return; 989 v = api_pop(g); 990 api_branch_if(g, &v, 0, s->continue_lbl); 991 } 992 993 /* ============================================================ 994 * Dynamic stack allocation / variadics (stubs) 995 * ============================================================ */ 996 997 void kit_cg_alloca(KitCg* g, uint32_t align, KitCgTypeId result_ptr_type) { 998 ApiSValue sz; 999 CgTarget* T; 1000 KitCgTypeId pty; 1001 Operand sz_op; 1002 CGLocal rr; 1003 Operand dst; 1004 if (!g) return; 1005 T = g->target; 1006 sz = api_pop(g); 1007 pty = resolve_type(g->c, result_ptr_type); 1008 if (!pty) pty = cg_type_ptr_to(g->c, builtin_id(KIT_CG_BUILTIN_VOID)); 1009 if (api_unevaluated(g)) { 1010 api_release(g, &sz); 1011 api_push(g, api_uneval_value(g, pty)); 1012 api_const_set_top(g, api_const_unknown(pty)); 1013 return; 1014 } 1015 sz_op = api_sv_op_is(&sz, OPK_IMM) 1016 ? sz.op 1017 : api_force_local(g, &sz, api_sv_type(&sz)); 1018 rr = api_alloc_temp_local(g, pty); 1019 dst = api_op_local(rr, pty); 1020 T->alloca_(T, dst, sz_op, align ? align : 16); 1021 api_release(g, &sz); 1022 api_push(g, api_make_sv(dst, pty)); 1023 } 1024 1025 void kit_cg_vararg_start(KitCg* g) { 1026 ApiSValue ap; 1027 CgTarget* T; 1028 Operand ap_op; 1029 if (!g) return; 1030 T = g->target; 1031 ap = api_pop(g); 1032 if (api_unevaluated(g)) { 1033 api_release(g, &ap); 1034 return; 1035 } 1036 ap_op = api_force_local(g, &ap, api_sv_type(&ap)); 1037 T->va_start_(T, ap_op); 1038 api_release(g, &ap); 1039 } 1040 1041 void kit_cg_vararg_next(KitCg* g, KitCgTypeId type) { 1042 ApiSValue ap; 1043 CgTarget* T; 1044 KitCgTypeId ty; 1045 Operand ap_op; 1046 CGLocal rr; 1047 Operand dst; 1048 if (!g) return; 1049 T = g->target; 1050 ty = resolve_type(g->c, type); 1051 if (!ty) return; 1052 ap = api_pop(g); 1053 if (api_unevaluated(g)) { 1054 api_release(g, &ap); 1055 api_push(g, api_uneval_value(g, ty)); 1056 api_const_set_top(g, api_const_unknown(ty)); 1057 return; 1058 } 1059 ap_op = api_force_local(g, &ap, api_sv_type(&ap)); 1060 rr = api_alloc_temp_local(g, ty); 1061 dst = api_op_local(rr, ty); 1062 T->va_arg_(T, dst, ap_op, ty); 1063 api_release(g, &ap); 1064 api_push(g, api_make_sv(dst, ty)); 1065 } 1066 1067 void kit_cg_vararg_end(KitCg* g) { 1068 ApiSValue ap; 1069 CgTarget* T; 1070 Operand ap_op; 1071 if (!g) return; 1072 T = g->target; 1073 ap = api_pop(g); 1074 if (api_unevaluated(g)) { 1075 api_release(g, &ap); 1076 return; 1077 } 1078 ap_op = api_force_local(g, &ap, api_sv_type(&ap)); 1079 T->va_end_(T, ap_op); 1080 api_release(g, &ap); 1081 } 1082 1083 void kit_cg_vararg_copy(KitCg* g) { 1084 ApiSValue src, dst; 1085 CgTarget* T; 1086 Operand src_op, dst_op; 1087 if (!g) return; 1088 T = g->target; 1089 src = api_pop(g); 1090 dst = api_pop(g); 1091 if (api_unevaluated(g)) { 1092 api_release(g, &src); 1093 api_release(g, &dst); 1094 return; 1095 } 1096 src_op = api_force_local(g, &src, api_sv_type(&src)); 1097 dst_op = api_force_local(g, &dst, api_sv_type(&dst)); 1098 T->va_copy_(T, dst_op, src_op); 1099 api_release(g, &src); 1100 api_release(g, &dst); 1101 } 1102 1103 /* ============================================================ 1104 * Memory operations (stubs) 1105 * ============================================================ */ 1106 1107 void kit_cg_memcpy(KitCg* g, uint64_t size, KitCgMemAccess dst_access, 1108 KitCgMemAccess src_access) { 1109 ApiSValue src, dst; 1110 CgTarget* T; 1111 AggregateAccess agg; 1112 Operand dst_op, src_op; 1113 if (!g) return; 1114 api_local_const_memory_boundary(g); 1115 (void)src_access; 1116 if (size > UINT32_MAX) { 1117 compiler_panic(g->c, g->cur_loc, "KitCg: memcpy size exceeds CgTarget"); 1118 return; 1119 } 1120 T = g->target; 1121 src = api_pop(g); 1122 dst = api_pop(g); 1123 if (api_unevaluated(g)) { 1124 api_release(g, &src); 1125 api_release(g, &dst); 1126 return; 1127 } 1128 api_require_pointer_value(g, "memcpy destination", api_sv_type(&dst)); 1129 api_require_pointer_value(g, "memcpy source", api_sv_type(&src)); 1130 dst_op = api_force_local(g, &dst, api_sv_type(&dst)); 1131 src_op = api_force_local(g, &src, api_sv_type(&src)); 1132 memset(&agg, 0, sizeof agg); 1133 agg.size = (u32)size; 1134 agg.align = dst_access.align ? dst_access.align : (u32)size; 1135 T->copy_bytes(T, dst_op, src_op, agg); 1136 api_release(g, &dst); 1137 api_release(g, &src); 1138 } 1139 1140 void kit_cg_memmove(KitCg* g, uint64_t size, KitCgMemAccess dst_access, 1141 KitCgMemAccess src_access) { 1142 ApiSValue src, dst; 1143 Operand args[3]; 1144 if (!g) return; 1145 api_local_const_memory_boundary(g); 1146 (void)dst_access; 1147 (void)src_access; 1148 if (size > INT64_MAX) { 1149 compiler_panic(g->c, g->cur_loc, "KitCg: memmove size exceeds CgTarget"); 1150 return; 1151 } 1152 src = api_pop(g); 1153 dst = api_pop(g); 1154 if (api_unevaluated(g)) { 1155 api_release(g, &src); 1156 api_release(g, &dst); 1157 return; 1158 } 1159 api_require_pointer_value(g, "memmove destination", api_sv_type(&dst)); 1160 api_require_pointer_value(g, "memmove source", api_sv_type(&src)); 1161 args[0] = api_force_local(g, &dst, api_sv_type(&dst)); 1162 args[1] = api_force_local(g, &src, api_sv_type(&src)); 1163 args[2] = api_op_imm((i64)size, builtin_id(KIT_CG_BUILTIN_I64)); 1164 g->target->intrinsic(g->target, INTRIN_MEMMOVE, NULL, 0, args, 3); 1165 api_release(g, &dst); 1166 api_release(g, &src); 1167 } 1168 1169 void kit_cg_memset(KitCg* g, uint8_t val, uint64_t size, 1170 KitCgMemAccess dst_access) { 1171 ApiSValue dst; 1172 CgTarget* T; 1173 AggregateAccess agg; 1174 Operand dst_op, byte_val; 1175 if (!g) return; 1176 api_local_const_memory_boundary(g); 1177 if (size > UINT32_MAX) { 1178 compiler_panic(g->c, g->cur_loc, "KitCg: memset size exceeds CgTarget"); 1179 return; 1180 } 1181 T = g->target; 1182 dst = api_pop(g); 1183 if (api_unevaluated(g)) { 1184 api_release(g, &dst); 1185 return; 1186 } 1187 api_require_pointer_value(g, "memset destination", api_sv_type(&dst)); 1188 dst_op = api_force_local(g, &dst, api_sv_type(&dst)); 1189 byte_val = api_op_imm((i64)val, KIT_CG_TYPE_NONE); 1190 memset(&agg, 0, sizeof agg); 1191 agg.size = (u32)size; 1192 agg.align = dst_access.align ? dst_access.align : (u32)size; 1193 T->set_bytes(T, dst_op, byte_val, agg); 1194 api_release(g, &dst); 1195 } 1196 1197 /* log2 of a {1,2,4,8} scale, else -1. */ 1198 static int cg_scale_to_log2(u32 scale) { 1199 switch (scale) { 1200 case 1: 1201 return 0; 1202 case 2: 1203 return 1; 1204 case 4: 1205 return 2; 1206 case 8: 1207 return 3; 1208 default: 1209 return -1; 1210 } 1211 } 1212 1213 static int cg_checked_scaled_offset(i64 index, u32 scale, i64 offset, 1214 i64* out) { 1215 i64 product; 1216 if (__builtin_mul_overflow(index, (i64)scale, &product)) return 0; 1217 if (__builtin_add_overflow(product, offset, out)) return 0; 1218 return 1; 1219 } 1220 1221 /* Shared core for kit_cg_elem / kit_cg_elem_scaled. `elem_size` is the index 1222 * stride in bytes; 0 means "derive from the base pointee size" (the kit_cg_elem 1223 * behavior). The place's access type is always the base pointee. */ 1224 static void api_cg_elem(KitCg* g, u32 elem_size, int64_t offset) { 1225 ApiSValue idx, base; 1226 CgTarget* T; 1227 KitCgTypeId base_ty, base_ptr_ty, elem_ty, idx_ty; 1228 const CgType* base_info; 1229 u32 elemsz; 1230 Operand base_op, idx_op; 1231 CGLocal base_local; 1232 if (!g) return; 1233 T = g->target; 1234 idx = api_pop(g); 1235 base = api_pop(g); 1236 base_ty = api_sv_type(&base); 1237 base_info = cg_type_get(g->c, base_ty); 1238 CG_REQUIRE(g, 1239 !api_is_lvalue_sv(&base) && base_info && 1240 base_info->kind == KIT_CG_TYPE_PTR, 1241 "KitCg: elem requires a pointer value base (decay an " 1242 "array to a pointer first)"); 1243 elem_ty = base_info->ptr.pointee; 1244 base_ptr_ty = base_ty; 1245 elemsz = elem_size ? elem_size : (u32)abi_cg_sizeof(g->c->abi, elem_ty); 1246 idx_ty = idx.type ? idx.type : idx.op.type; 1247 if (!idx_ty) idx_ty = builtin_id(KIT_CG_BUILTIN_I64); 1248 if (api_unevaluated(g)) { 1249 api_release(g, &base); 1250 api_release(g, &idx); 1251 api_push(g, api_uneval_place(g, elem_ty)); 1252 api_const_set_top(g, api_const_unknown(elem_ty)); 1253 return; 1254 } 1255 idx_op = api_force_local_unless_imm(g, &idx, idx_ty); 1256 1257 /* Constant index folds entirely into the displacement — no instructions, just 1258 * a larger offset on the place. A symbol-address base can remain a GLOBAL 1259 * place, folding the symbol addend with index*scale+offset instead of first 1260 * materializing &sym into a temporary. */ 1261 if (idx_op.kind == OPK_IMM) { 1262 i64 ofs; 1263 Operand place; 1264 if (cg_checked_scaled_offset(idx_op.v.imm, elemsz, offset, &ofs) && 1265 api_sv_kind(&base) == SV_OPERAND && base.op.kind == OPK_GLOBAL && 1266 !__builtin_add_overflow(base.op.v.global.addend, ofs, &ofs)) { 1267 place = api_op_global(base.op.v.global.sym, ofs, elem_ty); 1268 } else { 1269 base_op = api_force_local(g, &base, base_ptr_ty); 1270 base_local = base_op.v.local; 1271 if (cg_checked_scaled_offset(idx_op.v.imm, elemsz, offset, &ofs) && 1272 ofs >= INT32_MIN && ofs <= INT32_MAX) { 1273 place = api_op_indirect(base_local, (i32)ofs, elem_ty); 1274 } else if (cg_checked_scaled_offset(idx_op.v.imm, elemsz, offset, &ofs)) { 1275 CGLocal r = api_alloc_temp_local(g, base_ptr_ty); 1276 Operand ro = api_op_local(r, base_ptr_ty); 1277 T->binop(T, BO_IADD, ro, api_op_local(base_local, base_ptr_ty), 1278 api_op_imm(ofs, base_ptr_ty)); 1279 place = api_op_indirect(r, 0, elem_ty); 1280 } else { 1281 CGLocal ir = api_alloc_temp_local(g, idx_ty); 1282 Operand idx_local = api_op_local(ir, idx_ty); 1283 int lg2 = cg_scale_to_log2(elemsz); 1284 u8 log2_scale = 0; 1285 i32 place_offset; 1286 T->load_imm(T, idx_local, idx_op.v.imm); 1287 if (lg2 >= 0) { 1288 log2_scale = (u8)lg2; 1289 } else { 1290 CGLocal sr = api_alloc_temp_local(g, idx_ty); 1291 T->binop(T, BO_IMUL, api_op_local(sr, idx_ty), idx_local, 1292 api_op_imm((i64)elemsz, idx_ty)); 1293 ir = sr; 1294 } 1295 if (offset >= INT32_MIN && offset <= INT32_MAX) { 1296 place_offset = (i32)offset; 1297 } else { 1298 CGLocal r = api_alloc_temp_local(g, base_ptr_ty); 1299 Operand ro = api_op_local(r, base_ptr_ty); 1300 T->binop(T, BO_IADD, ro, api_op_local(base_local, base_ptr_ty), 1301 api_op_imm(offset, base_ptr_ty)); 1302 base_local = r; 1303 place_offset = 0; 1304 } 1305 place = api_op_indirect_indexed(base_local, ir, log2_scale, 1306 place_offset, elem_ty); 1307 } 1308 } 1309 api_release(g, &base); 1310 api_release(g, &idx); 1311 api_push(g, api_make_lv(place, elem_ty)); 1312 return; 1313 } 1314 1315 /* Dynamic index: build a scaled-index place so the backend emits one 1316 * [base + index*scale] addressing mode. A power-of-two element size rides in 1317 * log2_scale; any other size pre-multiplies the index once (scale 1). The 1318 * index is copied into a fresh local for unambiguous ownership. */ 1319 { 1320 int lg2 = cg_scale_to_log2(elemsz); 1321 CGLocal ir; 1322 u8 log2_scale; 1323 i32 place_offset; 1324 base_op = api_force_local(g, &base, base_ptr_ty); 1325 base_local = base_op.v.local; 1326 if (offset >= INT32_MIN && offset <= INT32_MAX) { 1327 place_offset = (i32)offset; 1328 } else { 1329 CGLocal r = api_alloc_temp_local(g, base_ptr_ty); 1330 Operand ro = api_op_local(r, base_ptr_ty); 1331 T->binop(T, BO_IADD, ro, api_op_local(base_local, base_ptr_ty), 1332 api_op_imm(offset, base_ptr_ty)); 1333 base_local = r; 1334 place_offset = 0; 1335 } 1336 /* Force the index to a local first so we can tell whether it is a dead 1337 * transient (provably not referenced elsewhere) that we can take over as 1338 * the index slot directly, skipping the ownership copy. */ 1339 idx_op = api_force_local_unless_imm(g, &idx, idx_ty); 1340 if (idx.op.kind == OPK_LOCAL) idx_op = idx.op; 1341 if (lg2 >= 0 && idx_op.kind == OPK_LOCAL && idx_op.v.local != base_local && 1342 api_coalesce_on(g) && api_temp_dead(g, idx_op.v.local)) { 1343 ir = idx_op.v.local; /* reuse the dead transient; no copy */ 1344 log2_scale = (u8)lg2; 1345 } else { 1346 ir = api_alloc_temp_local(g, idx_ty); 1347 if (lg2 >= 0) { 1348 T->copy(T, api_op_local(ir, idx_ty), idx_op); 1349 log2_scale = (u8)lg2; 1350 } else { 1351 T->binop(T, BO_IMUL, api_op_local(ir, idx_ty), idx_op, 1352 api_op_imm((i64)elemsz, idx_ty)); 1353 log2_scale = 0; 1354 } 1355 } 1356 api_release(g, &base); 1357 api_release(g, &idx); 1358 api_push(g, api_make_lv(api_op_indirect_indexed(base_local, ir, log2_scale, 1359 place_offset, elem_ty), 1360 elem_ty)); 1361 } 1362 } 1363 1364 void kit_cg_elem(KitCg* g, int64_t offset) { api_cg_elem(g, 0u, offset); } 1365 1366 void kit_cg_elem_scaled(KitCg* g, uint32_t elem_size, int64_t offset) { 1367 api_cg_elem(g, elem_size, offset); 1368 } 1369 1370 static void api_cg_field_at_place(KitCg* g, ApiSValue base, 1371 int64_t field_offset, KitCgTypeId field_ty) { 1372 CgTarget* T; 1373 KitCgTypeId base_ty; 1374 KitCgTypeId base_ptr_ty; 1375 Operand result; 1376 if (!g) return; 1377 T = g->target; 1378 api_ensure_local(g, &base); 1379 CG_REQUIRE(g, api_is_lvalue_sv(&base), 1380 "KitCg: field_at requires a place; deref a pointer first"); 1381 field_ty = resolve_type(g->c, field_ty); 1382 CG_REQUIRE(g, field_ty, "KitCg: field_at has invalid field type"); 1383 if (api_unevaluated(g)) { 1384 api_release(g, &base); 1385 api_push(g, api_uneval_place(g, field_ty)); 1386 api_const_set_top(g, api_const_unknown(field_ty)); 1387 return; 1388 } 1389 base_ty = api_sv_type(&base); 1390 if (!base_ty) base_ty = builtin_id(KIT_CG_BUILTIN_VOID); 1391 base_ptr_ty = cg_type_ptr_to(g->c, base_ty); 1392 if (base.op.kind == OPK_GLOBAL) { 1393 i64 addend; 1394 if (!__builtin_add_overflow(base.op.v.global.addend, field_offset, 1395 &addend)) { 1396 result = api_op_global(base.op.v.global.sym, addend, field_ty); 1397 api_release(g, &base); 1398 api_push(g, api_make_lv(result, field_ty)); 1399 return; 1400 } 1401 } else if (base.op.kind == OPK_INDIRECT && field_offset >= INT32_MIN && 1402 field_offset <= INT32_MAX) { 1403 i32 ofs; 1404 if (!__builtin_add_overflow(base.op.v.ind.ofs, (i32)field_offset, &ofs)) { 1405 /* Fold the field offset into the displacement, preserving any index/scale 1406 * a preceding `elem` left so `p[i].f` stays one 1407 * [base+index*scale+off]. */ 1408 result = api_op_indirect_indexed(base.op.v.ind.base, base.op.v.ind.index, 1409 base.op.v.ind.log2_scale, ofs, field_ty); 1410 api_release(g, &base); 1411 api_push(g, api_make_lv(result, field_ty)); 1412 return; 1413 } 1414 } 1415 { 1416 Operand base_addr = api_lvalue_addr(g, &base, base_ptr_ty); 1417 if (field_offset == 0) { 1418 result = base_addr; 1419 } else { 1420 CGLocal fr = api_alloc_temp_local(g, base_ptr_ty); 1421 result = api_op_local(fr, base_ptr_ty); 1422 T->binop(T, BO_IADD, result, base_addr, 1423 api_op_imm(field_offset, base_ptr_ty)); 1424 } 1425 api_release(g, &base); 1426 api_push( 1427 g, api_make_lv(api_op_indirect(result.v.local, 0, field_ty), field_ty)); 1428 } 1429 } 1430 1431 void kit_cg_field_at(KitCg* g, int64_t byte_offset, KitCgTypeId field_type) { 1432 ApiSValue base; 1433 if (!g) return; 1434 base = api_pop(g); 1435 api_cg_field_at_place(g, base, byte_offset, field_type); 1436 } 1437 1438 void kit_cg_field(KitCg* g, uint32_t field_index) { 1439 ApiSValue base; 1440 KitCgTypeId rec_ty; 1441 KitCgTypeId base_ty; 1442 KitCgTypeId field_ty; 1443 KitCgTypeId rec_ptr_ty; 1444 const CgType* rec_info; 1445 const ABIRecordLayout* layout; 1446 u32 field_offset; 1447 if (!g) return; 1448 base = api_pop(g); 1449 api_ensure_local(g, &base); 1450 base_ty = api_sv_type(&base); 1451 CG_REQUIRE(g, api_is_lvalue_sv(&base), 1452 "KitCg: field requires a record place; deref a pointer first"); 1453 rec_ty = base_ty; 1454 rec_ptr_ty = cg_type_ptr_to(g->c, rec_ty); 1455 layout = abi_cg_record_layout(g->c->abi, rec_ty); 1456 CG_REQUIRE(g, layout && field_index < layout->nfields, 1457 "KitCg: invalid field index"); 1458 rec_info = cg_type_get(g->c, rec_ty); 1459 CG_REQUIRE(g, 1460 rec_info && rec_info->kind == KIT_CG_TYPE_RECORD && 1461 field_index < rec_info->record.nfields, 1462 "KitCg: invalid record base"); 1463 field_ty = rec_info->record.fields[field_index].type; 1464 field_offset = layout->fields[field_index].offset; 1465 if (api_unevaluated(g)) { 1466 ApiSValue sv = api_uneval_place(g, field_ty); 1467 if (layout->fields[field_index].bit_width != 0 || 1468 (rec_info->record.fields[field_index].flags & KIT_CG_FIELD_BITFIELD) != 1469 0) { 1470 sv.bitfield.bit_offset = layout->fields[field_index].bit_offset; 1471 sv.bitfield.bit_width = layout->fields[field_index].bit_width; 1472 sv.bitfield.bit_storage_size = layout->fields[field_index].storage_size; 1473 sv.bitfield.bit_signed = 1474 rec_info->record.fields[field_index].bit_signed ? 1u : 0u; 1475 } 1476 api_release(g, &base); 1477 api_push(g, sv); 1478 api_const_set_top(g, api_const_unknown(field_ty)); 1479 return; 1480 } 1481 if (layout->fields[field_index].bit_width != 0 || 1482 (rec_info->record.fields[field_index].flags & KIT_CG_FIELD_BITFIELD) != 1483 0) { 1484 Operand base_addr; 1485 ApiSValue sv; 1486 CG_REQUIRE(g, layout->fields[field_index].bit_width != 0, 1487 "KitCg: zero-width bit-field access"); 1488 /* Project to a bit-field PLACE: the place addresses the enclosing storage 1489 * unit and carries the bit-field geometry from the record layout. A plain 1490 * load/store on this place performs the extract/insert; there is no 1491 * separate bit-field memop and no bit-field rider on KitCgMemAccess. */ 1492 base_addr = api_lvalue_addr(g, &base, rec_ptr_ty); 1493 sv = api_make_lv(base_addr, field_ty); 1494 sv.bitfield.bit_offset = layout->fields[field_index].bit_offset; 1495 sv.bitfield.bit_width = layout->fields[field_index].bit_width; 1496 sv.bitfield.bit_storage_size = layout->fields[field_index].storage_size; 1497 sv.bitfield.bit_signed = 1498 rec_info->record.fields[field_index].bit_signed ? 1u : 0u; 1499 api_release(g, &base); 1500 api_push(g, sv); 1501 return; 1502 } 1503 api_cg_field_at_place(g, base, (int64_t)field_offset, field_ty); 1504 } 1505 1506 void kit_cg_field_bits(KitCg* g, uint16_t bit_offset, uint16_t bit_width, 1507 uint32_t bit_storage_size, int bit_signed) { 1508 ApiSValue* top; 1509 if (!g || g->sp == 0) return; 1510 top = &g->stack[g->sp - 1u]; 1511 CG_REQUIRE(g, api_is_lvalue_sv(top), 1512 "KitCg: field_bits requires a place destination"); 1513 top->bitfield.bit_offset = bit_offset; 1514 top->bitfield.bit_width = bit_width; 1515 top->bitfield.bit_storage_size = bit_storage_size; 1516 top->bitfield.bit_signed = bit_signed ? 1u : 0u; 1517 } 1518 1519 /* ============================================================ 1520 * Calls / return 1521 * ============================================================ */ 1522 1523 /* Shared scaffolding for kit_cg_call / kit_cg_call_symbol. The two 1524 * public entry points differ only in how the callee is obtained and in 1525 * their pre-call stack-depth check; everything else (arg packaging, return 1526 * storage allocation, post-call release, result push) is identical. These 1527 * helpers carry the common shape and are the natural targets for any future 1528 * change that wants to vary call-shape policy (e.g. an ABI-driven storage 1529 * decision). */