cg_adapter.h (10695B)
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_type_stack / cg_value_flags / cg_lv_aux in struct 9 * Parser) 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, carried parallel to cg_type_stack / cg_value_flags. 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 typedef enum BinOp { 63 BO_IADD, 64 BO_ISUB, 65 BO_IMUL, 66 BO_SDIV, 67 BO_UDIV, 68 BO_SREM, 69 BO_UREM, 70 BO_FADD, 71 BO_FSUB, 72 BO_FMUL, 73 BO_FDIV, 74 BO_AND, 75 BO_OR, 76 BO_XOR, 77 BO_SHL, 78 BO_SHR_S, 79 BO_SHR_U, 80 } BinOp; 81 82 typedef enum UnOp { 83 UO_NEG, 84 UO_NOT, 85 UO_BNOT, 86 } UnOp; 87 88 /* Compare markers used by the C parser. The integer block plus the four 89 * relational FP markers (CMP_LT_F..CMP_GE_F) cover the bare C operators; the 90 * latter map to the *ordered* FP predicates in pcg_fp_cmp. The trailing 91 * ordered/unordered block mirrors the global CmpOp / public KitCgFpCmpOp so 92 * the C99 comparison builtins (islessgreater -> CMP_ONE_F) and any 93 * negation-driven unordered duals are reachable from the frontend 1:1. */ 94 typedef enum CmpOp { 95 CMP_EQ, 96 CMP_NE, 97 CMP_LT_S, 98 CMP_LE_S, 99 CMP_GT_S, 100 CMP_GE_S, 101 CMP_LT_U, 102 CMP_LE_U, 103 CMP_GT_U, 104 CMP_GE_U, 105 /* Relational FP operator markers (bare `< <= > >=` on floats) -> ordered. */ 106 CMP_LT_F, 107 CMP_LE_F, 108 CMP_GT_F, 109 CMP_GE_F, 110 /* Ordered FP relationals (NaN -> false). */ 111 CMP_OEQ_F, 112 CMP_ONE_F, 113 CMP_OLT_F, 114 CMP_OLE_F, 115 CMP_OGT_F, 116 CMP_OGE_F, 117 /* Unordered FP relationals (NaN -> true). */ 118 CMP_UEQ_F, 119 CMP_UNE_F, 120 CMP_ULT_F, 121 CMP_ULE_F, 122 CMP_UGT_F, 123 CMP_UGE_F, 124 } CmpOp; 125 126 typedef enum AtomicOp { 127 AO_XCHG, 128 AO_ADD, 129 AO_SUB, 130 AO_AND, 131 AO_OR, 132 AO_XOR, 133 AO_NAND, 134 } AtomicOp; 135 136 typedef enum MemOrder { 137 MO_RELAXED, 138 MO_CONSUME, 139 MO_ACQUIRE, 140 MO_RELEASE, 141 MO_ACQ_REL, 142 MO_SEQ_CST, 143 } MemOrder; 144 145 typedef enum IntrinKind { 146 INTRIN_NONE = 0, 147 INTRIN_POPCOUNT, 148 INTRIN_CTZ, 149 INTRIN_CLZ, 150 INTRIN_MEMCPY, 151 INTRIN_MEMMOVE, 152 INTRIN_MEMSET, 153 INTRIN_PREFETCH, 154 INTRIN_ASSUME_ALIGNED, 155 INTRIN_EXPECT, 156 INTRIN_UNREACHABLE, 157 INTRIN_TRAP, 158 INTRIN_SETJMP, 159 INTRIN_LONGJMP, 160 INTRIN_SADD_OVERFLOW, 161 INTRIN_UADD_OVERFLOW, 162 INTRIN_SSUB_OVERFLOW, 163 INTRIN_USUB_OVERFLOW, 164 INTRIN_SMUL_OVERFLOW, 165 INTRIN_UMUL_OVERFLOW, 166 } IntrinKind; 167 168 typedef enum AsmDir { ASM_IN, ASM_OUT, ASM_INOUT } AsmDir; 169 170 typedef struct AsmConstraint { 171 const char* str; 172 Sym name; 173 const Type* type; 174 Sym reg; /* hard-register name for a GNU local register variable; 0 = none */ 175 u8 dir; 176 u8 pad[3]; 177 } AsmConstraint; 178 179 typedef enum FrameSlotKind { 180 FS_LOCAL, 181 FS_PARAM, 182 FS_SPILL, 183 FS_SRET, 184 FS_ALLOCA, 185 } FrameSlotKind; 186 187 typedef enum FrameSlotFlag { 188 FSF_NONE = 0, 189 FSF_ADDR_TAKEN = 1u << 0, 190 FSF_VOLATILE = 1u << 1, 191 } FrameSlotFlag; 192 193 typedef struct FrameSlotDesc { 194 const Type* type; 195 Sym name; 196 SrcLoc loc; 197 u32 size; 198 u32 align; 199 u8 kind; 200 u8 pad; 201 u16 flags; 202 } FrameSlotDesc; 203 204 typedef struct CGParamDesc { 205 u32 index; 206 Sym name; 207 const Type* type; 208 FrameSlot slot; 209 const void* abi; 210 const void* incoming; 211 u32 nincoming; 212 SrcLoc loc; 213 } CGParamDesc; 214 215 typedef enum CGFuncDescFlag { 216 CGFD_NONE = 0, 217 CGFD_NORETURN = 1u << 0, 218 } CGFuncDescFlag; 219 220 typedef struct CGFuncDesc { 221 ObjSymId sym; 222 ObjSecId text_section_id; 223 u32 group_id; 224 const Type* fn_type; 225 const void* abi; 226 const CGParamDesc* params; 227 u32 nparams; 228 SrcLoc loc; 229 u32 flags; 230 KitCgInlinePolicy inline_policy; 231 } CGFuncDesc; 232 233 typedef struct Parser Parser; 234 235 KitCgTypeId pcg_tid(Parser*, const Type*); 236 KitCgMemAccess pcg_mem(Parser*, const Type*); 237 const Type* pcg_top_type(Parser*); 238 const Type* pcg_top2_type(Parser*); 239 void pcg_retag_top(Parser*, const Type*); 240 void pcg_push_type(Parser*, const Type*); 241 void pcg_drop_type(Parser*); 242 void pcg_dup_type(Parser*); 243 void pcg_swap_type(Parser*); 244 /* Structural CG ops that also mirror onto the typed shadow stack. */ 245 void pcg_dup(Parser*); 246 void pcg_swap(Parser*); 247 void pcg_drop(Parser*); 248 int pcg_top_is_bitfield(Parser*); 249 void pcg_set_top_bitfield(Parser*); 250 int pcg_top_is_register(Parser*); 251 void pcg_set_top_register(Parser*); 252 int pcg_top_is_lvalue(Parser*); 253 int pcg_top_is_modifiable_lvalue(Parser*); 254 int pcg_top_is_null_ptr_const(Parser*); 255 void pcg_set_top_lvalue(Parser*); 256 void pcg_rot3_type(Parser*); 257 int pcg_emit_enabled(Parser*); 258 void pcg_codegen_suppress_push(Parser*); 259 void pcg_codegen_suppress_pop(Parser*); 260 261 KitCgIntBinOp pcg_int_binop(BinOp); 262 KitCgFpBinOp pcg_fp_binop(BinOp); 263 KitCgIntCmpOp pcg_int_cmp(CmpOp); 264 KitCgFpCmpOp pcg_fp_cmp(CmpOp); 265 KitCgAtomicOp pcg_atomic_op(AtomicOp); 266 KitCgMemOrder pcg_mem_order(MemOrder); 267 int pcg_type_is_fp(const Type*); 268 int pcg_type_is_signed(const Type*); 269 270 FrameSlot pcg_local(Parser*, const FrameSlotDesc*); 271 FrameSlot pcg_param_slot(Parser*, u32, const FrameSlotDesc*); 272 void pcg_param(Parser*, const CGParamDesc*); 273 void pcg_func_begin(Parser*, const CGFuncDesc*); 274 void pcg_func_end(Parser*); 275 void pcg_set_loc(Parser*, SrcLoc); 276 void pcg_push_int(Parser*, i64, const Type*); 277 void pcg_push_float(Parser*, double, const Type*); 278 void pcg_push_local_typed(Parser*, FrameSlot, const Type*); 279 void pcg_push_global(Parser*, ObjSymId, const Type*); 280 void pcg_load(Parser*); 281 void pcg_addr(Parser*); 282 void pcg_push_label_addr(Parser*, CGLabel); 283 void pcg_computed_goto(Parser*, const CGLabel*, u32); 284 CGLabel pcg_label_new(Parser*); 285 void pcg_label_place(Parser*, CGLabel); 286 void pcg_jump(Parser*, CGLabel); 287 void pcg_branch_true(Parser*, CGLabel); 288 void pcg_branch_false(Parser*, CGLabel); 289 void pcg_store(Parser*); 290 void pcg_deref(Parser*, const Type*); 291 292 /* ---- Lvalue auxiliary access ---- 293 * 294 * pcg_top_lv_aux returns a mutable pointer to TOS's lvalue aux, used by 295 * parse_postfix and the initializer / compound-assignment paths to fold 296 * field offsets and bit-field metadata inline. Returns NULL if the parser 297 * stack is empty; behavior on a non-lvalue TOS is the caller's responsibility 298 * (parse_postfix has already validated lvalueness before calling). */ 299 PcgLvAux* pcg_top_lv_aux(Parser*); 300 PcgLvAux* pcg_lv_aux_at(Parser*, u32 depth); 301 302 /* ---- Lvalue chain helpers ---- 303 * 304 * Each maps directly to the canonical encodings in doc/INDIRECT.md without 305 * emitting any intermediate field / index / addr_offset CG op. Field offsets 306 * and array scales are accumulated on the TOS lvalue's aux; the next 307 * pcg_load / pcg_store / pcg_addr consumes them. */ 308 309 /* Fold `s.f` (or any path-resolved field selection) into the TOS lvalue. 310 * byte_offset is the cumulative offset within the record; ty is the field 311 * type; bf_* are bit-field metadata (bf_width == 0 for non-bitfields). The 312 * caller is responsible for verifying TOS is an lvalue of a record type. */ 313 void pcg_lv_member(Parser*, i64 byte_offset, const Type* field_ty, 314 u16 bf_offset, u16 bf_width, u32 bf_storage_size); 315 316 /* Attach `[index]` to the TOS lvalue. PRECONDITION: the index value has just 317 * been pushed onto the CG stack (and parser stack) above the lvalue base; 318 * the parser stack therefore has [base_lv, index] at depth [1, 0]. This call 319 * records `scale = elem_size` on the base's aux, drops the index parser 320 * slot (leaving the index value on the CG stack for the eventual memop), 321 * and retags the surviving slot as elem_ty (lvalue). */ 322 void pcg_lv_subscript(Parser*, u32 elem_size, const Type* elem_ty); 323 324 /* Decay an array lvalue at TOS into a pointer-to-element rvalue. Emits 325 * kit_cg_addr (or a no-op for pointer-rvalue bases) and folds any pending 326 * EA modifiers into the resulting pointer via ptr arithmetic. After return, 327 * TOS is a pointer rvalue of type `*arr_ty->elem`. */ 328 void pcg_decay_array(Parser*, const Type* arr_ty); 329 330 void pcg_binop(Parser*, BinOp); 331 void pcg_unop(Parser*, UnOp); 332 void pcg_cmp(Parser*, CmpOp); 333 void pcg_convert(Parser*, const Type*); 334 void pcg_inc_dec(Parser*, BinOp, int); 335 void pcg_call(Parser*, u32, const Type*); 336 void pcg_call_symbol(Parser*, KitCgSym, u32, const Type*); 337 void pcg_ret(Parser*, int); 338 void pcg_alloca(Parser*); 339 void pcg_va_arg(Parser*, const Type*); 340 void pcg_va_start(Parser*); 341 void pcg_va_end(Parser*); 342 void pcg_va_copy(Parser*); 343 void pcg_atomic_load(Parser*, MemOrder); 344 void pcg_atomic_store(Parser*, MemOrder); 345 void pcg_atomic_rmw(Parser*, AtomicOp, MemOrder); 346 void pcg_atomic_cas(Parser*, MemOrder, MemOrder); 347 void pcg_fence(Parser*, MemOrder); 348 void pcg_intrinsic_unary_to_int(Parser*, IntrinKind); 349 void pcg_intrinsic_void(Parser*, IntrinKind); 350 void pcg_syscall(Parser*, u32 nargs, const Type* long_ty); 351 void pcg_frame_or_return_address(Parser*, int is_return, u32 level); 352 void pcg_inline_asm(Parser*, const char*, const AsmConstraint*, u32, 353 const AsmConstraint*, u32, const Sym*, u32); 354 355 #endif