cg_adapter.h (11965B)
1 #ifndef KIT_PARSE_CG_ADAPTER_H 2 #define KIT_PARSE_CG_ADAPTER_H 3 4 /* cg_adapter.h — the C parser <-> codegen seam. 5 * 6 * Declares the `pcg_*` helpers (implemented in cg_adapter.c) plus the 7 * C-frontend types they operate on. Every `pcg_*` call keeps the parser's 8 * typed shadow stack (cg_slot_stack in struct Parser, one PcgSlot per value) 9 * in lockstep with the public CG stack and folds C lvalue chains into 10 * single effective-address memops; the parser never calls stack-affecting 11 * `kit_cg_*` ops directly. See doc/FRONTENDS.md for the design. */ 12 13 #include <kit/cg.h> 14 15 #include "c_support.h" 16 #include "type/type.h" 17 18 typedef KitCg CG; 19 typedef KitCgLabel CGLabel; 20 typedef KitCgLocal FrameSlot; 21 22 #define FRAME_SLOT_NONE KIT_CG_LOCAL_NONE 23 #define OBJ_GROUP_NONE 0u 24 25 /* Lvalue auxiliary state, one per PcgSlot (the `aux` field). 26 * 27 * The C parser tracks one logical "C-language value" per stack slot. When that 28 * slot is a C-language lvalue (PCG_VALUE_LVALUE), the aux below records the 29 * pending effective-address modifiers and bit-field metadata that the next 30 * load / store / addr will fold onto the CG memop. 31 * 32 * Lvalue chains (`s.f`, `a[i].g`, etc.) accumulate into this aux instead of 33 * emitting per-step CG ops: there is no CG-level `field` / `index` / 34 * `addr_offset` op anymore. Field offsets bump `offset`; subscripts set 35 * `scale` and leave the evaluated index value on the CG stack just above the 36 * lvalue base. Bit-field selections fill `bit_*`. The aux is consumed by the 37 * very next pcg_load / pcg_store / pcg_addr that crosses the slot. 38 * 39 * `base_kind` records what the CG-stack base under this lvalue actually is — 40 * either an OPK_LOCAL produced by push_local (PCG_LV_BASE_LOCAL) or a 41 * pointer rvalue from push_symbol_addr, push_local_addr, dereference, or 42 * pointer arithmetic (PCG_LV_BASE_POINTER_RV). Stream A's CG-side memops 43 * accept either shape uniformly; pcg_addr uses the distinction to decide 44 * whether to emit kit_cg_addr or treat the base as already-a-pointer. */ 45 typedef enum PcgLvBaseKind { 46 PCG_LV_BASE_LOCAL = 0, 47 PCG_LV_BASE_POINTER_RV = 1, 48 } PcgLvBaseKind; 49 50 typedef struct PcgLvAux { 51 i64 offset; 52 u32 scale; 53 u16 bit_offset; 54 u16 bit_width; 55 u32 storage_size; 56 u8 bit_signed; 57 u8 base_kind; /* PcgLvBaseKind */ 58 u8 is_subobject; /* lvalue is a member/element of a larger CG object */ 59 u8 pad[5]; 60 } PcgLvAux; 61 62 /* One shadow-stack slot: the CG type of a logical C value plus its parser-side 63 * value flags (PCG_VALUE_*) and lvalue aux, kept in lockstep with the CG value 64 * stack. The parser pushes/dups/swaps/rotates whole slots. 65 * 66 * `cg_id` is the lowered KitCgTypeId of `type`, stamped together with `type` 67 * (see pcg_slot_set_type) so the hot pcg_mem / pcg_load queries read it directly 68 * instead of re-crossing the Type*->CgId bridge via type_cg_id_in_pool on every 69 * operand. KIT_CG_TYPE_NONE doubles as "type is NULL or not yet lowered"; the 70 * lazy filler (pcg_slot_cg_id) lowers on first read so it tracks any later 71 * record completion exactly like a direct type_cg_id_in_pool call would. cg_id 72 * is a pure function of `type`, so whole-slot copies (dup/swap/rot3) carry it. */ 73 typedef struct PcgSlot { 74 const Type* type; 75 PcgLvAux aux; 76 KitCgTypeId cg_id; 77 u8 flags; 78 } PcgSlot; 79 80 typedef enum BinOp { 81 BO_IADD, 82 BO_ISUB, 83 BO_IMUL, 84 BO_SDIV, 85 BO_UDIV, 86 BO_SREM, 87 BO_UREM, 88 BO_FADD, 89 BO_FSUB, 90 BO_FMUL, 91 BO_FDIV, 92 BO_AND, 93 BO_OR, 94 BO_XOR, 95 BO_SHL, 96 BO_SHR_S, 97 BO_SHR_U, 98 } BinOp; 99 100 typedef enum UnOp { 101 UO_NEG, 102 UO_NOT, 103 UO_BNOT, 104 } UnOp; 105 106 /* Compare markers used by the C parser. The integer block plus the four 107 * relational FP markers (CMP_LT_F..CMP_GE_F) cover the bare C operators; the 108 * latter map to the *ordered* FP predicates in pcg_fp_cmp. The trailing 109 * ordered/unordered block mirrors the global CmpOp / public KitCgFpCmpOp so 110 * the C99 comparison builtins (islessgreater -> CMP_ONE_F) and any 111 * negation-driven unordered duals are reachable from the frontend 1:1. */ 112 typedef enum CmpOp { 113 CMP_EQ, 114 CMP_NE, 115 CMP_LT_S, 116 CMP_LE_S, 117 CMP_GT_S, 118 CMP_GE_S, 119 CMP_LT_U, 120 CMP_LE_U, 121 CMP_GT_U, 122 CMP_GE_U, 123 /* Relational FP operator markers (bare `< <= > >=` on floats) -> ordered. */ 124 CMP_LT_F, 125 CMP_LE_F, 126 CMP_GT_F, 127 CMP_GE_F, 128 /* Ordered FP relationals (NaN -> false). */ 129 CMP_OEQ_F, 130 CMP_ONE_F, 131 CMP_OLT_F, 132 CMP_OLE_F, 133 CMP_OGT_F, 134 CMP_OGE_F, 135 /* Unordered FP relationals (NaN -> true). */ 136 CMP_UEQ_F, 137 CMP_UNE_F, 138 CMP_ULT_F, 139 CMP_ULE_F, 140 CMP_UGT_F, 141 CMP_UGE_F, 142 } CmpOp; 143 144 typedef enum AtomicOp { 145 AO_XCHG, 146 AO_ADD, 147 AO_SUB, 148 AO_AND, 149 AO_OR, 150 AO_XOR, 151 AO_NAND, 152 } AtomicOp; 153 154 typedef enum MemOrder { 155 MO_RELAXED, 156 MO_CONSUME, 157 MO_ACQUIRE, 158 MO_RELEASE, 159 MO_ACQ_REL, 160 MO_SEQ_CST, 161 } MemOrder; 162 163 typedef enum IntrinKind { 164 INTRIN_NONE = 0, 165 INTRIN_POPCOUNT, 166 INTRIN_CTZ, 167 INTRIN_CLZ, 168 INTRIN_MEMCPY, 169 INTRIN_MEMMOVE, 170 INTRIN_MEMSET, 171 INTRIN_PREFETCH, 172 INTRIN_ASSUME_ALIGNED, 173 INTRIN_EXPECT, 174 INTRIN_UNREACHABLE, 175 INTRIN_TRAP, 176 INTRIN_SETJMP, 177 INTRIN_LONGJMP, 178 INTRIN_SADD_OVERFLOW, 179 INTRIN_UADD_OVERFLOW, 180 INTRIN_SSUB_OVERFLOW, 181 INTRIN_USUB_OVERFLOW, 182 INTRIN_SMUL_OVERFLOW, 183 INTRIN_UMUL_OVERFLOW, 184 } IntrinKind; 185 186 typedef enum AsmDir { ASM_IN, ASM_OUT, ASM_INOUT } AsmDir; 187 188 typedef struct AsmConstraint { 189 const char* str; 190 Sym name; 191 const Type* type; 192 Sym reg; /* hard-register name for a GNU local register variable; 0 = none */ 193 u8 dir; 194 u8 pad[3]; 195 } AsmConstraint; 196 197 typedef enum FrameSlotKind { 198 FS_LOCAL, 199 FS_PARAM, 200 FS_SPILL, 201 FS_SRET, 202 FS_ALLOCA, 203 } FrameSlotKind; 204 205 typedef enum FrameSlotFlag { 206 FSF_NONE = 0, 207 FSF_ADDR_TAKEN = 1u << 0, 208 FSF_VOLATILE = 1u << 1, 209 } FrameSlotFlag; 210 211 typedef struct FrameSlotDesc { 212 const Type* type; 213 Sym name; 214 SrcLoc loc; 215 u32 size; 216 u32 align; 217 u8 kind; 218 u8 pad; 219 u16 flags; 220 } FrameSlotDesc; 221 222 typedef struct CGParamDesc { 223 u32 index; 224 Sym name; 225 const Type* type; 226 FrameSlot slot; 227 const void* abi; 228 const void* incoming; 229 u32 nincoming; 230 SrcLoc loc; 231 } CGParamDesc; 232 233 typedef enum CGFuncDescFlag { 234 CGFD_NONE = 0, 235 CGFD_NORETURN = 1u << 0, 236 } CGFuncDescFlag; 237 238 typedef struct CGFuncDesc { 239 ObjSymId sym; 240 ObjSecId text_section_id; 241 u32 group_id; 242 const Type* fn_type; 243 const void* abi; 244 const CGParamDesc* params; 245 u32 nparams; 246 SrcLoc loc; 247 u32 flags; 248 KitCgInlinePolicy inline_policy; 249 } CGFuncDesc; 250 251 typedef struct Parser Parser; 252 253 KitCgTypeId pcg_tid(Parser*, const Type*); 254 KitCgMemAccess pcg_mem(Parser*, const Type*); 255 const Type* pcg_top_type(Parser*); 256 const Type* pcg_top2_type(Parser*); 257 void pcg_retag_top(Parser*, const Type*); 258 void pcg_retag_keep_flags(Parser*, u32 depth, const Type*); 259 void pcg_push_type(Parser*, const Type*); 260 void pcg_drop_type(Parser*); 261 void pcg_dup_type(Parser*); 262 void pcg_swap_type(Parser*); 263 /* Structural CG ops that also mirror onto the typed shadow stack. */ 264 void pcg_dup(Parser*); 265 void pcg_swap(Parser*); 266 void pcg_drop(Parser*); 267 /* Reclaim dead compiler-temp frame slots at a statement boundary; safe because 268 * kit_cg_reclaim_temps acts only when the CG value stack is empty (g->sp == 0). */ 269 void pcg_reclaim_temps(Parser*); 270 int pcg_top_is_bitfield(Parser*); 271 void pcg_set_top_bitfield(Parser*); 272 int pcg_top_is_register(Parser*); 273 void pcg_set_top_register(Parser*); 274 int pcg_top_is_lvalue(Parser*); 275 int pcg_top_is_modifiable_lvalue(Parser*); 276 int pcg_top_is_null_ptr_const(Parser*); 277 void pcg_set_top_lvalue(Parser*); 278 void pcg_rot3_type(Parser*); 279 int pcg_emit_enabled(Parser*); 280 void pcg_codegen_suppress_push(Parser*); 281 void pcg_codegen_suppress_pop(Parser*); 282 283 KitCgIntBinOp pcg_int_binop(BinOp); 284 KitCgFpBinOp pcg_fp_binop(BinOp); 285 KitCgIntCmpOp pcg_int_cmp(CmpOp); 286 KitCgFpCmpOp pcg_fp_cmp(CmpOp); 287 KitCgAtomicOp pcg_atomic_op(AtomicOp); 288 KitCgMemOrder pcg_mem_order(MemOrder); 289 int pcg_type_is_fp(const Type*); 290 int pcg_type_is_signed(const Type*); 291 292 FrameSlot pcg_local(Parser*, const FrameSlotDesc*); 293 FrameSlot pcg_param_slot(Parser*, u32, const FrameSlotDesc*); 294 void pcg_param(Parser*, const CGParamDesc*); 295 void pcg_func_begin(Parser*, const CGFuncDesc*); 296 void pcg_func_end(Parser*); 297 void pcg_set_loc(Parser*, SrcLoc); 298 void pcg_push_int(Parser*, i64, const Type*); 299 void pcg_push_float(Parser*, double, const Type*); 300 void pcg_push_local_typed(Parser*, FrameSlot, const Type*); 301 void pcg_push_global(Parser*, ObjSymId, const Type*); 302 void pcg_load(Parser*); 303 void pcg_addr(Parser*); 304 void pcg_push_label_addr(Parser*, CGLabel); 305 void pcg_computed_goto(Parser*, const CGLabel*, u32); 306 CGLabel pcg_label_new(Parser*); 307 void pcg_label_place(Parser*, CGLabel); 308 void pcg_jump(Parser*, CGLabel); 309 void pcg_branch_true(Parser*, CGLabel); 310 void pcg_branch_false(Parser*, CGLabel); 311 void pcg_store(Parser*); 312 /* Store whose assignment value is discarded (replaces `pcg_store; pcg_drop`). */ 313 void pcg_store_void(Parser*); 314 void pcg_deref(Parser*, const Type*); 315 316 /* ---- Lvalue auxiliary access ---- 317 * 318 * pcg_top_lv_aux returns a mutable pointer to TOS's lvalue aux, used by 319 * parse_postfix and the initializer / compound-assignment paths to fold 320 * field offsets and bit-field metadata inline. Returns NULL if the parser 321 * stack is empty; behavior on a non-lvalue TOS is the caller's responsibility 322 * (parse_postfix has already validated lvalueness before calling). */ 323 PcgLvAux* pcg_top_lv_aux(Parser*); 324 PcgLvAux* pcg_lv_aux_at(Parser*, u32 depth); 325 326 /* ---- Lvalue chain helpers ---- 327 * 328 * Each maps directly to the canonical encodings in doc/INDIRECT.md without 329 * emitting any intermediate field / index / addr_offset CG op. Field offsets 330 * and array scales are accumulated on the TOS lvalue's aux; the next 331 * pcg_load / pcg_store / pcg_addr consumes them. */ 332 333 /* Fold `s.f` (or any path-resolved field selection) into the TOS lvalue. 334 * byte_offset is the cumulative offset within the record; ty is the field 335 * type; bf_* are bit-field metadata (bf_width == 0 for non-bitfields). The 336 * caller is responsible for verifying TOS is an lvalue of a record type. */ 337 void pcg_lv_member(Parser*, i64 byte_offset, const Type* field_ty, 338 u16 bf_offset, u16 bf_width, u32 bf_storage_size); 339 340 /* Attach `[index]` to the TOS lvalue. PRECONDITION: the index value has just 341 * been pushed onto the CG stack (and parser stack) above the lvalue base; 342 * the parser stack therefore has [base_lv, index] at depth [1, 0]. This call 343 * records `scale = elem_size` on the base's aux, drops the index parser 344 * slot (leaving the index value on the CG stack for the eventual memop), 345 * and retags the surviving slot as elem_ty (lvalue). */ 346 void pcg_lv_subscript(Parser*, u32 elem_size, const Type* elem_ty); 347 348 /* Decay an array lvalue at TOS into a pointer-to-element rvalue. Emits 349 * kit_cg_addr (or a no-op for pointer-rvalue bases) and folds any pending 350 * EA modifiers into the resulting pointer via ptr arithmetic. After return, 351 * TOS is a pointer rvalue of type `*arr_ty->elem`. */ 352 void pcg_decay_array(Parser*, const Type* arr_ty); 353 354 void pcg_binop(Parser*, BinOp); 355 void pcg_unop(Parser*, UnOp); 356 void pcg_cmp(Parser*, CmpOp); 357 void pcg_convert(Parser*, const Type*); 358 void pcg_inc_dec(Parser*, BinOp, int); 359 void pcg_call(Parser*, u32, const Type*); 360 void pcg_call_symbol(Parser*, KitCgSym, u32, const Type*); 361 void pcg_ret(Parser*, int); 362 void pcg_alloca(Parser*); 363 void pcg_va_arg(Parser*, const Type*); 364 void pcg_va_start(Parser*); 365 void pcg_va_end(Parser*); 366 void pcg_va_copy(Parser*); 367 void pcg_atomic_load(Parser*, MemOrder); 368 void pcg_atomic_store(Parser*, MemOrder); 369 void pcg_atomic_rmw(Parser*, AtomicOp, MemOrder); 370 void pcg_atomic_cas(Parser*, MemOrder, MemOrder); 371 void pcg_fence(Parser*, MemOrder); 372 void pcg_intrinsic_unary_to_int(Parser*, IntrinKind); 373 void pcg_intrinsic_void(Parser*, IntrinKind); 374 void pcg_syscall(Parser*, u32 nargs, const Type* long_ty); 375 void pcg_frame_or_return_address(Parser*, int is_return, u32 level); 376 void pcg_readcyclecounter(Parser*); 377 void pcg_inline_asm(Parser*, const char*, const AsmConstraint*, u32, 378 const AsmConstraint*, u32, const Sym*, u32); 379 380 #endif