kit

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

cgtarget.h (19626B)


      1 #ifndef KIT_CG_CGTARGET_H
      2 #define KIT_CG_CGTARGET_H
      3 
      4 #include <kit/cg.h>
      5 #include <kit/compile.h>
      6 
      7 #include "core/core.h"
      8 #include "obj/obj.h"
      9 #include "cg/cgir.h"
     10 
     11 /* Forward-declared (same as arch/mc.h) so a CgTarget can carry an optional
     12  * Debug producer without this header depending on debug/debug.h. */
     13 typedef struct Debug Debug;
     14 
     15 typedef struct CgFinishPolicy {
     16   u8 output_kind;          /* KitCgOutputKind */
     17   u8 interposition_policy; /* KitCgInterpositionPolicy */
     18   u8 pad[2];
     19   const ObjSymId* preserved_symbols;
     20   u32 npreserved_symbols;
     21 } CgFinishPolicy;
     22 
     23 typedef struct CgTarget CgTarget;
     24 struct CgTarget {
     25   /* Typed IR lowering context. Subclasses extend. */
     26   Compiler* c;
     27   ObjBuilder* obj;
     28 
     29   /* Optional DWARF producer, created by the backend's `make` when
     30    * opts->debug_info is set (else NULL). The session reads this back into
     31    * its own g->debug to drive func/line/emit; the backend's MCEmitter
     32    * shares the same object for line-row emission. */
     33   Debug* debug;
     34 
     35   CgFinishPolicy finish_policy;
     36 
     37   /* The target represents values as untyped machine words (registers/stack
     38    * slots), so a same-width, same-register-class bitcast — pointer<->intptr,
     39    * pointer<->pointer — moves no bits and the cg layer may elide it by retyping
     40    * the value in place (api_cg_convert_kind). Set by the single-pass native
     41    * backend (NativeDirectTarget). Left 0 by the typed backends — the C-source
     42    * target needs the explicit cast to emit valid C, and the recording target
     43    * keeps the convert so the optimizer's own copy-propagation owns the call. */
     44   u8 untyped_values;
     45 
     46   /* ---- function lifecycle ---- */
     47   void (*func_begin)(CgTarget*, const CGFuncDesc*);
     48   void (*func_end)(CgTarget*);
     49 
     50   /* Symbol-aliasing hook. Optional (may be NULL). cg invokes this from
     51    * kit_cg_alias after the obj symbol-table mirror is wired so the
     52    * backend can emit any out-of-band representation it needs — e.g. the
     53    * C-source target writes
     54    *   `T alias_sym(...) __attribute__((alias("target")));`
     55    * because the alias relationship isn't expressible by sharing a
     56    * (section, value) pair the way a relocatable object can. Native
     57    * machine-code backends don't need this hook because obj_symbol_define
     58    * already aliases the bytes. `type` is the alias's CG type (function
     59    * or object), needed by the C target to render the prototype. */
     60   void (*alias)(CgTarget*, ObjSymId alias_sym, ObjSymId target_sym,
     61                 KitCgTypeId type);
     62 
     63   /* ---- locals ---- */
     64   CGLocal (*local)(CgTarget*, const CGLocalDesc*);
     65   void (*local_addr)(CgTarget*, Operand dst, const CGLocalDesc*, CGLocal);
     66   CGLocal (*param)(CgTarget*, const CGParamDesc*);
     67   /* Optional debug-info query after function frame layout is finalized.
     68    * Targets return a target-authored location for semantic local storage; CG
     69    * owns deciding which source locals/params get emitted and translating the
     70    * target-neutral CGDebugLoc into the debug producer API. */
     71   int (*local_debug_loc)(CgTarget*, CGLocal, CGDebugLoc*);
     72 
     73   /* Optional. Reclaim the frame homes of transient compiler temporaries
     74    * (CG_LOCAL_TRANSIENT) that are now dead. cg calls this only at a statement
     75    * boundary where the value stack is empty (kit_cg_reclaim_temps), so no value
     76    * references any temp; the single-pass backend drops their cache entries and
     77    * recycles their slots, bounding the -O0 frame. NULL on the recording/C-source
     78    * targets, which own frame layout differently. */
     79   void (*reclaim_temps)(CgTarget*);
     80 
     81   /* ---- labels and control flow ---- */
     82   Label (*label_new)(CgTarget*);
     83   void (*label_place)(CgTarget*, Label);
     84   void (*jump)(CgTarget*, Label);
     85   /* Fused compare-and-branch. cg's preferred form: avoids materializing 0/1
     86    * for a normal `if (a < b)`. For an arbitrary i1 in a local, callers
     87    * synthesize cmp_branch(CMP_NE, val, IMM_ZERO, label). */
     88   void (*cmp_branch)(CgTarget*, CmpOp, Operand a, Operand b, Label);
     89 
     90   /* Structured switch dispatch.
     91    *
     92    * Optional: when NULL, cg's shared `cg_lower_switch_default` runs and
     93    * lowers in terms of cmp_branch / jump / indirect_branch / data ops —
     94    * the path every native arch uses. Backends override switch_ only when
     95    * they can express the construct natively: the C-source target emits
     96    * `switch (val) { case V: goto L_V; ... default: goto L_def; }`; a
     97    * future WASM target would emit `br_table`.
     98    *
     99    * The descriptor carries the full structured form (selector + paired
    100    * cases + default + frontend hint); density policy lives in
    101    * cg_lower_switch_default. */
    102   void (*switch_)(CgTarget*, const CGSwitchDesc*);
    103 
    104   /* Optional. When non-NULL and it returns 0, the target cannot realize a
    105    * jump-table dispatch built from a rodata table of code-label addresses
    106    * (Wasm: linear memory holds no code addresses and there is no computed
    107    * branch). kit_cg_switch then routes dense/forced-table plans through
    108    * `switch_` (e.g. br_table) instead of the label-table + indirect_branch
    109    * lowering. NULL means the label-table path is supported (every native
    110    * arch). */
    111   int (*supports_label_table)(CgTarget*);
    112 
    113   /* Indirect branch primitive: transfer control to the address in
    114    * `addr` (an OPK_LOCAL holding a function-local label address).
    115    *
    116    * Required on every native arch and used by:
    117    *   - kit_cg_computed_goto for direct-threaded dispatch
    118    *   - opt-level jump-table lowerings of IR_SWITCH (when implemented)
    119    *
    120    * `valid_targets[0..ntargets)` is the closed set of labels the address
    121    * can resolve to. Backends use it for branch-target hardening (BTI,
    122    * PAC, x86 CFG, IBT) and opt uses it to build the CFG; opt requires
    123    * ntargets > 0. */
    124   void (*indirect_branch)(CgTarget*, Operand addr, const Label* valid_targets,
    125                           u32 ntargets);
    126 
    127   /* Materialize the runtime address of a function-local label into
    128    * `dst`. The label must already exist (label_new); it does not
    129    * need to be placed yet. Backends emit the target's relative address
    130    * materialization:
    131    * x86_64 `lea L(%rip), %r`, aarch64 `adr X, L`, riscv `auipc/addi`.
    132    *
    133    * The resulting pointer is a function-local label address (per the
    134    * public kit_cg_push_label_addr contract) and must only be consumed
    135    * by indirect_branch inside the defining function's activation. */
    136   void (*load_label_addr)(CgTarget*, Operand dst, Label label);
    137 
    138   /* Optional source-backend hook for function-local static data definitions
    139    * that need function label scope, currently used for C `&&label`
    140    * dispatch-table initializers. Returning non-zero from begin means the
    141    * target consumes bytes/zeros/label addresses until end; ordinary object
    142    * data emission is skipped for that definition. */
    143   int (*local_static_data_begin)(CgTarget*, const CGLocalStaticDataDesc*);
    144   /* data == NULL means append len zero bytes. */
    145   void (*local_static_data_write)(CgTarget*, const u8* data, u64 len);
    146   void (*local_static_data_label_addr)(CgTarget*, Label target, i64 addend,
    147                                        u32 width, u32 address_space);
    148   void (*local_static_data_end)(CgTarget*);
    149 
    150   /* Optional. When non-NULL, kit_cg_data_label_addr panics with the
    151    * returned target-specific message before reaching object-data emission. Lets
    152    * targets that cannot resolve function-local label addresses in
    153    * static-data initializers (e.g. the Wasm backend) fail with a
    154    * recognizable, target-prefixed diagnostic. The returned string must remain
    155    * valid for the lifetime of the panic call (string literals are typical). */
    156   const char* (*data_label_addr_unsupported_msg)(CgTarget*);
    157 
    158   /* ---- structured control flow ----
    159    * Mirrors CG's scope ops. CG passes explicit break/continue targets so C
    160    * `for` continues can land on the increment expression rather than the loop
    161    * header. Real backends shim these onto label_new/label_place/jump.
    162    * The WASM backend consumes them natively to emit block/loop with
    163    * structurally-bounded br targets, which is what gives WASM its CFI.
    164    *
    165    * `result_type` is reserved for structured expression results on WASM (NULL
    166    * for the statement case used by C); other backends ignore it. */
    167   CGScope (*scope_begin)(CgTarget*, const CGScopeDesc*);
    168   void (*scope_end)(CgTarget*, CGScope);
    169   void (*break_to)(CgTarget*, CGScope);
    170   void (*continue_to)(CgTarget*, CGScope);
    171 
    172   /* ---- data movement (split, no overloading) ---- */
    173   void (*load_imm)(CgTarget*, Operand dst /*LOCAL*/, i64 imm);
    174   void (*load_const)(CgTarget*, Operand dst /*LOCAL*/, ConstBytes);
    175   void (*copy)(CgTarget*, Operand dst /*LOCAL*/, Operand src /*LOCAL*/);
    176   void (*load)(CgTarget*, Operand dst /*LOCAL*/,
    177                Operand addr /*LOCAL|GLOBAL|INDIRECT*/, MemAccess);
    178   void (*store)(CgTarget*, Operand addr /*LOCAL|GLOBAL|INDIRECT*/,
    179                 Operand src /*LOCAL|IMM*/, MemAccess);
    180   void (*addr_of)(CgTarget*, Operand dst /*LOCAL*/,
    181                   Operand lv /*LOCAL|GLOBAL|INDIRECT*/);
    182   /* Materializes the address of a thread-local symbol into `dst`. Distinct
    183    * from addr_of because TLS resolution can be a multi-instruction sequence
    184    * or a runtime call (e.g. GD model), not a cheap addressing mode. The
    185    * backend chooses the TLS model (LE/IE/LD/GD) from c->target and the
    186    * symbol's visibility. Subsequent accesses go through OPK_INDIRECT on the
    187    * resulting pointer; this lets opt hoist the materialization via LICM. */
    188   void (*tls_addr_of)(CgTarget*, Operand dst /*LOCAL*/, ObjSymId sym,
    189                       i64 addend);
    190   void (*copy_bytes)(CgTarget*, Operand dst_addr, Operand src_addr,
    191                      AggregateAccess);
    192   void (*set_bytes)(CgTarget*, Operand dst_addr, Operand byte_value,
    193                     AggregateAccess);
    194   /* Bit-fields are not a separate CgTarget method: a bit-field load/store rides
    195    * the generic `load`/`store` above with a bit-field MemAccess (bf_width !=
    196    * 0). Each CgTarget impl translates it (NativeDirectTarget -> NativeTarget's
    197    * bitfield_load/store; IrRecorder -> CG_IR_BITFIELD_LOAD/STORE). */
    198 
    199   /* ---- arithmetic, compare, convert ----
    200    * binop/unop/cmp accept OPK_LOCAL or OPK_IMM in source operand positions
    201    * (`a`, `b`); `dst` is always OPK_LOCAL. The backend chooses between an
    202    * imm-form encoding and materializing the literal into a scratch
    203    * local based on whether the value fits the instruction's imm
    204    * field. FP binops and UO_FNEG require local sources — FP literals reach the
    205    * value stack through load_const into OPK_LOCAL. cg and opt's machinize/emit
    206    * both rely on this contract to pass small constants through without
    207    * burning a value-stack local on materialization. */
    208   void (*binop)(CgTarget*, BinOp, Operand dst /*LOCAL*/,
    209                 Operand a /*LOCAL|IMM*/, Operand b /*LOCAL|IMM*/);
    210   void (*unop)(CgTarget*, UnOp, Operand dst /*LOCAL*/, Operand a /*LOCAL|IMM*/);
    211   void (*cmp)(CgTarget*, CmpOp, Operand dst /*LOCAL*/, Operand a /*LOCAL|IMM*/,
    212               Operand b /*LOCAL|IMM*/); /* materialize 0/1 */
    213   void (*convert)(CgTarget*, ConvKind, Operand dst, Operand src);
    214 
    215   /* ---- calls / return ----
    216    * CGCallDesc carries the type-checked signature, semantic callee operand,
    217    * local arguments, and local result destinations. The semantic target does
    218    * not expose calling-convention lowering; native targets derive physical
    219    * argument/return placement from fn_type and local metadata internally.
    220    * `result` is the single local destination, or CG_LOCAL_NONE for void. */
    221   void (*call)(CgTarget*, const CGCallDesc*);
    222   /* Pure query: can `d` be emitted as a sibling (tail) call on this target,
    223    * given the current target state? Returns NULL if yes; otherwise a short,
    224    * static, human-readable string naming the blocker, used verbatim in the
    225    * musttail diagnostic. Must not emit code and must not abort.
    226    *
    227    * Realizable means the target can transfer control to the callee while
    228    * preserving the source-level call/return semantics of this function. CG
    229    * verifies type compatibility before setting CG_CALL_TAIL; target-specific
    230    * blockers such as variadic lowering, frame teardown constraints, or
    231    * unavailable tail-call support are reported here.
    232    *
    233    * CG owns the tail policy: it calls this first and only sets CG_CALL_TAIL
    234    * when it returns NULL, so a NULL result must guarantee a later call() with
    235    * CG_CALL_TAIL can emit the sibling call. May itself be NULL, meaning the
    236    * target supports no tail calls at all. */
    237   const char* (*tail_call_unrealizable_reason)(CgTarget*, const CGCallDesc*);
    238   /* Return from the function. `value` is the single returned local, or
    239    * CG_LOCAL_NONE for a void return. */
    240   void (*ret)(CgTarget*, CGLocal value);
    241   /* Control terminator marking statically-unreachable code (the C
    242    * __builtin_unreachable point). Like ret/jump it ends the current basic
    243    * block: no fall-through successor is implied. Backends typically emit a
    244    * trap instruction (brk/ud2/ebreak), a Wasm `unreachable`, or a
    245    * `__builtin_unreachable()` in the C-source target; an interpreter faults.
    246    * Distinct from INTRIN_TRAP, which is an expression-level intrinsic that
    247    * does not terminate the block. */
    248   void (*unreachable)(CgTarget*);
    249 
    250   /* ---- alloca ----
    251    * Dynamic stack allocation. `size` is i64 bytes; `align` is the required
    252    * alignment of the returned pointer. Backend grows the (linear-memory or
    253    * native) shadow stack, returns the pointer in `dst`. v1 only emits this
    254    * via __builtin_alloca; C VLAs are not parsed (__STDC_NO_VLA__). */
    255   void (*alloca_)(CgTarget*, Operand dst /*LOCAL*/, Operand size, u32 align);
    256 
    257   /* ---- variadics ----
    258    * va_list type is per-arch (defined in <stdarg.h>); these methods
    259    * implement the four C macros after builtin substitution. ap is always
    260    * passed as &ap. */
    261   void (*va_start_)(CgTarget*, Operand ap_addr);
    262   void (*va_arg_)(CgTarget*, Operand dst /*LOCAL*/, Operand ap_addr,
    263                   KitCgTypeId t);
    264   void (*va_end_)(CgTarget*, Operand ap_addr);
    265   void (*va_copy_)(CgTarget*, Operand dst_ap_addr, Operand src_ap_addr);
    266 
    267   /* ---- atomics ---- */
    268   void (*atomic_load)(CgTarget*, Operand dst /*LOCAL*/, Operand addr, MemAccess,
    269                       KitCgMemOrder);
    270   void (*atomic_store)(CgTarget*, Operand addr, Operand src, MemAccess,
    271                        KitCgMemOrder);
    272   void (*atomic_rmw)(CgTarget*, KitCgAtomicOp,
    273                      Operand dst /*LOCAL: prior value*/, Operand addr,
    274                      Operand val, MemAccess, KitCgMemOrder);
    275   void (*atomic_cas)(CgTarget*, Operand prior /*LOCAL*/,
    276                      Operand ok /*LOCAL, i1*/, Operand addr, Operand expected,
    277                      Operand desired, MemAccess, KitCgMemOrder success,
    278                      KitCgMemOrder failure);
    279   void (*fence)(CgTarget*, KitCgMemOrder);
    280 
    281   /* ---- compiler intrinsics ----
    282    * Typed dispatch for builtins whose lowering is backend-relevant
    283    * (inline-vs-libcall, inline sequence selection) or whose semantics opt
    284    * cares about (hint pattern matching, exhaustiveness). The IR carries
    285    * IR_INTRINSIC + IRIntrinAux.kind; the wrapped target receives the same call
    286    * at lowering time with materialized operands.
    287    *
    288    * Operand shapes by IntrinKind:
    289    *   POPCOUNT/CTZ/CLZ/BSWAP*  : dsts[0] LOCAL result; args[0] LOCAL input
    290    *   MEMCPY/MEMMOVE           : dsts none; args = (dst_addr, src_addr, n)
    291    *   MEMSET                   : dsts none; args = (dst_addr, byte, n)
    292    *   PREFETCH                 : dsts none; args = (addr [, rw [, locality]])
    293    *   ASSUME_ALIGNED           : dsts[0] LOCAL; args = (ptr, align [, offset])
    294    *   EXPECT                   : dsts[0] LOCAL; args = (val, expected)
    295    *   TRAP                     : dsts none; args none
    296    *   SETJMP                   : dsts[0] LOCAL i32 result; args = (&buf)
    297    *   LONGJMP                  : dsts none; args = (&buf, val); no return
    298    *   ADD/SUB/MUL_OVERFLOW     : dsts[0] LOCAL result, dsts[1] LOCAL i1
    299    * overflow; args = (a, b)
    300    *
    301    * Backends that lack an inline sequence for a given kind may emit a
    302    * normal IR_CALL-shaped sequence to a runtime entry (e.g. memcpy) — the
    303    * IR records intent, the backend chooses mechanism. Hint kinds may be
    304    * lowered as no-ops where the arch has nothing to emit. */
    305   void (*intrinsic)(CgTarget*, IntrinKind, Operand* dsts, u32 ndst,
    306                     const Operand* args, u32 narg);
    307 
    308   /* ---- inline asm ----
    309    * Per-arch constraint binding + template assembly, packaged as one block.
    310    *   ins[i] are pre-evaluated input operands.
    311    *   out_ops[i] is filled by the arch with the location holding the result
    312    *     for outs[i]; the caller (cg) reads them out after the call.
    313    *   "=&r" early-clobber outputs must be allocated disjoint from any input.
    314    * opt_cgtarget records this as a single IR_ASM_BLOCK; the wrapped target
    315    * receives the same call at lowering time with materialized operands. */
    316   int (*asm_is_reg_constraint)(CgTarget*, const char* constraint);
    317   void (*asm_block)(CgTarget*, const char* tmpl, const AsmConstraint* outs,
    318                     u32 nout, Operand* out_ops, const AsmConstraint* ins,
    319                     u32 nin, const Operand* in_ops, const Sym* clobbers,
    320                     u32 nclob, u32 clobber_abi_sets);
    321 
    322   /* Optional: handle a top-level `__asm__("...")` block (file scope, not
    323    * inside a function). Backends that leave this NULL fall back to the
    324    * generic asm-parser path through KitCg.mc. Wasm overrides this to
    325    * diagnose-and-fail since the wasm module has no native asm parser. */
    326   void (*file_scope_asm)(CgTarget*, const char* src, size_t len);
    327 
    328   /* ---- source-location tracking ----
    329    * Sets the SrcLoc inherited by subsequent emit-side calls (binop/load/...).
    330    * opt_cgtarget stamps it on every recorded Inst. Sticky until the next
    331    * set_loc. */
    332   void (*set_loc)(CgTarget*, SrcLoc);
    333 
    334   /* ---- end-of-TU hook ----
    335    * No-op for plain target CGTargets. opt_cgtarget runs cross-function passes
    336    * (inlining + cleanup) and lowers all buffered IR functions into the
    337    * wrapped target CgTarget. Drivers must call this after the last func_end and
    338    * before reading from `obj` or calling debug_emit. */
    339   void (*finalize)(CgTarget*);
    340 
    341   void (*destroy)(CgTarget*);
    342 };
    343 
    344 /* Shared switch lowering. cg's kit_cg_switch installs this as the
    345  * default target->switch_ behavior; opt's pass_emit calls it when
    346  * replaying IR_SWITCH against a backend that doesn't override switch_.
    347  * Emits a cmp-and-branch chain over (target->cmp_branch + target->jump)
    348  * — fast at -O0 and the input shape an opt-level jump-table rewrite
    349  * starts from. */
    350 void cg_lower_switch_default(CgTarget* t, const CGSwitchDesc* desc);
    351 
    352 CgTarget* cgtarget_new(Compiler*, ObjBuilder*);
    353 void cgtarget_set_finish_policy(CgTarget*, const CgFinishPolicy*);
    354 void cgtarget_finalize(CgTarget*);
    355 void cgtarget_free(CgTarget*);
    356 
    357 /* A CGBackend is the unit the registry hands out: "give me a CgTarget for
    358  * this Compiler + ObjBuilder + emit options." */
    359 typedef struct CGBackend {
    360   const char* name;
    361   CgTarget* (*make)(Compiler*, ObjBuilder*, const KitCodeOptions*);
    362 } CGBackend;
    363 
    364 /* Pick the right CGBackend for a session given the compiler's target arch
    365  * and the per-emit CodeOptions. Returns NULL when no backend in this build can
    366  * serve the request. */
    367 const CGBackend* cg_backend_for_session(const Compiler*, const KitCodeOptions*);
    368 
    369 /* Human-readable arch name for diagnostics, independent of which backends
    370  * are compiled in (so it can name a target whose backend is disabled). */
    371 const char* arch_kind_name(KitArchKind);
    372 
    373 #endif