kit

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

ir.h (23770B)


      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 } OptOperandKind;
     67 #define OPK_REG OPT_OPK_REG
     68 
     69 typedef struct OptOperand {
     70   u8 kind;
     71   u8 cls;
     72   u8 pad[2];
     73   KitCgTypeId type;
     74   union {
     75     i64 imm;
     76     Reg reg;
     77     FrameSlot frame_slot;
     78     CGLocal local;
     79     struct {
     80       ObjSymId sym;
     81       i64 addend;
     82     } global;
     83     struct {
     84       Reg base;
     85       Reg index;
     86       u8 log2_scale;
     87       i32 ofs;
     88     } ind;
     89   } v;
     90 } OptOperand;
     91 #define Operand OptOperand
     92 
     93 typedef enum OptCGLocalStorageKind {
     94   CG_LOCAL_STORAGE_FRAME,
     95   CG_LOCAL_STORAGE_REG,
     96 } OptCGLocalStorageKind;
     97 
     98 typedef struct OptCGLocalStorage {
     99   u8 kind;
    100   u8 pad[3];
    101   union {
    102     Reg reg;
    103     FrameSlot frame_slot;
    104   } v;
    105 } OptCGLocalStorage;
    106 #define CGLocalStorage OptCGLocalStorage
    107 #define CGLocalStorageKind OptCGLocalStorageKind
    108 
    109 typedef struct OptCGABIPart {
    110   Operand op;
    111   u32 offset;
    112 } OptCGABIPart;
    113 #define CGABIPart OptCGABIPart
    114 
    115 typedef struct OptCGABIValue {
    116   KitCgTypeId type;
    117   const ABIArgInfo* abi;
    118   Operand storage;
    119   CGABIPart* parts;
    120   u32 nparts;
    121 } OptCGABIValue;
    122 #define CGABIValue OptCGABIValue
    123 
    124 typedef enum OptCGCallPlanMoveKind {
    125   CG_CALL_PLAN_REG,
    126   CG_CALL_PLAN_STACK,
    127   CG_CALL_PLAN_TAIL_STACK,
    128   CG_CALL_PLAN_SRC_VALUE,
    129   CG_CALL_PLAN_SRC_ADDR,
    130 } OptCGCallPlanMoveKind;
    131 
    132 typedef struct OptCGCallPlanMove {
    133   Operand src;
    134   Operand dst;
    135   MemAccess mem;
    136   u32 src_offset;
    137   u32 stack_offset;
    138   Reg dst_reg;
    139   u8 src_kind;
    140   u8 dst_kind;
    141   u8 cls;
    142   u8 pad;
    143 } OptCGCallPlanMove;
    144 #define CGCallPlanMove OptCGCallPlanMove
    145 
    146 typedef struct OptCGCallPlanRet {
    147   Operand dst;
    148   MemAccess mem;
    149   u32 dst_offset;
    150   Reg src_reg;
    151   u8 cls;
    152   u8 pad[3];
    153 } OptCGCallPlanRet;
    154 #define CGCallPlanRet OptCGCallPlanRet
    155 
    156 typedef struct OptCGCallPlan {
    157   Operand callee;
    158   CGCallPlanMove* args;
    159   CGCallPlanRet* rets;
    160   u32 nargs;
    161   u32 nrets;
    162   u32 stack_arg_size;
    163   u32 clobber_mask[3];
    164   u32 return_mask[3];
    165   u16 flags;
    166   u8 has_sret;
    167   u8 is_variadic;
    168 } OptCGCallPlan;
    169 #define CGCallPlan OptCGCallPlan
    170 
    171 typedef struct OptCGParamDesc {
    172   u32 index;
    173   Sym name;
    174   KitCgTypeId type;
    175   u32 size;
    176   u32 align;
    177   u32 flags;
    178   SrcLoc loc;
    179   CGLocalStorage storage;
    180   const ABIArgInfo* abi;
    181   const CGABIPart* incoming;
    182   u32 nincoming;
    183 } OptCGParamDesc;
    184 #define CGParamDesc OptCGParamDesc
    185 
    186 typedef struct OptCGCallDesc {
    187   KitCgTypeId fn_type;
    188   Operand callee;
    189   CGABIValue* args;
    190   CGABIValue ret;
    191   u32 nargs;
    192   u16 flags;
    193   u8 tail_policy;
    194   u8 pad;
    195   KitCgInlinePolicy inline_policy;
    196   const ABIFuncInfo* abi;
    197 } OptCGCallDesc;
    198 #define CGCallDesc OptCGCallDesc
    199 
    200 typedef struct OptCGFuncDesc {
    201   ObjSymId sym;
    202   ObjSecId text_section_id;
    203   ObjGroupId group_id;
    204   KitCgTypeId fn_type;
    205   KitCgTypeId result_type; /* KIT_CG_TYPE_NONE/void == no result */
    206   const CGParamDesc* params;
    207   u32 nparams;
    208   SrcLoc loc;
    209   u32 flags;
    210   KitCgInlinePolicy inline_policy;
    211   u8 atomize;
    212   u8 pad[3];
    213   const ABIFuncInfo* abi;
    214 } OptCGFuncDesc;
    215 #define CGFuncDesc OptCGFuncDesc
    216 
    217 typedef struct OptCGScopeDesc {
    218   u8 kind;
    219   u8 pad[3];
    220   Label break_label;
    221   Label continue_label;
    222   Operand cond;
    223   KitCgTypeId result_type;
    224 } OptCGScopeDesc;
    225 #define CGScopeDesc OptCGScopeDesc
    226 
    227 /* SSA value id. VAL_NONE=0 is reserved as a sentinel. Recorded CG virtual
    228  * registers live in Func's pseudo-register table; before pseudo-reg SSA,
    229  * OPK_REG operands carry those mutable Reg ids. After pseudo-reg SSA, OPK_REG
    230  * operands carry Val ids. The mode is explicit on Func, not encoded in the
    231  * numeric id. */
    232 typedef u32 Val;
    233 #define VAL_NONE 0u
    234 
    235 typedef Reg PReg;
    236 #define PREG_NONE ((PReg)REG_NONE)
    237 
    238 typedef u32 InstId;
    239 #define INST_ID_NONE 0u
    240 
    241 /* IROps mirror the CGTarget surface 1:1 plus a handful of SSA-only ops
    242  * (IR_PHI, IR_CONST_I, IR_CONST_BYTES). Each CGTarget method records as
    243  * exactly one Inst, so level-1 replay is a flat walk that re-issues
    244  * each Inst as one wrapped target call. doc/OPT.md §1.1. */
    245 typedef enum IROp {
    246   IR_NOP,
    247   /* SSA-only constants (used by const-propagation; recording uses
    248    * load_imm/load_const which become IR_LOAD_IMM/IR_LOAD_CONST). */
    249   IR_CONST_I,
    250   IR_CONST_BYTES,
    251 
    252   /* Param/frame declarations recorded from CGTarget.param. The frame
    253    * slot id table lives separately on Func; this op records the
    254    * sequence point so replay can re-issue target->param in order. */
    255   IR_PARAM_DECL,
    256 
    257   /* Address-bearing data movement. */
    258   IR_LOAD_IMM,       /* opnds[0] dst REG; extra.imm = imm */
    259   IR_LOAD_CONST,     /* opnds[0] dst REG; extra.cbytes */
    260   IR_COPY,           /* opnds[0] dst REG, opnds[1] src REG */
    261   IR_LOAD,           /* opnds[0] dst REG, opnds[1] addr; extra.mem */
    262   IR_STORE,          /* opnds[0] addr, opnds[1] src REG|IMM; extra.mem */
    263   IR_ADDR_OF,        /* opnds[0] dst REG, opnds[1] lv (LOCAL|GLOBAL|INDIRECT) */
    264   IR_TLS_ADDR_OF,    /* opnds[0] dst REG; extra.aux = IRTlsAux */
    265   IR_AGG_COPY,       /* opnds[0] dst, opnds[1] src; extra.aux = IRAggAux */
    266   IR_AGG_SET,        /* opnds[0] dst, opnds[1] byte; extra.aux = IRAggAux */
    267   IR_BITFIELD_LOAD,  /* opnds[0] dst REG, opnds[1] record; extra.aux */
    268   IR_BITFIELD_STORE, /* opnds[0] record, opnds[1] src; extra.aux */
    269 
    270   /* Arithmetic / cmp / convert. opnds[0] dst REG, [1] a, optionally [2] b.
    271    * extra.imm carries the BinOp/UnOp/CmpOp/ConvKind tag. */
    272   IR_BINOP,
    273   IR_UNOP,
    274   IR_CMP,
    275   IR_CONVERT,
    276 
    277   /* Calls. extra.aux = IRCallAux (see below). defs = result OPK_REG ids:
    278    * PReg before opt_build_reg_ssa, Val after it. */
    279   IR_CALL,
    280 
    281   /* Phis. extra.aux = IRPhiAux. */
    282   IR_PHI,
    283 
    284   /* Control flow / scopes. */
    285   IR_BR,              /* unconditional. block.succ[0] = target block id. */
    286   IR_CONDBR,          /* opnds[0] cond REG; succ[0] = true, succ[1] = false. */
    287   IR_CMP_BRANCH,      /* fused. opnds = [a, b]; extra.imm = CmpOp;
    288                          succ[0] = taken, succ[1] = fallthrough. */
    289   IR_SWITCH,          /* multi-target structured switch. opnds[0] = selector;
    290                          extra.aux = IRSwitchAux; succ[0..ncases) = case
    291                          blocks, succ[ncases] = default block. */
    292   IR_INDIRECT_BRANCH, /* opnds[0] = addr REG; extra.aux = IRIndirectAux.
    293                          succ[0..nvalid) = the valid target blocks. */
    294   IR_LOAD_LABEL_ADDR, /* opnds[0] dst REG; extra.imm = target block id. */
    295   IR_LOCAL_STATIC_DATA_BEGIN,      /* extra.aux = CgIrLocalStaticBeginAux */
    296   IR_LOCAL_STATIC_DATA_WRITE,      /* extra.aux = CgIrLocalStaticWriteAux */
    297   IR_LOCAL_STATIC_DATA_LABEL_ADDR, /* extra.aux = CgIrLocalStaticLabelAux */
    298   IR_LOCAL_STATIC_DATA_END,
    299   IR_RET,         /* extra.aux = IRRetAux* (NULL for void). */
    300   IR_UNREACHABLE, /* control terminator; no operands, no successors. */
    301   IR_SCOPE_BEGIN, /* extra.aux = IRScopeAux. */
    302   IR_SCOPE_END,   /* extra.imm = scope id (Val). */
    303   IR_BREAK_TO,    /* extra.imm = scope id (Val). */
    304   IR_CONTINUE_TO, /* extra.imm = scope id (Val). */
    305 
    306   /* alloca / variadics. */
    307   IR_ALLOCA,   /* opnds = [dst REG, size]; extra.imm = align */
    308   IR_VA_START, /* opnds = [ap] */
    309   IR_VA_ARG,   /* opnds = [dst REG, ap]; extra.aux = KitCgTypeId*/
    310   IR_VA_END,   /* opnds = [ap] */
    311   IR_VA_COPY,  /* opnds = [dst, src] */
    312 
    313   /* Atomics. */
    314   IR_ATOMIC_LOAD,  /* opnds = [dst, addr]; extra.aux = IRAtomicAux */
    315   IR_ATOMIC_STORE, /* opnds = [addr, src]; extra.aux = IRAtomicAux */
    316   IR_ATOMIC_RMW,   /* opnds = [dst, addr, val]; extra.aux */
    317   IR_ATOMIC_CAS,   /* defs = [prior, ok] OPK_REG ids; extra.aux = IRCasAux */
    318   IR_FENCE,        /* extra.imm = KitCgMemOrder */
    319 
    320   /* Inline asm. extra.aux = IRAsmAux. */
    321   IR_ASM_BLOCK,
    322 
    323   /* Compiler intrinsics (see arch.h IntrinKind). extra.aux = IRIntrinAux. */
    324   IR_INTRINSIC,
    325 
    326   /* set_loc is *not* an IR op — SrcLoc is sticky on Inst.loc, applied at
    327    * recording time from the wrapper's pending_loc. */
    328 } IROp;
    329 
    330 /* ---- per-op aux structs ---- */
    331 
    332 typedef struct IRTlsAux {
    333   ObjSymId sym;
    334   i64 addend;
    335 } IRTlsAux;
    336 
    337 typedef struct IRSwitchAuxCase {
    338   u64 value;
    339   u32 block; /* successor block id */
    340 } IRSwitchAuxCase;
    341 
    342 typedef struct IRSwitchAux {
    343   KitCgTypeId selector_type;
    344   IRSwitchAuxCase* cases;
    345   u32 ncases;
    346   u32 default_block; /* if no default, this is the post-switch fallthrough */
    347   u8 has_default;    /* 1 if frontend supplied an explicit default */
    348   u8 hint;           /* KitCgSwitchHint */
    349   u8 pad[2];
    350 } IRSwitchAux;
    351 
    352 typedef struct IRIndirectAux {
    353   u32* targets; /* successor block ids; same as Block.succ[0..ntargets) */
    354   u32 ntargets;
    355 } IRIndirectAux;
    356 
    357 typedef struct IRAggAux {
    358   AggregateAccess access;
    359 } IRAggAux;
    360 
    361 typedef struct IRBitFieldAux {
    362   BitFieldAccess access;
    363 } IRBitFieldAux;
    364 
    365 #define OPT_REG_CLASSES 3u
    366 #define OPT_MAX_HARD_REGS 32u
    367 
    368 /* One per-class clobber bitmask set; see Func.inst_clobbers. */
    369 typedef u32 OptInstClobberMask[OPT_REG_CLASSES];
    370 #define OPT_MAX_SCRATCH_REGS 4u
    371 
    372 typedef struct IRGepAux {
    373   KitCgTypeId base_type;
    374   u32 nindices;
    375   i64* indices;
    376 } IRGepAux;
    377 
    378 typedef struct IRPhiAux {
    379   u32 npreds;
    380   u32* pred_blocks;
    381   Val* pred_vals;
    382   u32 slot_id; /* 0 if not from mem2reg; else 1-based FrameSlot id */
    383   u32 reg_id;  /* 0 if not from mutable-pseudo SSA; else original Reg id */
    384 } IRPhiAux;
    385 
    386 #define IRF_NO_COALESCE (1u << 0)
    387 
    388 /* IR_CALL aux. The CGTarget interface is rich enough that we keep the
    389  * full descriptor for replay; SSA passes inspect args/results in their
    390  * Val form via the CGABIValue.storage.v.reg fields where applicable. */
    391 typedef struct IRCallAux {
    392   CGCallDesc desc;
    393   CGCallPlan plan;
    394   u8 plan_valid;
    395   u8 use_plan_replay;
    396   u8 pad[2];
    397   /* Result Vals (one per ABI-decomposed return part). 0 for void. */
    398   u32 nresults;
    399   Val* results;
    400   /* For SSA-aware passes: the call may have args that are REG operands
    401    * (carrying Val uses). They live in desc.args[i].storage.v.reg or
    402    * desc.args[i].parts[k].op.v.reg. */
    403 } IRCallAux;
    404 
    405 typedef struct IRRetAux {
    406   u8 present; /* 0 → void return; ret(NULL) at replay */
    407   CGABIValue val;
    408 } IRRetAux;
    409 
    410 typedef struct IRScopeAux {
    411   CGScopeDesc desc;
    412   u32 scope_id; /* 1-based; the CGScope handed back to the caller */
    413   /* The caller-supplied break/continue labels translated to block ids; if the
    414    * desc passed LABEL_NONE we leave 0 (unused — break_to/continue_to is
    415    * illegal in that case). */
    416   u32 loop_break_block;
    417   u32 loop_continue_block;
    418 } IRScopeAux;
    419 
    420 typedef struct IRAtomicAux {
    421   MemAccess mem;
    422   KitCgMemOrder mo;
    423   u8 op; /* KitCgAtomicOp; valid for IR_ATOMIC_RMW */
    424 } IRAtomicAux;
    425 
    426 typedef struct IRCasAux {
    427   MemAccess mem;
    428   KitCgMemOrder success;
    429   KitCgMemOrder failure;
    430 } IRCasAux;
    431 
    432 typedef struct IRAsmAux {
    433   const char* tmpl;
    434   AsmConstraint* outs;
    435   AsmConstraint* ins;
    436   Sym* clobbers;
    437   Operand*
    438       out_ops;     /* nout slots; the wrapped target may fill in REG location */
    439   Operand* in_ops; /* nin slots; recorded by w_asm_block, xlat'd at replay */
    440   u32 nout, nin, nclob;
    441   /* KitCgAsmClobberAbiSet bits: an arch-neutral "clobbers the whole caller/
    442    * callee-saved set" the backend expands against its own register file. */
    443   u32 clobber_abi_sets;
    444   /* Filled by opt_machinize from backend register-name resolution. */
    445   u32 clobber_mask[OPT_REG_CLASSES];
    446   i32* out_fixed_regs; /* nout, -1 when unconstrained */
    447   i32* in_fixed_regs;  /* nin, -1 when unconstrained */
    448   u8* out_fixed_cls;   /* RegClass, parallel to out_fixed_regs */
    449   u8* in_fixed_cls;    /* RegClass, parallel to in_fixed_regs */
    450   u32* out_allowed_masks; /* nout, 0 when the whole class is allowed */
    451   u32* in_allowed_masks;  /* nin, 0 when the whole class is allowed */
    452   u8* out_allowed_cls;    /* RegClass, parallel to out_allowed_masks */
    453   u8* in_allowed_cls;     /* RegClass, parallel to in_allowed_masks */
    454 } IRAsmAux;
    455 
    456 typedef struct IRIntrinAux {
    457   IntrinKind kind;
    458   Operand* dsts;    /* ndst */
    459   Operand* args;    /* narg */
    460   Val* result_vals; /* one per dst that's a REG, parallel to dsts */
    461   u32 ndst, narg;
    462 } IRIntrinAux;
    463 
    464 typedef struct IRParamDeclAux {
    465   CGParamDesc desc;
    466 } IRParamDeclAux;
    467 
    468 /* ---- frame slots / params ---- */
    469 
    470 typedef struct IRFrameSlot {
    471   FrameSlot id;
    472   KitCgTypeId type;
    473   Sym name;
    474   SrcLoc loc;
    475   u32 size;
    476   u32 align;
    477   u8 kind; /* FrameSlotKind */
    478   u8 pad;
    479   u16 flags; /* FrameSlotFlag */
    480 } IRFrameSlot;
    481 
    482 typedef struct IRParam {
    483   u32 index;
    484   Sym name;
    485   KitCgTypeId type;
    486   u32 size;
    487   u32 align;
    488   u32 flags; /* CGLocalFlag */
    489   CGLocalStorage storage;
    490   const ABIArgInfo* abi;
    491   SrcLoc loc;
    492 } IRParam;
    493 
    494 typedef struct IRLocal {
    495   u32 id;
    496   CGLocalDesc desc;
    497   CGLocalStorage storage;
    498   FrameSlot home_slot;
    499   u8 address_taken;
    500   u8 pad[3];
    501 } IRLocal;
    502 
    503 /* ---- Inst / Block / Func ---- */
    504 
    505 typedef struct Inst {
    506   u16 op;
    507   u16 flags;
    508   InstId id;
    509   SrcLoc loc; /* sticky from CGTarget.set_loc at recording */
    510   KitCgTypeId type;
    511   /* Primary and multi-result OPK_REG definitions. Before opt_build_reg_ssa,
    512    * these hold PReg ids matching destination operands. After opt_build_reg_ssa,
    513    * they hold Val ids. */
    514   Val def;
    515   u32 ndefs;
    516   Val* defs;
    517   /* Operands. We use Operand instead of Val so that the original
    518    * CGTarget call shape (IMM / LOCAL / GLOBAL / INDIRECT in addition to
    519    * REG) round-trips at replay. Before opt_build_reg_ssa, OPK_REG operands
    520    * are mutable PReg uses/defs; after it, they are Val uses/defs. */
    521   u32 nopnds;
    522   Operand* opnds;
    523   union {
    524     i64 imm;
    525     ConstBytes cbytes;
    526     MemAccess mem;
    527     void* aux;
    528   } extra;
    529 } Inst;
    530 
    531 typedef struct Block {
    532   u32 id;
    533   Inst* insts;
    534   u32 ninsts, cap;
    535   u32* preds;
    536   u32 npreds;
    537   /* Variable-length successor list. ir_block_new arena-allocates a
    538    * 2-slot array (large enough for ordinary terminators); ops with
    539    * more successors (IR_SWITCH, IR_INDIRECT_BRANCH) reallocate via
    540    * ir_block_set_nsucc before writing. nsucc names the prefix in use. */
    541   u32* succ;
    542   u32 nsucc;
    543   u32 succ_cap;
    544   u32 loop_depth;
    545   u32 frequency;
    546   /* MCLabel id pre-allocated by w_label_new for blocks created via
    547    * cg_label_new. Stable from recording through pass_emit. Blocks
    548    * created internally by opt (fallthroughs, scope-implicit blocks)
    549    * leave this at MC_LABEL_NONE; pass_emit's ensure_label mints one
    550    * on demand for those. */
    551   MCLabel mc_label;
    552 } Block;
    553 
    554 typedef struct MFunc {
    555   Block* blocks;
    556   u32 nblocks;
    557   u32 entry;
    558   u32* emit_order;
    559   u32 emit_order_n;
    560   u32 emit_order_cap;
    561 } MFunc;
    562 
    563 typedef enum OptAllocKind {
    564   OPT_ALLOC_NONE,
    565   OPT_ALLOC_HARD,
    566   OPT_ALLOC_SPILL,
    567   OPT_ALLOC_SPLIT,
    568 } OptAllocKind;
    569 
    570 typedef enum OptLocKind {
    571   OPT_LOC_NONE,
    572   OPT_LOC_HARD,
    573   OPT_LOC_STACK,
    574 } OptLocKind;
    575 
    576 typedef struct OptLoc {
    577   u8 kind;
    578   u8 cls;
    579   Reg hard_reg;
    580   FrameSlot spill_slot;
    581 } OptLoc;
    582 
    583 typedef struct OptAllocSegment {
    584   u32 start;
    585   u32 end;
    586   u32 block;
    587   u8 loc_kind;
    588   u8 cls;
    589   Reg hard_reg;
    590   FrameSlot spill_slot;
    591   FrameSlot spill_home;
    592   u8 reload_at_start;
    593   u8 store_at_end;
    594   u8 pad[2];
    595   u32 next;
    596 } OptAllocSegment;
    597 
    598 typedef struct OptPRegInfo {
    599   u32 first_pos;
    600   u32 last_pos;
    601   u32 live_length;
    602   u32 frequency; /* legacy aggregate priority score */
    603   u32 use_freq;
    604   u32 def_freq;
    605   u32 live_block_freq;
    606   u32 live_across_call_freq;
    607   u32 spill_cost;
    608   i32 tied_hard_reg; /* -1 means no fixed/tied physical register need. */
    609   Reg hard_reg;
    610   FrameSlot spill_slot;
    611   u8 alloc_kind;         /* OptAllocKind */
    612   u8 cls;                /* RegClass */
    613   i8 preferred_hard_reg; /* soft hint for allocator; -1 = no hint */
    614   u8 pad[1];
    615   u32 forbidden_hard_regs; /* bit r means PReg may not allocate hard reg r. */
    616   u32 allowed_hard_regs;   /* 0 means unrestricted; otherwise positive mask. */
    617   /* Subset of forbidden_hard_regs that comes from a fixed-register machine
    618    * clobber (an instruction live across this value destroys reg r — see
    619    * Func.inst_clobbers). Unlike soft forbids, the return-register hint must not
    620    * clear these: a value cannot live in a register clobbered within its range.
    621    */
    622   u32 clobbered_hard_regs;
    623 } OptPRegInfo;
    624 
    625 typedef enum OptUseKind {
    626   OPT_USE_OPERAND,
    627   OPT_USE_INDIRECT_BASE,
    628   OPT_USE_INDIRECT_INDEX,
    629   OPT_USE_PHI_INPUT,
    630 } OptUseKind;
    631 
    632 typedef struct OptUse {
    633   Val val;
    634   u32 block;
    635   u32 inst;
    636   InstId inst_id;
    637   u32 next_for_val;
    638   u32 operand_index;
    639   u32 phi_pred_index;
    640   Operand* operand;
    641   u8 kind; /* OptUseKind */
    642   u8 pad[3];
    643 } OptUse;
    644 
    645 typedef struct Func {
    646   Arena* arena;
    647   Compiler* c;
    648   CGFuncDesc desc; /* preserved for level-1 replay func_begin */
    649   ObjSymId name;   /* alias for desc.sym (kept for older callers) */
    650   KitCgTypeId type;
    651   Block* blocks;
    652   u32 nblocks, blocks_cap;
    653   u32 entry;
    654 
    655   IRFrameSlot* frame_slots;
    656   u32 nframe_slots, frame_slots_cap;
    657   IRParam* params;
    658   u32 nparams, params_cap;
    659   IRLocal* locals;
    660   u32 nlocals, locals_cap;
    661 
    662   /* Value table. Index 0 is VAL_NONE; first allocated Val is 1. */
    663   u32* val_def_block;
    664   u32* val_def_inst;
    665   KitCgTypeId* val_type;
    666   u8* val_cls; /* RegClass per Val, used by replay to reconstruct REG operands
    667                 */
    668   u32 nvals, vals_cap;
    669 
    670   /* Mutable pseudo-register table. Index 0 is unused so CG virtual Reg ids can
    671    * be used directly. During initial recording and O1, OPK_REG operands name
    672    * these persistent storage locations. O2 converts them to Val ids with
    673    * opt_build_reg_ssa before running SSA passes. */
    674   KitCgTypeId* preg_type;
    675   u8* preg_cls;
    676   u32 npregs, pregs_cap;
    677 
    678   /* Scope id table. Indexed by scope_id (1-based). Values map to
    679    * IRScopeAux entries (via the IR_SCOPE_BEGIN inst). Stored as a flat
    680    * pointer table for O(1) lookup during scope_end/break/continue
    681    * recording and replay. */
    682   Inst** scope_aux_inst;
    683   u32 nscopes, scopes_cap;
    684 
    685   /* Emit order: the sequence in which blocks first became `cur` during
    686    * recording. This is the natural order CG's emit cursor visited each
    687    * block, so replay must follow it (block-creation order can differ —
    688    * e.g. label_new(L) precedes cmp_branch but the cmp_branch's
    689    * fallthrough block is what physically follows the cmp_branch in
    690    * code). Blocks not present here are unreachable / unplaced; replay
    691    * skips them. */
    692   u32* emit_order;
    693   u32 emit_order_n, emit_order_cap;
    694 
    695   Target opt_target;
    696   u8 opt_has_target;
    697   u8 opt_rewritten;
    698   u8 opt_reg_ssa;
    699   u8 pad0;
    700   u16 opt_live_words;
    701   u32 opt_used_loc_words;
    702   u32 opt_alloc_hard_loc_words;
    703   u32 opt_alloc_stack_loc_words;
    704   u32 opt_alloc_stack_slots;
    705   u32 opt_position_count;
    706   u64 opt_alloc_hard_point_visits;
    707   u64 opt_alloc_stack_point_visits;
    708   u64 opt_alloc_hard_word_ors;
    709   u64 opt_alloc_stack_word_ors;
    710   u64 opt_alloc_hard_mark_points;
    711   u64 opt_alloc_stack_mark_points;
    712   u64 opt_rewrite_inserted_insts;
    713   u64 opt_rewrite_live_words_touched;
    714   u64 opt_dde_live_words_touched;
    715   u64 opt_coalesce_moves_seen;
    716   u64 opt_coalesce_candidates;
    717   u64 opt_coalesce_conflicts;
    718   u64 opt_coalesce_merge_attempts;
    719   u64 opt_coalesce_merges;
    720   InstId next_inst_id;
    721   /* Per-instruction fixed-register clobbers (one bitmask per reg class),
    722    * indexed by InstId, sized [next_inst_id]. Built in opt_machinize_native from
    723    * the target's machine_op_clobbers hook; consulted by the allocator
    724    * (pass_lower) to keep values live across an instruction out of the registers
    725    * its encoding destroys (x86 idiv → rax/rdx, etc.). NULL when no instruction
    726    * clobbers. */
    727   OptInstClobberMask* inst_clobbers;
    728   u32 inst_clobbers_cap;
    729   OptPRegInfo* preg_info; /* indexed by the current allocation reg namespace */
    730   OptLoc* preg_locs;      /* canonical final allocation locations by PReg */
    731   MFunc* mir;             /* physical post-allocation IR; HIR stays virtual */
    732   u32* opt_coalesce_parent;
    733   u32* opt_coalesce_size;
    734   OptAllocSegment* opt_alloc_segments;
    735   u32 opt_nalloc_segments;
    736   u32 opt_alloc_segments_cap;
    737   u32* opt_first_segment_by_preg;
    738 
    739   OptUse* opt_uses;
    740   u32 opt_nuses, opt_uses_cap;
    741   u32* opt_first_use_by_val; /* indexed by Val, OPT_USE_NONE if no uses */
    742   u32 opt_first_use_by_val_cap;
    743   u32 opt_valid_analyses;
    744 
    745   Reg opt_hard_regs[OPT_REG_CLASSES][OPT_MAX_HARD_REGS];
    746   u32 opt_hard_reg_count[OPT_REG_CLASSES];
    747   CGPhysRegInfo opt_phys_regs[OPT_REG_CLASSES][OPT_MAX_HARD_REGS];
    748   u32 opt_phys_reg_count[OPT_REG_CLASSES];
    749   Reg opt_scratch_regs[OPT_REG_CLASSES][OPT_MAX_SCRATCH_REGS];
    750   u32 opt_scratch_reg_count[OPT_REG_CLASSES];
    751   u32 opt_caller_saved[OPT_REG_CLASSES]; /* bit r set if hard reg r is
    752                                             caller-saved */
    753   u32 opt_callee_saved[OPT_REG_CLASSES];
    754   u32 opt_reserved_regs[OPT_REG_CLASSES];
    755   u32 opt_arg_regs[OPT_REG_CLASSES];
    756   u32 opt_ret_regs[OPT_REG_CLASSES];
    757 } Func;
    758 
    759 /* ---- API ---- */
    760 
    761 Func* ir_func_new(Compiler*, const CGFuncDesc*);
    762 
    763 u32 ir_block_new(Func*);
    764 FrameSlot ir_frame_slot_new(Func*, const FrameSlotDesc*);
    765 void ir_param_add(Func*, const CGParamDesc*);
    766 u32 ir_local_add(Func*, const CGLocalDesc*, CGLocalStorage);
    767 
    768 Val ir_alloc_val(Func*, KitCgTypeId, u8 cls);
    769 void ir_ensure_val(Func*, Val, KitCgTypeId, u8 cls);
    770 PReg ir_alloc_preg(Func*, KitCgTypeId, u8 cls);
    771 void ir_ensure_preg(Func*, PReg, KitCgTypeId, u8 cls);
    772 
    773 Inst* ir_emit(Func*, u32 block, IROp);
    774 InstId ir_inst_id_new(Func*);
    775 void ir_assign_inst_id(Func*, Inst*);
    776 
    777 /* Resize a block's successor array. Used by ops with >2 successors
    778  * (IR_SWITCH, IR_INDIRECT_BRANCH). Always sets nsucc to n. */
    779 void ir_block_set_nsucc(Func*, u32 block, u32 n);
    780 
    781 /* Append `block` to f->emit_order if not already present. Called by
    782  * the wrapper whenever cur transitions to a block. */
    783 void ir_note_emit(Func*, u32 block);
    784 /* Append Inst to block; caller fills op-specific fields. The Inst is
    785  * arena-resident; its address is stable until the Block.insts array is
    786  * reallocated by phi insertion (which fixes up references). */
    787 
    788 #endif