kit

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

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