kit

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

cgir.h (18729B)


      1 #ifndef KIT_CG_CGIR_H
      2 #define KIT_CG_CGIR_H
      3 
      4 /* The CG codegen vocabulary: the operand model, the op enums (BinOp/UnOp/CmpOp/
      5  * ConvKind/IntrinKind), memory-access descriptors, and the call/func/switch/
      6  * scope descriptors. These are the data types the CgTarget vtable (cgtarget.h)
      7  * and the recorder IR (cg/ir.h) are both built from. Split out of cgtarget.h so
      8  * a consumer that only speaks operands (the native backends, the recorder IR,
      9  * the interpreter) need not depend on the backend contract. */
     10 
     11 #include <kit/cg.h>
     12 
     13 #include "core/core.h"
     14 #include "obj/obj.h"
     15 
     16 typedef u32 CGLocal;
     17 #define CG_LOCAL_NONE 0u
     18 
     19 /* Vector / SIMD forward compat: vector ops will arrive as new variants in
     20  * the BinOp, UnOp, CmpOp, ConvKind families. Backend switches over these
     21  * enums must use `default:` (unreachable / panic) rather than exhaustive
     22  * case lists, so adding a new variant later does not silently mis-handle on
     23  * backends that haven't been taught about it. Vector loads/stores reuse the
     24  * existing load/store methods with vector-typed Operands and appropriate
     25  * MemAccess. */
     26 
     27 /* Integer/float binary ops. Edge-case semantics are fully defined (no undefined
     28  * behavior) in doc/IR.md: iadd/isub/imul (and UO_NEG) wrap modulo 2^width;
     29  * sdiv/udiv/srem/urem and the shifts have a portable default plus an opt-in
     30  * target-defined mode selected per instruction via CgIrInstFlag (src/cg/ir.h).
     31  * FP ops are strict IEEE-754 in the target's default rounding/exception
     32  * environment; there is no FP remainder op (the frontend calls fmod). */
     33 typedef enum BinOp {
     34   BO_IADD,
     35   BO_ISUB,
     36   BO_IMUL,
     37   BO_SDIV,
     38   BO_UDIV,
     39   BO_SREM,
     40   BO_UREM,
     41   BO_FADD,
     42   BO_FSUB,
     43   BO_FMUL,
     44   BO_FDIV,
     45   BO_AND,
     46   BO_OR,
     47   BO_XOR,
     48   BO_SHL,
     49   BO_SHR_S,
     50   BO_SHR_U,
     51 } BinOp;
     52 
     53 typedef enum UnOp {
     54   UO_NEG,
     55   UO_FNEG,
     56   UO_NOT,  /* logical: 0/1 */
     57   UO_BNOT, /* bitwise ~  */
     58 } UnOp;
     59 
     60 /* Compares producing i1. The 10 integer members (CMP_EQ..CMP_GE_U) are total
     61  * and 1:1 with KitCgIntCmpOp; on integers CMP_EQ/CMP_NE are plain equality.
     62  *
     63  * The 12 floating-point members form a disjoint block laid out *after* the
     64  * integer block, in the same order as the public KitCgFpCmpOp, and are
     65  * IEEE-complete: each predicate encodes ordered (NaN -> false) vs unordered
     66  * (NaN -> true) explicitly, so the distinction reaches every backend. The
     67  * identity used throughout the backends is unordered-R == NOT(ordered-not-R)
     68  * (e.g. ULT == !(OGE), UNE == !(OEQ)). CMP_OEQ_F is the FP boundary: an op is a
     69  * floating compare iff op >= CMP_OEQ_F. */
     70 typedef enum CmpOp {
     71   CMP_EQ,
     72   CMP_NE,
     73   CMP_LT_S,
     74   CMP_LE_S,
     75   CMP_GT_S,
     76   CMP_GE_S,
     77   CMP_LT_U,
     78   CMP_LE_U,
     79   CMP_GT_U,
     80   CMP_GE_U,
     81   /* Ordered FP relationals (NaN -> false). */
     82   CMP_OEQ_F,
     83   CMP_ONE_F,
     84   CMP_OLT_F,
     85   CMP_OLE_F,
     86   CMP_OGT_F,
     87   CMP_OGE_F,
     88   /* Unordered FP relationals (NaN -> true). */
     89   CMP_UEQ_F,
     90   CMP_UNE_F,
     91   CMP_ULT_F,
     92   CMP_ULE_F,
     93   CMP_UGT_F,
     94   CMP_UGE_F,
     95 } CmpOp;
     96 
     97 /* Conversions. Widths must order correctly (sext/zext widen, trunc narrows,
     98  * bitcast preserves byte size). itof, fext, and ftrunc round to nearest-even;
     99  * ftoi_s/ftoi_u round toward zero with a portable saturating out-of-range
    100  * default (NaN -> 0) and an opt-in target-defined mode
    101  * (CG_IR_INST_TARGET_FPTOINT_EDGES in src/cg/ir.h). Full rules in doc/IR.md. */
    102 typedef enum ConvKind {
    103   CV_SEXT,
    104   CV_ZEXT,
    105   CV_TRUNC,
    106   CV_ITOF_S,
    107   CV_ITOF_U,
    108   CV_FTOI_S,
    109   CV_FTOI_U,
    110   CV_FEXT,
    111   CV_FTRUNC,
    112   CV_BITCAST,
    113 } ConvKind;
    114 
    115 /* Atomic op kinds (KitCgAtomicOp) and memory orders (KitCgMemOrder) come
    116  * straight from the public API. Which orders are legal depends on the atomic
    117  * op: load excludes release/acq_rel; store excludes acquire/consume/acq_rel;
    118  * CAS failure order is one of relaxed/consume/acquire/seq_cst and no stronger
    119  * than success. See the Atomics edge-case rules in doc/IR.md (mirrored by
    120  * kit_cg_atomic_is_legal). */
    121 
    122 /* Compiler-intrinsic kinds dispatched through CgTarget.intrinsic and carried
    123  * on IR_INTRINSIC via IRIntrinAux.kind. The set is bounded: a backend
    124  * must know each one to choose inline-vs-libcall. Hint intrinsics
    125  * (EXPECT/TRAP/PREFETCH/ASSUME_ALIGNED) ride the same dispatch:
    126  * the backend decides whether they emit an instruction or a no-op.
    127  * `unreachable` is NOT here: it is a first-class control terminator with
    128  * its own CgTarget hook (see below), not an intrinsic.
    129  *
    130  * Not every C builtin lives here. Parser-evaluated builtins
    131  * (__builtin_offsetof, __builtin_constant_p, __builtin_choose_expr,
    132  * __builtin_types_compatible_p) fold at parse and never reach IR. Builtins
    133  * that already have dedicated CgTarget methods (alloca, va_*, atomics) keep
    134  * them. Returns-twice and no-return control intrinsics use this dispatch so
    135  * opt can preserve their CFG effects without growing backend vtable hooks. */
    136 typedef enum IntrinKind {
    137   INTRIN_NONE = 0,
    138 
    139   /* bit ops */
    140   INTRIN_POPCOUNT,
    141   INTRIN_CTZ,
    142   INTRIN_CLZ,
    143   INTRIN_BSWAP,
    144 
    145   /* memory. memcpy/memset are the dedicated copy_bytes/set_bytes hooks
    146    * (kit_cg_memcpy/_memset); only memmove flows through the intrinsic path. */
    147   INTRIN_MEMMOVE,
    148   INTRIN_PREFETCH,
    149   INTRIN_ASSUME_ALIGNED,
    150 
    151   /* hints */
    152   INTRIN_EXPECT,
    153   INTRIN_TRAP,
    154 
    155   /* OS trap: args[0] is the syscall number, args[1..6] are integer/pointer
    156    * payloads; dsts[0] receives the target long result. */
    157   INTRIN_SYSCALL,
    158 
    159   /* non-local control */
    160   INTRIN_SETJMP,
    161   INTRIN_LONGJMP,
    162 
    163   /* checked arith — multi-result (value, overflow_flag) */
    164   INTRIN_SADD_OVERFLOW,
    165   INTRIN_UADD_OVERFLOW,
    166   INTRIN_SSUB_OVERFLOW,
    167   INTRIN_USUB_OVERFLOW,
    168   INTRIN_SMUL_OVERFLOW,
    169   INTRIN_UMUL_OVERFLOW,
    170 
    171   /* baremetal CPU control — single-instruction, no operands unless noted.
    172    * dsts/args empty except IRQ_SAVE (dsts[0] = saved interrupt state) and
    173    * IRQ_RESTORE (args[0] = state to restore). Privileged forms (WFI/WFE/SEV
    174    * and the IRQ family) trap at user level; backends still emit the one
    175    * instruction and frontends gate any runtime use behind a capability test. */
    176   INTRIN_CPU_NOP,
    177   INTRIN_CPU_YIELD,
    178   INTRIN_WFI,
    179   INTRIN_WFE,
    180   INTRIN_SEV,
    181   INTRIN_ISB,
    182   INTRIN_DMB,
    183   INTRIN_DSB,
    184   INTRIN_IRQ_SAVE,
    185   INTRIN_IRQ_RESTORE,
    186   INTRIN_IRQ_ENABLE,
    187   INTRIN_IRQ_DISABLE,
    188 
    189   /* frame-pointer-chain introspection — value-producing, single immediate
    190    * operand (the constant level). args[0] is the level (OPK_IMM); dsts[0] is
    191    * the void* result. Lowered as an unrolled FP walk; modeled as an ordinary
    192    * frame-dependent memory read (IR_INTRINSIC is already conservatively
    193    * side-effecting in opt, so it is never hoisted, CSE'd, or eliminated). */
    194   INTRIN_FRAME_ADDRESS,
    195   INTRIN_RETURN_ADDRESS,
    196 
    197   /* Read the target cycle/timestamp counter. No operands; dsts[0] receives the
    198    * unsigned 64-bit result. Modeled like a frame-dependent read (IR_INTRINSIC
    199    * is conservatively side-effecting, so it is never hoisted/CSE'd/removed). */
    200   INTRIN_READCYCLECOUNTER,
    201 
    202   /* High half of a full-width two-operand product. */
    203   INTRIN_SMUL_HIGH,
    204   INTRIN_UMUL_HIGH,
    205 
    206   /* Internal stack-protector guard read. No operands; dsts[0] receives one
    207    * pointer-width word. This is emitted only for target ABIs whose guard has
    208    * no linkable object symbol (Linux/Android x86-64 use fs:0x28). */
    209   INTRIN_STACK_GUARD,
    210 } IntrinKind;
    211 
    212 typedef enum OpKind {
    213   OPK_IMM,
    214   OPK_LOCAL,    /* typed semantic local */
    215   OPK_GLOBAL,   /* address: symbol+addend, not a load */
    216   OPK_INDIRECT, /* [local + ofs], with optional indexed local */
    217 } OpKind;
    218 
    219 /* Per-operand hint flags carried in Operand.flags. Additive: set by the cg layer
    220  * at a specific op's call site on a transient copy of the operand (never on a
    221  * value-stack entry), read only by the -O0 NativeDirectTarget; every other
    222  * backend (opt recorder, c_target, wasm, check) ignores them. */
    223 typedef enum OpFlag {
    224   OPK_FLAG_NONE = 0,
    225   /* This OPK_LOCAL operand names a transient that is provably dead after the op
    226    * consuming it (confirmed by api_temp_dead). The -O0 backend may then rename
    227    * the local's live cache register to the op's destination (transfer ownership)
    228    * instead of materializing the source and emitting a routing mov / in-place
    229    * convert into a fresh register. Set only on a copy/convert source. */
    230   OPK_FLAG_KILL = 1u << 0,
    231 } OpFlag;
    232 
    233 typedef enum CGLocalFlag {
    234   CG_LOCAL_FLAG_NONE = 0,
    235   CG_LOCAL_ADDR_TAKEN = 1u << 0,
    236   CG_LOCAL_MEMORY_REQUIRED = 1u << 1,
    237   /* A transient compiler temporary minted per-subexpression (api_alloc_temp_local
    238    * is its sole producer). Its frame home is recyclable: at a statement boundary
    239    * the value stack holds no live reference to it, so the single-pass backend
    240    * returns its slot to a free list and reuses it for the next statement's temps
    241    * (kit_cg_reclaim_temps). Declared locals/params never carry this flag and keep
    242    * their home for the whole function. */
    243   CG_LOCAL_TRANSIENT = 1u << 2,
    244 } CGLocalFlag;
    245 
    246 typedef struct CGLocalDesc {
    247   KitCgTypeId type;
    248   Sym name;
    249   SrcLoc loc;
    250   u32 size;
    251   u32 align;
    252   u32 flags; /* CGLocalFlag */
    253 } CGLocalDesc;
    254 
    255 typedef enum MemFlag {
    256   MF_NONE = 0,
    257   MF_VOLATILE = 1u << 0,
    258   MF_ATOMIC = 1u << 1,
    259   MF_RESTRICT = 1u << 2,
    260   MF_READONLY = 1u << 3,
    261   MF_WRITEONLY = 1u << 4,
    262   MF_UNALIGNED = 1u << 5,
    263   /* Sign-extending narrow load (the C frontend's widening-signed-load lever):
    264    * the value comes from a signed char/short/int lvalue whose only widening is a
    265    * sign-extension to a wider type, so a backend that honors this flag emits a
    266    * single sign-extending load (aa64 ldrsb/ldrsh into the X register, filling
    267    * the whole register) instead of a zero-extending load plus a CV_SEXT convert.
    268    * Non-bitfield integer loads only. A backend that does not honor it ignores
    269    * the bit and emits a plain load; the shared -O0 NDT only elides the following
    270    * CV_SEXT when the backend advertises NativeRegInfo.load_sext, so leaving
    271    * the convert in place stays correct there. */
    272   MF_SEXT_LOAD = 1u << 6,
    273 } MemFlag;
    274 
    275 typedef enum AliasKind {
    276   ALIAS_UNKNOWN,
    277   ALIAS_LOCAL,
    278   ALIAS_GLOBAL,
    279   ALIAS_PARAM,
    280   ALIAS_HEAP,
    281   ALIAS_STRING,
    282 } AliasKind;
    283 
    284 typedef struct AliasRoot {
    285   u8 kind; /* AliasKind */
    286   u8 pad[3];
    287   union {
    288     i32 local_id;
    289     ObjSymId global;
    290     u32 param_idx;
    291     Sym string_id;
    292   } v;
    293 } AliasRoot;
    294 
    295 typedef struct MemAccess {
    296   KitCgTypeId type; /* codegen object type accessed */
    297   u32 size;         /* ABI byte size of this access (storage-unit size for a
    298                      * bit-field) */
    299   u32 align;        /* known byte alignment; 0 means unknown */
    300   u16 flags;        /* MemFlag */
    301   u16 addr_space;
    302   /* Bit-field rider: when bf_width != 0 this access is a bit-field, so `load`
    303    * extracts (shift+mask+extend) and `store` inserts (read-modify-write) within
    304    * the storage unit described by {type,size}. The CgTarget impls translate
    305    * this to the physical NativeTarget bitfield_load/store (or the recorder IR
    306    * op); the semantic CgTarget no longer carries a separate bit-field method.
    307    */
    308   u16 bf_offset; /* target-endian bit offset within the storage unit */
    309   u16 bf_width;  /* 0 => not a bit-field access */
    310   u8 bf_signed;  /* signed extraction on load */
    311   u8 bf_pad[3];
    312   AliasRoot alias;
    313 } MemAccess;
    314 
    315 typedef struct ConstBytes {
    316   KitCgTypeId type;
    317   const u8* bytes; /* ABI representation, little/big endian per target */
    318   u32 size;
    319   u32 align;
    320 } ConstBytes;
    321 
    322 typedef struct AggregateAccess {
    323   KitCgTypeId type;
    324   u32 size;
    325   u32 align;
    326   MemAccess mem;
    327 } AggregateAccess;
    328 
    329 typedef struct BitFieldAccess {
    330   KitCgTypeId field_type;
    331   MemAccess storage;
    332   u32 storage_offset; /* byte offset from record base */
    333   u16 bit_offset;     /* target-endian bit offset within storage unit */
    334   u16 bit_width;      /* may be 0 for zero-width layout barriers */
    335   u8 signed_;
    336   u8 pad[3];
    337 } BitFieldAccess;
    338 
    339 /* Reconstruct the BitFieldAccess a CgTarget impl needs from the bit-field
    340  * MemAccess that rides the generic load/store (bf_width != 0). The storage unit
    341  * is {m.type, m.size}; the bit geometry is the bf_* rider. */
    342 static inline BitFieldAccess bf_from_mem(MemAccess m) {
    343   BitFieldAccess bf = {0};
    344   bf.field_type = m.type;
    345   bf.storage = m;
    346   bf.storage.bf_offset = 0;
    347   bf.storage.bf_width = 0;
    348   bf.storage.bf_signed = 0;
    349   bf.bit_offset = m.bf_offset;
    350   bf.bit_width = m.bf_width;
    351   bf.signed_ = m.bf_signed;
    352   return bf;
    353 }
    354 
    355 typedef struct Operand {
    356   u8 kind;
    357   u8 flags; /* OpFlag bitset; 0 except where a cg op sets a per-call-site hint */
    358   u8 pad[2];
    359   KitCgTypeId type;
    360   union {
    361     i64 imm;
    362     CGLocal local;
    363     struct {
    364       ObjSymId sym;
    365       i64 addend;
    366     } global;
    367     struct {
    368       CGLocal base;
    369       CGLocal index; /* CG_LOCAL_NONE when no index operand */
    370       u8 log2_scale; /* 0..3 -> 1/2/4/8 bytes; ignored when no index */
    371       i32 ofs;
    372     } ind;
    373   } v;
    374 } Operand;
    375 
    376 typedef struct CGParamDesc {
    377   u32 index;
    378   Sym name;
    379   KitCgTypeId type;
    380   u32 size;
    381   u32 align;
    382   u32 flags; /* CGLocalFlag */
    383   SrcLoc loc;
    384 } CGParamDesc;
    385 
    386 /* text_section_id and group_id are per-function so that -ffunction-sections,
    387  * __attribute__((section)) on functions, and COMDAT for C11 inline-with-
    388  * external-definition all work with no extra plumbing. Decl.section_id already
    389  * carries the user's request; CG/decl decides the section name policy
    390  * (default .text, vs .text.<sym> under -ffunction-sections, vs explicit
    391  * attribute). The backend just writes to the named section. */
    392 /* Phase 2 attribute-derived hints. The backends are free to ignore these;
    393  * they exist so the parser can communicate _Noreturn / __attribute__
    394  * info down to CG without forcing every backend to consult the Decl. */
    395 typedef enum CGFuncDescFlag {
    396   CGFD_NONE = 0,
    397   CGFD_NORETURN = 1u << 0,
    398 } CGFuncDescFlag;
    399 
    400 typedef struct CGFuncDesc {
    401   ObjSymId sym;
    402   ObjSecId text_section_id;
    403   ObjGroupId group_id; /* OBJ_GROUP_NONE if none */
    404   KitCgTypeId fn_type;
    405   KitCgTypeId result_type; /* KIT_CG_TYPE_NONE/void == no result */
    406   const CGParamDesc* params;
    407   u32 nparams;
    408   SrcLoc loc;
    409   u32 flags; /* CGFuncDescFlag */
    410   KitCgInlinePolicy inline_policy;
    411   u16 sym_bind; /* SymBind */
    412   u16 sym_kind; /* SymKind */
    413   u8 sym_vis;   /* SymVis */
    414   u8 atomize;
    415   u8 pad[2];
    416 } CGFuncDesc;
    417 
    418 typedef enum CGCallFlag {
    419   CG_CALL_NONE = 0,
    420   /* Sibling call. The target emits a tail-position call and does NOT emit a
    421    * return-style continuation. CG will not invoke target->ret afterwards.
    422    *
    423    * Realizability is verified before this flag is set: CG only sets it after
    424    * tail_call_unrealizable_reason() returns NULL for the same desc and call
    425    * state, so the target can emit the sibling call unconditionally. The
    426    * target may assert/compiler_panic if the flag is set on an unrealizable
    427    * desc, but that is an internal-consistency check — fallback and
    428    * diagnostics for unrealizable tail calls are CG's responsibility, not the
    429    * target's. */
    430   CG_CALL_TAIL = 1u << 0,
    431 } CGCallFlag;
    432 
    433 typedef struct CGCallDesc {
    434   KitCgTypeId fn_type;
    435   Operand callee;
    436   const CGLocal* args;
    437   CGLocal result; /* CG_LOCAL_NONE == void callee (no result) */
    438   u32 nargs;
    439   u16 flags;      /* CGCallFlag */
    440   u8 tail_policy; /* KitCgTailPolicy; meaningful when CG_CALL_TAIL is set.
    441                    * The opt recorder accepts every tail and preserves this so
    442                    * the replay can pick: emit tail (realizable), fall back to
    443                    * call+ret (ALLOWED), or diagnose (MUST). */
    444   u8 pad;
    445   KitCgInlinePolicy inline_policy;
    446   /* Bit i set => args[i] is a transient that is provably dead after this call
    447    * (no live value-stack reference). The -O0 NativeDirectTarget may then source
    448    * such an arg from its live register and drop it without writing back,
    449    * instead of the spill-to-home + reload round-trip; a live (clear-bit) arg is
    450    * spilled like any other live-across value. Bits >= 64 are 0 (treated as
    451    * live: correct, just unoptimized). Other backends ignore it. */
    452   u64 arg_dead_mask;
    453 } CGCallDesc;
    454 
    455 typedef u32 Label;
    456 #define LABEL_NONE 0
    457 
    458 typedef enum ScopeKind {
    459   SCOPE_BLOCK, /* break exits forward */
    460   SCOPE_LOOP,  /* break exits forward; continue uses explicit target */
    461 } ScopeKind;
    462 
    463 typedef u32 CGScope;
    464 #define CG_SCOPE_NONE 0u
    465 
    466 typedef struct CGScopeDesc {
    467   u8 kind; /* ScopeKind */
    468   u8 pad[3];
    469   Label break_label; /* explicit target for break; LABEL_NONE => target creates
    470                         one */
    471   Label continue_label;    /* explicit target for continue; LABEL_NONE for
    472                               non-loops */
    473   KitCgTypeId result_type; /* reserved for structured expression results */
    474 } CGScopeDesc;
    475 
    476 typedef struct AsmConstraint {
    477   const char* str;  /* GCC-style: "r", "=&r", "+m", "i", "0" ... */
    478   Sym name;         /* GCC `[name]` symbolic operand; 0 if absent */
    479   KitCgTypeId type; /* codegen type of the bound expression (output lvalue or
    480                        input rvalue). Drives type width for the binder.
    481                        NULL only for hand-built test constraints (binder
    482                        falls back to a 64-bit int default). */
    483   Sym reg;          /* Explicit hard-register name ("r10"/"x8"/...) this operand
    484                        must occupy — a GNU local register variable bound as an
    485                        operand; 0 = unconstrained. Only the target's register
    486                        file resolves the name to a physical register. */
    487   u8 dir;           /* KitCgAsmDir */
    488   u8 pad[3];
    489 } AsmConstraint;
    490 
    491 typedef struct CGSwitchCase {
    492   /* Bit pattern matched against the selector; interpreted using
    493    * selector_type's width and signedness (signed comparison uses
    494    * sign-extension to selector_type's width). */
    495   u64 value;
    496   Label label;
    497 } CGSwitchCase;
    498 
    499 typedef struct CGSwitchDesc {
    500   Operand selector; /* OPK_LOCAL or OPK_IMM */
    501   KitCgTypeId selector_type;
    502   Label default_label; /* LABEL_NONE means "fall through past the switch" */
    503   const CGSwitchCase* cases;
    504   u32 ncases;
    505   u8 hint;      /* KitCgSwitchHint */
    506   u8 opt_level; /* 0 direct, 1 optimized; public 2 is normalized to 1 */
    507   u8 pad[2];
    508 } CGSwitchDesc;
    509 
    510 typedef struct CGLocalStaticDataDesc {
    511   ObjSymId sym;
    512   KitCgTypeId type;
    513   KitCgDataDefAttrs attrs;
    514   u32 align;
    515 } CGLocalStaticDataDesc;
    516 
    517 typedef enum CGDebugLocKind {
    518   CG_DEBUG_LOC_NONE,
    519   CG_DEBUG_LOC_FRAME,
    520   CG_DEBUG_LOC_REG,
    521   CG_DEBUG_LOC_GLOBAL,
    522 } CGDebugLocKind;
    523 
    524 typedef struct CGDebugLoc {
    525   u8 kind; /* CGDebugLocKind */
    526   u8 pad[3];
    527   union {
    528     /* Offset in the same target-defined frame-base coordinate system that the
    529      * target/debugger pair uses to materialize frame-relative variables. CG
    530      * treats this as opaque target data and only maps it into the debug
    531      * producer's generic frame-location form. */
    532     i32 frame_ofs;
    533     u32 reg;
    534     ObjSymId global;
    535   } v;
    536 } CGDebugLoc;
    537 #endif