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