ir.h (27896B)
1 #ifndef KIT_IR_H 2 #define KIT_IR_H 3 4 #include "abi/abi.h" 5 #include "arch/native_target.h" 6 #include "core/arena.h" 7 #include "core/core.h" 8 9 /* Optimizer-private physical/virtual operand model. 10 * 11 * The semantic CG API now exposes only CGLocal/Label/CgTarget concepts. 12 * O1 still needs a mutable pseudo-register and frame-slot view for liveness, 13 * allocation, and MIR. Keep those names local to src/opt by remapping the old 14 * optimizer tokens after including the semantic headers. Do not move these 15 * fields back into cg/cgtarget.h. */ 16 typedef NativeFrameSlot OptFrameSlot; 17 #define FrameSlot OptFrameSlot 18 #define FRAME_SLOT_NONE NATIVE_FRAME_SLOT_NONE 19 20 typedef NativeFrameSlotKind OptFrameSlotKind; 21 #define FrameSlotKind OptFrameSlotKind 22 #define FS_LOCAL NATIVE_FRAME_SLOT_LOCAL 23 #define FS_PARAM NATIVE_FRAME_SLOT_PARAM 24 #define FS_SPILL NATIVE_FRAME_SLOT_SPILL 25 #define FS_ALLOCA NATIVE_FRAME_SLOT_ALLOCA 26 #define FS_OUTGOING NATIVE_FRAME_SLOT_OUTGOING 27 #define FS_SAVE NATIVE_FRAME_SLOT_SAVE 28 29 typedef NativeFrameSlotFlag OptFrameSlotFlag; 30 #define FSF_ADDR_TAKEN NATIVE_FRAME_SLOT_ADDR_TAKEN 31 #define FSF_MEMORY_REQUIRED NATIVE_FRAME_SLOT_MEMORY_REQUIRED 32 #define FSF_FIXED_OFFSET NATIVE_FRAME_SLOT_FIXED_OFFSET 33 #define FSF_VOLATILE (1u << 8) 34 35 typedef NativeFrameSlotDesc OptFrameSlotDesc; 36 #define FrameSlotDesc OptFrameSlotDesc 37 38 typedef NativeKnownFrameDesc OptCGKnownFrameDesc; 39 #define CGKnownFrameDesc OptCGKnownFrameDesc 40 41 typedef NativeAllocClass RegClass; 42 #define RC_INT NATIVE_REG_INT 43 #define RC_FP NATIVE_REG_FP 44 #define RC_VEC NATIVE_REG_VEC 45 46 /* The single float-ABI-aware value register-class decision (defined in ir.c). 47 * All optimizer passes route through it so their classifications agree. */ 48 u8 opt_value_reg_class(Compiler* c, KitCgTypeId ty); 49 50 #define CG_REG_ALLOCABLE NATIVE_REG_ALLOCABLE 51 #define CG_REG_CALLER_SAVED NATIVE_REG_CALLER_SAVED 52 #define CG_REG_CALLEE_SAVED NATIVE_REG_CALLEE_SAVED 53 #define CG_REG_ARG NATIVE_REG_ARG 54 #define CG_REG_RET NATIVE_REG_RET 55 #define CG_REG_RESERVED NATIVE_REG_RESERVED 56 57 typedef NativePhysRegInfo OptCGPhysRegInfo; 58 #define CGPhysRegInfo OptCGPhysRegInfo 59 60 typedef enum OptOperandKind { 61 OPT_OPK_IMM = OPK_IMM, 62 OPT_OPK_LOCAL = OPK_LOCAL, 63 OPT_OPK_GLOBAL = OPK_GLOBAL, 64 OPT_OPK_INDIRECT = OPK_INDIRECT, 65 OPT_OPK_REG = 0xf0u, 66 /* Post-allocation rematerialization recipe: the value is the address of a 67 * frame slot, not the scalar stored in that slot. Semantic HIR never carries 68 * this kind; location MIR and native emission own it. */ 69 OPT_OPK_FRAME_ADDR = 0xf1u, 70 /* Post-allocation scalar spill home. Unlike OPK_LOCAL (semantic frame 71 * storage/lvalue), this denotes the value stored in the frame slot. */ 72 OPT_OPK_STACK = 0xf2u, 73 } OptOperandKind; 74 #define OPK_REG OPT_OPK_REG 75 #define OPK_FRAME_ADDR OPT_OPK_FRAME_ADDR 76 #define OPK_STACK OPT_OPK_STACK 77 78 /* Location kinds for the components of an OPK_INDIRECT effective address. 79 * HIR is zero-initialized and therefore remains register-only. MIR may name a 80 * spilled pointer/index by its stable frame home, or retain a cheap frame 81 * address rematerialization recipe, without inventing a scratch-register 82 * value whose lifetime leaks across instructions. */ 83 typedef enum OptIndirectLocKind { 84 OPT_INDIRECT_REG = 0, 85 OPT_INDIRECT_FRAME = 1, 86 OPT_INDIRECT_FRAME_ADDR = 2, 87 } OptIndirectLocKind; 88 89 typedef enum OptOperandFlag { 90 /* Transient walker annotation, never stored in IR. It lets callbacks that 91 * lower or classify recipes distinguish an embedded effective-address 92 * component from a first-class scalar operand without changing the compact 93 * callback ABI. */ 94 OPT_OPERAND_WALK_INDIRECT_PART = 1u << 0, 95 } OptOperandFlag; 96 97 /* Operand riders (O1-PATTERNS L7/L8). Default 0 = no rider, so every existing 98 * operand and every backend that does not implement the fold keeps behaving as 99 * before. The recognition passes only stamp a rider after the consuming 100 * NativeTarget advertises the matching capability hook 101 * (can_fold_shift_into_alu / can_fold_extend_into_addr), so a backend that 102 * cannot emit the folded form never sees a rider it would have to drop. */ 103 typedef enum OptOpIndexExt { 104 OPT_IDX_EXT_NONE = 0, /* index used at full width (X register) */ 105 OPT_IDX_EXT_SXTW = 1, /* index is a 32-bit value sign-extended to 64 (W,sxtw) */ 106 OPT_IDX_EXT_UXTW = 2, /* index is a 32-bit value zero-extended to 64 (W,uxtw) */ 107 } OptOpIndexExt; 108 109 typedef struct OptOperand { 110 u8 kind; 111 u8 cls; 112 /* L7 shift rider: when nonzero and kind == OPK_REG, the register value is 113 * pre-shifted left by `shift` (1..4) before the consuming ALU op uses it, 114 * emitted as the shifted-register form (`add xD,xA,xS,lsl #shift`). 0 = the 115 * register is used as-is. Only stamped on a binop's second source operand. */ 116 u8 shift; 117 u8 flags; /* OptOperandFlag; stored operands normally have no flags */ 118 KitCgTypeId type; 119 union { 120 i64 imm; 121 Reg reg; 122 FrameSlot frame_slot; 123 CGLocal local; 124 struct { 125 ObjSymId sym; 126 i64 addend; 127 } global; 128 struct { 129 Reg base; 130 Reg index; 131 u8 log2_scale; 132 /* L8 extend rider (OptOpIndexExt): when SXTW/UXTW, `index` names a 32-bit 133 * value that the addressing mode widens to 64 bits with the recorded 134 * extend (`[Xbase, Wm, sxtw #log2_scale]`). NONE = full-width X index. */ 135 u8 index_ext; 136 /* OptIndirectLocKind for `base` and `index`. */ 137 u8 base_kind; 138 u8 index_kind; 139 i32 ofs; 140 /* The component value types travel with location MIR. Spill-slot 141 * coloring is intentionally allowed to reuse a physical frame slot for 142 * unrelated values, so the slot descriptor cannot be used to recover a 143 * spilled base/index type after allocation. HIR initializes these from 144 * the source PRegs; MIR preserves them while changing only location. */ 145 KitCgTypeId base_type; 146 KitCgTypeId index_type; 147 } ind; 148 } v; 149 } OptOperand; 150 #define Operand OptOperand 151 152 typedef enum OptCGLocalStorageKind { 153 CG_LOCAL_STORAGE_FRAME, 154 CG_LOCAL_STORAGE_REG, 155 } OptCGLocalStorageKind; 156 157 typedef struct OptCGLocalStorage { 158 u8 kind; 159 u8 pad[3]; 160 union { 161 Reg reg; 162 FrameSlot frame_slot; 163 } v; 164 } OptCGLocalStorage; 165 #define CGLocalStorage OptCGLocalStorage 166 #define CGLocalStorageKind OptCGLocalStorageKind 167 168 typedef struct OptCGABIPart { 169 Operand op; 170 u32 offset; 171 } OptCGABIPart; 172 #define CGABIPart OptCGABIPart 173 174 typedef struct OptCGABIValue { 175 KitCgTypeId type; 176 const ABIArgInfo* abi; 177 Operand storage; 178 CGABIPart* parts; 179 u32 nparts; 180 } OptCGABIValue; 181 #define CGABIValue OptCGABIValue 182 183 typedef enum OptCGCallPlanMoveKind { 184 CG_CALL_PLAN_REG, 185 CG_CALL_PLAN_STACK, 186 CG_CALL_PLAN_TAIL_STACK, 187 CG_CALL_PLAN_SRC_VALUE, 188 CG_CALL_PLAN_SRC_ADDR, 189 } OptCGCallPlanMoveKind; 190 191 typedef struct OptCGCallPlanMove { 192 Operand src; 193 Operand dst; 194 MemAccess mem; 195 u32 src_offset; 196 u32 stack_offset; 197 Reg dst_reg; 198 u8 src_kind; 199 u8 dst_kind; 200 u8 cls; 201 u8 pad; 202 } OptCGCallPlanMove; 203 #define CGCallPlanMove OptCGCallPlanMove 204 205 typedef struct OptCGCallPlanRet { 206 Operand dst; 207 MemAccess mem; 208 u32 dst_offset; 209 Reg src_reg; 210 u8 cls; 211 u8 pad[3]; 212 } OptCGCallPlanRet; 213 #define CGCallPlanRet OptCGCallPlanRet 214 215 typedef struct OptCGCallPlan { 216 Operand callee; 217 CGCallPlanMove* args; 218 CGCallPlanRet* rets; 219 u32 nargs; 220 u32 nrets; 221 u32 stack_arg_size; 222 u32 clobber_mask[NATIVE_REG_CLASS_COUNT]; 223 u16 flags; 224 u8 has_sret; 225 u8 is_variadic; 226 } OptCGCallPlan; 227 #define CGCallPlan OptCGCallPlan 228 229 typedef struct OptCGParamDesc { 230 u32 index; 231 Sym name; 232 KitCgTypeId type; 233 u32 size; 234 u32 align; 235 u32 flags; 236 SrcLoc loc; 237 CGLocalStorage storage; 238 const ABIArgInfo* abi; 239 const CGABIPart* incoming; 240 u32 nincoming; 241 } OptCGParamDesc; 242 #define CGParamDesc OptCGParamDesc 243 244 typedef struct OptCGCallDesc { 245 KitCgTypeId fn_type; 246 Operand callee; 247 CGABIValue* args; 248 CGABIValue ret; 249 u32 nargs; 250 u16 flags; 251 u8 tail_policy; 252 u8 pad; 253 KitCgInlinePolicy inline_policy; 254 const ABIFuncInfo* abi; 255 } OptCGCallDesc; 256 #define CGCallDesc OptCGCallDesc 257 258 typedef struct OptCGFuncDesc { 259 ObjSymId sym; 260 ObjSecId text_section_id; 261 ObjGroupId group_id; 262 KitCgTypeId fn_type; 263 KitCgTypeId result_type; /* KIT_CG_TYPE_NONE/void == no result */ 264 const CGParamDesc* params; 265 u32 nparams; 266 SrcLoc loc; 267 u32 flags; 268 KitCgInlinePolicy inline_policy; 269 u8 atomize; 270 u8 pad[3]; 271 const ABIFuncInfo* abi; 272 } OptCGFuncDesc; 273 #define CGFuncDesc OptCGFuncDesc 274 275 typedef struct OptCGScopeDesc { 276 u8 kind; 277 u8 pad[3]; 278 Label break_label; 279 Label continue_label; 280 Operand cond; 281 KitCgTypeId result_type; 282 } OptCGScopeDesc; 283 #define CGScopeDesc OptCGScopeDesc 284 285 /* SSA value id. VAL_NONE=0 is reserved as a sentinel. Recorded CG virtual 286 * registers live in Func's pseudo-register table; before pseudo-reg SSA, 287 * OPK_REG operands carry those mutable Reg ids. After pseudo-reg SSA, OPK_REG 288 * operands carry Val ids. The mode is explicit on Func, not encoded in the 289 * numeric id. */ 290 typedef u32 Val; 291 #define VAL_NONE 0u 292 293 typedef Reg PReg; 294 #define PREG_NONE ((PReg)REG_NONE) 295 296 typedef u32 InstId; 297 #define INST_ID_NONE 0u 298 299 /* IROps mirror the CGTarget surface 1:1 plus a handful of SSA-only ops 300 * (IR_PHI, IR_CONST_I, IR_CONST_BYTES). Each CGTarget method records as 301 * exactly one Inst, so level-1 replay is a flat walk that re-issues 302 * each Inst as one wrapped target call. doc/OPT.md §1.1. */ 303 typedef enum IROp { 304 IR_NOP, 305 /* SSA-only constants (used by const-propagation; recording uses 306 * load_imm/load_const which become IR_LOAD_IMM/IR_LOAD_CONST). */ 307 IR_CONST_I, 308 IR_CONST_BYTES, 309 310 /* Param/frame declarations recorded from CGTarget.param. The frame 311 * slot id table lives separately on Func; this op records the 312 * sequence point so replay can re-issue target->param in order. */ 313 IR_PARAM_DECL, 314 315 /* Address-bearing data movement. */ 316 IR_LOAD_IMM, /* opnds[0] dst REG; extra.imm = imm */ 317 IR_LOAD_CONST, /* opnds[0] dst REG; extra.cbytes */ 318 IR_COPY, /* opnds[0] dst REG, opnds[1] src REG */ 319 IR_LOAD, /* opnds[0] dst REG, opnds[1] addr; extra.mem */ 320 IR_STORE, /* opnds[0] addr, opnds[1] src REG|IMM; extra.mem */ 321 IR_ADDR_OF, /* opnds[0] dst REG, opnds[1] lv (LOCAL|GLOBAL|INDIRECT) */ 322 IR_TLS_ADDR_OF, /* opnds[0] dst REG; extra.aux = IRTlsAux */ 323 IR_AGG_COPY, /* opnds[0] dst, opnds[1] src; extra.aux = IRAggAux */ 324 IR_AGG_SET, /* opnds[0] dst, opnds[1] byte; extra.aux = IRAggAux */ 325 IR_BITFIELD_LOAD, /* opnds[0] dst REG, opnds[1] record; extra.aux */ 326 IR_BITFIELD_STORE, /* opnds[0] record, opnds[1] src; extra.aux */ 327 328 /* Arithmetic / cmp / convert. opnds[0] dst REG, [1] a, optionally [2] b. 329 * extra.imm carries the BinOp/UnOp/CmpOp/ConvKind tag. */ 330 IR_BINOP, 331 IR_UNOP, 332 IR_CMP, 333 IR_CONVERT, 334 335 /* Calls. extra.aux = IRCallAux (see below). defs = result OPK_REG ids: 336 * PReg before opt_build_reg_ssa, Val after it. */ 337 IR_CALL, 338 339 /* Phis. extra.aux = IRPhiAux. */ 340 IR_PHI, 341 342 /* Control flow / scopes. */ 343 IR_BR, /* unconditional. block.succ[0] = target block id. */ 344 IR_CONDBR, /* opnds[0] cond REG; succ[0] = true, succ[1] = false. */ 345 IR_CMP_BRANCH, /* fused. opnds = [a, b]; extra.imm = CmpOp; 346 succ[0] = taken, succ[1] = fallthrough. */ 347 IR_SWITCH, /* multi-target structured switch. opnds[0] = selector; 348 extra.aux = IRSwitchAux; succ[0..ncases) = case 349 blocks, succ[ncases] = default block. */ 350 IR_INDIRECT_BRANCH, /* opnds[0] = addr REG; extra.aux = IRIndirectAux. 351 succ[0..nvalid) = the valid target blocks. */ 352 IR_LOAD_LABEL_ADDR, /* opnds[0] dst REG; extra.imm = target block id. */ 353 IR_LOCAL_STATIC_DATA_BEGIN, /* extra.aux = CgIrLocalStaticBeginAux */ 354 IR_LOCAL_STATIC_DATA_WRITE, /* extra.aux = CgIrLocalStaticWriteAux */ 355 IR_LOCAL_STATIC_DATA_LABEL_ADDR, /* extra.aux = CgIrLocalStaticLabelAux */ 356 IR_LOCAL_STATIC_DATA_END, 357 IR_RET, /* extra.aux = IRRetAux* (NULL for void). */ 358 IR_UNREACHABLE, /* control terminator; no operands, no successors. */ 359 IR_SCOPE_BEGIN, /* extra.aux = IRScopeAux. */ 360 IR_SCOPE_END, /* extra.imm = scope id (Val). */ 361 IR_BREAK_TO, /* extra.imm = scope id (Val). */ 362 IR_CONTINUE_TO, /* extra.imm = scope id (Val). */ 363 364 /* alloca / variadics. */ 365 IR_ALLOCA, /* opnds = [dst REG, size]; extra.imm = align */ 366 IR_VA_START, /* opnds = [ap] */ 367 IR_VA_ARG, /* opnds = [dst REG, ap]; extra.aux = KitCgTypeId*/ 368 IR_VA_END, /* opnds = [ap] */ 369 IR_VA_COPY, /* opnds = [dst, src] */ 370 371 /* Atomics. */ 372 IR_ATOMIC_LOAD, /* opnds = [dst, addr]; extra.aux = IRAtomicAux */ 373 IR_ATOMIC_STORE, /* opnds = [addr, src]; extra.aux = IRAtomicAux */ 374 IR_ATOMIC_RMW, /* opnds = [dst, addr, val]; extra.aux */ 375 IR_ATOMIC_CAS, /* defs = [prior, ok] OPK_REG ids; extra.aux = IRCasAux */ 376 IR_FENCE, /* extra.imm = KitCgMemOrder */ 377 378 /* Inline asm. extra.aux = IRAsmAux. */ 379 IR_ASM_BLOCK, 380 381 /* Compiler intrinsics (see arch.h IntrinKind). extra.aux = IRIntrinAux. */ 382 IR_INTRINSIC, 383 384 /* set_loc is *not* an IR op — SrcLoc is sticky on Inst.loc, applied at 385 * recording time from the wrapper's pending_loc. */ 386 } IROp; 387 388 /* ---- per-op aux structs ---- */ 389 390 typedef struct IRTlsAux { 391 ObjSymId sym; 392 i64 addend; 393 } IRTlsAux; 394 395 typedef struct IRSwitchAuxCase { 396 u64 value; 397 u32 block; /* successor block id */ 398 } IRSwitchAuxCase; 399 400 typedef struct IRSwitchAux { 401 KitCgTypeId selector_type; 402 IRSwitchAuxCase* cases; 403 u32 ncases; 404 u32 default_block; /* if no default, this is the post-switch fallthrough */ 405 u8 has_default; /* 1 if frontend supplied an explicit default */ 406 u8 hint; /* KitCgSwitchHint */ 407 u8 pad[2]; 408 } IRSwitchAux; 409 410 typedef struct IRIndirectAux { 411 u32* targets; /* successor block ids; same as Block.succ[0..ntargets) */ 412 u32 ntargets; 413 } IRIndirectAux; 414 415 typedef struct IRAggAux { 416 AggregateAccess access; 417 } IRAggAux; 418 419 typedef struct IRBitFieldAux { 420 BitFieldAccess access; 421 } IRBitFieldAux; 422 423 #define OPT_REG_CLASSES NATIVE_REG_CLASS_COUNT 424 #define OPT_MAX_HARD_REGS NATIVE_MAX_HARD_REGS 425 426 /* One per-class clobber bitmask set; see Func.inst_clobbers. */ 427 typedef NativeRegMaskSet OptInstClobberMask; 428 #define OPT_MAX_EMIT_TEMPS 4u 429 430 typedef struct IRGepAux { 431 KitCgTypeId base_type; 432 u32 nindices; 433 i64* indices; 434 } IRGepAux; 435 436 typedef struct IRPhiAux { 437 u32 npreds; 438 u32* pred_blocks; 439 Val* pred_vals; 440 u32 slot_id; /* 0 if not from mem2reg; else 1-based FrameSlot id */ 441 u32 reg_id; /* 0 if not from mutable-pseudo SSA; else original Reg id */ 442 } IRPhiAux; 443 444 #define IRF_NO_COALESCE (1u << 0) 445 446 /* IR_CALL aux. The CGTarget interface is rich enough that we keep the 447 * full descriptor for replay; SSA passes inspect args/results in their 448 * Val form via the CGABIValue.storage.v.reg fields where applicable. */ 449 typedef struct IRCallAux { 450 CGCallDesc desc; 451 CGCallPlan plan; 452 u8 plan_valid; 453 u8 use_plan_replay; 454 u8 pad[2]; 455 /* Result Vals (one per ABI-decomposed return part). 0 for void. */ 456 u32 nresults; 457 Val* results; 458 /* For SSA-aware passes: the call may have args that are REG operands 459 * (carrying Val uses). They live in desc.args[i].storage.v.reg or 460 * desc.args[i].parts[k].op.v.reg. */ 461 } IRCallAux; 462 463 typedef struct IRRetAux { 464 u8 present; /* 0 → void return; ret(NULL) at replay */ 465 CGABIValue val; 466 } IRRetAux; 467 468 typedef struct IRScopeAux { 469 CGScopeDesc desc; 470 u32 scope_id; /* 1-based; the CGScope handed back to the caller */ 471 /* The caller-supplied break/continue labels translated to block ids; if the 472 * desc passed LABEL_NONE we leave 0 (unused — break_to/continue_to is 473 * illegal in that case). */ 474 u32 loop_break_block; 475 u32 loop_continue_block; 476 } IRScopeAux; 477 478 typedef struct IRAtomicAux { 479 MemAccess mem; 480 KitCgMemOrder mo; 481 u8 op; /* KitCgAtomicOp; valid for IR_ATOMIC_RMW */ 482 } IRAtomicAux; 483 484 typedef struct IRCasAux { 485 MemAccess mem; 486 KitCgMemOrder success; 487 KitCgMemOrder failure; 488 } IRCasAux; 489 490 typedef struct IRAsmRegRequirement { 491 /* A register constraint resolved once at the HIR -> native-policy boundary. 492 * It remains an instruction-local placement request; regalloc never turns it 493 * into a persistent MIR location. */ 494 u32 allowed_mask; /* 0 means any operand register in `cls` */ 495 i32 fixed_reg; /* -1 means no fixed register */ 496 u8 cls; /* RegClass */ 497 u8 present; /* constraint requires a register */ 498 u8 pad[2]; 499 } IRAsmRegRequirement; 500 501 typedef struct IRAsmAux { 502 const char* tmpl; 503 AsmConstraint* outs; 504 AsmConstraint* ins; 505 Sym* clobbers; 506 Operand* 507 out_ops; /* nout slots; the wrapped target may fill in REG location */ 508 Operand* in_ops; /* nin slots; recorded by w_asm_block, xlat'd at replay */ 509 u32 nout, nin, nclob; 510 /* KitCgAsmClobberAbiSet bits: an arch-neutral "clobbers the whole caller/ 511 * callee-saved set" the backend expands against its own register file. */ 512 u32 clobber_abi_sets; 513 /* Filled by opt_machinize from backend register-name resolution. */ 514 u32 clobber_mask[OPT_REG_CLASSES]; 515 IRAsmRegRequirement* out_reg_reqs; /* nout */ 516 IRAsmRegRequirement* in_reg_reqs; /* nin */ 517 u8 has_memory_constraint; 518 u8 pad[3]; 519 } IRAsmAux; 520 521 typedef struct IRIntrinAux { 522 IntrinKind kind; 523 Operand* dsts; /* ndst */ 524 Operand* args; /* narg */ 525 Val* result_vals; /* one per dst that's a REG, parallel to dsts */ 526 u32 ndst, narg; 527 } IRIntrinAux; 528 529 typedef struct IRParamDeclAux { 530 CGParamDesc desc; 531 } IRParamDeclAux; 532 533 /* ---- frame slots / params ---- */ 534 535 typedef struct IRFrameSlot { 536 FrameSlot id; 537 KitCgTypeId type; 538 Sym name; 539 SrcLoc loc; 540 u32 size; 541 u32 align; 542 /* Spill-traffic priority (O1.md W1.0). Aggregated from the allocator's final 543 * spill-slot assignment: the saturating sum of the spill cost of every 544 * coalesce group assigned to this (possibly reused) slot, where the per-group 545 * cost is `2*use_freq + def_freq + live_across_call_freq + live_block_freq` 546 * (pass_live.c). The known-frame emitter presents slots to the backend in 547 * descending-priority order so the hottest spills get the smallest final frame 548 * displacement (disp8 on x64, inside the scaled reach on aa64, inside the 549 * +/-2KB imm12 window on rv64). 0 for homed/address-taken locals (v1). */ 550 u32 priority; 551 u8 kind; /* FrameSlotKind */ 552 u8 pad; 553 u16 flags; /* FrameSlotFlag */ 554 } IRFrameSlot; 555 556 typedef struct IRParam { 557 u32 index; 558 Sym name; 559 KitCgTypeId type; 560 u32 size; 561 u32 align; 562 u32 flags; /* CGLocalFlag */ 563 CGLocalStorage storage; 564 const ABIArgInfo* abi; 565 SrcLoc loc; 566 } IRParam; 567 568 typedef struct IRLocal { 569 u32 id; 570 CGLocalDesc desc; 571 CGLocalStorage storage; 572 FrameSlot home_slot; 573 u8 address_taken; 574 u8 pad[3]; 575 } IRLocal; 576 577 /* ---- Inst / Block / Func ---- */ 578 579 typedef struct Inst { 580 u16 op; 581 u16 flags; 582 InstId id; 583 SrcLoc loc; /* sticky from CGTarget.set_loc at recording */ 584 KitCgTypeId type; 585 /* Primary and multi-result OPK_REG definitions. Before opt_build_reg_ssa, 586 * these hold PReg ids matching destination operands. After opt_build_reg_ssa, 587 * they hold Val ids. */ 588 Val def; 589 u32 ndefs; 590 Val* defs; 591 /* Operands. We use Operand instead of Val so that the original 592 * CGTarget call shape (IMM / LOCAL / GLOBAL / INDIRECT in addition to 593 * REG) round-trips at replay. Before opt_build_reg_ssa, OPK_REG operands 594 * are mutable PReg uses/defs; after it, they are Val uses/defs. */ 595 u32 nopnds; 596 Operand* opnds; 597 union { 598 i64 imm; 599 ConstBytes cbytes; 600 MemAccess mem; 601 void* aux; 602 } extra; 603 } Inst; 604 605 typedef struct Block { 606 u32 id; 607 Inst* insts; 608 u32 ninsts, cap; 609 u32* preds; 610 u32 npreds; 611 /* Variable-length successor list. ir_block_new arena-allocates a 612 * 2-slot array (large enough for ordinary terminators); ops with 613 * more successors (IR_SWITCH, IR_INDIRECT_BRANCH) reallocate via 614 * ir_block_set_nsucc before writing. nsucc names the prefix in use. */ 615 u32* succ; 616 u32 nsucc; 617 u32 succ_cap; 618 u32 loop_depth; 619 u32 frequency; 620 /* MCLabel id pre-allocated by w_label_new for blocks created via 621 * cg_label_new. Stable from recording through pass_emit. Blocks 622 * created internally by opt (fallthroughs, scope-implicit blocks) 623 * leave this at MC_LABEL_NONE; pass_emit's ensure_label mints one 624 * on demand for those. */ 625 MCLabel mc_label; 626 } Block; 627 628 typedef struct MFunc { 629 Block* blocks; 630 u32 nblocks; 631 u32 blocks_cap; 632 u32 entry; 633 u32* emit_order; 634 u32 emit_order_n; 635 u32 emit_order_cap; 636 } MFunc; 637 638 typedef enum OptAllocKind { 639 OPT_ALLOC_NONE, 640 OPT_ALLOC_HARD, 641 OPT_ALLOC_SPILL, 642 } OptAllocKind; 643 644 typedef enum OptLocKind { 645 OPT_LOC_NONE, 646 OPT_LOC_HARD, 647 OPT_LOC_STACK, 648 } OptLocKind; 649 650 typedef struct OptLoc { 651 u8 kind; 652 u8 cls; 653 Reg hard_reg; 654 FrameSlot spill_slot; 655 } OptLoc; 656 657 typedef struct OptPRegInfo { 658 u32 first_pos; 659 u32 last_pos; 660 u32 live_length; 661 u32 frequency; /* legacy aggregate priority score */ 662 u32 use_freq; 663 u32 def_freq; 664 u32 live_block_freq; 665 u32 live_across_call_freq; 666 u32 spill_cost; 667 Reg hard_reg; 668 FrameSlot spill_slot; 669 u8 alloc_kind; /* OptAllocKind */ 670 u8 cls; /* RegClass */ 671 i8 preferred_hard_reg; /* soft hint for allocator; -1 = no hint */ 672 u8 pad[1]; 673 /* Authoritative correctness constraint. Bit r means this unsplit PReg may 674 * not be allocated to hard register r because an instruction boundary in 675 * its live range reserves or destroys that register. ABI placement 676 * preferences are independent soft hints and never mutate this mask. */ 677 u32 forbidden_hard_regs; 678 } OptPRegInfo; 679 680 typedef enum OptUseKind { 681 OPT_USE_OPERAND, 682 OPT_USE_INDIRECT_BASE, 683 OPT_USE_INDIRECT_INDEX, 684 OPT_USE_PHI_INPUT, 685 } OptUseKind; 686 687 typedef struct OptUse { 688 Val val; 689 u32 block; 690 u32 inst; 691 InstId inst_id; 692 u32 next_for_val; 693 u32 operand_index; 694 u32 phi_pred_index; 695 Operand* operand; 696 u8 kind; /* OptUseKind */ 697 u8 pad[3]; 698 } OptUse; 699 700 typedef struct Func { 701 Arena* arena; 702 Compiler* c; 703 CGFuncDesc desc; /* preserved for level-1 replay func_begin */ 704 ObjSymId name; /* alias for desc.sym (kept for older callers) */ 705 KitCgTypeId type; 706 Block* blocks; 707 u32 nblocks, blocks_cap; 708 u32 entry; 709 710 IRFrameSlot* frame_slots; 711 u32 nframe_slots, frame_slots_cap; 712 IRParam* params; 713 u32 nparams, params_cap; 714 IRLocal* locals; 715 u32 nlocals, locals_cap; 716 717 /* Value table. Index 0 is VAL_NONE; first allocated Val is 1. */ 718 u32* val_def_block; 719 u32* val_def_inst; 720 KitCgTypeId* val_type; 721 u8* val_cls; /* RegClass per Val, used by replay to reconstruct REG operands 722 */ 723 u32 nvals, vals_cap; 724 725 /* Mutable pseudo-register table. Index 0 is unused so CG virtual Reg ids can 726 * be used directly. During initial recording and O1, OPK_REG operands name 727 * these persistent storage locations. O2 converts them to Val ids with 728 * opt_build_reg_ssa before running SSA passes. */ 729 KitCgTypeId* preg_type; 730 u8* preg_cls; 731 u32 npregs, pregs_cap; 732 733 /* Scope id table. Indexed by scope_id (1-based). Values map to 734 * IRScopeAux entries (via the IR_SCOPE_BEGIN inst). Stored as a flat 735 * pointer table for O(1) lookup during scope_end/break/continue 736 * recording and replay. */ 737 Inst** scope_aux_inst; 738 u32 nscopes, scopes_cap; 739 740 /* Emit order: the sequence in which blocks first became `cur` during 741 * recording. This is the natural order CG's emit cursor visited each 742 * block, so replay must follow it (block-creation order can differ — 743 * e.g. label_new(L) precedes cmp_branch but the cmp_branch's 744 * fallthrough block is what physically follows the cmp_branch in 745 * code). Blocks not present here are unreachable / unplaced; replay 746 * skips them. */ 747 u32* emit_order; 748 u32 emit_order_n, emit_order_cap; 749 750 Target opt_target; 751 u8 opt_has_target; 752 u8 opt_rewritten; 753 u8 opt_reg_ssa; 754 u8 pad0; 755 u16 opt_live_words; 756 u32 opt_used_loc_words; 757 u32 opt_alloc_hard_loc_words; 758 u32 opt_alloc_stack_loc_words; 759 u32 opt_alloc_stack_slots; 760 u32 opt_position_count; 761 u64 opt_alloc_hard_point_visits; 762 u64 opt_alloc_stack_point_visits; 763 u64 opt_alloc_hard_word_ors; 764 u64 opt_alloc_stack_word_ors; 765 u64 opt_alloc_hard_mark_points; 766 u64 opt_alloc_stack_mark_points; 767 u64 opt_rewrite_inserted_insts; 768 u64 opt_rewrite_live_words_touched; 769 u64 opt_dde_live_words_touched; 770 u64 opt_coalesce_moves_seen; 771 u64 opt_coalesce_candidates; 772 u64 opt_coalesce_conflicts; 773 u64 opt_coalesce_merge_attempts; 774 u64 opt_coalesce_merges; 775 InstId next_inst_id; 776 /* Per-instruction fixed-register clobbers (one bitmask per reg class), 777 * indexed by InstId, sized [next_inst_id]. Derived from the target's 778 * machine_op_clobbers hook by opt_machinize_native, then refreshed at the 779 * final pre-allocation HIR-shape boundary. Consulted by the allocator 780 * (pass_lower) to keep values live across an instruction out of the registers 781 * its encoding destroys (x86 idiv → rax/rdx, etc.). NULL when no instruction 782 * clobbers. */ 783 OptInstClobberMask* inst_clobbers; 784 u32 inst_clobbers_cap; 785 OptPRegInfo* preg_info; /* indexed by the current allocation reg namespace */ 786 OptLoc* preg_locs; /* canonical final allocation locations by PReg */ 787 MFunc* mir; /* physical post-allocation IR; HIR stays virtual */ 788 u32* opt_coalesce_parent; 789 u32* opt_coalesce_size; 790 /* Set when the linear O1 move-coalescing pass (pass_coalesce.c, 791 * opt_coalesce_linear) has populated opt_coalesce_parent in the no-SSA O1 792 * regalloc path. Distinguishes "O1 with a union-find" from the parked O2 793 * matrix coalescer (which also sets opt_coalesce_parent): the O1 immediate 794 * fold-through-copy in pass_combine.c keys on this, not on the mere presence 795 * of opt_coalesce_parent. */ 796 int opt_o1_coalescing; 797 798 OptUse* opt_uses; 799 u32 opt_nuses, opt_uses_cap; 800 u32* opt_first_use_by_val; /* indexed by Val, OPT_USE_NONE if no uses */ 801 u32 opt_first_use_by_val_cap; 802 u32 opt_valid_analyses; 803 804 Reg opt_hard_regs[OPT_REG_CLASSES][OPT_MAX_HARD_REGS]; 805 u32 opt_hard_reg_count[OPT_REG_CLASSES]; 806 CGPhysRegInfo opt_phys_regs[OPT_REG_CLASSES][OPT_MAX_HARD_REGS]; 807 u32 opt_phys_reg_count[OPT_REG_CLASSES]; 808 Reg emit_temp_regs[OPT_REG_CLASSES][OPT_MAX_EMIT_TEMPS]; 809 u32 emit_temp_reg_count[OPT_REG_CLASSES]; 810 /* Backend-private inline-asm staging registers. Kept as a mask because MIR 811 * verification only needs to enforce that they never become value 812 * locations; final asm emission leases the ordered descriptor list. */ 813 u32 asm_temp_mask[OPT_REG_CLASSES]; 814 u32 opt_caller_saved[OPT_REG_CLASSES]; /* bit r set if hard reg r is 815 caller-saved */ 816 u32 opt_callee_saved[OPT_REG_CLASSES]; 817 u32 opt_reserved_regs[OPT_REG_CLASSES]; 818 u32 opt_arg_regs[OPT_REG_CLASSES]; 819 u32 opt_ret_regs[OPT_REG_CLASSES]; 820 } Func; 821 822 /* ---- API ---- */ 823 824 Func* ir_func_new(Compiler*, const CGFuncDesc*); 825 826 u32 ir_block_new(Func*); 827 FrameSlot ir_frame_slot_new(Func*, const FrameSlotDesc*); 828 void ir_param_add(Func*, const CGParamDesc*); 829 u32 ir_local_add(Func*, const CGLocalDesc*, CGLocalStorage); 830 831 Val ir_alloc_val(Func*, KitCgTypeId, u8 cls); 832 void ir_ensure_val(Func*, Val, KitCgTypeId, u8 cls); 833 PReg ir_alloc_preg(Func*, KitCgTypeId, u8 cls); 834 void ir_ensure_preg(Func*, PReg, KitCgTypeId, u8 cls); 835 836 Inst* ir_emit(Func*, u32 block, IROp); 837 InstId ir_inst_id_new(Func*); 838 void ir_assign_inst_id(Func*, Inst*); 839 840 /* Resize a block's successor array. Used by ops with >2 successors 841 * (IR_SWITCH, IR_INDIRECT_BRANCH). Always sets nsucc to n. */ 842 void ir_block_set_nsucc(Func*, u32 block, u32 n); 843 844 /* Append `block` to f->emit_order if not already present. Called by 845 * the wrapper whenever cur transitions to a block. */ 846 void ir_note_emit(Func*, u32 block); 847 /* Append Inst to block; caller fills op-specific fields. The Inst is 848 * arena-resident; its address is stable until the Block.insts array is 849 * reallocated by phi insertion (which fixes up references). */ 850 851 #endif