kit

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

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