control.c (37245B)
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 return (KitCgLabel)g->target->label_new(g->target); 7 } 8 9 void kit_cg_label_place(KitCg* g, KitCgLabel label) { 10 if (!g) return; 11 api_local_const_control_boundary(g); 12 g->target->label_place(g->target, (Label)label); 13 } 14 15 void kit_cg_jump(KitCg* g, KitCgLabel label) { 16 if (!g) return; 17 api_local_const_control_boundary(g); 18 g->target->jump(g->target, (Label)label); 19 } 20 21 void api_branch_if(KitCg* g, ApiSValue* v, int branch_when_true, Label label) { 22 CgTarget* T; 23 KitCgTypeId ty; 24 if (!g) return; 25 api_local_const_control_boundary(g); 26 T = g->target; 27 ty = v->type ? v->type : builtin_id(KIT_CG_BUILTIN_I32); 28 if (v->op.kind == OPK_IMM && v->kind == SV_OPERAND) { 29 if ((v->op.v.imm != 0) == !!branch_when_true) T->jump(T, label); 30 api_release(g, v); 31 return; 32 } 33 if (v->kind == SV_CMP) { 34 CmpOp op = branch_when_true ? v->delayed.cmp.op 35 : api_invert_cmp(v->delayed.cmp.op); 36 T->cmp_branch(T, op, v->delayed.cmp.a, v->delayed.cmp.b, label); 37 api_release(g, v); 38 return; 39 } 40 if (api_is_i128_type(g->c, ty)) { 41 KitCgTypeId i128 = builtin_id(KIT_CG_BUILTIN_I128); 42 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 43 KitCgTypeId ps[2] = {i128, i128}; 44 ApiSValue args[2]; 45 ApiSValue cmp; 46 args[0] = *v; 47 args[1] = api_make_sv(api_op_imm(0, i128), i128); 48 api_runtime_call_values(g, "__kit_ucmpti2", i32, ps, 2, args); 49 cmp = api_pop(g); 50 api_branch_if(g, &cmp, branch_when_true, label); 51 return; 52 } 53 /* Split-lane 8-byte truthiness: branch on (lo | hi) != 0. The value is 54 * memory-resident, so a single-slot CMP_NE-vs-zero would only see the low 55 * word; OR the two lanes into an i32 first. */ 56 if (api_is_wide8_scalar_type(g->c, ty)) { 57 KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32); 58 Operand orl = api_wide8_or_lanes(g, v, ty); 59 Operand zero = api_op_imm(0, i32); 60 T->cmp_branch(T, branch_when_true ? CMP_NE : CMP_EQ, orl, zero, label); 61 api_release(g, v); 62 return; 63 } 64 { 65 Operand a = api_force_local(g, v, ty); 66 Operand zero = api_op_imm(0, ty); 67 T->cmp_branch(T, branch_when_true ? CMP_NE : CMP_EQ, a, zero, label); 68 api_release(g, v); 69 } 70 } 71 72 void kit_cg_branch_true(KitCg* g, KitCgLabel label) { 73 ApiSValue v; 74 if (!g) return; 75 v = api_pop(g); 76 api_branch_if(g, &v, 1, (Label)label); 77 } 78 79 void kit_cg_branch_false(KitCg* g, KitCgLabel label) { 80 ApiSValue v; 81 if (!g) return; 82 v = api_pop(g); 83 api_branch_if(g, &v, 0, (Label)label); 84 } 85 86 void cg_lower_switch_default(CgTarget* t, const CGSwitchDesc* d) { 87 /* Cmp-and-branch chain: one cmp_branch per case, then jump to 88 * default (or fall through if LABEL_NONE). The fallback shape; the 89 * frontend-facing kit_cg_switch picks chain vs. jump-table up 90 * front (see cg_plan_switch) and routes here only for the chain 91 * case. Backend overrides (the C target's switch_) and opt's IR 92 * replay both reach this from outside the cg API, so the lowering 93 * stays target-only and uses just cmp_branch + jump. */ 94 for (u32 i = 0; i < d->ncases; ++i) { 95 Operand imm = api_op_imm((i64)d->cases[i].value, d->selector_type); 96 t->cmp_branch(t, CMP_EQ, d->selector, imm, d->cases[i].label); 97 } 98 if (d->default_label != LABEL_NONE) { 99 t->jump(t, d->default_label); 100 } 101 } 102 103 /* Density / sizing thresholds for the O1 jump-table heuristic. At O0 104 * we only honor an explicit KIT_CG_SWITCH_JUMP_TABLE hint — the policy 105 * is single-pass and tied to frontend intent rather than analysis. O1 106 * runs a linear scan over the case list and picks a single global table 107 * when the cases are dense enough; clustering / multi-table layouts are 108 * out of scope here. */ 109 #define CG_SWITCH_TABLE_MIN_CASES_O1 4u 110 #define CG_SWITCH_TABLE_MAX_SPAN_O1 4096u 111 #define CG_SWITCH_TABLE_DENSITY_RECIP_O1 4u /* ncases * recip >= span */ 112 113 typedef enum CGSwitchPlanKind { 114 CG_SWITCH_PLAN_CHAIN, 115 CG_SWITCH_PLAN_TABLE, 116 } CGSwitchPlanKind; 117 118 typedef struct CGSwitchPlan { 119 CGSwitchPlanKind kind; 120 i64 vmin; 121 u64 span; 122 } CGSwitchPlan; 123 124 /* Single pass over the cases array: derive (vmin, vmax, span) in the 125 * selector type's signed interpretation. Frontends store case values as 126 * the u64 bit pattern of an i64 sign-extended from the selector's 127 * width, so api_sign_extend_width recovers the source ordering for 128 * both signed and unsigned selectors that fit in i64. Returns 0 if the 129 * selector type is unusable for our policy (>64 bits) or if span 130 * overflows u64. */ 131 static int cg_switch_extents(Compiler* c, const CGSwitchDesc* d, i64* out_vmin, 132 u64* out_span) { 133 u32 width; 134 i64 vmin; 135 i64 vmax; 136 u32 i; 137 width = kit_cg_type_int_width((KitCompiler*)c, d->selector_type); 138 if (!width || width > 64u) return 0; 139 vmin = INT64_MAX; 140 vmax = INT64_MIN; 141 for (i = 0; i < d->ncases; ++i) { 142 i64 vi = (width == 64u) ? (i64)d->cases[i].value 143 : api_sign_extend_width(d->cases[i].value, width); 144 if (vi < vmin) vmin = vi; 145 if (vi > vmax) vmax = vi; 146 } 147 if (vmax < vmin) return 0; 148 { 149 u64 delta = (u64)vmax - (u64)vmin; 150 if (delta == UINT64_MAX) return 0; 151 *out_span = delta + 1u; 152 } 153 *out_vmin = vmin; 154 return 1; 155 } 156 157 static CGSwitchPlan cg_plan_switch(KitCg* g, const CGSwitchDesc* d) { 158 CGSwitchPlan plan; 159 plan.kind = CG_SWITCH_PLAN_CHAIN; 160 plan.vmin = 0; 161 plan.span = 0; 162 if (d->ncases == 0) return plan; 163 if (d->default_label == LABEL_NONE) return plan; 164 if (d->hint == KIT_CG_SWITCH_BRANCH_CHAIN) return plan; 165 if (!cg_switch_extents(g->c, d, &plan.vmin, &plan.span)) return plan; 166 if (d->hint == KIT_CG_SWITCH_JUMP_TABLE) { 167 /* Frontend explicitly opted in. Honor unless the span is wildly 168 * out of bounds — a forced hint shouldn't blow up code size on a 169 * misshapen switch. */ 170 if (plan.span > CG_SWITCH_TABLE_MAX_SPAN_O1) return plan; 171 plan.kind = CG_SWITCH_PLAN_TABLE; 172 return plan; 173 } 174 /* TARGET_DEFAULT: O0 keeps the chain; O1+ runs the density check. */ 175 if (d->opt_level == 0) return plan; 176 if (d->opt_level >= 2 && plan.span != d->ncases) return plan; 177 if (d->ncases < CG_SWITCH_TABLE_MIN_CASES_O1) return plan; 178 if (plan.span > CG_SWITCH_TABLE_MAX_SPAN_O1) return plan; 179 if (plan.span > (u64)d->ncases * CG_SWITCH_TABLE_DENSITY_RECIP_O1) 180 return plan; 181 plan.kind = CG_SWITCH_PLAN_TABLE; 182 return plan; 183 } 184 185 /* Emit a dense jump-table dispatch using cg-API ops. The selector value 186 * is still on the value stack on entry. Routing through the cg API 187 * means the same primitives work for direct CG (lowered to machine 188 * ops) and for the opt wrapper (recorded as IR_BINOP / IR_CMP_BRANCH / 189 * IR_LOAD / IR_INDIRECT_BRANCH, which pass_emit already lowers 190 * natively without ever materializing IR_SWITCH). */ 191 static void cg_emit_switch_table(KitCg* g, const CGSwitchDesc* d, 192 const CGSwitchPlan* plan) { 193 Compiler* c; 194 Heap* h; 195 KitCgTypeId sel_ty; 196 u32 sel_w; 197 KitCgTypeId i64_ty; 198 KitCgTypeId void_ptr_ty; 199 KitCgTypeId arr_ty; 200 Label* labels; 201 KitCgLabel* targets; 202 ObjSymId table_sym; 203 KitCgDecl decl; 204 KitCgMemAccess acc; 205 u64 i; 206 u32 width; 207 c = g->c; 208 h = (Heap*)c->ctx->heap; 209 sel_ty = d->selector_type; 210 sel_w = kit_cg_type_int_width((KitCompiler*)c, sel_ty); 211 i64_ty = builtin_id(KIT_CG_BUILTIN_I64); 212 void_ptr_ty = cg_type_ptr_to(c, builtin_id(KIT_CG_BUILTIN_VOID)); 213 214 /* 1. Keep the original selector, then compute idx = sel - vmin 215 * (selector_type wraparound is what we want; the unsigned bounds 216 * check below catches both negative-underflow and positive-overflow 217 * cases). The selector copy lets us recompute idx after the bounds 218 * branch instead of carrying a duplicated arithmetic value across 219 * control flow. */ 220 kit_cg_dup(g); /* [sel, sel] */ 221 kit_cg_push_int(g, (uint64_t)plan->vmin, sel_ty); 222 kit_cg_int_binop(g, KIT_CG_INT_SUB, 0); /* [sel, idx] */ 223 224 /* 2. Bounds check: branch to default when idx u> span-1. */ 225 kit_cg_dup(g); /* [sel, idx, idx] */ 226 kit_cg_push_int(g, plan->span - 1u, sel_ty); 227 kit_cg_int_cmp(g, KIT_CG_INT_GT_U); /* [sel, idx, cond] */ 228 kit_cg_branch_true(g, (KitCgLabel)d->default_label); /* [sel, idx] */ 229 230 /* 3. Recompute idx from the preserved selector for table addressing. */ 231 kit_cg_drop(g); /* [sel] */ 232 kit_cg_push_int(g, (uint64_t)plan->vmin, sel_ty); 233 kit_cg_int_binop(g, KIT_CG_INT_SUB, 0); /* [idx] */ 234 235 /* 4. Widen idx to i64 so the subsequent index multiply runs at 236 * pointer width regardless of selector signedness. The bounds 237 * check above already established 0 <= idx < span, so zext is 238 * value-preserving. */ 239 if (sel_w < 64u) { 240 kit_cg_zext(g, i64_ty); 241 } 242 243 /* 5. Build the dense label[] table and emit the rodata table. */ 244 labels = 245 (Label*)h->alloc(h, (size_t)plan->span * sizeof *labels, _Alignof(Label)); 246 if (!labels) compiler_panic(c, g->cur_loc, "kit_cg_switch: oom"); 247 for (i = 0; i < plan->span; ++i) labels[i] = d->default_label; 248 width = sel_w; 249 for (i = 0; i < d->ncases; ++i) { 250 i64 vi = (width == 64u) ? (i64)d->cases[i].value 251 : api_sign_extend_width(d->cases[i].value, width); 252 u64 table_index = (u64)(vi - plan->vmin); 253 labels[table_index] = d->cases[i].label; 254 } 255 table_sym = api_emit_label_table(g, labels, (u32)plan->span); 256 h->free(h, labels, (size_t)plan->span * sizeof *labels); 257 if (table_sym == OBJ_SYM_NONE) { 258 /* api_emit_label_table panics on real failure; this only fires if 259 * a future caller asks for a 0-entry table (which cg_plan_switch 260 * already rules out). */ 261 compiler_panic(c, g->cur_loc, "kit_cg_switch: table emission failed"); 262 return; 263 } 264 arr_ty = kit_cg_type_array((KitCompiler*)c, void_ptr_ty, plan->span); 265 memset(&decl, 0, sizeof decl); 266 decl.kind = KIT_CG_DECL_OBJECT; 267 decl.sym.bind = KIT_SB_LOCAL; 268 decl.sym.visibility = KIT_CG_VIS_DEFAULT; 269 decl.as.object.flags = KIT_CG_OBJ_READONLY; 270 api_remember_sym(g, table_sym, arr_ty, decl); 271 272 /* 6. Load table[idx]: bitcast the table pointer to a pointer-to-element so 273 * the element place carries the pointer-size scale, put the index on top, 274 * project the element place, then load it. */ 275 kit_cg_push_symbol_addr(g, (KitCgSym)table_sym, 0); /* [idx, &table] */ 276 kit_cg_bitcast(g, cg_type_ptr_to(c, void_ptr_ty)); /* &table : void** */ 277 kit_cg_swap(g); /* [&table, idx] */ 278 kit_cg_elem(g, 0); /* [&table[idx]] */ 279 memset(&acc, 0, sizeof acc); 280 acc.type = void_ptr_ty; 281 acc.align = (uint32_t)c->target.ptr_align; 282 kit_cg_load(g, acc); /* [label_addr] */ 283 284 /* 7. Indirect branch with the full closed target set (every case + 285 * default), so backends doing branch-target hardening (BTI/IBT/CFG) 286 * can stamp landing pads on every reachable label. */ 287 targets = (KitCgLabel*)h->alloc(h, (d->ncases + 1u) * sizeof *targets, 288 _Alignof(KitCgLabel)); 289 if (!targets) compiler_panic(c, g->cur_loc, "kit_cg_switch: oom"); 290 for (i = 0; i < d->ncases; ++i) { 291 targets[i] = (KitCgLabel)d->cases[i].label; 292 } 293 targets[d->ncases] = (KitCgLabel)d->default_label; 294 kit_cg_computed_goto(g, targets, d->ncases + 1u); 295 h->free(h, targets, (d->ncases + 1u) * sizeof *targets); 296 } 297 298 void kit_cg_switch(KitCg* g, KitCgSwitch sw) { 299 ApiSValue selector; 300 CGSwitchDesc desc; 301 Heap* h; 302 CGSwitchCase* cases = NULL; 303 CGSwitchPlan plan; 304 int native_switch_override; 305 if (!g) return; 306 if (g->sp == 0) return; 307 api_local_const_control_boundary(g); 308 memset(&desc, 0, sizeof desc); 309 desc.selector_type = resolve_type(g->c, sw.selector_type); 310 if (!desc.selector_type) { 311 ApiSValue tmp = g->stack[g->sp - 1u]; 312 desc.selector_type = api_sv_type(&tmp); 313 } 314 desc.default_label = (Label)sw.default_label; 315 desc.ncases = sw.ncases; 316 desc.hint = (u8)sw.hint; 317 desc.opt_level = (u8)g->opt_level; 318 if (sw.ncases) { 319 h = g->c->ctx->heap; 320 cases = (CGSwitchCase*)h->alloc(h, sw.ncases * sizeof(CGSwitchCase), 321 _Alignof(CGSwitchCase)); 322 if (!cases) 323 compiler_panic(g->c, g->cur_loc, "kit_cg_switch: out of memory"); 324 for (u32 i = 0; i < sw.ncases; ++i) { 325 cases[i].value = sw.cases[i].value; 326 cases[i].label = (Label)sw.cases[i].label; 327 } 328 desc.cases = cases; 329 } 330 331 /* Direct O0 targets may override switch_ for a single-pass branch-chain 332 * lowering. Still honor an explicit jump-table hint so tests and frontends 333 * can exercise the semantic label-table path without enabling O1. */ 334 native_switch_override = (g->target->switch_ && g->opt_level == 0 && 335 desc.hint != KIT_CG_SWITCH_JUMP_TABLE); 336 plan = native_switch_override ? (CGSwitchPlan){CG_SWITCH_PLAN_CHAIN, 0, 0} 337 : cg_plan_switch(g, &desc); 338 339 /* The label-table lowering materializes a rodata table of code-label 340 * addresses and an indirect branch. Targets that can't express that (Wasm) 341 * realize dense dispatch through their switch_ hook (br_table) instead, so 342 * hand the plan—hint and all—to switch_ rather than the table path. */ 343 if (plan.kind == CG_SWITCH_PLAN_TABLE && g->target->switch_ && 344 g->target->supports_label_table && 345 !g->target->supports_label_table(g->target)) { 346 plan.kind = CG_SWITCH_PLAN_CHAIN; 347 } 348 349 if (plan.kind == CG_SWITCH_PLAN_TABLE) { 350 /* Selector stays on the value stack; cg_emit_switch_table consumes 351 * it via cg-API ops so the path also records cleanly under opt. */ 352 metrics_count(g->c, "cg.switch.table", 1); 353 cg_emit_switch_table(g, &desc, &plan); 354 } else { 355 metrics_count(g->c, "cg.switch.chain", 1); 356 selector = api_pop(g); 357 desc.selector = 358 api_force_local_unless_imm(g, &selector, desc.selector_type); 359 if (g->target->switch_) { 360 g->target->switch_(g->target, &desc); 361 } else { 362 cg_lower_switch_default(g->target, &desc); 363 } 364 api_release(g, &selector); 365 } 366 if (cases) { 367 h = g->c->ctx->heap; 368 h->free(h, cases, sw.ncases * sizeof(CGSwitchCase)); 369 } 370 } 371 372 void kit_cg_push_label_addr(KitCg* g, KitCgLabel label, KitCgTypeId ptr_type) { 373 KitCgTypeId ty; 374 CGLocal r; 375 Operand dst; 376 if (!g) return; 377 ty = resolve_type(g->c, ptr_type); 378 if (!ty) ty = cg_type_ptr_to(g->c, builtin_id(KIT_CG_BUILTIN_VOID)); 379 r = api_alloc_temp_local(g, ty); 380 dst = api_op_local(r, ty); 381 g->target->load_label_addr(g->target, dst, (Label)label); 382 api_push(g, api_make_sv(dst, ty)); 383 } 384 385 void kit_cg_computed_goto(KitCg* g, const KitCgLabel* valid_targets, 386 uint32_t ntargets) { 387 ApiSValue target; 388 KitCgTypeId target_ty; 389 Operand target_op; 390 if (!g) return; 391 if (!valid_targets || ntargets == 0) { 392 compiler_panic(g->c, g->cur_loc, 393 "kit_cg_computed_goto: valid_targets must be non-empty"); 394 return; 395 } 396 api_local_const_control_boundary(g); 397 target = api_pop(g); 398 target_ty = api_sv_type(&target); 399 target_op = api_force_local(g, &target, target_ty); 400 g->target->indirect_branch(g->target, target_op, (const Label*)valid_targets, 401 ntargets); 402 api_release(g, &target); 403 } 404 405 void kit_cg_unreachable(KitCg* g) { 406 if (!g) return; 407 api_local_const_control_boundary(g); 408 g->target->unreachable(g->target); 409 } 410 411 /* ============================================================ 412 * Scopes / structured control flow 413 * ============================================================ */ 414 415 KitCgScope api_scope_handle(u32 idx, u32 generation) { 416 return (KitCgScope)((generation << 8) | ((idx + 1u) & 0xffu)); 417 } 418 419 ApiCgScope* api_scope_from_handle(KitCg* g, KitCgScope scope, int require_top, 420 const char* who) { 421 u32 scope_index; 422 u32 generation; 423 ApiCgScope* s; 424 if (!g || scope == 0) return NULL; 425 scope_index = ((u32)scope & 0xffu); 426 generation = ((u32)scope >> 8); 427 if (scope_index == 0 || scope_index > API_CG_MAX_SCOPES) { 428 compiler_panic(g->c, g->cur_loc, "%.*s: invalid scope handle", 429 SLICE_ARG(slice_from_cstr(who))); 430 return NULL; 431 } 432 scope_index--; 433 if (scope_index >= g->nscopes) { 434 compiler_panic(g->c, g->cur_loc, "%.*s: stale scope handle", 435 SLICE_ARG(slice_from_cstr(who))); 436 return NULL; 437 } 438 if (require_top && scope_index + 1u != g->nscopes) { 439 compiler_panic(g->c, g->cur_loc, "%.*s: non-LIFO scope end", 440 SLICE_ARG(slice_from_cstr(who))); 441 return NULL; 442 } 443 s = &g->scopes[scope_index]; 444 if (!s->active || s->generation != generation) { 445 compiler_panic(g->c, g->cur_loc, "%.*s: stale scope handle", 446 SLICE_ARG(slice_from_cstr(who))); 447 return NULL; 448 } 449 return s; 450 } 451 452 int api_scope_has_result(const ApiCgScope* s) { 453 return s->result_type != KIT_CG_TYPE_NONE; 454 } 455 456 void api_scope_store_result(KitCg* g, ApiCgScope* s, ApiSValue* result) { 457 Operand dst; 458 Operand src; 459 if (!api_scope_has_result(s)) return; 460 dst = api_op_local(s->result_local, s->result_type); 461 src = api_sv_op_is_local_or_imm(result) 462 ? result->op 463 : api_force_local(g, result, s->result_type); 464 g->target->store(g->target, dst, src, 465 api_mem_for_lvalue(g, &dst, s->result_type)); 466 api_release(g, result); 467 } 468 469 void api_scope_push_result(KitCg* g, ApiCgScope* s) { 470 Operand dst; 471 Operand src; 472 CGLocal r; 473 if (!api_scope_has_result(s)) return; 474 r = api_alloc_temp_local(g, s->result_type); 475 dst = api_op_local(r, s->result_type); 476 src = api_op_local(s->result_local, s->result_type); 477 g->target->load(g->target, dst, src, 478 api_mem_for_lvalue(g, &src, s->result_type)); 479 api_push(g, api_make_sv(dst, s->result_type)); 480 } 481 482 static KitCgScope api_scope_begin_kind(KitCg* g, u8 kind, 483 KitCgTypeId result_type) { 484 Label break_lbl, cont_lbl; 485 CGScopeDesc d; 486 ApiCgScope* s; 487 CGScope target_scope; 488 u32 idx; 489 if (!g) return 0; 490 break_lbl = g->target->label_new(g->target); 491 cont_lbl = 492 (kind == SCOPE_LOOP) ? g->target->label_new(g->target) : LABEL_NONE; 493 api_local_const_control_boundary(g); 494 if (cont_lbl != LABEL_NONE) g->target->label_place(g->target, cont_lbl); 495 496 if (g->nscopes >= API_CG_MAX_SCOPES) { 497 compiler_panic(g->c, g->cur_loc, "KitCg: too many nested scopes"); 498 return 0; 499 } 500 idx = g->nscopes; 501 s = &g->scopes[idx]; 502 s->break_lbl = break_lbl; 503 s->continue_lbl = cont_lbl; 504 s->result_type = resolve_type(g->c, result_type); 505 s->generation = ++g->scope_generation; 506 if (s->generation == 0) s->generation = ++g->scope_generation; 507 s->active = 1; 508 g->nscopes++; 509 510 memset(&d, 0, sizeof d); 511 d.kind = kind; 512 d.break_label = break_lbl; 513 d.continue_label = cont_lbl; 514 d.result_type = s->result_type; 515 target_scope = g->target->scope_begin(g->target, &d); 516 s->target_scope = target_scope; 517 s->result_local = CG_LOCAL_NONE; 518 if (api_scope_has_result(s)) { 519 CGLocalDesc ld; 520 memset(&ld, 0, sizeof ld); 521 ld.type = s->result_type; 522 ld.size = abi_cg_sizeof(g->c->abi, result_type); 523 ld.align = abi_cg_alignof(g->c->abi, result_type); 524 ld.flags = CG_LOCAL_MEMORY_REQUIRED; 525 s->result_local = g->target->local(g->target, &ld); 526 } 527 528 return api_scope_handle(idx, s->generation); 529 } 530 531 KitCgScope kit_cg_scope_begin(KitCg* g, KitCgTypeId result_type) { 532 return api_scope_begin_kind(g, (u8)SCOPE_LOOP, result_type); 533 } 534 535 KitCgScope kit_cg_block_begin(KitCg* g, KitCgTypeId result_type) { 536 return api_scope_begin_kind(g, (u8)SCOPE_BLOCK, result_type); 537 } 538 539 KitCgLabel kit_cg_scope_break_label(KitCg* g, KitCgScope scope) { 540 ApiCgScope* s = 541 api_scope_from_handle(g, scope, 0, "KitCg: scope_break_label"); 542 return s ? (KitCgLabel)s->break_lbl : (KitCgLabel)0; 543 } 544 545 KitCgLabel kit_cg_scope_continue_label(KitCg* g, KitCgScope scope) { 546 ApiCgScope* s = 547 api_scope_from_handle(g, scope, 0, "KitCg: scope_continue_label"); 548 return s ? (KitCgLabel)s->continue_lbl : (KitCgLabel)0; 549 } 550 551 void kit_cg_scope_end(KitCg* g, KitCgScope scope) { 552 ApiCgScope* s = api_scope_from_handle(g, scope, 1, "KitCg: scope_end"); 553 if (!s) return; 554 if (api_scope_has_result(s)) { 555 ApiSValue result = api_pop(g); 556 api_scope_store_result(g, s, &result); 557 } 558 api_local_const_control_boundary(g); 559 g->target->label_place(g->target, s->break_lbl); 560 g->target->scope_end(g->target, s->target_scope); 561 api_scope_push_result(g, s); 562 s->active = 0; 563 g->nscopes--; 564 } 565 566 void kit_cg_break(KitCg* g, KitCgScope scope) { 567 ApiCgScope* s = api_scope_from_handle(g, scope, 0, "KitCg: break"); 568 if (!s) return; 569 if (api_scope_has_result(s)) { 570 ApiSValue result = api_pop(g); 571 api_scope_store_result(g, s, &result); 572 } 573 api_local_const_control_boundary(g); 574 g->target->jump(g->target, s->break_lbl); 575 } 576 577 void kit_cg_break_true(KitCg* g, KitCgScope scope) { 578 ApiCgScope* s; 579 ApiSValue cond; 580 if (!g || scope == 0) return; 581 s = api_scope_from_handle(g, scope, 0, "KitCg: break_true"); 582 if (!s) return; 583 cond = api_pop(g); 584 585 if (api_scope_has_result(s)) { 586 ApiSValue result = api_pop(g); 587 if (cond.kind == SV_OPERAND && cond.op.kind == OPK_IMM) { 588 if (cond.op.v.imm != 0) { 589 api_scope_store_result(g, s, &result); 590 api_local_const_control_boundary(g); 591 g->target->jump(g->target, s->break_lbl); 592 } else { 593 api_release(g, &result); 594 } 595 api_release(g, &cond); 596 } else { 597 Label skip = g->target->label_new(g->target); 598 api_branch_if(g, &cond, 0, skip); 599 api_scope_store_result(g, s, &result); 600 api_local_const_control_boundary(g); 601 g->target->jump(g->target, s->break_lbl); 602 api_local_const_control_boundary(g); 603 g->target->label_place(g->target, skip); 604 } 605 } else { 606 api_branch_if(g, &cond, 1, s->break_lbl); 607 } 608 } 609 610 void kit_cg_break_false(KitCg* g, KitCgScope scope) { 611 ApiCgScope* s; 612 ApiSValue cond; 613 if (!g || scope == 0) return; 614 s = api_scope_from_handle(g, scope, 0, "KitCg: break_false"); 615 if (!s) return; 616 cond = api_pop(g); 617 618 if (api_scope_has_result(s)) { 619 ApiSValue result = api_pop(g); 620 if (cond.kind == SV_OPERAND && cond.op.kind == OPK_IMM) { 621 if (cond.op.v.imm == 0) { 622 api_scope_store_result(g, s, &result); 623 api_local_const_control_boundary(g); 624 g->target->jump(g->target, s->break_lbl); 625 } else { 626 api_release(g, &result); 627 } 628 api_release(g, &cond); 629 } else { 630 Label skip = g->target->label_new(g->target); 631 api_branch_if(g, &cond, 1, skip); 632 api_scope_store_result(g, s, &result); 633 api_local_const_control_boundary(g); 634 g->target->jump(g->target, s->break_lbl); 635 api_local_const_control_boundary(g); 636 g->target->label_place(g->target, skip); 637 } 638 } else { 639 api_branch_if(g, &cond, 0, s->break_lbl); 640 } 641 } 642 643 /* continue jumps to the loop header, which a forward-only block scope does not 644 * have (its continue_lbl is LABEL_NONE). Reject it with a clean diagnostic 645 * rather than emitting a jump to a nonexistent label. */ 646 static int api_require_loop_scope(KitCg* g, const ApiCgScope* s, 647 const char* op) { 648 if (s->continue_lbl == LABEL_NONE) { 649 compiler_panic(g->c, g->cur_loc, 650 "KitCg: %s is not valid on a forward-only block scope", op); 651 return 0; 652 } 653 return 1; 654 } 655 656 void kit_cg_continue(KitCg* g, KitCgScope scope) { 657 ApiCgScope* s = api_scope_from_handle(g, scope, 0, "KitCg: continue"); 658 if (!s) return; 659 if (!api_require_loop_scope(g, s, "continue")) return; 660 api_local_const_control_boundary(g); 661 g->target->jump(g->target, s->continue_lbl); 662 } 663 664 void kit_cg_continue_true(KitCg* g, KitCgScope scope) { 665 ApiCgScope* s; 666 ApiSValue v; 667 if (!g || scope == 0) return; 668 s = api_scope_from_handle(g, scope, 0, "KitCg: continue_true"); 669 if (!s) return; 670 if (!api_require_loop_scope(g, s, "continue_true")) return; 671 v = api_pop(g); 672 api_branch_if(g, &v, 1, s->continue_lbl); 673 } 674 675 void kit_cg_continue_false(KitCg* g, KitCgScope scope) { 676 ApiCgScope* s; 677 ApiSValue v; 678 if (!g || scope == 0) return; 679 s = api_scope_from_handle(g, scope, 0, "KitCg: continue_false"); 680 if (!s) return; 681 if (!api_require_loop_scope(g, s, "continue_false")) return; 682 v = api_pop(g); 683 api_branch_if(g, &v, 0, s->continue_lbl); 684 } 685 686 /* ============================================================ 687 * Dynamic stack allocation / variadics (stubs) 688 * ============================================================ */ 689 690 void kit_cg_alloca(KitCg* g, uint32_t align, KitCgTypeId result_ptr_type) { 691 ApiSValue sz; 692 CgTarget* T; 693 KitCgTypeId pty; 694 Operand sz_op; 695 CGLocal rr; 696 Operand dst; 697 if (!g) return; 698 T = g->target; 699 sz = api_pop(g); 700 pty = resolve_type(g->c, result_ptr_type); 701 if (!pty) pty = cg_type_ptr_to(g->c, builtin_id(KIT_CG_BUILTIN_VOID)); 702 sz_op = api_sv_op_is(&sz, OPK_IMM) 703 ? sz.op 704 : api_force_local(g, &sz, api_sv_type(&sz)); 705 rr = api_alloc_temp_local(g, pty); 706 dst = api_op_local(rr, pty); 707 T->alloca_(T, dst, sz_op, align ? align : 16); 708 api_release(g, &sz); 709 api_push(g, api_make_sv(dst, pty)); 710 } 711 712 void kit_cg_vararg_start(KitCg* g) { 713 ApiSValue ap; 714 CgTarget* T; 715 Operand ap_op; 716 if (!g) return; 717 T = g->target; 718 ap = api_pop(g); 719 ap_op = api_force_local(g, &ap, api_sv_type(&ap)); 720 T->va_start_(T, ap_op); 721 api_release(g, &ap); 722 } 723 724 void kit_cg_vararg_next(KitCg* g, KitCgTypeId type) { 725 ApiSValue ap; 726 CgTarget* T; 727 KitCgTypeId ty; 728 Operand ap_op; 729 CGLocal rr; 730 Operand dst; 731 if (!g) return; 732 T = g->target; 733 ty = resolve_type(g->c, type); 734 if (!ty) return; 735 ap = api_pop(g); 736 ap_op = api_force_local(g, &ap, api_sv_type(&ap)); 737 rr = api_alloc_temp_local(g, ty); 738 dst = api_op_local(rr, ty); 739 T->va_arg_(T, dst, ap_op, ty); 740 api_release(g, &ap); 741 api_push(g, api_make_sv(dst, ty)); 742 } 743 744 void kit_cg_vararg_end(KitCg* g) { 745 ApiSValue ap; 746 CgTarget* T; 747 Operand ap_op; 748 if (!g) return; 749 T = g->target; 750 ap = api_pop(g); 751 ap_op = api_force_local(g, &ap, api_sv_type(&ap)); 752 T->va_end_(T, ap_op); 753 api_release(g, &ap); 754 } 755 756 void kit_cg_vararg_copy(KitCg* g) { 757 ApiSValue src, dst; 758 CgTarget* T; 759 Operand src_op, dst_op; 760 if (!g) return; 761 T = g->target; 762 src = api_pop(g); 763 dst = api_pop(g); 764 src_op = api_force_local(g, &src, api_sv_type(&src)); 765 dst_op = api_force_local(g, &dst, api_sv_type(&dst)); 766 T->va_copy_(T, dst_op, src_op); 767 api_release(g, &src); 768 api_release(g, &dst); 769 } 770 771 /* ============================================================ 772 * Memory operations (stubs) 773 * ============================================================ */ 774 775 void kit_cg_memcpy(KitCg* g, uint64_t size, KitCgMemAccess dst_access, 776 KitCgMemAccess src_access) { 777 ApiSValue src, dst; 778 CgTarget* T; 779 AggregateAccess agg; 780 Operand dst_op, src_op; 781 if (!g) return; 782 api_local_const_memory_boundary(g); 783 (void)src_access; 784 if (size > UINT32_MAX) { 785 compiler_panic(g->c, g->cur_loc, "KitCg: memcpy size exceeds CgTarget"); 786 return; 787 } 788 T = g->target; 789 src = api_pop(g); 790 dst = api_pop(g); 791 api_require_pointer_value(g, "memcpy destination", api_sv_type(&dst)); 792 api_require_pointer_value(g, "memcpy source", api_sv_type(&src)); 793 dst_op = api_force_local(g, &dst, api_sv_type(&dst)); 794 src_op = api_force_local(g, &src, api_sv_type(&src)); 795 memset(&agg, 0, sizeof agg); 796 agg.size = (u32)size; 797 agg.align = dst_access.align ? dst_access.align : (u32)size; 798 T->copy_bytes(T, dst_op, src_op, agg); 799 api_release(g, &dst); 800 api_release(g, &src); 801 } 802 803 void kit_cg_memmove(KitCg* g, uint64_t size, KitCgMemAccess dst_access, 804 KitCgMemAccess src_access) { 805 ApiSValue src, dst; 806 Operand args[3]; 807 if (!g) return; 808 api_local_const_memory_boundary(g); 809 (void)dst_access; 810 (void)src_access; 811 if (size > INT64_MAX) { 812 compiler_panic(g->c, g->cur_loc, "KitCg: memmove size exceeds CgTarget"); 813 return; 814 } 815 src = api_pop(g); 816 dst = api_pop(g); 817 api_require_pointer_value(g, "memmove destination", api_sv_type(&dst)); 818 api_require_pointer_value(g, "memmove source", api_sv_type(&src)); 819 args[0] = api_force_local(g, &dst, api_sv_type(&dst)); 820 args[1] = api_force_local(g, &src, api_sv_type(&src)); 821 args[2] = api_op_imm((i64)size, builtin_id(KIT_CG_BUILTIN_I64)); 822 g->target->intrinsic(g->target, INTRIN_MEMMOVE, NULL, 0, args, 3); 823 api_release(g, &dst); 824 api_release(g, &src); 825 } 826 827 void kit_cg_memset(KitCg* g, uint8_t val, uint64_t size, 828 KitCgMemAccess dst_access) { 829 ApiSValue dst; 830 CgTarget* T; 831 AggregateAccess agg; 832 Operand dst_op, byte_val; 833 if (!g) return; 834 api_local_const_memory_boundary(g); 835 if (size > UINT32_MAX) { 836 compiler_panic(g->c, g->cur_loc, "KitCg: memset size exceeds CgTarget"); 837 return; 838 } 839 T = g->target; 840 dst = api_pop(g); 841 api_require_pointer_value(g, "memset destination", api_sv_type(&dst)); 842 dst_op = api_force_local(g, &dst, api_sv_type(&dst)); 843 byte_val = api_op_imm((i64)val, KIT_CG_TYPE_NONE); 844 memset(&agg, 0, sizeof agg); 845 agg.size = (u32)size; 846 agg.align = dst_access.align ? dst_access.align : (u32)size; 847 T->set_bytes(T, dst_op, byte_val, agg); 848 api_release(g, &dst); 849 } 850 851 /* log2 of a {1,2,4,8} scale, else -1. */ 852 static int cg_scale_to_log2(u32 scale) { 853 switch (scale) { 854 case 1: 855 return 0; 856 case 2: 857 return 1; 858 case 4: 859 return 2; 860 case 8: 861 return 3; 862 default: 863 return -1; 864 } 865 } 866 867 void kit_cg_elem(KitCg* g, int64_t offset) { 868 ApiSValue idx, base; 869 CgTarget* T; 870 KitCgTypeId base_ty, base_ptr_ty, elem_ty, idx_ty; 871 const CgType* base_info; 872 u32 elemsz; 873 Operand base_op, idx_op; 874 CGLocal base_local; 875 if (!g) return; 876 T = g->target; 877 idx = api_pop(g); 878 base = api_pop(g); 879 base_ty = api_sv_type(&base); 880 base_info = cg_type_get(g->c, base_ty); 881 if (api_is_lvalue_sv(&base) || !base_info || 882 base_info->kind != KIT_CG_TYPE_PTR) { 883 compiler_panic(g->c, g->cur_loc, 884 "KitCg: elem requires a pointer value base (decay an " 885 "array to a pointer first)"); 886 return; 887 } 888 elem_ty = base_info->ptr.pointee; 889 base_ptr_ty = base_ty; 890 elemsz = (u32)abi_cg_sizeof(g->c->abi, elem_ty); 891 idx_ty = idx.type ? idx.type : idx.op.type; 892 if (!idx_ty) idx_ty = builtin_id(KIT_CG_BUILTIN_I64); 893 base_op = api_force_local(g, &base, base_ptr_ty); 894 base_local = base_op.v.local; 895 idx_op = api_force_local_unless_imm(g, &idx, idx_ty); 896 897 /* Constant index folds entirely into the displacement — no instructions, just 898 * a larger offset on the OPK_INDIRECT. */ 899 if (idx_op.kind == OPK_IMM) { 900 i64 ofs = idx_op.v.imm * (i64)elemsz + offset; 901 Operand place; 902 if (ofs >= INT32_MIN && ofs <= INT32_MAX) { 903 place = api_op_indirect(base_local, (i32)ofs, elem_ty); 904 } else { 905 CGLocal r = api_alloc_temp_local(g, base_ptr_ty); 906 Operand ro = api_op_local(r, base_ptr_ty); 907 T->binop(T, BO_IADD, ro, api_op_local(base_local, base_ptr_ty), 908 api_op_imm(ofs, base_ptr_ty)); 909 place = api_op_indirect(r, 0, elem_ty); 910 } 911 api_release(g, &base); 912 api_release(g, &idx); 913 api_push(g, api_make_lv(place, elem_ty)); 914 return; 915 } 916 917 /* Dynamic index: build a scaled-index place so the backend emits one 918 * [base + index*scale] addressing mode. A power-of-two element size rides in 919 * log2_scale; any other size pre-multiplies the index once (scale 1). The 920 * index is copied into a fresh local for unambiguous ownership. */ 921 { 922 int lg2 = cg_scale_to_log2(elemsz); 923 CGLocal ir = api_alloc_temp_local(g, idx_ty); 924 Operand iro = api_op_local(ir, idx_ty); 925 u8 log2_scale; 926 /* Refresh idx_op: allocating `ir` may have materialized a delayed index. */ 927 idx_op = api_force_local_unless_imm(g, &idx, idx_ty); 928 if (idx.op.kind == OPK_LOCAL) idx_op = idx.op; 929 if (lg2 >= 0) { 930 T->copy(T, iro, idx_op); 931 log2_scale = (u8)lg2; 932 } else { 933 T->binop(T, BO_IMUL, iro, idx_op, api_op_imm((i64)elemsz, idx_ty)); 934 log2_scale = 0; 935 } 936 api_release(g, &base); 937 api_release(g, &idx); 938 api_push(g, api_make_lv(api_op_indirect_indexed(base_local, ir, log2_scale, 939 (i32)offset, elem_ty), 940 elem_ty)); 941 } 942 } 943 944 void kit_cg_field(KitCg* g, uint32_t field_index) { 945 ApiSValue base; 946 CgTarget* T; 947 KitCgTypeId rec_ty; 948 KitCgTypeId base_ty; 949 KitCgTypeId field_ty; 950 KitCgTypeId rec_ptr_ty; 951 const CgType* rec_info; 952 const ABIRecordLayout* layout; 953 u32 field_offset; 954 Operand result; 955 CGLocal rr; 956 if (!g) return; 957 T = g->target; 958 base = api_pop(g); 959 api_ensure_local(g, &base); 960 base_ty = api_sv_type(&base); 961 if (!api_is_lvalue_sv(&base)) { 962 compiler_panic(g->c, g->cur_loc, 963 "KitCg: field requires a record place; deref a pointer " 964 "first"); 965 api_release(g, &base); 966 return; 967 } 968 rec_ty = base_ty; 969 rec_ptr_ty = cg_type_ptr_to(g->c, rec_ty); 970 layout = abi_cg_record_layout(g->c->abi, rec_ty); 971 if (!layout || field_index >= layout->nfields) { 972 compiler_panic(g->c, g->cur_loc, "KitCg: invalid field index"); 973 return; 974 } 975 rec_info = cg_type_get(g->c, rec_ty); 976 if (!rec_info || rec_info->kind != KIT_CG_TYPE_RECORD || 977 field_index >= rec_info->record.nfields) { 978 compiler_panic(g->c, g->cur_loc, "KitCg: invalid record base"); 979 return; 980 } 981 field_ty = rec_info->record.fields[field_index].type; 982 field_offset = layout->fields[field_index].offset; 983 if (layout->fields[field_index].bit_width != 0 || 984 (rec_info->record.fields[field_index].flags & KIT_CG_FIELD_BITFIELD) != 985 0) { 986 Operand base_addr; 987 ApiSValue sv; 988 if (layout->fields[field_index].bit_width == 0) { 989 compiler_panic(g->c, g->cur_loc, "KitCg: zero-width bit-field access"); 990 api_release(g, &base); 991 return; 992 } 993 /* Project to a bit-field PLACE: the place addresses the enclosing storage 994 * unit and carries the bit-field geometry from the record layout. A plain 995 * load/store on this place performs the extract/insert; there is no 996 * separate bit-field memop and no bit-field rider on KitCgMemAccess. */ 997 base_addr = api_lvalue_addr(g, &base, rec_ptr_ty); 998 sv = api_make_lv(base_addr, field_ty); 999 sv.bitfield.bit_offset = layout->fields[field_index].bit_offset; 1000 sv.bitfield.bit_width = layout->fields[field_index].bit_width; 1001 sv.bitfield.bit_storage_size = layout->fields[field_index].storage_size; 1002 sv.bitfield.bit_signed = 1003 rec_info->record.fields[field_index].bit_signed ? 1u : 0u; 1004 api_release(g, &base); 1005 api_push(g, sv); 1006 return; 1007 } 1008 if (base.op.kind == OPK_GLOBAL) { 1009 result = 1010 api_op_global(base.op.v.global.sym, 1011 base.op.v.global.addend + (i64)field_offset, field_ty); 1012 api_push(g, api_make_lv(result, field_ty)); 1013 } else if (base.op.kind == OPK_INDIRECT && field_offset <= (u32)INT32_MAX && 1014 base.op.v.ind.ofs <= INT32_MAX - (i32)field_offset) { 1015 /* Fold the field offset into the displacement, preserving any index/scale 1016 * a preceding `elem` left so `p[i].f` stays one [base+index*scale+off]. */ 1017 result = api_op_indirect_indexed( 1018 base.op.v.ind.base, base.op.v.ind.index, base.op.v.ind.log2_scale, 1019 base.op.v.ind.ofs + (i32)field_offset, field_ty); 1020 api_push(g, api_make_lv(result, field_ty)); 1021 } else { 1022 Operand base_addr; 1023 rr = api_alloc_temp_local(g, rec_ptr_ty); 1024 base_addr = api_op_local(rr, rec_ptr_ty); 1025 T->addr_of(T, base_addr, base.op); 1026 api_release(g, &base); 1027 if (field_offset == 0) { 1028 result = base_addr; 1029 } else { 1030 CGLocal fr = api_alloc_temp_local(g, rec_ptr_ty); 1031 result = api_op_local(fr, rec_ptr_ty); 1032 T->binop(T, BO_IADD, result, base_addr, 1033 api_op_imm((i64)field_offset, rec_ptr_ty)); 1034 api_release_temp_local(g, base_addr.v.local); 1035 } 1036 api_push( 1037 g, api_make_lv(api_op_indirect(result.v.local, 0, field_ty), field_ty)); 1038 } 1039 } 1040 1041 void kit_cg_field_bits(KitCg* g, uint16_t bit_offset, uint16_t bit_width, 1042 uint32_t bit_storage_size, int bit_signed) { 1043 ApiSValue* top; 1044 if (!g || g->sp == 0) return; 1045 top = &g->stack[g->sp - 1u]; 1046 if (!api_is_lvalue_sv(top)) { 1047 compiler_panic(g->c, g->cur_loc, 1048 "KitCg: field_bits requires a place destination"); 1049 return; 1050 } 1051 top->bitfield.bit_offset = bit_offset; 1052 top->bitfield.bit_width = bit_width; 1053 top->bitfield.bit_storage_size = bit_storage_size; 1054 top->bitfield.bit_signed = bit_signed ? 1u : 0u; 1055 } 1056 1057 /* ============================================================ 1058 * Calls / return 1059 * ============================================================ */ 1060 1061 /* Shared scaffolding for kit_cg_call / kit_cg_call_symbol. The two 1062 * public entry points differ only in how the callee is obtained and in 1063 * their pre-call stack-depth check; everything else (arg packaging, return 1064 * storage allocation, post-call release, result push) is identical. These 1065 * helpers carry the common shape and are the natural targets for any future 1066 * change that wants to vary call-shape policy (e.g. an ABI-driven storage 1067 * decision). */