kit

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

commit ee0b2db079b32741e80fc9468ac6e02859749b8f
parent 65c4e689262219f9f490a734e135fab821f098f0
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 15 Jun 2026 15:20:40 -0700

Implement C CG stack cutover

Diffstat:
Mdoc/FRONTENDS.md | 68+++++++++++++++++++++++++++-----------------------------------------
Mdoc/INTERFACES.md | 10+++++-----
Alang/c/parse/cg.c | 799+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alang/c/parse/cg.h | 284+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dlang/c/parse/cg_adapter.c | 1365-------------------------------------------------------------------------------
Dlang/c/parse/cg_adapter.h | 380-------------------------------------------------------------------------------
Mlang/c/parse/parse.c | 74+++++++++++++++++++++++++++++++++++++-------------------------------------
Mlang/c/parse/parse.h | 2+-
Mlang/c/parse/parse_expr.c | 1776++++++++++++++++++++++++++++++++++---------------------------------------------
Mlang/c/parse/parse_init.c | 91++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mlang/c/parse/parse_priv.h | 9+++++----
Mlang/c/parse/parse_stmt.c | 172++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mlang/c/parse/parse_type.c | 10+++++-----
Msrc/cg/control.c | 3++-
Msrc/cg/memory.c | 7++++++-
Mtest/asm/hostas_cross.sh | 12+++++-------
Mtest/asm/hostas_toy.sh | 13+++++--------
Mtest/ecosystem/README.md | 2+-
Mtest/ecosystem/known_bugs/yyjson-write-label.md | 2+-
Mtest/ecosystem/run.sh | 4++--
Mtest/parse/CORPUS.md | 1+
Mtest/parse/cases/6_5_15_06_cond_struct_qualifier_mismatch.c | 10+++++-----
Atest/parse/cases/6_5_16_08_suppressed_assign.c | 7+++++++
Atest/parse/cases/6_5_16_08_suppressed_assign.expected | 1+
Mtest/parse/cases/gnu_inline_control_flow.c | 2+-
25 files changed, 2104 insertions(+), 3000 deletions(-)

diff --git a/doc/FRONTENDS.md b/doc/FRONTENDS.md @@ -184,47 +184,33 @@ declarations and the TU driver in `parse.c`, expressions in `parse_expr.c`, types/declarators in `parse_type.c`, initializers in `parse_init.c`, and statements in `parse_stmt.c`. -Because lowering happens inline, the parser maintains a **typed value stack** -that shadows the CG operand stack. Each entry records the C-language type of a -value, value flags (lvalue / modifiable-lvalue / bit-field / register / -null-pointer-constant), and an lvalue auxiliary record. The fields are -`cg_type_stack`, `cg_value_flags`, and `cg_lv_aux` in `struct Parser` -(`parse_priv.h`). This shadow stack is the parser's entire notion of "the -expression evaluated so far": the CG side holds the runtime values, the parser -side holds the static types and lvalue bookkeeping needed to drive the next op -correctly (signedness for division/compare/shift, pointer-element size for -arithmetic, bit-field metadata for loads/stores, and so on). - -### The cg_adapter.h coupling point - -`lang/c/parse/cg_adapter.c` and its header `cg_adapter.h` are the real -parser <-> codegen seam, and the most load-bearing design decision in the C -frontend. The header declares a family of `pcg_*` helpers that look like thin -wrappers over the public `kit_cg_*` API but do two extra jobs on every call: - -- **They keep the parser's typed value stack in lockstep with the CG stack.** - `pcg_dup`/`pcg_swap`/`pcg_drop` mirror the structural op onto the type stack; - `pcg_push_int` / `pcg_load` / `pcg_binop` / `pcg_convert` push, retag, or pop - type-stack entries alongside emitting the CG op. The parser never calls - `kit_cg_*` for stack-affecting ops directly — it goes through `pcg_*` so - the two stacks can never drift. - -- **They fold C lvalue chains into a single CG memop.** There is no CG-level - `field` / `index` / `addr_offset` op. Instead a chain like `a[i].g` - accumulates a byte offset, an index scale, and bit-field metadata onto the - TOS lvalue's `PcgLvAux` (`pcg_lv_member`, `pcg_lv_subscript`, - `pcg_decay_array`), and the *next* `pcg_load` / `pcg_store` / `pcg_addr` - consumes that aux and emits one effective-address memop. The aux also records - whether the base is a frame local or an already-computed pointer rvalue, so - address-taking knows whether to emit a `kit_cg_addr` or treat the base as - a pointer. - -The implication: **the C frontend's lowering strategy is encoded in this -adapter, not in the public CG contract.** It is where C-specific decisions -(lvalue-as-effective-address, lazy load/store/addr, the typed shadow stack) -are concentrated. A change to how C lowers indirection lands here, not in the -backends. (`pcg_emit_enabled` also lets the parser run constant-folding / -sizeof contexts with codegen suppressed while still tracking types.) +Because lowering happens inline, the parser uses the **CG operand stack as its +typed expression stack**. Each CG stack entry carries frontend-owned +`lang_type`/`lang_flags` facts: the C type plus value flags for lvalue, +modifiable-lvalue, bit-field, register, and null-pointer-constant state. The +parser reads those facts with `kit_cg_slot_info()` and stamps fresh results with +`kit_cg_retag_*()`; structural CG stack ops move the facts with the value. + +### Parser codegen helpers + +`lang/c/parse/cg.c` and `cg.h` are the parser <-> codegen seam. They are plain +C-semantic helper functions over the public `kit_cg_*` API, not a second stack +or adapter layer. They centralize C-specific details such as C-to-CG type +lowering, lvalue flags, usual conversions, op-enum mapping, inc/dec lowering, +atomics, intrinsics, inline asm, and assignment value preservation. + +Lvalues are eager CG places. A chain like `a[i].g` is built as ordinary CG +addressing operations (`addr`/`bitcast`, `elem`, `field_at`, optional +`field_bits`) as the parser recognizes it. The CG layer folds those places into +the final memop where the target can use a fused effective address; the parser +does not keep a separate lvalue auxiliary record. Loads, stores, address-taking, +and `store_keep` consume the same CG places, with the C facts kept only in the +inline lang fields. + +Codegen suppression is CG unevaluated mode. The parser still has semantic forks +for C control-shape rules and constant-expression legality, but type-only and +constant-evaluation paths preserve CG stack shape and C slot facts without +target emission. ### Semantic layers — type / decl / sem / abi diff --git a/doc/INTERFACES.md b/doc/INTERFACES.md @@ -309,11 +309,11 @@ map (the wasm frontend reaching `src/wasm/wasm.h`). | wasm (`lang/wasm/`) | `kit_wasm_frontend_vtable` (`wasm.h`) | `src/wasm/wasm.h` — the shared module-model subsystem; the one frontend→`src/` edge | **Frontend-tier notes:** -- `lang/c/parse/cg_adapter.h` is the C parser's codegen adapter: it wraps `cg.h` - with C-semantic sugar (lvalue auxiliaries, the type stack, `pcg_*` helpers). It - is the real coupling point between the C parser and codegen, and it is worth - understanding when working in either: it carries the C frontend's own policy on - top of the generic `cg.h`, so a change to one frequently belongs in the other. +- `lang/c/parse/cg.h` is the C parser's codegen helper surface over public + `cg.h`. C slot facts live directly on CG stack entries as opaque + `lang_type`/`lang_flags`; `cg.c` adds C-semantic helpers for conversions, + lvalue flags, place construction, stores, atomics, intrinsics, and inline asm + without maintaining a parser-owned value stack. - `lang/wasm/wasm.h` exposes `kit_wasm_wat_to_wasm()` — a WAT-to-wasm helper living in a frontend-public header, used by wasm tooling/tests. diff --git a/lang/c/parse/cg.c b/lang/c/parse/cg.c @@ -0,0 +1,799 @@ +#include <string.h> + +#include "parse/parse_priv.h" + +#define C_CG_VALUE_LVALUE ((uint16_t)1u) +#define C_CG_VALUE_MODIFIABLE ((uint16_t)2u) +#define C_CG_VALUE_BITFIELD ((uint16_t)4u) +#define C_CG_VALUE_NULL_PTR_CONST ((uint16_t)8u) +#define C_CG_VALUE_REGISTER ((uint16_t)16u) + +KitCgTypeId c_cg_tid(Parser* p, const Type* ty) { + return type_cg_id_in_pool(p->c, p->pool, ty); +} + +static KitCgSlotInfo c_cg_slot(Parser* p, u32 depth) { + return kit_cg_slot_info(p->cg, depth); +} + +static uint16_t c_cg_lvalue_flags_for_type(const Type* ty) { + uint16_t flags = C_CG_VALUE_LVALUE; + if (ty && !(ty->qual & Q_CONST) && ty->kind != TY_ARRAY && + ty->kind != TY_FUNC && ty->kind != TY_VOID) { + flags |= C_CG_VALUE_MODIFIABLE; + } + return flags; +} + +static KitCgTypeId c_cg_slot_cg_id(Parser* p, u32 depth, const Type* fallback) { + KitCgSlotInfo info = c_cg_slot(p, depth); + return info.cg_type ? info.cg_type : c_cg_tid(p, fallback); +} + +static KitCgTypeId c_cg_top_cg_id(Parser* p) { + return c_cg_slot_cg_id(p, 0, c_cg_top_type(p)); +} + +static KitCgTypeId c_cg_top2_cg_id(Parser* p) { + return c_cg_slot_cg_id(p, 1, c_cg_top2_type(p)); +} + +static KitCgMemAccess c_cg_mem_id(Parser* p, KitCgTypeId id, const Type* ty) { + KitCgMemAccess m; + memset(&m, 0, sizeof m); + m.type = id; + m.align = (u32)kit_cg_type_align(p->c, id); + if (ty && (ty->qual & Q_VOLATILE)) m.flags |= KIT_CG_MEM_VOLATILE; + if (type_is_int(ty) && c_cg_type_is_signed(ty)) + m.flags |= KIT_CG_MEM_SOURCE_SIGNED; + return m; +} + +KitCgMemAccess c_cg_mem(Parser* p, const Type* ty) { + return c_cg_mem_id(p, c_cg_tid(p, ty), ty); +} + +const Type* c_cg_top_type(Parser* p) { + return (const Type*)c_cg_slot(p, 0).lang_type; +} + +const Type* c_cg_top2_type(Parser* p) { + return (const Type*)c_cg_slot(p, 1).lang_type; +} + +void c_cg_retag_top(Parser* p, const Type* ty) { + kit_cg_retag_top(p->cg, ty, 0); +} + +void c_cg_retag_at(Parser* p, u32 depth, const Type* ty, uint16_t flags) { + kit_cg_retag_at(p->cg, depth, ty, flags); +} + +void c_cg_retag_keep_flags(Parser* p, u32 depth, const Type* ty) { + KitCgSlotInfo info = c_cg_slot(p, depth); + kit_cg_retag_at(p->cg, depth, ty, info.lang_flags); +} + +void c_cg_dup(Parser* p) { kit_cg_dup(p->cg); } + +void c_cg_swap(Parser* p) { kit_cg_swap(p->cg); } + +void c_cg_drop(Parser* p) { kit_cg_drop(p->cg); } + +void c_cg_reclaim_temps(Parser* p) { kit_cg_reclaim_temps(p->cg); } + +uint32_t c_cg_stack_depth(Parser* p) { return kit_cg_stack_depth(p->cg); } + +void c_cg_drop_to_depth(Parser* p, uint32_t depth) { + while (kit_cg_stack_depth(p->cg) > depth) kit_cg_drop(p->cg); +} + +int c_cg_top_is_bitfield(Parser* p) { + return (c_cg_slot(p, 0).lang_flags & C_CG_VALUE_BITFIELD) != 0; +} + +void c_cg_set_top_bitfield(Parser* p) { + kit_cg_set_top_flags(p->cg, C_CG_VALUE_BITFIELD, 0); +} + +int c_cg_top_is_register(Parser* p) { + return (c_cg_slot(p, 0).lang_flags & C_CG_VALUE_REGISTER) != 0; +} + +void c_cg_set_top_register(Parser* p) { + kit_cg_set_top_flags(p->cg, C_CG_VALUE_REGISTER, 0); +} + +int c_cg_top_is_lvalue(Parser* p) { + return (c_cg_slot(p, 0).lang_flags & C_CG_VALUE_LVALUE) != 0; +} + +int c_cg_top_is_modifiable_lvalue(Parser* p) { + uint16_t flags = c_cg_slot(p, 0).lang_flags; + return (flags & (C_CG_VALUE_LVALUE | C_CG_VALUE_MODIFIABLE)) == + (C_CG_VALUE_LVALUE | C_CG_VALUE_MODIFIABLE); +} + +int c_cg_top_is_null_ptr_const(Parser* p) { + return (c_cg_slot(p, 0).lang_flags & C_CG_VALUE_NULL_PTR_CONST) != 0; +} + +void c_cg_set_top_lvalue(Parser* p) { + const Type* ty = c_cg_top_type(p); + kit_cg_retag_top(p->cg, ty, c_cg_lvalue_flags_for_type(ty)); +} + +int c_cg_emit_enabled(Parser* p) { return p && p->suppress_codegen == 0; } + +void c_cg_codegen_suppress_push(Parser* p) { + if (!p) return; + ++p->suppress_codegen; + kit_cg_unevaluated_push(p->cg); +} + +void c_cg_codegen_suppress_pop(Parser* p) { + if (!p) return; + if (!p->suppress_codegen) + perr(p, "internal parser codegen suppression underflow"); + kit_cg_unevaluated_pop(p->cg); + --p->suppress_codegen; +} + +int c_cg_type_is_fp(const Type* ty) { + return ty && type_kind_is_fp((TypeKind)ty->kind); +} + +int c_cg_type_is_signed(const Type* ty) { return type_is_signed_integer(ty); } + +KitCgIntBinOp c_cg_int_binop(BinOp op) { + switch (op) { + case BO_IADD: + return KIT_CG_INT_ADD; + case BO_ISUB: + return KIT_CG_INT_SUB; + case BO_IMUL: + return KIT_CG_INT_MUL; + case BO_SDIV: + return KIT_CG_INT_SDIV; + case BO_UDIV: + return KIT_CG_INT_UDIV; + case BO_SREM: + return KIT_CG_INT_SREM; + case BO_UREM: + return KIT_CG_INT_UREM; + case BO_AND: + return KIT_CG_INT_AND; + case BO_OR: + return KIT_CG_INT_OR; + case BO_XOR: + return KIT_CG_INT_XOR; + case BO_SHL: + return KIT_CG_INT_SHL; + case BO_SHR_S: + return KIT_CG_INT_ASHR; + case BO_SHR_U: + return KIT_CG_INT_LSHR; + default: + return KIT_CG_INT_ADD; + } +} + +KitCgFpBinOp c_cg_fp_binop(BinOp op) { + switch (op) { + case BO_FADD: + return KIT_CG_FP_ADD; + case BO_FSUB: + return KIT_CG_FP_SUB; + case BO_FMUL: + return KIT_CG_FP_MUL; + case BO_FDIV: + return KIT_CG_FP_DIV; + default: + return KIT_CG_FP_ADD; + } +} + +KitCgIntCmpOp c_cg_int_cmp(CmpOp op) { + switch (op) { + case CMP_EQ: + return KIT_CG_INT_EQ; + case CMP_NE: + return KIT_CG_INT_NE; + case CMP_LT_S: + return KIT_CG_INT_LT_S; + case CMP_LE_S: + return KIT_CG_INT_LE_S; + case CMP_GT_S: + return KIT_CG_INT_GT_S; + case CMP_GE_S: + return KIT_CG_INT_GE_S; + case CMP_LT_U: + return KIT_CG_INT_LT_U; + case CMP_LE_U: + return KIT_CG_INT_LE_U; + case CMP_GT_U: + return KIT_CG_INT_GT_U; + case CMP_GE_U: + return KIT_CG_INT_GE_U; + default: + return KIT_CG_INT_EQ; + } +} + +KitCgFpCmpOp c_cg_fp_cmp(CmpOp op) { + switch (op) { + case CMP_EQ: + return KIT_CG_FP_OEQ; + case CMP_NE: + return KIT_CG_FP_UNE; + case CMP_LT_F: + case CMP_OLT_F: + return KIT_CG_FP_OLT; + case CMP_LE_F: + case CMP_OLE_F: + return KIT_CG_FP_OLE; + case CMP_GT_F: + case CMP_OGT_F: + return KIT_CG_FP_OGT; + case CMP_GE_F: + case CMP_OGE_F: + return KIT_CG_FP_OGE; + case CMP_OEQ_F: + return KIT_CG_FP_OEQ; + case CMP_ONE_F: + return KIT_CG_FP_ONE; + case CMP_UEQ_F: + return KIT_CG_FP_UEQ; + case CMP_UNE_F: + return KIT_CG_FP_UNE; + case CMP_ULT_F: + return KIT_CG_FP_ULT; + case CMP_ULE_F: + return KIT_CG_FP_ULE; + case CMP_UGT_F: + return KIT_CG_FP_UGT; + case CMP_UGE_F: + return KIT_CG_FP_UGE; + default: + return KIT_CG_FP_OEQ; + } +} + +KitCgAtomicOp c_cg_atomic_op(AtomicOp op) { + switch (op) { + case AO_XCHG: + return KIT_CG_ATOMIC_XCHG; + case AO_ADD: + return KIT_CG_ATOMIC_ADD; + case AO_SUB: + return KIT_CG_ATOMIC_SUB; + case AO_AND: + return KIT_CG_ATOMIC_AND; + case AO_OR: + return KIT_CG_ATOMIC_OR; + case AO_XOR: + return KIT_CG_ATOMIC_XOR; + case AO_NAND: + return KIT_CG_ATOMIC_NAND; + } + return KIT_CG_ATOMIC_XCHG; +} + +KitCgMemOrder c_cg_mem_order(MemOrder ord) { return (KitCgMemOrder)ord; } + +static int c_cg_slot_is_volatile(const FrameSlotDesc* fsd) { + return fsd && ((fsd->flags & FSF_VOLATILE) || + (fsd->type && (fsd->type->qual & Q_VOLATILE))); +} + +FrameSlot c_cg_local(Parser* p, const FrameSlotDesc* fsd) { + KitCgLocalAttrs attrs; + if (!c_cg_emit_enabled(p)) return FRAME_SLOT_NONE; + memset(&attrs, 0, sizeof attrs); + attrs.name = fsd->name; + attrs.align = fsd->align; + attrs.debug_type = type_cg_debug_in_pool(p->cg, p->c, p->pool, fsd->type); + if (c_cg_slot_is_volatile(fsd)) attrs.flags |= KIT_CG_LOCAL_MEMORY_REQUIRED; + return kit_cg_local(p->cg, c_cg_tid(p, fsd->type), attrs); +} + +FrameSlot c_cg_param_slot(Parser* p, u32 index, const FrameSlotDesc* fsd) { + KitCgLocalAttrs attrs; + if (!c_cg_emit_enabled(p)) return FRAME_SLOT_NONE; + memset(&attrs, 0, sizeof attrs); + attrs.name = fsd->name; + attrs.align = fsd->align; + attrs.debug_type = type_cg_debug_in_pool(p->cg, p->c, p->pool, fsd->type); + if (c_cg_slot_is_volatile(fsd)) attrs.flags |= KIT_CG_LOCAL_MEMORY_REQUIRED; + return kit_cg_param(p->cg, index, c_cg_tid(p, fsd->type), attrs); +} + +void c_cg_param(Parser* p, const CGParamDesc* pd) { + (void)p; + (void)pd; +} + +void c_cg_func_begin(Parser* p, const CGFuncDesc* fd) { + KitCgFuncAttrs attrs; + if (!c_cg_emit_enabled(p)) return; + memset(&attrs, 0, sizeof attrs); + attrs.inline_policy = fd->inline_policy; + attrs.debug_type = type_cg_debug_in_pool(p->cg, p->c, p->pool, fd->fn_type); + if (fd->flags & CGFD_NORETURN) attrs.flags |= KIT_CG_FUNC_NORETURN; + kit_cg_func_begin_attrs(p->cg, fd->sym, attrs); +} + +void c_cg_func_end(Parser* p) { + if (c_cg_emit_enabled(p)) kit_cg_func_end(p->cg); +} + +void c_cg_set_loc(Parser* p, SrcLoc loc) { kit_cg_set_loc(p->cg, loc); } + +static void c_cg_push_dummy_place(Parser* p, const Type* ty) { + const Type* base = ty ? ty : type_void(p->pool); + const Type* ptr_ty = type_ptr(p->pool, base); + kit_cg_push_null(p->cg, c_cg_tid(p, ptr_ty)); + kit_cg_deref(p->cg, 0); +} + +void c_cg_push_int(Parser* p, i64 v, const Type* ty) { + uint16_t flags = 0; + kit_cg_push_int(p->cg, (uint64_t)v, c_cg_tid(p, ty)); + if (v == 0) flags |= C_CG_VALUE_NULL_PTR_CONST; + kit_cg_retag_top(p->cg, ty, flags); +} + +void c_cg_push_float(Parser* p, double v, const Type* ty) { + kit_cg_push_float(p->cg, v, c_cg_tid(p, ty)); + kit_cg_retag_top(p->cg, ty, 0); +} + +void c_cg_push_local_typed(Parser* p, FrameSlot s, const Type* ty) { + if (s == FRAME_SLOT_NONE) { + c_cg_push_dummy_place(p, ty); + } else { + kit_cg_push_local(p->cg, s); + } + kit_cg_retag_top(p->cg, ty, c_cg_lvalue_flags_for_type(ty)); +} + +void c_cg_push_global(Parser* p, ObjSymId sym, const Type* ty) { + if (sym == OBJ_SYM_NONE) { + c_cg_push_dummy_place(p, ty); + } else if (ty && ty->kind == TY_FUNC) { + kit_cg_push_symbol_addr(p->cg, sym, 0); + } else { + kit_cg_push_symbol_addr(p->cg, sym, 0); + kit_cg_deref(p->cg, 0); + } + kit_cg_retag_top(p->cg, ty, c_cg_lvalue_flags_for_type(ty)); +} + +void c_cg_load(Parser* p) { + const Type* ty = c_cg_top_type(p); + kit_cg_load(p->cg, c_cg_mem_id(p, c_cg_top_cg_id(p), ty)); + kit_cg_retag_top(p->cg, ty, 0); +} + +void c_cg_addr(Parser* p) { + const Type* ty = c_cg_top_type(p); + const Type* ptr_ty = type_ptr(p->pool, ty ? ty : type_void(p->pool)); + if (!(ty && ty->kind == TY_FUNC)) kit_cg_addr(p->cg); + kit_cg_retag_top(p->cg, ptr_ty, 0); +} + +void c_cg_push_label_addr(Parser* p, CGLabel label) { + const Type* vp = type_ptr(p->pool, type_void(p->pool)); + kit_cg_push_label_addr(p->cg, label, c_cg_tid(p, vp)); + kit_cg_retag_top(p->cg, vp, 0); +} + +void c_cg_computed_goto(Parser* p, const CGLabel* targets, u32 ntargets) { + kit_cg_computed_goto(p->cg, targets, ntargets); +} + +CGLabel c_cg_label_new(Parser* p) { + if (!c_cg_emit_enabled(p)) return (CGLabel)1; + return kit_cg_label_new(p->cg); +} + +void c_cg_label_place(Parser* p, CGLabel l) { + if (c_cg_emit_enabled(p)) kit_cg_label_place(p->cg, l); +} + +void c_cg_jump(Parser* p, CGLabel l) { + if (c_cg_emit_enabled(p)) kit_cg_jump(p->cg, l); +} + +void c_cg_branch_true(Parser* p, CGLabel l) { + if (!c_cg_emit_enabled(p)) { + if (c_cg_stack_depth(p)) c_cg_drop(p); + return; + } + kit_cg_branch_true(p->cg, l); +} + +void c_cg_branch_false(Parser* p, CGLabel l) { + if (!c_cg_emit_enabled(p)) { + if (c_cg_stack_depth(p)) c_cg_drop(p); + return; + } + kit_cg_branch_false(p->cg, l); +} + +void c_cg_store(Parser* p) { + const Type* lv_ty = c_cg_top2_type(p); + const Type* rv_ty = c_cg_top_type(p); + const Type* mem_ty = lv_ty; + KitCgTypeId mem_cg = c_cg_top2_cg_id(p); + if (rv_ty && type_is_ptr(rv_ty) && (!lv_ty || !type_is_ptr(lv_ty))) { + mem_ty = rv_ty; + mem_cg = c_cg_top_cg_id(p); + } + kit_cg_store_keep(p->cg, c_cg_mem_id(p, mem_cg, mem_ty)); + kit_cg_retag_top(p->cg, rv_ty, 0); +} + +void c_cg_store_void(Parser* p) { + const Type* lv_ty = c_cg_top2_type(p); + const Type* rv_ty = c_cg_top_type(p); + const Type* mem_ty = lv_ty; + KitCgTypeId mem_cg = c_cg_top2_cg_id(p); + if (rv_ty && type_is_ptr(rv_ty) && (!lv_ty || !type_is_ptr(lv_ty))) { + mem_ty = rv_ty; + mem_cg = c_cg_top_cg_id(p); + } + kit_cg_store(p->cg, c_cg_mem_id(p, mem_cg, mem_ty)); +} + +void c_cg_deref(Parser* p, const Type* pointee) { + const Type* ptr_ty = c_cg_top_type(p); + if (pointee && pointee->kind == TY_FUNC) { + c_cg_retag_top(p, pointee); + c_cg_set_top_lvalue(p); + return; + } + if (ptr_ty && ptr_ty->kind == TY_PTR && ptr_ty->ptr.pointee != pointee) { + const Type* want_ptr_ty = type_ptr(p->pool, pointee); + kit_cg_bitcast(p->cg, c_cg_tid(p, want_ptr_ty)); + c_cg_retag_top(p, want_ptr_ty); + } + kit_cg_deref(p->cg, 0); + kit_cg_retag_top(p->cg, pointee, c_cg_lvalue_flags_for_type(pointee)); +} + +void c_cg_lv_member(Parser* p, i64 byte_offset, const Type* field_ty, + u16 bf_offset, u16 bf_width, u32 bf_storage_size) { + const Type* base_ty = c_cg_top_type(p); + int was_lvalue = c_cg_top_is_lvalue(p); + int base_is_rvalue_agg = + !was_lvalue && base_ty && + (base_ty->kind == TY_STRUCT || base_ty->kind == TY_UNION); + uint16_t flags = 0; + kit_cg_field_at(p->cg, byte_offset, c_cg_tid(p, field_ty)); + if (bf_width) { + kit_cg_field_bits(p->cg, bf_offset, bf_width, bf_storage_size, + c_cg_type_is_signed(field_ty)); + } + if (was_lvalue) + flags = c_cg_lvalue_flags_for_type(field_ty); + else if (base_is_rvalue_agg) + flags = C_CG_VALUE_LVALUE; + if (bf_width) flags |= C_CG_VALUE_BITFIELD; + kit_cg_retag_top(p->cg, field_ty, flags); +} + +void c_cg_lv_subscript(Parser* p, u32 elem_size, const Type* elem_ty) { + (void)elem_size; + kit_cg_elem(p->cg, 0); + kit_cg_retag_top(p->cg, elem_ty, c_cg_lvalue_flags_for_type(elem_ty)); +} + +void c_cg_decay_array(Parser* p, const Type* arr_ty) { + const Type* ptr_ty = type_ptr(p->pool, arr_ty->arr.elem); + kit_cg_addr(p->cg); + kit_cg_bitcast(p->cg, c_cg_tid(p, ptr_ty)); + kit_cg_retag_top(p->cg, ptr_ty, 0); +} + +void c_cg_binop(Parser* p, BinOp op) { + const Type* result = c_cg_top2_type(p); + if (op == BO_FADD || op == BO_FSUB || op == BO_FMUL || op == BO_FDIV) + kit_cg_fp_binop(p->cg, c_cg_fp_binop(op), KIT_CG_FP_NONE); + else + kit_cg_int_binop(p->cg, c_cg_int_binop(op), KIT_CG_INTOP_NONE); + kit_cg_retag_top(p->cg, result, 0); +} + +void c_cg_unop(Parser* p, UnOp op) { + const Type* ty = c_cg_top_type(p); + if (op == UO_NEG && c_cg_type_is_fp(ty)) { + kit_cg_fp_unop(p->cg, KIT_CG_FP_NEG, KIT_CG_FP_NONE); + } else { + KitCgIntUnOp iop = op == UO_NOT ? KIT_CG_INT_NOT + : op == UO_BNOT ? KIT_CG_INT_BNOT + : KIT_CG_INT_NEG; + kit_cg_int_unop(p->cg, iop, KIT_CG_INTOP_NONE); + } + kit_cg_retag_top(p->cg, ty, 0); +} + +void c_cg_cmp(Parser* p, CmpOp op) { + if (op >= CMP_LT_F || + ((op == CMP_EQ || op == CMP_NE) && c_cg_type_is_fp(c_cg_top_type(p)))) + kit_cg_fp_cmp(p->cg, c_cg_fp_cmp(op)); + else + kit_cg_int_cmp(p->cg, c_cg_int_cmp(op)); + kit_cg_retag_top(p->cg, type_prim(p->pool, TY_INT), 0); +} + +void c_cg_convert(Parser* p, const Type* dst) { + const Type* src = c_cg_top_type(p); + KitCgTypeId src_id = c_cg_top_cg_id(p); + KitCgTypeId id = c_cg_tid(p, dst); + u32 ss = (u32)kit_cg_type_size(p->c, src_id); + u32 ds = (u32)kit_cg_type_size(p->c, id); + int si = type_is_int(src) || type_is_ptr(src); + int di = type_is_int(dst) || type_is_ptr(dst); + int sf = c_cg_type_is_fp(src); + int df = c_cg_type_is_fp(dst); + if (src == dst) return; + if (src && (src->kind == TY_STRUCT || src->kind == TY_UNION)) { + c_cg_retag_keep_flags(p, 0, dst); + return; + } + if (dst->kind == TY_BOOL && src->kind != TY_BOOL) { + if (sf) { + kit_cg_push_float(p->cg, 0.0, src_id); + kit_cg_fp_cmp(p->cg, KIT_CG_FP_UNE); + } else { + kit_cg_push_int(p->cg, 0, src_id); + kit_cg_int_cmp(p->cg, KIT_CG_INT_NE); + } + kit_cg_trunc(p->cg, id); + kit_cg_retag_top(p->cg, dst, 0); + return; + } + if (type_is_ptr(src) && type_is_ptr(dst)) { + kit_cg_bitcast(p->cg, id); + } else if (si && di) { + if (ds < ss) + kit_cg_trunc(p->cg, id); + else if (ds > ss && type_is_int(src) && c_cg_type_is_signed(src)) + kit_cg_sext(p->cg, id); + else if (ds > ss) + kit_cg_zext(p->cg, id); + else if (type_is_ptr(src) != type_is_ptr(dst)) + kit_cg_bitcast(p->cg, id); + } else if (type_is_int(src) && df) { + if (c_cg_type_is_signed(src)) + kit_cg_sint_to_float(p->cg, id, KIT_CG_ROUND_DEFAULT); + else + kit_cg_uint_to_float(p->cg, id, KIT_CG_ROUND_DEFAULT); + } else if (sf && type_is_int(dst)) { + if (c_cg_type_is_signed(dst)) + kit_cg_float_to_sint(p->cg, id, KIT_CG_ROUND_DEFAULT); + else + kit_cg_float_to_uint(p->cg, id, KIT_CG_ROUND_DEFAULT); + } else if (sf && df) { + if (ds > ss) + kit_cg_fpext(p->cg, id); + else if (ds < ss) + kit_cg_fptrunc(p->cg, id); + } else { + kit_cg_bitcast(p->cg, id); + } + kit_cg_retag_top(p->cg, dst, 0); +} + +static void c_cg_emit_inc_step(Parser* p, const Type* ty, BinOp op, + const Type* step_ty, u32 step) { + if (c_cg_type_is_fp(ty)) { + c_cg_push_float(p, 1.0, ty); + c_cg_binop(p, op == BO_ISUB ? BO_FSUB : BO_FADD); + } else { + i64 amount = (ty && ty->kind == TY_PTR) ? (i64)step : 1; + c_cg_push_int(p, amount, step_ty); + c_cg_binop(p, op); + } +} + +void c_cg_inc_dec(Parser* p, BinOp op, int post) { + const Type* ty = c_cg_top_type(p); + const Type* step_ty = ty; + u32 step = 1; + KitCgMemAccess access = c_cg_mem_id(p, c_cg_top_cg_id(p), ty); + if (ty && ty->kind == TY_PTR) { + const Type* pointee = ty->ptr.pointee; + if (pointee && pointee->kind == TY_VOID) + perr(p, "pointer arithmetic on void pointer"); + step = c_abi_sizeof(p->abi, p->pool, pointee); + step_ty = c_abi_ptrdiff_type(p->abi, p->pool); + } + c_cg_dup(p); + c_cg_load(p); + if (post) { + FrameSlotDesc fsd; + FrameSlot tmp; + memset(&fsd, 0, sizeof fsd); + fsd.type = ty; + fsd.size = c_abi_sizeof(p->abi, p->pool, ty); + fsd.align = c_abi_alignof(p->abi, p->pool, ty); + fsd.kind = FS_LOCAL; + tmp = c_cg_local(p, &fsd); + c_cg_push_local_typed(p, tmp, ty); + c_cg_swap(p); + c_cg_store_void(p); + c_cg_push_local_typed(p, tmp, ty); + c_cg_load(p); + c_cg_emit_inc_step(p, ty, op, step_ty, step); + kit_cg_store(p->cg, access); + c_cg_push_local_typed(p, tmp, ty); + c_cg_load(p); + } else { + c_cg_emit_inc_step(p, ty, op, step_ty, step); + kit_cg_store_keep(p->cg, access); + kit_cg_retag_top(p->cg, ty, 0); + } +} + +void c_cg_call(Parser* p, u32 nargs, const Type* fn_type) { + kit_cg_call_default(p->cg, nargs, c_cg_tid(p, fn_type)); + if (fn_type && fn_type->kind == TY_FUNC && fn_type->fn.ret->kind != TY_VOID) + kit_cg_retag_top(p->cg, fn_type->fn.ret, 0); +} + +void c_cg_call_symbol(Parser* p, KitCgSym sym, u32 nargs, const Type* fn_type) { + KitCgCallAttrs attrs; + memset(&attrs, 0, sizeof attrs); + kit_cg_call_symbol(p->cg, sym, nargs, attrs); + if (fn_type && fn_type->kind == TY_FUNC && fn_type->fn.ret->kind != TY_VOID) + kit_cg_retag_top(p->cg, fn_type->fn.ret, 0); +} + +void c_cg_ret(Parser* p, int has_value) { + if (has_value) { + kit_cg_ret(p->cg); + } else if (c_cg_emit_enabled(p)) { + if (p->cur_func_ret && p->cur_func_ret->kind != TY_VOID) + kit_cg_unreachable(p->cg); + else + kit_cg_ret(p->cg); + } +} + +void c_cg_alloca(Parser* p) { + const Type* vp = type_ptr(p->pool, type_void(p->pool)); + kit_cg_alloca(p->cg, 16, c_cg_tid(p, vp)); + kit_cg_retag_top(p->cg, vp, 0); +} + +void c_cg_va_arg(Parser* p, const Type* ty) { + kit_cg_vararg_next(p->cg, c_cg_tid(p, ty)); + kit_cg_retag_top(p->cg, ty, 0); +} + +void c_cg_va_start(Parser* p) { kit_cg_vararg_start(p->cg); } + +void c_cg_va_end(Parser* p) { kit_cg_vararg_end(p->cg); } + +void c_cg_va_copy(Parser* p) { kit_cg_vararg_copy(p->cg); } + +void c_cg_atomic_load(Parser* p, MemOrder ord) { + const Type* pty = c_cg_top_type(p); + const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty; + kit_cg_atomic_load(p->cg, c_cg_mem(p, ty), c_cg_mem_order(ord)); + kit_cg_retag_top(p->cg, ty, 0); +} + +void c_cg_atomic_store(Parser* p, MemOrder ord) { + const Type* pty = c_cg_top2_type(p); + const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty; + kit_cg_atomic_store(p->cg, c_cg_mem(p, ty), c_cg_mem_order(ord)); +} + +void c_cg_atomic_rmw(Parser* p, AtomicOp op, MemOrder ord) { + const Type* pty = c_cg_top2_type(p); + const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty; + kit_cg_atomic_rmw(p->cg, c_cg_mem(p, ty), c_cg_atomic_op(op), + c_cg_mem_order(ord)); + kit_cg_retag_top(p->cg, ty, 0); +} + +void c_cg_atomic_cas(Parser* p, MemOrder succ, MemOrder fail) { + const Type* ty = c_cg_top2_type(p); + kit_cg_atomic_cmpxchg(p->cg, c_cg_mem(p, ty), c_cg_mem_order(succ), + c_cg_mem_order(fail), 0); + c_cg_retag_at(p, 1, ty, 0); + c_cg_retag_top(p, type_prim(p->pool, TY_BOOL)); +} + +void c_cg_fence(Parser* p, MemOrder ord) { + kit_cg_atomic_fence(p->cg, c_cg_mem_order(ord)); +} + +void c_cg_intrinsic_unary_to_int(Parser* p, IntrinKind k) { + KitCgIntrinsic ck = k == INTRIN_CLZ ? KIT_CG_INTRIN_CLZ + : k == INTRIN_CTZ ? KIT_CG_INTRIN_CTZ + : KIT_CG_INTRIN_POPCOUNT; + const Type* ity = type_prim(p->pool, TY_INT); + kit_cg_intrinsic(p->cg, ck, 1, c_cg_tid(p, ity)); + kit_cg_retag_top(p->cg, ity, 0); +} + +void c_cg_intrinsic_void(Parser* p, IntrinKind k) { + if (k == INTRIN_UNREACHABLE) { + kit_cg_unreachable(p->cg); + } else { + kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_TRAP, 0, + kit_cg_type_builtin(p->c, KIT_CG_BUILTIN_VOID)); + } +} + +void c_cg_syscall(Parser* p, u32 nargs, const Type* long_ty) { + kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_SYSCALL, nargs, c_cg_tid(p, long_ty)); + kit_cg_retag_top(p->cg, long_ty, 0); +} + +void c_cg_readcyclecounter(Parser* p) { + const Type* ull = type_prim(p->pool, TY_ULLONG); + kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_READCYCLECOUNTER, 0, c_cg_tid(p, ull)); + kit_cg_retag_top(p->cg, ull, 0); +} + +void c_cg_frame_or_return_address(Parser* p, int is_return, u32 level) { + const Type* void_ptr = type_ptr(p->pool, type_void(p->pool)); + KitCgIntrinsic intrin = + is_return ? KIT_CG_INTRIN_RETURN_ADDRESS : KIT_CG_INTRIN_FRAME_ADDRESS; + c_cg_push_int(p, (i64)level, type_prim(p->pool, TY_INT)); + kit_cg_intrinsic(p->cg, intrin, 1, c_cg_tid(p, void_ptr)); + kit_cg_retag_top(p->cg, void_ptr, 0); +} + +void c_cg_inline_asm(Parser* p, const char* tmpl, const AsmConstraint* outs, + u32 nout, const AsmConstraint* ins, u32 nin, + const Sym* clobbers, u32 nclob) { + KitCgInlineAsm a; + KitCgAsmOperand* o = NULL; + KitCgAsmOperand* in = NULL; + KitSym* cl = NULL; + memset(&a, 0, sizeof a); + a.tmpl = kit_sym_intern(p->c, kit_slice_cstr(tmpl ? tmpl : "")); + if (nout) { + o = arena_zarray(p->pool->arena, KitCgAsmOperand, nout); + for (u32 i = 0; i < nout; ++i) { + o[i].constraint = + kit_sym_intern(p->c, kit_slice_cstr(outs[i].str ? outs[i].str : "")); + o[i].name = outs[i].name; + o[i].type = c_cg_tid(p, outs[i].type); + o[i].reg = outs[i].reg; + o[i].dir = KIT_CG_ASM_OUT; + } + } + if (nin) { + in = arena_zarray(p->pool->arena, KitCgAsmOperand, nin); + for (u32 i = 0; i < nin; ++i) { + in[i].constraint = + kit_sym_intern(p->c, kit_slice_cstr(ins[i].str ? ins[i].str : "")); + in[i].name = ins[i].name; + in[i].type = c_cg_tid(p, ins[i].type); + in[i].reg = ins[i].reg; + in[i].dir = (ins[i].dir == ASM_INOUT) ? KIT_CG_ASM_INOUT : KIT_CG_ASM_IN; + } + } + if (nclob) { + cl = arena_array(p->pool->arena, KitSym, nclob); + for (u32 i = 0; i < nclob; ++i) cl[i] = clobbers[i]; + } + a.outputs = o; + a.noutputs = nout; + a.inputs = in; + a.ninputs = nin; + a.clobbers = cl; + a.nclobbers = nclob; + kit_cg_inline_asm(p->cg, a); + for (u32 i = 0; i < nout; ++i) { + u32 depth = nout - 1u - i; + c_cg_retag_at(p, depth, outs[i].type, 0); + } +} diff --git a/lang/c/parse/cg.h b/lang/c/parse/cg.h @@ -0,0 +1,284 @@ +#ifndef KIT_PARSE_CG_H +#define KIT_PARSE_CG_H + +/* cg.h — the C parser <-> codegen seam. + * + * Declares C-semantic helpers implemented in cg.c. The expression stack is the + * public KitCg stack; C type/category facts live in KitCg slot metadata and + * are read back through kit_cg_slot_info. */ + +#include <kit/cg.h> + +#include "c_support.h" +#include "type/type.h" + +typedef KitCg CG; +typedef KitCgLabel CGLabel; +typedef KitCgLocal FrameSlot; + +#define FRAME_SLOT_NONE KIT_CG_LOCAL_NONE +#define OBJ_GROUP_NONE 0u + +typedef enum BinOp { + BO_IADD, + BO_ISUB, + BO_IMUL, + BO_SDIV, + BO_UDIV, + BO_SREM, + BO_UREM, + BO_FADD, + BO_FSUB, + BO_FMUL, + BO_FDIV, + BO_AND, + BO_OR, + BO_XOR, + BO_SHL, + BO_SHR_S, + BO_SHR_U, +} BinOp; + +typedef enum UnOp { + UO_NEG, + UO_NOT, + UO_BNOT, +} UnOp; + +/* Compare markers used by the C parser. The integer block plus the four + * relational FP markers (CMP_LT_F..CMP_GE_F) cover the bare C operators; the + * latter map to the *ordered* FP predicates in c_cg_fp_cmp. The trailing + * ordered/unordered block mirrors the global CmpOp / public KitCgFpCmpOp so + * the C99 comparison builtins (islessgreater -> CMP_ONE_F) and any + * negation-driven unordered duals are reachable from the frontend 1:1. */ +typedef enum CmpOp { + CMP_EQ, + CMP_NE, + CMP_LT_S, + CMP_LE_S, + CMP_GT_S, + CMP_GE_S, + CMP_LT_U, + CMP_LE_U, + CMP_GT_U, + CMP_GE_U, + /* Relational FP operator markers (bare `< <= > >=` on floats) -> ordered. */ + CMP_LT_F, + CMP_LE_F, + CMP_GT_F, + CMP_GE_F, + /* Ordered FP relationals (NaN -> false). */ + CMP_OEQ_F, + CMP_ONE_F, + CMP_OLT_F, + CMP_OLE_F, + CMP_OGT_F, + CMP_OGE_F, + /* Unordered FP relationals (NaN -> true). */ + CMP_UEQ_F, + CMP_UNE_F, + CMP_ULT_F, + CMP_ULE_F, + CMP_UGT_F, + CMP_UGE_F, +} CmpOp; + +typedef enum AtomicOp { + AO_XCHG, + AO_ADD, + AO_SUB, + AO_AND, + AO_OR, + AO_XOR, + AO_NAND, +} AtomicOp; + +typedef enum MemOrder { + MO_RELAXED, + MO_CONSUME, + MO_ACQUIRE, + MO_RELEASE, + MO_ACQ_REL, + MO_SEQ_CST, +} MemOrder; + +typedef enum IntrinKind { + INTRIN_NONE = 0, + INTRIN_POPCOUNT, + INTRIN_CTZ, + INTRIN_CLZ, + INTRIN_MEMCPY, + INTRIN_MEMMOVE, + INTRIN_MEMSET, + INTRIN_PREFETCH, + INTRIN_ASSUME_ALIGNED, + INTRIN_EXPECT, + INTRIN_UNREACHABLE, + INTRIN_TRAP, + INTRIN_SETJMP, + INTRIN_LONGJMP, + INTRIN_SADD_OVERFLOW, + INTRIN_UADD_OVERFLOW, + INTRIN_SSUB_OVERFLOW, + INTRIN_USUB_OVERFLOW, + INTRIN_SMUL_OVERFLOW, + INTRIN_UMUL_OVERFLOW, +} IntrinKind; + +typedef enum AsmDir { ASM_IN, ASM_OUT, ASM_INOUT } AsmDir; + +typedef struct AsmConstraint { + const char* str; + Sym name; + const Type* type; + Sym reg; /* hard-register name for a GNU local register variable; 0 = none */ + u8 dir; + u8 pad[3]; +} AsmConstraint; + +typedef enum FrameSlotKind { + FS_LOCAL, + FS_PARAM, + FS_SPILL, + FS_SRET, + FS_ALLOCA, +} FrameSlotKind; + +typedef enum FrameSlotFlag { + FSF_NONE = 0, + FSF_ADDR_TAKEN = 1u << 0, + FSF_VOLATILE = 1u << 1, +} FrameSlotFlag; + +typedef struct FrameSlotDesc { + const Type* type; + Sym name; + SrcLoc loc; + u32 size; + u32 align; + u8 kind; + u8 pad; + u16 flags; +} FrameSlotDesc; + +typedef struct CGParamDesc { + u32 index; + Sym name; + const Type* type; + FrameSlot slot; + const void* abi; + const void* incoming; + u32 nincoming; + SrcLoc loc; +} CGParamDesc; + +typedef enum CGFuncDescFlag { + CGFD_NONE = 0, + CGFD_NORETURN = 1u << 0, +} CGFuncDescFlag; + +typedef struct CGFuncDesc { + ObjSymId sym; + ObjSecId text_section_id; + u32 group_id; + const Type* fn_type; + const void* abi; + const CGParamDesc* params; + u32 nparams; + SrcLoc loc; + u32 flags; + KitCgInlinePolicy inline_policy; +} CGFuncDesc; + +typedef struct Parser Parser; + +KitCgTypeId c_cg_tid(Parser*, const Type*); +KitCgMemAccess c_cg_mem(Parser*, const Type*); +const Type* c_cg_top_type(Parser*); +const Type* c_cg_top2_type(Parser*); +void c_cg_retag_top(Parser*, const Type*); +void c_cg_retag_at(Parser*, u32 depth, const Type*, uint16_t flags); +void c_cg_retag_keep_flags(Parser*, u32 depth, const Type*); +void c_cg_dup(Parser*); +void c_cg_swap(Parser*); +void c_cg_drop(Parser*); +void c_cg_reclaim_temps(Parser*); +uint32_t c_cg_stack_depth(Parser*); +void c_cg_drop_to_depth(Parser*, uint32_t depth); +int c_cg_top_is_bitfield(Parser*); +void c_cg_set_top_bitfield(Parser*); +int c_cg_top_is_register(Parser*); +void c_cg_set_top_register(Parser*); +int c_cg_top_is_lvalue(Parser*); +int c_cg_top_is_modifiable_lvalue(Parser*); +int c_cg_top_is_null_ptr_const(Parser*); +void c_cg_set_top_lvalue(Parser*); +int c_cg_emit_enabled(Parser*); +void c_cg_codegen_suppress_push(Parser*); +void c_cg_codegen_suppress_pop(Parser*); + +KitCgIntBinOp c_cg_int_binop(BinOp); +KitCgFpBinOp c_cg_fp_binop(BinOp); +KitCgIntCmpOp c_cg_int_cmp(CmpOp); +KitCgFpCmpOp c_cg_fp_cmp(CmpOp); +KitCgAtomicOp c_cg_atomic_op(AtomicOp); +KitCgMemOrder c_cg_mem_order(MemOrder); +int c_cg_type_is_fp(const Type*); +int c_cg_type_is_signed(const Type*); + +FrameSlot c_cg_local(Parser*, const FrameSlotDesc*); +FrameSlot c_cg_param_slot(Parser*, u32, const FrameSlotDesc*); +void c_cg_param(Parser*, const CGParamDesc*); +void c_cg_func_begin(Parser*, const CGFuncDesc*); +void c_cg_func_end(Parser*); +void c_cg_set_loc(Parser*, SrcLoc); +void c_cg_push_int(Parser*, i64, const Type*); +void c_cg_push_float(Parser*, double, const Type*); +void c_cg_push_local_typed(Parser*, FrameSlot, const Type*); +void c_cg_push_global(Parser*, ObjSymId, const Type*); +void c_cg_load(Parser*); +void c_cg_addr(Parser*); +void c_cg_push_label_addr(Parser*, CGLabel); +void c_cg_computed_goto(Parser*, const CGLabel*, u32); +CGLabel c_cg_label_new(Parser*); +void c_cg_label_place(Parser*, CGLabel); +void c_cg_jump(Parser*, CGLabel); +void c_cg_branch_true(Parser*, CGLabel); +void c_cg_branch_false(Parser*, CGLabel); +void c_cg_store(Parser*); +/* Store whose assignment value is discarded (replaces `c_cg_store; c_cg_drop`). + */ +void c_cg_store_void(Parser*); +void c_cg_deref(Parser*, const Type*); +void c_cg_lv_member(Parser*, i64 byte_offset, const Type* field_ty, + u16 bf_offset, u16 bf_width, u32 bf_storage_size); +void c_cg_lv_subscript(Parser*, u32 elem_size, const Type* elem_ty); +void c_cg_decay_array(Parser*, const Type* arr_ty); + +void c_cg_binop(Parser*, BinOp); +void c_cg_unop(Parser*, UnOp); +void c_cg_cmp(Parser*, CmpOp); +void c_cg_convert(Parser*, const Type*); +void c_cg_inc_dec(Parser*, BinOp, int); +void c_cg_call(Parser*, u32, const Type*); +void c_cg_call_symbol(Parser*, KitCgSym, u32, const Type*); +void c_cg_ret(Parser*, int); +void c_cg_alloca(Parser*); +void c_cg_va_arg(Parser*, const Type*); +void c_cg_va_start(Parser*); +void c_cg_va_end(Parser*); +void c_cg_va_copy(Parser*); +void c_cg_atomic_load(Parser*, MemOrder); +void c_cg_atomic_store(Parser*, MemOrder); +void c_cg_atomic_rmw(Parser*, AtomicOp, MemOrder); +void c_cg_atomic_cas(Parser*, MemOrder, MemOrder); +void c_cg_fence(Parser*, MemOrder); +void c_cg_intrinsic_unary_to_int(Parser*, IntrinKind); +void c_cg_intrinsic_void(Parser*, IntrinKind); +void c_cg_syscall(Parser*, u32 nargs, const Type* long_ty); +void c_cg_frame_or_return_address(Parser*, int is_return, u32 level); +void c_cg_readcyclecounter(Parser*); +void c_cg_inline_asm(Parser*, const char*, const AsmConstraint*, u32, + const AsmConstraint*, u32, const Sym*, u32); + +#endif diff --git a/lang/c/parse/cg_adapter.c b/lang/c/parse/cg_adapter.c @@ -1,1365 +0,0 @@ -#include <string.h> - -#include "parse/parse_priv.h" - -KitCgTypeId pcg_tid(Parser* p, const Type* ty) { - return type_cg_id_in_pool(p->c, p->pool, ty); -} - -/* Set both a slot's C type and its lowered CG id together so the two never - * drift. Every site that writes slot->type must go through this (or copy a - * whole slot, which carries cg_id along). The id is lowered lazily on first - * read (pcg_slot_cg_id), not here, so an incomplete record stamped at push time - * still lowers to its real layout once completed -- identical to a direct - * type_cg_id_in_pool call. */ -static void pcg_slot_set_type(PcgSlot* s, const Type* ty) { - s->type = ty; - s->cg_id = KIT_CG_TYPE_NONE; /* lazily lowered on first read */ -} - -/* The slot's lowered CG id, lowering+caching on first read. A NULL type yields - * KIT_CG_TYPE_NONE every time (the sentinel also means "unfilled", but - * type_cg_id_in_pool(NULL) is constant so re-lowering is free). For a real type - * the first read crosses the bridge once; later reads are a load. */ -static KitCgTypeId pcg_slot_cg_id(Parser* p, PcgSlot* s) { - if (s->cg_id == KIT_CG_TYPE_NONE && s->type) - s->cg_id = type_cg_id_in_pool(p->c, p->pool, s->type); - return s->cg_id; -} - -/* The cached CG id of the TOS slot's type, lowering lazily on first read. - * Empty-stack-safe (returns NONE), matching pcg_tid(p, pcg_top_type(p)). */ -static KitCgTypeId pcg_top_cg_id(Parser* p) { - return p->cg_type_sp - ? pcg_slot_cg_id(p, &p->cg_slot_stack[p->cg_type_sp - 1u]) - : KIT_CG_TYPE_NONE; -} - -/* The cached CG id of the depth-2 slot's type (matching pcg_top2_type). */ -static KitCgTypeId pcg_top2_cg_id(Parser* p) { - return p->cg_type_sp >= 2 - ? pcg_slot_cg_id(p, &p->cg_slot_stack[p->cg_type_sp - 2u]) - : KIT_CG_TYPE_NONE; -} - -#define PCG_VALUE_LVALUE 1u -#define PCG_VALUE_MODIFIABLE 2u -#define PCG_VALUE_BITFIELD 4u -#define PCG_VALUE_NULL_PTR_CONST 8u -#define PCG_VALUE_REGISTER 16u - -static u8 pcg_lvalue_flags_for_type(const Type* ty) { - u8 flags = PCG_VALUE_LVALUE; - if (ty && !(ty->qual & Q_CONST) && ty->kind != TY_ARRAY && - ty->kind != TY_FUNC && ty->kind != TY_VOID) { - flags |= PCG_VALUE_MODIFIABLE; - } - return flags; -} - -/* Build a MemAccess from an already-lowered CG id plus the C type (for its - * qualifiers). Lets a caller that already has the slot's cached cg_id skip the - * type_cg_id_in_pool re-crossing pcg_mem(ty) does twice (lower + align). */ -static KitCgMemAccess pcg_mem_id(Parser* p, KitCgTypeId id, const Type* ty) { - KitCgMemAccess m; - memset(&m, 0, sizeof m); - m.type = id; - m.align = (u32)kit_cg_type_align(p->c, id); - if (ty && (ty->qual & Q_VOLATILE)) m.flags |= KIT_CG_MEM_VOLATILE; - if (type_is_int(ty) && pcg_type_is_signed(ty)) - m.flags |= KIT_CG_MEM_SOURCE_SIGNED; - return m; -} - -KitCgMemAccess pcg_mem(Parser* p, const Type* ty) { - return pcg_mem_id(p, pcg_tid(p, ty), ty); -} - -static void pcg_aux_clear(PcgLvAux* a) { - a->offset = 0; - a->scale = 0; - a->bit_offset = 0; - a->bit_width = 0; - a->storage_size = 0; - a->bit_signed = 0; - a->base_kind = PCG_LV_BASE_LOCAL; - a->is_subobject = 0; - a->pad[0] = a->pad[1] = a->pad[2] = a->pad[3] = a->pad[4] = 0; -} - -static void pcg_stack_grow(Parser* p, u32 want) { - PcgSlot* ns; - u32 nc; - if (p->cg_type_cap >= want) return; - nc = p->cg_type_cap ? p->cg_type_cap * 2u : 64u; - while (nc < want) nc *= 2u; - ns = arena_zarray(p->pool->arena, PcgSlot, nc); - if (!ns) perr(p, "out of memory in CG shadow stack"); - if (p->cg_slot_stack && p->cg_type_sp) - memcpy(ns, p->cg_slot_stack, sizeof(*ns) * p->cg_type_sp); - p->cg_slot_stack = ns; - p->cg_type_cap = nc; -} - -void pcg_push_type(Parser* p, const Type* ty) { - PcgSlot* s; - pcg_stack_grow(p, p->cg_type_sp + 1u); - s = &p->cg_slot_stack[p->cg_type_sp]; - pcg_slot_set_type(s, ty); - s->flags = 0; - pcg_aux_clear(&s->aux); - ++p->cg_type_sp; -} - -void pcg_drop_type(Parser* p) { - if (p->cg_type_sp) --p->cg_type_sp; -} - -void pcg_dup_type(Parser* p) { - PcgSlot top; - if (p->cg_type_sp) { - top = p->cg_slot_stack[p->cg_type_sp - 1u]; - } else { - pcg_slot_set_type(&top, NULL); - top.flags = 0; - pcg_aux_clear(&top.aux); - } - /* Copy the slot out before push: pcg_stack_grow may reallocate. The whole - * slot (cg_id included) is copied back at the end, so dup preserves the id. - */ - pcg_push_type(p, top.type); - if (p->cg_type_sp) p->cg_slot_stack[p->cg_type_sp - 1u] = top; -} - -void pcg_swap_type(Parser* p) { - if (p->cg_type_sp >= 2) { - PcgSlot tmp = p->cg_slot_stack[p->cg_type_sp - 1u]; - p->cg_slot_stack[p->cg_type_sp - 1u] = p->cg_slot_stack[p->cg_type_sp - 2u]; - p->cg_slot_stack[p->cg_type_sp - 2u] = tmp; - } -} - -void pcg_rot3_type(Parser* p) { - if (p->cg_type_sp >= 3) { - PcgSlot a = p->cg_slot_stack[p->cg_type_sp - 3u]; - p->cg_slot_stack[p->cg_type_sp - 3u] = p->cg_slot_stack[p->cg_type_sp - 2u]; - p->cg_slot_stack[p->cg_type_sp - 2u] = p->cg_slot_stack[p->cg_type_sp - 1u]; - p->cg_slot_stack[p->cg_type_sp - 1u] = a; - } -} - -/* Structural CG ops that must keep the parser's typed shadow stack in lockstep - * with the CG stack: each emits the CG op (when codegen is enabled) and mirrors - * the same structural effect onto the type stack. */ -void pcg_dup(Parser* p) { - if (pcg_emit_enabled(p)) kit_cg_dup(p->cg); - pcg_dup_type(p); -} - -void pcg_swap(Parser* p) { - if (pcg_emit_enabled(p)) kit_cg_swap(p->cg); - pcg_swap_type(p); -} - -void pcg_drop(Parser* p) { - if (pcg_emit_enabled(p)) kit_cg_drop(p->cg); - pcg_drop_type(p); -} - -/* Reclaim dead compiler-temp slots at a statement boundary. The authoritative - * "no value is live" witness is the CG value stack depth, which - * kit_cg_reclaim_temps checks (g->sp == 0): a stray non-empty call is a safe - * no-op rather than a miscompile. (The parser's typed shadow stack cg_type_sp - * is NOT a reliable witness — it is not drained in lockstep with the value - * stack.) */ -void pcg_reclaim_temps(Parser* p) { - if (!pcg_emit_enabled(p)) return; - kit_cg_reclaim_temps(p->cg); -} - -PcgLvAux* pcg_top_lv_aux(Parser* p) { - return p->cg_type_sp ? &p->cg_slot_stack[p->cg_type_sp - 1u].aux : NULL; -} - -PcgLvAux* pcg_lv_aux_at(Parser* p, u32 depth) { - return (p->cg_type_sp > depth) - ? &p->cg_slot_stack[p->cg_type_sp - 1u - depth].aux - : NULL; -} - -const Type* pcg_top_type(Parser* p) { - return p->cg_type_sp ? p->cg_slot_stack[p->cg_type_sp - 1u].type : NULL; -} - -const Type* pcg_top2_type(Parser* p) { - return p->cg_type_sp >= 2 ? p->cg_slot_stack[p->cg_type_sp - 2u].type : NULL; -} - -void pcg_retag_top(Parser* p, const Type* ty) { - if (p->cg_type_sp) { - pcg_slot_set_type(&p->cg_slot_stack[p->cg_type_sp - 1u], ty); - p->cg_slot_stack[p->cg_type_sp - 1u].flags = 0; - pcg_aux_clear(&p->cg_slot_stack[p->cg_type_sp - 1u].aux); - } -} - -/* Replace the type of the slot `depth` below the top (depth 0 == top) while - * preserving its value flags and lvalue aux. Unlike pcg_retag_top this keeps - * the slot's lvalue-ness/bitfield/aux intact — used where only the C type - * changes (e.g. struct unqualification, narrowing a compound-assign LHS). */ -void pcg_retag_keep_flags(Parser* p, u32 depth, const Type* ty) { - if (p->cg_type_sp > depth) - pcg_slot_set_type(&p->cg_slot_stack[p->cg_type_sp - 1u - depth], ty); -} - -int pcg_top_is_bitfield(Parser* p) { - return p->cg_type_sp && - (p->cg_slot_stack[p->cg_type_sp - 1u].flags & PCG_VALUE_BITFIELD) != 0; -} - -void pcg_set_top_bitfield(Parser* p) { - if (p->cg_type_sp) - p->cg_slot_stack[p->cg_type_sp - 1u].flags |= PCG_VALUE_BITFIELD; -} - -int pcg_top_is_register(Parser* p) { - return p->cg_type_sp && - (p->cg_slot_stack[p->cg_type_sp - 1u].flags & PCG_VALUE_REGISTER) != 0; -} - -void pcg_set_top_register(Parser* p) { - if (p->cg_type_sp) - p->cg_slot_stack[p->cg_type_sp - 1u].flags |= PCG_VALUE_REGISTER; -} - -int pcg_top_is_lvalue(Parser* p) { - return p->cg_type_sp && - (p->cg_slot_stack[p->cg_type_sp - 1u].flags & PCG_VALUE_LVALUE) != 0; -} - -int pcg_top_is_modifiable_lvalue(Parser* p) { - return p->cg_type_sp && (p->cg_slot_stack[p->cg_type_sp - 1u].flags & - (PCG_VALUE_LVALUE | PCG_VALUE_MODIFIABLE)) == - (PCG_VALUE_LVALUE | PCG_VALUE_MODIFIABLE); -} - -int pcg_top_is_null_ptr_const(Parser* p) { - return p->cg_type_sp && (p->cg_slot_stack[p->cg_type_sp - 1u].flags & - PCG_VALUE_NULL_PTR_CONST) != 0; -} - -void pcg_set_top_lvalue(Parser* p) { - const Type* ty = pcg_top_type(p); - if (p->cg_type_sp) - p->cg_slot_stack[p->cg_type_sp - 1u].flags = pcg_lvalue_flags_for_type(ty); -} - -int pcg_emit_enabled(Parser* p) { return p && p->suppress_codegen == 0; } - -void pcg_codegen_suppress_push(Parser* p) { - if (p) ++p->suppress_codegen; -} - -void pcg_codegen_suppress_pop(Parser* p) { - if (!p) return; - if (!p->suppress_codegen) - perr(p, "internal parser codegen suppression underflow"); - --p->suppress_codegen; -} - -int pcg_type_is_fp(const Type* ty) { - return ty && type_kind_is_fp((TypeKind)ty->kind); -} - -int pcg_type_is_signed(const Type* ty) { return type_is_signed_integer(ty); } - -KitCgIntBinOp pcg_int_binop(BinOp op) { - switch (op) { - case BO_IADD: - return KIT_CG_INT_ADD; - case BO_ISUB: - return KIT_CG_INT_SUB; - case BO_IMUL: - return KIT_CG_INT_MUL; - case BO_SDIV: - return KIT_CG_INT_SDIV; - case BO_UDIV: - return KIT_CG_INT_UDIV; - case BO_SREM: - return KIT_CG_INT_SREM; - case BO_UREM: - return KIT_CG_INT_UREM; - case BO_AND: - return KIT_CG_INT_AND; - case BO_OR: - return KIT_CG_INT_OR; - case BO_XOR: - return KIT_CG_INT_XOR; - case BO_SHL: - return KIT_CG_INT_SHL; - case BO_SHR_S: - return KIT_CG_INT_ASHR; - case BO_SHR_U: - return KIT_CG_INT_LSHR; - default: - return KIT_CG_INT_ADD; - } -} - -KitCgFpBinOp pcg_fp_binop(BinOp op) { - switch (op) { - case BO_FADD: - return KIT_CG_FP_ADD; - case BO_FSUB: - return KIT_CG_FP_SUB; - case BO_FMUL: - return KIT_CG_FP_MUL; - case BO_FDIV: - return KIT_CG_FP_DIV; - default: - return KIT_CG_FP_ADD; - } -} - -KitCgIntCmpOp pcg_int_cmp(CmpOp op) { - switch (op) { - case CMP_EQ: - return KIT_CG_INT_EQ; - case CMP_NE: - return KIT_CG_INT_NE; - case CMP_LT_S: - return KIT_CG_INT_LT_S; - case CMP_LE_S: - return KIT_CG_INT_LE_S; - case CMP_GT_S: - return KIT_CG_INT_GT_S; - case CMP_GE_S: - return KIT_CG_INT_GE_S; - case CMP_LT_U: - return KIT_CG_INT_LT_U; - case CMP_LE_U: - return KIT_CG_INT_LE_U; - case CMP_GT_U: - return KIT_CG_INT_GT_U; - case CMP_GE_U: - return KIT_CG_INT_GE_U; - default: - return KIT_CG_INT_EQ; - } -} - -KitCgFpCmpOp pcg_fp_cmp(CmpOp op) { - switch (op) { - case CMP_EQ: - return KIT_CG_FP_OEQ; - case CMP_NE: - /* C `!=` on floats is *unordered* not-equal: `NaN != x` is true (and - * `__builtin_isnan(x)` lowers to `x != x`). Map to UNE, not ONE. */ - return KIT_CG_FP_UNE; - case CMP_LT_F: - case CMP_OLT_F: - return KIT_CG_FP_OLT; - case CMP_LE_F: - case CMP_OLE_F: - return KIT_CG_FP_OLE; - case CMP_GT_F: - case CMP_OGT_F: - return KIT_CG_FP_OGT; - case CMP_GE_F: - case CMP_OGE_F: - return KIT_CG_FP_OGE; - case CMP_OEQ_F: - return KIT_CG_FP_OEQ; - case CMP_ONE_F: - return KIT_CG_FP_ONE; - case CMP_UEQ_F: - return KIT_CG_FP_UEQ; - case CMP_UNE_F: - return KIT_CG_FP_UNE; - case CMP_ULT_F: - return KIT_CG_FP_ULT; - case CMP_ULE_F: - return KIT_CG_FP_ULE; - case CMP_UGT_F: - return KIT_CG_FP_UGT; - case CMP_UGE_F: - return KIT_CG_FP_UGE; - default: - return KIT_CG_FP_OEQ; - } -} - -KitCgAtomicOp pcg_atomic_op(AtomicOp op) { - switch (op) { - case AO_XCHG: - return KIT_CG_ATOMIC_XCHG; - case AO_ADD: - return KIT_CG_ATOMIC_ADD; - case AO_SUB: - return KIT_CG_ATOMIC_SUB; - case AO_AND: - return KIT_CG_ATOMIC_AND; - case AO_OR: - return KIT_CG_ATOMIC_OR; - case AO_XOR: - return KIT_CG_ATOMIC_XOR; - case AO_NAND: - return KIT_CG_ATOMIC_NAND; - } - return KIT_CG_ATOMIC_XCHG; -} - -KitCgMemOrder pcg_mem_order(MemOrder ord) { return (KitCgMemOrder)ord; } - -static int pcg_slot_is_volatile(const FrameSlotDesc* fsd) { - return fsd && ((fsd->flags & FSF_VOLATILE) || - (fsd->type && (fsd->type->qual & Q_VOLATILE))); -} - -FrameSlot pcg_local(Parser* p, const FrameSlotDesc* fsd) { - KitCgLocalAttrs attrs; - memset(&attrs, 0, sizeof attrs); - if (!pcg_emit_enabled(p)) return FRAME_SLOT_NONE; - attrs.name = fsd->name; - attrs.align = fsd->align; - attrs.debug_type = type_cg_debug_in_pool(p->cg, p->c, p->pool, fsd->type); - if (pcg_slot_is_volatile(fsd)) attrs.flags |= KIT_CG_LOCAL_MEMORY_REQUIRED; - /* FSF_ADDR_TAKEN is no longer propagated to CG: there is no - * KIT_CG_LOCAL_ADDRESS_TAKEN attribute. The C-side flag stays for any - * parser-internal uses; opt's opt_promote_scalar_locals (Stream I) decides - * register-promotion from observed access patterns, not from the flag. */ - return kit_cg_local(p->cg, pcg_tid(p, fsd->type), attrs); -} - -FrameSlot pcg_param_slot(Parser* p, u32 index, const FrameSlotDesc* fsd) { - KitCgLocalAttrs attrs; - if (!pcg_emit_enabled(p)) return FRAME_SLOT_NONE; - memset(&attrs, 0, sizeof attrs); - attrs.name = fsd->name; - attrs.align = fsd->align; - attrs.debug_type = type_cg_debug_in_pool(p->cg, p->c, p->pool, fsd->type); - if (pcg_slot_is_volatile(fsd)) attrs.flags |= KIT_CG_LOCAL_MEMORY_REQUIRED; - return kit_cg_param(p->cg, index, pcg_tid(p, fsd->type), attrs); -} - -void pcg_param(Parser* p, const CGParamDesc* pd) { - (void)p; - (void)pd; -} - -void pcg_func_begin(Parser* p, const CGFuncDesc* fd) { - if (pcg_emit_enabled(p)) { - KitCgFuncAttrs attrs; - memset(&attrs, 0, sizeof attrs); - attrs.inline_policy = fd->inline_policy; - attrs.debug_type = type_cg_debug_in_pool(p->cg, p->c, p->pool, fd->fn_type); - if (fd->flags & CGFD_NORETURN) attrs.flags |= KIT_CG_FUNC_NORETURN; - kit_cg_func_begin_attrs(p->cg, fd->sym, attrs); - } -} - -void pcg_func_end(Parser* p) { - if (pcg_emit_enabled(p)) kit_cg_func_end(p->cg); -} - -void pcg_set_loc(Parser* p, SrcLoc loc) { - if (pcg_emit_enabled(p)) kit_cg_set_loc(p->cg, loc); -} - -void pcg_push_int(Parser* p, i64 v, const Type* ty) { - if (pcg_emit_enabled(p)) { - kit_cg_push_int(p->cg, (uint64_t)v, pcg_tid(p, ty)); - } - pcg_push_type(p, ty); - if (v == 0 && p->cg_type_sp) { - p->cg_slot_stack[p->cg_type_sp - 1u].flags |= PCG_VALUE_NULL_PTR_CONST; - } -} - -void pcg_push_float(Parser* p, double v, const Type* ty) { - if (pcg_emit_enabled(p)) kit_cg_push_float(p->cg, v, pcg_tid(p, ty)); - pcg_push_type(p, ty); -} - -/* Tag the TOS place as a bit-field place when `lv` carries bit-field geometry. - * The frontend builds the storage-unit place itself (materialize + deref), so - * it attaches the descriptor here; a plain load/store then extracts/inserts the - * field. Must be called on the place each time a fresh one is built (each deref - * yields a plain place). No-op for non-bit-field lvalues. */ -static void pcg_apply_bitfield(Parser* p, const PcgLvAux* lv) { - if (lv && lv->bit_width) { - kit_cg_field_bits(p->cg, lv->bit_offset, lv->bit_width, lv->storage_size, - lv->bit_signed); - } -} - -/* A trivially-addressable lvalue: a bare local slot with no pending field - * offset, array scale, or bit-field. Its PLACE is already on the CG stack - * (push_local), so a load/store can consume it directly without first - * materializing an address. */ -static int pcg_lv_is_trivial_local(const PcgLvAux* lv) { - return lv && lv->base_kind == PCG_LV_BASE_LOCAL && lv->offset == 0 && - lv->scale == 0 && lv->bit_width == 0; -} - -/* Materialize the TOS lvalue (folding its pending field offset / array scale - * into the address) as a single pointer rvalue. Defined below; the memops use - * it to build an explicit place before load/store. */ -static void pcg_materialize_lv_to_ptr(Parser* p, const Type* result_ptr_ty); -static void pcg_lv_to_memop_place(Parser* p, const Type* field_ty); - -void pcg_push_local_typed(Parser* p, FrameSlot s, const Type* ty) { - if (pcg_emit_enabled(p)) kit_cg_push_local(p->cg, s); - pcg_push_type(p, ty); - if (p->cg_type_sp) { - p->cg_slot_stack[p->cg_type_sp - 1u].flags = pcg_lvalue_flags_for_type(ty); - p->cg_slot_stack[p->cg_type_sp - 1u].aux.base_kind = PCG_LV_BASE_LOCAL; - } -} - -void pcg_push_global(Parser* p, ObjSymId sym, const Type* ty) { - /* push_symbol_addr produces a pointer rvalue; the parser tags the slot as - * a C-language lvalue with PCG_LV_BASE_POINTER_RV so subsequent - * load/store/addr know the base is already a pointer. The cg layer accepts - * pointer-rvalue bases for memops uniformly (Stream A). */ - if (pcg_emit_enabled(p)) kit_cg_push_symbol_addr(p->cg, sym, 0); - pcg_push_type(p, ty); - if (p->cg_type_sp) { - p->cg_slot_stack[p->cg_type_sp - 1u].flags = pcg_lvalue_flags_for_type(ty); - p->cg_slot_stack[p->cg_type_sp - 1u].aux.base_kind = PCG_LV_BASE_POINTER_RV; - } -} - -void pcg_load(Parser* p) { - const Type* ty = pcg_top_type(p); - int was_lvalue = pcg_top_is_lvalue(p); - if (pcg_emit_enabled(p)) { - PcgLvAux* lv = pcg_top_lv_aux(p); - KitCgTypeId cg_id = pcg_top_cg_id(p); /* cached TOS-slot id, lowered once */ - KitCgMemAccess access = pcg_mem_id(p, cg_id, ty); - /* Snapshot bit-field geometry before materialize clears the aux. */ - PcgLvAux bf = lv ? *lv : (PcgLvAux){0}; - /* Build the PLACE the strict load requires. A trivial local already has its - * PLACE on the CG stack (push_local) and loads directly; anything else is - * reduced to a single pointer (materialize, or a pointer-rvalue base) and - * then deref'd to a place. A bit-field place is then tagged with its bit - * geometry so the load extracts the field. */ - if (!was_lvalue || !pcg_lv_is_trivial_local(lv)) { - if (was_lvalue) - pcg_lv_to_memop_place(p, ty); - else - kit_cg_deref(p->cg, 0); - } - pcg_apply_bitfield(p, &bf); - kit_cg_load(p->cg, access); - } - if (was_lvalue && p->cg_type_sp) { - /* The emit block above can rewrite the TOS slot's type (materialize / - * deref push/drop), so restore it to the loaded value's type `ty` here -- - * stamping cg_id alongside it. */ - pcg_slot_set_type(&p->cg_slot_stack[p->cg_type_sp - 1u], ty); - p->cg_slot_stack[p->cg_type_sp - 1u].flags = 0; - pcg_aux_clear(&p->cg_slot_stack[p->cg_type_sp - 1u].aux); - } -} - -/* Materialize the pending EA on the TOS lvalue as a pointer rvalue. - * Postcondition: TOS is a pointer rvalue of type result_ptr_ty (which the - * caller has computed as type_ptr(pool, current_lv_type)) and the CG stack - * holds that single pointer where the lvalue's [base] or [base, index] used - * to be. Aux is cleared. - * - * The materialization sequence depends on the aux: - * base_kind == LOCAL: - * - scale == 0, offset == 0: addr - * - scale == 0, offset != 0: addr ; ptr_to_int ; +offset ; - * int_to_ptr - * - scale != 0: addr ; ptr_to_int ; idx*scale + ofs ; - * int_to_ptr base_kind == POINTER_RV: - * - scale == 0, offset == 0: no-op - * - scale == 0, offset != 0: ptr_to_int ; +offset ; int_to_ptr - * - scale != 0: ptr_to_int ; idx*scale + ofs ; - * int_to_ptr */ -static void pcg_materialize_lv_to_ptr(Parser* p, const Type* result_ptr_ty) { - PcgLvAux* lv = pcg_top_lv_aux(p); - int emit = pcg_emit_enabled(p); - PcgLvBaseKind base_kind = - lv ? (PcgLvBaseKind)lv->base_kind : PCG_LV_BASE_LOCAL; - i64 ofs = lv ? lv->offset : 0; - u32 scale = lv ? lv->scale : 0u; - const Type* idx_ty = c_abi_ptrdiff_type(p->abi, p->pool); - KitCgTypeId idx_tid = pcg_tid(p, idx_ty); - KitCgTypeId ptr_tid = pcg_tid(p, result_ptr_ty); - if (scale == 0 && ofs == 0) { - if (base_kind == PCG_LV_BASE_LOCAL) { - if (emit) kit_cg_addr(p->cg); - } - /* Already a pointer with no pending modifiers. */ - } else if (scale == 0) { - if (emit) { - if (base_kind == PCG_LV_BASE_LOCAL) kit_cg_addr(p->cg); - kit_cg_ptr_to_int(p->cg, idx_tid); - kit_cg_push_int(p->cg, (uint64_t)ofs, idx_tid); - kit_cg_int_binop(p->cg, KIT_CG_INT_ADD, KIT_CG_INTOP_NONE); - kit_cg_int_to_ptr(p->cg, ptr_tid); - } - } else { - /* CG stack on entry: [base_ptr_now, index]. Compute - * base_ptr_now + index*scale + ofs. */ - if (emit) { - if (base_kind == PCG_LV_BASE_LOCAL) { - kit_cg_swap(p->cg); /* [index, base_lv] */ - kit_cg_addr(p->cg); /* [index, base_ptr] */ - kit_cg_swap(p->cg); /* [base_ptr, index] */ - } - kit_cg_swap(p->cg); /* [index, base_ptr] */ - kit_cg_ptr_to_int(p->cg, idx_tid); - kit_cg_swap(p->cg); /* [base_int, index] */ - kit_cg_push_int(p->cg, (uint64_t)scale, idx_tid); - kit_cg_int_binop(p->cg, KIT_CG_INT_MUL, KIT_CG_INTOP_NONE); - kit_cg_int_binop(p->cg, KIT_CG_INT_ADD, KIT_CG_INTOP_NONE); - if (ofs != 0) { - kit_cg_push_int(p->cg, (uint64_t)ofs, idx_tid); - kit_cg_int_binop(p->cg, KIT_CG_INT_ADD, KIT_CG_INTOP_NONE); - } - kit_cg_int_to_ptr(p->cg, ptr_tid); - } - } - pcg_retag_top(p, result_ptr_ty); - { - PcgLvAux* out = pcg_top_lv_aux(p); - if (out) out->base_kind = PCG_LV_BASE_POINTER_RV; - } -} - -/* Build the PLACE a load/store reads, from the TOS lvalue, keeping a constant - * field/element offset as the deref displacement (so the backend folds it into - * the load/store: `ldr w, [base, #ofs]`) rather than baking it into an explicit - * `base + ofs` pointer the way pcg_materialize_lv_to_ptr + kit_cg_deref(0) - * does. Mirrors that pair's postcondition (CG TOS is the place; parser slot is - * the field pointer type with aux cleared). The pointer is reinterpreted to the - * field type with a bitcast — free on the native backend (a no-op same-width - * cast), an explicit cast on the C-source backend. Indexed (scale != 0) and - * out-of-displacement-range offsets fall back to the explicit-pointer fold. - * Caller guarantees emit is enabled. */ -static void pcg_lv_to_memop_place(Parser* p, const Type* field_ty) { - PcgLvAux* lv = pcg_top_lv_aux(p); - PcgLvBaseKind base_kind = - lv ? (PcgLvBaseKind)lv->base_kind : PCG_LV_BASE_LOCAL; - i64 ofs = lv ? lv->offset : 0; - u32 scale = lv ? lv->scale : 0u; - const Type* fptr = type_ptr(p->pool, field_ty); - if (ofs < INT32_MIN || ofs > INT32_MAX) { - /* Out-of-int32 displacement can't ride a deref/EA offset; materialize an - * explicit base+ofs pointer. */ - pcg_materialize_lv_to_ptr(p, fptr); - kit_cg_deref(p->cg, 0); - return; - } - if (scale != 0u) { - /* Scaled subscript: build one fused [base + index*scale + ofs] place via - * kit_cg_elem_scaled rather than materializing a standalone - * base+index*scale+ofs pointer (an explicit mul/add + a copy into the deref - * base). CG stack on entry is [base, index]; decay the base to a field_ty* - * pointer value, then fuse. The index stride is the containing array's - * element size (scale) while the access type is field_ty, so `a[i].f` - * strides by sizeof(elem) but reads `f`. */ - kit_cg_swap(p->cg); /* [index, base] */ - if (base_kind == PCG_LV_BASE_LOCAL) - kit_cg_addr(p->cg); /* [index, base_ptr] */ - kit_cg_bitcast(p->cg, pcg_tid(p, fptr)); /* [index, (T*)base] */ - kit_cg_swap(p->cg); /* [base, index] */ - kit_cg_elem_scaled(p->cg, scale, ofs); /* [place] */ - } else { - if (base_kind == PCG_LV_BASE_LOCAL) kit_cg_addr(p->cg); - kit_cg_bitcast(p->cg, pcg_tid(p, fptr)); - kit_cg_deref(p->cg, ofs); - } - pcg_retag_top(p, fptr); - { - PcgLvAux* out = pcg_top_lv_aux(p); - if (out) out->base_kind = PCG_LV_BASE_POINTER_RV; - } -} - -void pcg_addr(Parser* p) { - const Type* ty = pcg_top_type(p); - pcg_materialize_lv_to_ptr(p, type_ptr(p->pool, ty)); -} - -/* Pushes the address of a label as a `void*` rvalue (GNU `&&label`). */ -void pcg_push_label_addr(Parser* p, CGLabel label) { - const Type* vp = type_ptr(p->pool, type_void(p->pool)); - if (pcg_emit_enabled(p)) kit_cg_push_label_addr(p->cg, label, pcg_tid(p, vp)); - pcg_push_type(p, vp); -} - -/* Pops the target pointer and emits a computed `goto *expr;`. */ -void pcg_computed_goto(Parser* p, const CGLabel* targets, u32 ntargets) { - if (pcg_emit_enabled(p)) kit_cg_computed_goto(p->cg, targets, ntargets); - pcg_drop_type(p); -} - -/* ---- Control flow ---- - * - * Label placement / jump / branch ops gate on emit; the conditional branches - * also pop the tested value off the type stack to mirror the CG-side consume. - * Suppressed parses, such as C99 `extern inline` bodies, do not open a CG - * function, so they use a nonzero dummy label only for semantic bookkeeping - * around break/continue/case validation. */ -CGLabel pcg_label_new(Parser* p) { - if (!pcg_emit_enabled(p)) return (CGLabel)1; - return kit_cg_label_new(p->cg); -} - -void pcg_label_place(Parser* p, CGLabel l) { - if (pcg_emit_enabled(p)) kit_cg_label_place(p->cg, l); -} - -void pcg_jump(Parser* p, CGLabel l) { - if (pcg_emit_enabled(p)) kit_cg_jump(p->cg, l); -} - -void pcg_branch_true(Parser* p, CGLabel l) { - if (pcg_emit_enabled(p)) kit_cg_branch_true(p->cg, l); - pcg_drop_type(p); -} - -void pcg_branch_false(Parser* p, CGLabel l) { - if (pcg_emit_enabled(p)) kit_cg_branch_false(p->cg, l); - pcg_drop_type(p); -} - -/* Store [lv, rv] -> [rv]. The expression-value of an assignment is the - * assigned rvalue, so the store sequence must leave a copy of rv on TOS. */ -/* keep_result == 0: consume [place, value] and leave nothing — the assignment's - * value is discarded (the dominant `expr;` / initializer case). Skipping the - * dup lets a still-delayed RHS flow straight into the destination local - * (kit_cg_store's scalar-local fast path), killing the temp->local routing mov. - * keep_result == 1: leave the assignment's value on the stack for an enclosing - * expression (`a = b = c`, `if ((x = f()))`). */ -static void pcg_store_impl(Parser* p, int keep_result) { - const Type* lv_ty = pcg_top2_type(p); - const Type* rv_ty = pcg_top_type(p); - const Type* mem_ty = lv_ty; - /* Cached CG ids of the two slots (lowered once, not re-crossed by pcg_mem). - */ - KitCgTypeId rv_cg = pcg_top_cg_id(p); - KitCgTypeId mem_cg = pcg_top2_cg_id(p); /* tracks mem_ty (= lv_ty) */ - int emit = pcg_emit_enabled(p); - /* The aux to consume lives on the lvalue slot at parser depth 1. */ - PcgLvAux* lv = pcg_lv_aux_at(p, 1); - /* Snapshot bit-field geometry before materialize clears the aux. */ - PcgLvAux bf = lv ? *lv : (PcgLvAux){0}; - KitCgMemAccess access; - if (rv_ty && type_is_ptr(rv_ty) && (!lv_ty || !type_is_ptr(lv_ty))) { - mem_ty = rv_ty; - mem_cg = rv_cg; - } - access = pcg_mem_id(p, mem_ty ? mem_cg : rv_cg, mem_ty ? mem_ty : rv_ty); - if (emit) { - int wide = - rv_ty && (rv_ty->kind == TY_INT128 || rv_ty->kind == TY_UINT128 || - rv_ty->kind == TY_LDOUBLE); - if (pcg_lv_is_trivial_local(lv) && !wide) { - /* The destination place is already on the CG stack: [place, value]. When - * the result is kept, dup rv first so a copy survives the store; when it - * is discarded, store straight from [place, value] so the RHS (possibly a - * still-delayed arith/cmp) lands directly in the local. */ - if (keep_result) { - kit_cg_dup(p->cg); - kit_cg_rot3(p->cg); - kit_cg_swap(p->cg); - } - kit_cg_store(p->cg, access); - } else { - /* Stash rv into a temp so the destination place can be built from the - * base — folding any field offset / array scale into an explicit pointer - * — then reload rv to store it and to leave it as the expression value. - */ - FrameSlotDesc fsd; - FrameSlot tmp; - KitCgMemAccess rv_access = pcg_mem_id(p, rv_cg, rv_ty); - int trivial = pcg_lv_is_trivial_local(lv); - memset(&fsd, 0, sizeof fsd); - fsd.type = rv_ty; - fsd.size = c_abi_sizeof(p->abi, p->pool, rv_ty); - fsd.align = c_abi_alignof(p->abi, p->pool, rv_ty); - fsd.kind = FS_LOCAL; - tmp = pcg_local(p, &fsd); - kit_cg_push_local(p->cg, tmp); /* [base.., value, &tmp] */ - kit_cg_swap(p->cg); /* [base.., &tmp, value] */ - kit_cg_store(p->cg, rv_access); /* [base..] (stash rv) */ - if (!trivial) { - /* Materialize operates on the parser-TOS lvalue; drop the rv type slot - * so the lvalue is on top and matches the CG [base..]. The pointer is - * deref'd to the PLACE the strict store requires, then tagged with the - * bit-field geometry so the store inserts the field. */ - pcg_drop_type(p); - pcg_lv_to_memop_place(p, lv_ty); /* [dst_place] */ - pcg_apply_bitfield(p, &bf); - pcg_push_type(p, rv_ty); - } - kit_cg_push_local(p->cg, tmp); - kit_cg_load(p->cg, rv_access); /* [dst, value] */ - kit_cg_store(p->cg, access); /* [] */ - if (keep_result) { - kit_cg_push_local(p->cg, tmp); - kit_cg_load(p->cg, rv_access); /* [value] */ - } - } - } - pcg_drop_type(p); - pcg_drop_type(p); - if (keep_result) pcg_push_type(p, rv_ty); -} - -void pcg_store(Parser* p) { pcg_store_impl(p, 1); } - -/* Store whose assignment value is discarded (the `expr;` / initializer case). - * Replaces the `pcg_store(p); pcg_drop(p)` idiom; avoids materializing the RHS - * into a temp just to drop it. */ -void pcg_store_void(Parser* p) { pcg_store_impl(p, 0); } - -void pcg_deref(Parser* p, const Type* pointee) { - const Type* ptr_ty = pcg_top_type(p); - if (pointee && pointee->kind == TY_FUNC) { - /* Function lvalues collapse to function pointers in C; no CG-level - * dereference is needed (functions aren't first-class data). */ - pcg_retag_top(p, pointee); - return; - } - if (ptr_ty && ptr_ty->kind == TY_PTR && ptr_ty->ptr.pointee != pointee) { - const Type* want_ptr_ty = type_ptr(p->pool, pointee); - if (pcg_emit_enabled(p)) kit_cg_bitcast(p->cg, pcg_tid(p, want_ptr_ty)); - pcg_retag_top(p, want_ptr_ty); - } - /* No kit_cg_indirect: the cg load/store accept pointer-rvalue bases - * directly. Mark the slot as a C-language lvalue with POINTER_RV base; the - * pointer stays on the CG stack untouched. */ - pcg_retag_top(p, pointee); - if (p->cg_type_sp) { - p->cg_slot_stack[p->cg_type_sp - 1u].flags = - pcg_lvalue_flags_for_type(pointee); - p->cg_slot_stack[p->cg_type_sp - 1u].aux.base_kind = PCG_LV_BASE_POINTER_RV; - } -} - -/* ---- Lvalue chain helpers ---- */ - -void pcg_lv_member(Parser* p, i64 byte_offset, const Type* field_ty, - u16 bf_offset, u16 bf_width, u32 bf_storage_size) { - PcgLvAux* lv = pcg_top_lv_aux(p); - int was_lvalue = pcg_top_is_lvalue(p); - const Type* base_ty = pcg_top_type(p); - /* A member of an rvalue aggregate (e.g. `mk().field` for a struct-returning - * call) lives in a CG-side temporary that is memory-backed, so the member is - * readable as an lvalue — but per C it is not a *modifiable* lvalue. */ - int base_is_rvalue_agg = - !was_lvalue && base_ty && - (base_ty->kind == TY_STRUCT || base_ty->kind == TY_UNION); - i64 saved_offset = lv ? lv->offset + byte_offset : byte_offset; - u32 saved_scale = lv ? lv->scale : 0u; - u8 saved_base_kind = lv ? lv->base_kind : PCG_LV_BASE_LOCAL; - /* Bumping the offset preserves the base kind and any earlier offset/scale - * accumulated on the chain (`a[i].f.g` keeps `scale = sizeof(elem)` and - * adds the field offsets). */ - pcg_retag_top(p, field_ty); - if (was_lvalue) { - pcg_set_top_lvalue(p); - } else if (base_is_rvalue_agg && p->cg_type_sp) { - p->cg_slot_stack[p->cg_type_sp - 1u].flags = PCG_VALUE_LVALUE; - } - /* pcg_retag_top cleared aux; re-apply the bumped offset and base kind. */ - { - PcgLvAux* lv_after = pcg_top_lv_aux(p); - if (lv_after) { - lv_after->offset = saved_offset; - lv_after->scale = saved_scale; - lv_after->base_kind = saved_base_kind; - lv_after->bit_offset = bf_offset; - lv_after->bit_width = bf_width; - lv_after->storage_size = bf_storage_size; - lv_after->bit_signed = pcg_type_is_signed(field_ty) ? 1u : 0u; - /* A member access narrows to a sub-object of a larger CG-tracked - * object; record it so aggregate reads materialize a pointer to the - * exact sub-object (the offset alone can be 0 for a first member). */ - lv_after->is_subobject = 1u; - } - if (bf_width && p->cg_type_sp) - p->cg_slot_stack[p->cg_type_sp - 1u].flags |= PCG_VALUE_BITFIELD; - } -} - -void pcg_lv_subscript(Parser* p, u32 elem_size, const Type* elem_ty) { - /* Stack on entry (parser side): [base_lv, index_rv]. - * Stack on entry (CG side): [base, index]. - * After this call (parser): [elem_lv] with aux.scale = elem_size. - * After this call (CG): [base, index] — unchanged; the eventual - * load/store consumes both via the EA. */ - PcgLvAux* base_lv = pcg_lv_aux_at(p, 1); - i64 saved_offset = base_lv ? base_lv->offset : 0; - u8 base_is_lvalue = - (p->cg_type_sp >= 2u && - (p->cg_slot_stack[p->cg_type_sp - 2u].flags & PCG_VALUE_LVALUE) != 0); - u8 saved_base_kind = !base_is_lvalue - ? PCG_LV_BASE_POINTER_RV - : (base_lv ? base_lv->base_kind : PCG_LV_BASE_LOCAL); - if (base_lv && base_lv->scale != 0) { - perr(p, "internal: nested subscript without materialization"); - } - pcg_drop_type(p); /* drop index parser slot */ - pcg_retag_top(p, elem_ty); /* retag base parser slot as element */ - pcg_set_top_lvalue(p); - { - PcgLvAux* lv = pcg_top_lv_aux(p); - if (lv) { - lv->offset = saved_offset; - lv->scale = elem_size; - lv->base_kind = saved_base_kind; - } - } -} - -void pcg_decay_array(Parser* p, const Type* arr_ty) { - const Type* ptr_ty = type_ptr(p->pool, arr_ty->arr.elem); - pcg_materialize_lv_to_ptr(p, ptr_ty); -} - -void pcg_binop(Parser* p, BinOp op) { - const Type* result = pcg_top2_type(p); - if (op == BO_FADD || op == BO_FSUB || op == BO_FMUL || op == BO_FDIV) { - if (pcg_emit_enabled(p)) { - kit_cg_fp_binop(p->cg, pcg_fp_binop(op), KIT_CG_FP_NONE); - } - } else { - if (pcg_emit_enabled(p)) { - kit_cg_int_binop(p->cg, pcg_int_binop(op), KIT_CG_INTOP_NONE); - } - } - pcg_drop_type(p); - pcg_retag_top(p, result); -} - -void pcg_unop(Parser* p, UnOp op) { - if (op == UO_NEG && pcg_type_is_fp(pcg_top_type(p))) { - if (pcg_emit_enabled(p)) - kit_cg_fp_unop(p->cg, KIT_CG_FP_NEG, KIT_CG_FP_NONE); - } else { - KitCgIntUnOp iop = op == UO_NOT ? KIT_CG_INT_NOT - : op == UO_BNOT ? KIT_CG_INT_BNOT - : KIT_CG_INT_NEG; - if (pcg_emit_enabled(p)) kit_cg_int_unop(p->cg, iop, KIT_CG_INTOP_NONE); - } -} - -void pcg_cmp(Parser* p, CmpOp op) { - /* The FP block starts at CMP_LT_F (relational operator markers) and runs - * through the ordered/unordered predicate members; everything below is - * integer. CMP_EQ/CMP_NE additionally route to FP when the operand type is - * floating (C `==`/`!=` on floats is ordered-equal / unordered-not-equal). */ - if (op >= CMP_LT_F || - ((op == CMP_EQ || op == CMP_NE) && pcg_type_is_fp(pcg_top_type(p)))) { - if (pcg_emit_enabled(p)) kit_cg_fp_cmp(p->cg, pcg_fp_cmp(op)); - } else { - if (pcg_emit_enabled(p)) kit_cg_int_cmp(p->cg, pcg_int_cmp(op)); - } - pcg_drop_type(p); - pcg_retag_top(p, type_prim(p->pool, TY_INT)); -} - -void pcg_convert(Parser* p, const Type* dst) { - const Type* src = pcg_top_type(p); - /* src is the TOS slot: read its cached id; dst is lowered once and reused for - * both its size and the convert id. */ - KitCgTypeId src_id = pcg_top_cg_id(p); - KitCgTypeId id = pcg_tid(p, dst); - u32 ss = (u32)kit_cg_type_size(p->c, src_id); - u32 ds = (u32)kit_cg_type_size(p->c, id); - int si = type_is_int(src) || type_is_ptr(src); - int di = type_is_int(dst) || type_is_ptr(dst); - int sf = pcg_type_is_fp(src); - int df = pcg_type_is_fp(dst); - int emit = pcg_emit_enabled(p); - if (src == dst) return; - /* C has no struct/union conversion: a pcg_convert reaching here with an - * aggregate on the stack is only reconciling a qualifier difference between - * two compatible types — e.g. the arms of a `?:` where one is a `const` - * struct lvalue (`*top`, top a `const T*`) and the other is not. The - * representation is identical, so retag the place to dst and emit nothing. - * Falling through would hand the aggregate to the scalar bitcast path, which - * pushes it as a value — illegal for an aggregate (api_push rejects it). The - * flag-preserving retag keeps it a place so the caller can copy it. */ - if (src->kind == TY_STRUCT || src->kind == TY_UNION) { - pcg_retag_keep_flags(p, 0, dst); - return; - } - /* Conversion to _Bool is "value != 0", not a truncation: a value whose set - * bits all lie above the bool storage width (e.g. 256, or the sign bit of a - * 128-bit operand) must still become 1. Emit an explicit compare-against-zero - * for any non-bool scalar source. */ - if (dst->kind == TY_BOOL && src->kind != TY_BOOL) { - if (emit) { - KitCgTypeId sid = src_id; /* cached TOS-slot id */ - if (sf) { - kit_cg_push_float(p->cg, 0.0, sid); - kit_cg_fp_cmp(p->cg, KIT_CG_FP_UNE); - } else { - kit_cg_push_int(p->cg, 0, sid); - kit_cg_int_cmp(p->cg, KIT_CG_INT_NE); - } - /* The compare yields a 0/1 int; narrow it to the bool storage width. */ - kit_cg_trunc(p->cg, id); - } - pcg_retag_top(p, dst); - return; - } - if (type_is_ptr(src) && type_is_ptr(dst)) { - if (emit) kit_cg_bitcast(p->cg, id); - pcg_retag_top(p, dst); - return; - } - if (si && di) { - if (ds < ss) { - if (emit) kit_cg_trunc(p->cg, id); - } else if (ds > ss && type_is_int(src) && pcg_type_is_signed(src)) { - if (emit) kit_cg_sext(p->cg, id); - } else if (ds > ss) { - if (emit) kit_cg_zext(p->cg, id); - } else if (type_is_ptr(src) != type_is_ptr(dst)) { - if (emit) kit_cg_bitcast(p->cg, id); - } - } else if (type_is_int(src) && df) { - if (pcg_type_is_signed(src)) { - if (emit) kit_cg_sint_to_float(p->cg, id, KIT_CG_ROUND_DEFAULT); - } else { - if (emit) kit_cg_uint_to_float(p->cg, id, KIT_CG_ROUND_DEFAULT); - } - } else if (sf && type_is_int(dst)) { - if (pcg_type_is_signed(dst)) { - if (emit) kit_cg_float_to_sint(p->cg, id, KIT_CG_ROUND_DEFAULT); - } else { - if (emit) kit_cg_float_to_uint(p->cg, id, KIT_CG_ROUND_DEFAULT); - } - } else if (sf && df) { - if (ds > ss) { - if (emit) kit_cg_fpext(p->cg, id); - } else if (ds < ss) { - if (emit) kit_cg_fptrunc(p->cg, id); - } - } else { - if (emit) kit_cg_bitcast(p->cg, id); - } - pcg_retag_top(p, dst); -} - -/* Emit "value <op> step" for an inc/dec, picking the float or integer binop - * based on the operand type. Floating operands step by 1.0 via an FP add/sub; - * pointers step by the pointee size and everything else by 1. */ -static void pcg_emit_inc_step(Parser* p, const Type* ty, BinOp op, - KitCgIntBinOp cg_op, const Type* step_ty, - u32 step) { - if (pcg_type_is_fp(ty)) { - BinOp fop = (op == BO_ISUB) ? BO_FSUB : BO_FADD; - kit_cg_push_float(p->cg, 1.0, pcg_tid(p, ty)); - kit_cg_fp_binop(p->cg, pcg_fp_binop(fop), KIT_CG_FP_NONE); - } else { - i64 amount = (ty && ty->kind == TY_PTR) ? (i64)step : 1; - kit_cg_push_int(p->cg, amount, pcg_tid(p, step_ty)); - kit_cg_int_binop(p->cg, cg_op, 0); - } -} - -void pcg_inc_dec(Parser* p, BinOp op, int post) { - const Type* ty = pcg_top_type(p); - /* Cache the TOS-slot id NOW: pcg_materialize_lv_to_ptr below retypes the slot - * to a pointer, so a later pcg_top_cg_id would no longer be `ty`'s id. */ - KitCgTypeId ty_cg = pcg_top_cg_id(p); - if (!pcg_emit_enabled(p)) { - /* Drop the lvalue parser slot and push the rvalue result type. */ - pcg_drop_type(p); - pcg_push_type(p, ty); - return; - } - { - KitCgIntBinOp cg_op = pcg_int_binop(op); - PcgLvAux* lv = pcg_top_lv_aux(p); - /* Snapshot bit-field geometry before materialize clears the aux. */ - PcgLvAux bf = lv ? *lv : (PcgLvAux){0}; - KitCgMemAccess access = pcg_mem_id(p, ty_cg, ty); - const Type* step_ty = ty; - u32 step = 1; - if (ty && ty->kind == TY_PTR) { - const Type* pointee = ty->ptr.pointee; - if (pointee && pointee->kind == TY_VOID) - perr(p, "pointer arithmetic on void pointer"); - step = c_abi_sizeof(p->abi, p->pool, pointee); - step_ty = c_abi_ptrdiff_type(p->abi, p->pool); - } - /* Materialize the lvalue to a single destination pointer so its address can - * be duplicated for the read-modify-write. */ - pcg_materialize_lv_to_ptr(p, type_ptr(p->pool, ty)); - { - FrameSlotDesc fsd; - FrameSlot tmp; - const Type* result_ty = ty; - KitCgMemAccess r_access = pcg_mem_id(p, ty_cg, result_ty); - memset(&fsd, 0, sizeof fsd); - fsd.type = result_ty; - fsd.size = c_abi_sizeof(p->abi, p->pool, result_ty); - fsd.align = c_abi_alignof(p->abi, p->pool, result_ty); - fsd.kind = FS_LOCAL; - tmp = pcg_local(p, &fsd); - kit_cg_dup(p->cg); /* [ptr, ptr] */ - kit_cg_deref(p->cg, 0); /* [ptr, place] */ - pcg_apply_bitfield(p, &bf); - kit_cg_load(p->cg, access); /* [ptr, old] */ - if (post) { - /* Stash old, compute new, store, then re-load old as result. */ - kit_cg_dup(p->cg); /* [ptr, old, old] */ - kit_cg_push_local(p->cg, tmp); - kit_cg_swap(p->cg); - kit_cg_store(p->cg, r_access); /* [ptr, old] */ - pcg_emit_inc_step(p, ty, op, cg_op, step_ty, step); /* [ptr, new] */ - kit_cg_swap(p->cg); /* [new, ptr] */ - kit_cg_deref(p->cg, 0); /* [new, place] */ - pcg_apply_bitfield(p, &bf); - kit_cg_swap(p->cg); /* [place, new] */ - kit_cg_store(p->cg, access); /* [] */ - kit_cg_push_local(p->cg, tmp); - kit_cg_load(p->cg, r_access); /* [old] */ - } else { - /* Compute new, stash new, store, then re-load new as result. */ - pcg_emit_inc_step(p, ty, op, cg_op, step_ty, step); /* [ptr, new] */ - kit_cg_dup(p->cg); /* [ptr, new, new] */ - kit_cg_push_local(p->cg, tmp); - kit_cg_swap(p->cg); - kit_cg_store(p->cg, r_access); /* [ptr, new] */ - kit_cg_swap(p->cg); /* [new, ptr] */ - kit_cg_deref(p->cg, 0); /* [new, place] */ - pcg_apply_bitfield(p, &bf); - kit_cg_swap(p->cg); /* [place, new] */ - kit_cg_store(p->cg, access); /* [] */ - kit_cg_push_local(p->cg, tmp); - kit_cg_load(p->cg, r_access); /* [new] */ - } - (void)step; - } - } - /* Parser stack: drop the lvalue slot, push the result rvalue type. */ - pcg_drop_type(p); - pcg_push_type(p, ty); -} - -void pcg_call(Parser* p, u32 nargs, const Type* fn_type) { - if (pcg_emit_enabled(p)) { - kit_cg_call_default(p->cg, nargs, pcg_tid(p, fn_type)); - } - for (u32 i = 0; i < nargs + 1u; ++i) pcg_drop_type(p); - if (fn_type && fn_type->kind == TY_FUNC && fn_type->fn.ret->kind != TY_VOID) { - pcg_push_type(p, fn_type->fn.ret); - } -} - -void pcg_call_symbol(Parser* p, KitCgSym sym, u32 nargs, const Type* fn_type) { - if (pcg_emit_enabled(p)) { - KitCgCallAttrs attrs; - memset(&attrs, 0, sizeof attrs); - kit_cg_call_symbol(p->cg, sym, nargs, attrs); - } - for (u32 i = 0; i < nargs; ++i) pcg_drop_type(p); - if (fn_type && fn_type->kind == TY_FUNC && fn_type->fn.ret->kind != TY_VOID) { - pcg_push_type(p, fn_type->fn.ret); - } -} - -void pcg_ret(Parser* p, int has_value) { - if (has_value) { - if (pcg_emit_enabled(p)) kit_cg_ret(p->cg); - pcg_drop_type(p); - } else if (pcg_emit_enabled(p)) { - /* No value supplied. For a void function this is the normal 0-result - * return. For a non-void function it is a UB fall-off-the-end or an - * already-diagnosed bare `return;` — terminate with unreachable rather - * than asking kit_cg_ret to pop a result that was never pushed. */ - if (p->cur_func_ret && p->cur_func_ret->kind != TY_VOID) - kit_cg_unreachable(p->cg); - else - kit_cg_ret(p->cg); - } -} - -void pcg_alloca(Parser* p) { - if (pcg_emit_enabled(p)) { - kit_cg_alloca(p->cg, 16, pcg_tid(p, type_ptr(p->pool, type_void(p->pool)))); - } - pcg_drop_type(p); - pcg_push_type(p, type_ptr(p->pool, type_void(p->pool))); -} - -void pcg_va_arg(Parser* p, const Type* ty) { - if (pcg_emit_enabled(p)) kit_cg_vararg_next(p->cg, pcg_tid(p, ty)); - pcg_drop_type(p); - pcg_push_type(p, ty); -} - -void pcg_va_start(Parser* p) { - if (pcg_emit_enabled(p)) kit_cg_vararg_start(p->cg); -} - -void pcg_va_end(Parser* p) { - if (pcg_emit_enabled(p)) kit_cg_vararg_end(p->cg); -} - -void pcg_va_copy(Parser* p) { - if (pcg_emit_enabled(p)) kit_cg_vararg_copy(p->cg); -} - -void pcg_atomic_load(Parser* p, MemOrder ord) { - const Type* pty = pcg_top_type(p); - const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty; - if (pcg_emit_enabled(p)) { - kit_cg_atomic_load(p->cg, pcg_mem(p, ty), pcg_mem_order(ord)); - } - pcg_retag_top(p, ty); -} - -void pcg_atomic_store(Parser* p, MemOrder ord) { - const Type* pty = pcg_top2_type(p); - const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty; - if (pcg_emit_enabled(p)) { - kit_cg_atomic_store(p->cg, pcg_mem(p, ty), pcg_mem_order(ord)); - } - pcg_drop_type(p); - pcg_drop_type(p); -} - -void pcg_atomic_rmw(Parser* p, AtomicOp op, MemOrder ord) { - const Type* pty = pcg_top2_type(p); - const Type* ty = (pty && pty->kind == TY_PTR) ? pty->ptr.pointee : pty; - if (pcg_emit_enabled(p)) { - kit_cg_atomic_rmw(p->cg, pcg_mem(p, ty), pcg_atomic_op(op), - pcg_mem_order(ord)); - } - pcg_drop_type(p); - pcg_retag_top(p, ty); -} - -void pcg_atomic_cas(Parser* p, MemOrder succ, MemOrder fail) { - const Type* ty = pcg_top2_type(p); - if (pcg_emit_enabled(p)) { - kit_cg_atomic_cmpxchg(p->cg, pcg_mem(p, ty), pcg_mem_order(succ), - pcg_mem_order(fail), 0); - } - pcg_drop_type(p); /* desired */ - pcg_drop_type(p); /* expected */ - pcg_drop_type(p); /* pointer */ - pcg_push_type(p, ty); - pcg_push_type(p, type_prim(p->pool, TY_BOOL)); -} - -void pcg_fence(Parser* p, MemOrder ord) { - if (pcg_emit_enabled(p)) kit_cg_atomic_fence(p->cg, pcg_mem_order(ord)); -} - -void pcg_intrinsic_unary_to_int(Parser* p, IntrinKind k) { - KitCgIntrinsic ck = k == INTRIN_CLZ ? KIT_CG_INTRIN_CLZ - : k == INTRIN_CTZ ? KIT_CG_INTRIN_CTZ - : KIT_CG_INTRIN_POPCOUNT; - const Type* ity = type_prim(p->pool, TY_INT); - if (pcg_emit_enabled(p)) { - kit_cg_intrinsic(p->cg, ck, 1, pcg_tid(p, ity)); - } - pcg_retag_top(p, ity); -} - -void pcg_intrinsic_void(Parser* p, IntrinKind k) { - if (k == INTRIN_UNREACHABLE) { - if (pcg_emit_enabled(p)) kit_cg_unreachable(p->cg); - } else { - if (pcg_emit_enabled(p)) { - kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_TRAP, 0, - kit_cg_type_builtin(p->c, KIT_CG_BUILTIN_VOID)); - } - } -} - -void pcg_syscall(Parser* p, u32 nargs, const Type* long_ty) { - if (pcg_emit_enabled(p)) - kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_SYSCALL, nargs, pcg_tid(p, long_ty)); - for (u32 i = 0; i < nargs; ++i) pcg_drop_type(p); - pcg_push_type(p, long_ty); -} - -/* __builtin_readcyclecounter(): read the target cycle/timestamp counter. No - * operands; pushes an unsigned long long result. */ -void pcg_readcyclecounter(Parser* p) { - const Type* ull = type_prim(p->pool, TY_ULLONG); - if (pcg_emit_enabled(p)) - kit_cg_intrinsic(p->cg, KIT_CG_INTRIN_READCYCLECOUNTER, 0, pcg_tid(p, ull)); - pcg_push_type(p, ull); -} - -/* __builtin_return_address(level) / __builtin_frame_address(level): emit the - * frame-pointer-chain intrinsic. The constant level rides as a single immediate - * operand (kept as OPK_IMM by kit_cg_intrinsic); the result is void*. */ -void pcg_frame_or_return_address(Parser* p, int is_return, u32 level) { - const Type* void_ptr = type_ptr(p->pool, type_void(p->pool)); - KitCgIntrinsic intrin = - is_return ? KIT_CG_INTRIN_RETURN_ADDRESS : KIT_CG_INTRIN_FRAME_ADDRESS; - pcg_push_int(p, (i64)level, type_prim(p->pool, TY_INT)); - if (pcg_emit_enabled(p)) - kit_cg_intrinsic(p->cg, intrin, 1, pcg_tid(p, void_ptr)); - /* kit_cg_intrinsic popped the level operand and pushed the void* result; - * retag the slot pcg_push_int added (also clears the level's value flags, - * e.g. the null-pointer-constant tag set for level 0). */ - pcg_retag_top(p, void_ptr); -} - -void pcg_inline_asm(Parser* p, const char* tmpl, const AsmConstraint* outs, - u32 nout, const AsmConstraint* ins, u32 nin, - const Sym* clobbers, u32 nclob) { - KitCgInlineAsm a; - KitCgAsmOperand* o = NULL; - KitCgAsmOperand* in = NULL; - KitSym* cl = NULL; - memset(&a, 0, sizeof a); - a.tmpl = kit_sym_intern(p->c, kit_slice_cstr(tmpl ? tmpl : "")); - if (nout) { - o = arena_zarray(p->pool->arena, KitCgAsmOperand, nout); - for (u32 i = 0; i < nout; ++i) { - o[i].constraint = - kit_sym_intern(p->c, kit_slice_cstr(outs[i].str ? outs[i].str : "")); - o[i].name = outs[i].name; - o[i].type = pcg_tid(p, outs[i].type); - o[i].reg = outs[i].reg; - o[i].dir = KIT_CG_ASM_OUT; - } - } - if (nin) { - in = arena_zarray(p->pool->arena, KitCgAsmOperand, nin); - for (u32 i = 0; i < nin; ++i) { - in[i].constraint = - kit_sym_intern(p->c, kit_slice_cstr(ins[i].str ? ins[i].str : "")); - in[i].name = ins[i].name; - in[i].type = pcg_tid(p, ins[i].type); - in[i].reg = ins[i].reg; - in[i].dir = (ins[i].dir == ASM_INOUT) ? KIT_CG_ASM_INOUT : KIT_CG_ASM_IN; - } - } - if (nclob) { - cl = arena_array(p->pool->arena, KitSym, nclob); - for (u32 i = 0; i < nclob; ++i) cl[i] = clobbers[i]; - } - a.outputs = o; - a.noutputs = nout; - a.inputs = in; - a.ninputs = nin; - a.clobbers = cl; - a.nclobbers = nclob; - if (pcg_emit_enabled(p)) kit_cg_inline_asm(p->cg, a); - /* Mirror kit_cg_inline_asm's stack effect on the parser's typed shadow stack. - */ - for (u32 i = 0; i < nin; ++i) pcg_drop_type(p); - for (u32 i = 0; i < nout; ++i) pcg_push_type(p, outs[i].type); -} diff --git a/lang/c/parse/cg_adapter.h b/lang/c/parse/cg_adapter.h @@ -1,380 +0,0 @@ -#ifndef KIT_PARSE_CG_ADAPTER_H -#define KIT_PARSE_CG_ADAPTER_H - -/* cg_adapter.h — the C parser <-> codegen seam. - * - * Declares the `pcg_*` helpers (implemented in cg_adapter.c) plus the - * C-frontend types they operate on. Every `pcg_*` call keeps the parser's - * typed shadow stack (cg_slot_stack in struct Parser, one PcgSlot per value) - * in lockstep with the public CG stack and folds C lvalue chains into - * single effective-address memops; the parser never calls stack-affecting - * `kit_cg_*` ops directly. See doc/FRONTENDS.md for the design. */ - -#include <kit/cg.h> - -#include "c_support.h" -#include "type/type.h" - -typedef KitCg CG; -typedef KitCgLabel CGLabel; -typedef KitCgLocal FrameSlot; - -#define FRAME_SLOT_NONE KIT_CG_LOCAL_NONE -#define OBJ_GROUP_NONE 0u - -/* Lvalue auxiliary state, one per PcgSlot (the `aux` field). - * - * The C parser tracks one logical "C-language value" per stack slot. When that - * slot is a C-language lvalue (PCG_VALUE_LVALUE), the aux below records the - * pending effective-address modifiers and bit-field metadata that the next - * load / store / addr will fold onto the CG memop. - * - * Lvalue chains (`s.f`, `a[i].g`, etc.) accumulate into this aux instead of - * emitting per-step CG ops: there is no CG-level `field` / `index` / - * `addr_offset` op anymore. Field offsets bump `offset`; subscripts set - * `scale` and leave the evaluated index value on the CG stack just above the - * lvalue base. Bit-field selections fill `bit_*`. The aux is consumed by the - * very next pcg_load / pcg_store / pcg_addr that crosses the slot. - * - * `base_kind` records what the CG-stack base under this lvalue actually is — - * either an OPK_LOCAL produced by push_local (PCG_LV_BASE_LOCAL) or a - * pointer rvalue from push_symbol_addr, push_local_addr, dereference, or - * pointer arithmetic (PCG_LV_BASE_POINTER_RV). Stream A's CG-side memops - * accept either shape uniformly; pcg_addr uses the distinction to decide - * whether to emit kit_cg_addr or treat the base as already-a-pointer. */ -typedef enum PcgLvBaseKind { - PCG_LV_BASE_LOCAL = 0, - PCG_LV_BASE_POINTER_RV = 1, -} PcgLvBaseKind; - -typedef struct PcgLvAux { - i64 offset; - u32 scale; - u16 bit_offset; - u16 bit_width; - u32 storage_size; - u8 bit_signed; - u8 base_kind; /* PcgLvBaseKind */ - u8 is_subobject; /* lvalue is a member/element of a larger CG object */ - u8 pad[5]; -} PcgLvAux; - -/* One shadow-stack slot: the CG type of a logical C value plus its parser-side - * value flags (PCG_VALUE_*) and lvalue aux, kept in lockstep with the CG value - * stack. The parser pushes/dups/swaps/rotates whole slots. - * - * `cg_id` is the lowered KitCgTypeId of `type`, stamped together with `type` - * (see pcg_slot_set_type) so the hot pcg_mem / pcg_load queries read it directly - * instead of re-crossing the Type*->CgId bridge via type_cg_id_in_pool on every - * operand. KIT_CG_TYPE_NONE doubles as "type is NULL or not yet lowered"; the - * lazy filler (pcg_slot_cg_id) lowers on first read so it tracks any later - * record completion exactly like a direct type_cg_id_in_pool call would. cg_id - * is a pure function of `type`, so whole-slot copies (dup/swap/rot3) carry it. */ -typedef struct PcgSlot { - const Type* type; - PcgLvAux aux; - KitCgTypeId cg_id; - u8 flags; -} PcgSlot; - -typedef enum BinOp { - BO_IADD, - BO_ISUB, - BO_IMUL, - BO_SDIV, - BO_UDIV, - BO_SREM, - BO_UREM, - BO_FADD, - BO_FSUB, - BO_FMUL, - BO_FDIV, - BO_AND, - BO_OR, - BO_XOR, - BO_SHL, - BO_SHR_S, - BO_SHR_U, -} BinOp; - -typedef enum UnOp { - UO_NEG, - UO_NOT, - UO_BNOT, -} UnOp; - -/* Compare markers used by the C parser. The integer block plus the four - * relational FP markers (CMP_LT_F..CMP_GE_F) cover the bare C operators; the - * latter map to the *ordered* FP predicates in pcg_fp_cmp. The trailing - * ordered/unordered block mirrors the global CmpOp / public KitCgFpCmpOp so - * the C99 comparison builtins (islessgreater -> CMP_ONE_F) and any - * negation-driven unordered duals are reachable from the frontend 1:1. */ -typedef enum CmpOp { - CMP_EQ, - CMP_NE, - CMP_LT_S, - CMP_LE_S, - CMP_GT_S, - CMP_GE_S, - CMP_LT_U, - CMP_LE_U, - CMP_GT_U, - CMP_GE_U, - /* Relational FP operator markers (bare `< <= > >=` on floats) -> ordered. */ - CMP_LT_F, - CMP_LE_F, - CMP_GT_F, - CMP_GE_F, - /* Ordered FP relationals (NaN -> false). */ - CMP_OEQ_F, - CMP_ONE_F, - CMP_OLT_F, - CMP_OLE_F, - CMP_OGT_F, - CMP_OGE_F, - /* Unordered FP relationals (NaN -> true). */ - CMP_UEQ_F, - CMP_UNE_F, - CMP_ULT_F, - CMP_ULE_F, - CMP_UGT_F, - CMP_UGE_F, -} CmpOp; - -typedef enum AtomicOp { - AO_XCHG, - AO_ADD, - AO_SUB, - AO_AND, - AO_OR, - AO_XOR, - AO_NAND, -} AtomicOp; - -typedef enum MemOrder { - MO_RELAXED, - MO_CONSUME, - MO_ACQUIRE, - MO_RELEASE, - MO_ACQ_REL, - MO_SEQ_CST, -} MemOrder; - -typedef enum IntrinKind { - INTRIN_NONE = 0, - INTRIN_POPCOUNT, - INTRIN_CTZ, - INTRIN_CLZ, - INTRIN_MEMCPY, - INTRIN_MEMMOVE, - INTRIN_MEMSET, - INTRIN_PREFETCH, - INTRIN_ASSUME_ALIGNED, - INTRIN_EXPECT, - INTRIN_UNREACHABLE, - INTRIN_TRAP, - INTRIN_SETJMP, - INTRIN_LONGJMP, - INTRIN_SADD_OVERFLOW, - INTRIN_UADD_OVERFLOW, - INTRIN_SSUB_OVERFLOW, - INTRIN_USUB_OVERFLOW, - INTRIN_SMUL_OVERFLOW, - INTRIN_UMUL_OVERFLOW, -} IntrinKind; - -typedef enum AsmDir { ASM_IN, ASM_OUT, ASM_INOUT } AsmDir; - -typedef struct AsmConstraint { - const char* str; - Sym name; - const Type* type; - Sym reg; /* hard-register name for a GNU local register variable; 0 = none */ - u8 dir; - u8 pad[3]; -} AsmConstraint; - -typedef enum FrameSlotKind { - FS_LOCAL, - FS_PARAM, - FS_SPILL, - FS_SRET, - FS_ALLOCA, -} FrameSlotKind; - -typedef enum FrameSlotFlag { - FSF_NONE = 0, - FSF_ADDR_TAKEN = 1u << 0, - FSF_VOLATILE = 1u << 1, -} FrameSlotFlag; - -typedef struct FrameSlotDesc { - const Type* type; - Sym name; - SrcLoc loc; - u32 size; - u32 align; - u8 kind; - u8 pad; - u16 flags; -} FrameSlotDesc; - -typedef struct CGParamDesc { - u32 index; - Sym name; - const Type* type; - FrameSlot slot; - const void* abi; - const void* incoming; - u32 nincoming; - SrcLoc loc; -} CGParamDesc; - -typedef enum CGFuncDescFlag { - CGFD_NONE = 0, - CGFD_NORETURN = 1u << 0, -} CGFuncDescFlag; - -typedef struct CGFuncDesc { - ObjSymId sym; - ObjSecId text_section_id; - u32 group_id; - const Type* fn_type; - const void* abi; - const CGParamDesc* params; - u32 nparams; - SrcLoc loc; - u32 flags; - KitCgInlinePolicy inline_policy; -} CGFuncDesc; - -typedef struct Parser Parser; - -KitCgTypeId pcg_tid(Parser*, const Type*); -KitCgMemAccess pcg_mem(Parser*, const Type*); -const Type* pcg_top_type(Parser*); -const Type* pcg_top2_type(Parser*); -void pcg_retag_top(Parser*, const Type*); -void pcg_retag_keep_flags(Parser*, u32 depth, const Type*); -void pcg_push_type(Parser*, const Type*); -void pcg_drop_type(Parser*); -void pcg_dup_type(Parser*); -void pcg_swap_type(Parser*); -/* Structural CG ops that also mirror onto the typed shadow stack. */ -void pcg_dup(Parser*); -void pcg_swap(Parser*); -void pcg_drop(Parser*); -/* Reclaim dead compiler-temp frame slots at a statement boundary; safe because - * kit_cg_reclaim_temps acts only when the CG value stack is empty (g->sp == 0). */ -void pcg_reclaim_temps(Parser*); -int pcg_top_is_bitfield(Parser*); -void pcg_set_top_bitfield(Parser*); -int pcg_top_is_register(Parser*); -void pcg_set_top_register(Parser*); -int pcg_top_is_lvalue(Parser*); -int pcg_top_is_modifiable_lvalue(Parser*); -int pcg_top_is_null_ptr_const(Parser*); -void pcg_set_top_lvalue(Parser*); -void pcg_rot3_type(Parser*); -int pcg_emit_enabled(Parser*); -void pcg_codegen_suppress_push(Parser*); -void pcg_codegen_suppress_pop(Parser*); - -KitCgIntBinOp pcg_int_binop(BinOp); -KitCgFpBinOp pcg_fp_binop(BinOp); -KitCgIntCmpOp pcg_int_cmp(CmpOp); -KitCgFpCmpOp pcg_fp_cmp(CmpOp); -KitCgAtomicOp pcg_atomic_op(AtomicOp); -KitCgMemOrder pcg_mem_order(MemOrder); -int pcg_type_is_fp(const Type*); -int pcg_type_is_signed(const Type*); - -FrameSlot pcg_local(Parser*, const FrameSlotDesc*); -FrameSlot pcg_param_slot(Parser*, u32, const FrameSlotDesc*); -void pcg_param(Parser*, const CGParamDesc*); -void pcg_func_begin(Parser*, const CGFuncDesc*); -void pcg_func_end(Parser*); -void pcg_set_loc(Parser*, SrcLoc); -void pcg_push_int(Parser*, i64, const Type*); -void pcg_push_float(Parser*, double, const Type*); -void pcg_push_local_typed(Parser*, FrameSlot, const Type*); -void pcg_push_global(Parser*, ObjSymId, const Type*); -void pcg_load(Parser*); -void pcg_addr(Parser*); -void pcg_push_label_addr(Parser*, CGLabel); -void pcg_computed_goto(Parser*, const CGLabel*, u32); -CGLabel pcg_label_new(Parser*); -void pcg_label_place(Parser*, CGLabel); -void pcg_jump(Parser*, CGLabel); -void pcg_branch_true(Parser*, CGLabel); -void pcg_branch_false(Parser*, CGLabel); -void pcg_store(Parser*); -/* Store whose assignment value is discarded (replaces `pcg_store; pcg_drop`). */ -void pcg_store_void(Parser*); -void pcg_deref(Parser*, const Type*); - -/* ---- Lvalue auxiliary access ---- - * - * pcg_top_lv_aux returns a mutable pointer to TOS's lvalue aux, used by - * parse_postfix and the initializer / compound-assignment paths to fold - * field offsets and bit-field metadata inline. Returns NULL if the parser - * stack is empty; behavior on a non-lvalue TOS is the caller's responsibility - * (parse_postfix has already validated lvalueness before calling). */ -PcgLvAux* pcg_top_lv_aux(Parser*); -PcgLvAux* pcg_lv_aux_at(Parser*, u32 depth); - -/* ---- Lvalue chain helpers ---- - * - * Each maps directly to the canonical encodings in doc/INDIRECT.md without - * emitting any intermediate field / index / addr_offset CG op. Field offsets - * and array scales are accumulated on the TOS lvalue's aux; the next - * pcg_load / pcg_store / pcg_addr consumes them. */ - -/* Fold `s.f` (or any path-resolved field selection) into the TOS lvalue. - * byte_offset is the cumulative offset within the record; ty is the field - * type; bf_* are bit-field metadata (bf_width == 0 for non-bitfields). The - * caller is responsible for verifying TOS is an lvalue of a record type. */ -void pcg_lv_member(Parser*, i64 byte_offset, const Type* field_ty, - u16 bf_offset, u16 bf_width, u32 bf_storage_size); - -/* Attach `[index]` to the TOS lvalue. PRECONDITION: the index value has just - * been pushed onto the CG stack (and parser stack) above the lvalue base; - * the parser stack therefore has [base_lv, index] at depth [1, 0]. This call - * records `scale = elem_size` on the base's aux, drops the index parser - * slot (leaving the index value on the CG stack for the eventual memop), - * and retags the surviving slot as elem_ty (lvalue). */ -void pcg_lv_subscript(Parser*, u32 elem_size, const Type* elem_ty); - -/* Decay an array lvalue at TOS into a pointer-to-element rvalue. Emits - * kit_cg_addr (or a no-op for pointer-rvalue bases) and folds any pending - * EA modifiers into the resulting pointer via ptr arithmetic. After return, - * TOS is a pointer rvalue of type `*arr_ty->elem`. */ -void pcg_decay_array(Parser*, const Type* arr_ty); - -void pcg_binop(Parser*, BinOp); -void pcg_unop(Parser*, UnOp); -void pcg_cmp(Parser*, CmpOp); -void pcg_convert(Parser*, const Type*); -void pcg_inc_dec(Parser*, BinOp, int); -void pcg_call(Parser*, u32, const Type*); -void pcg_call_symbol(Parser*, KitCgSym, u32, const Type*); -void pcg_ret(Parser*, int); -void pcg_alloca(Parser*); -void pcg_va_arg(Parser*, const Type*); -void pcg_va_start(Parser*); -void pcg_va_end(Parser*); -void pcg_va_copy(Parser*); -void pcg_atomic_load(Parser*, MemOrder); -void pcg_atomic_store(Parser*, MemOrder); -void pcg_atomic_rmw(Parser*, AtomicOp, MemOrder); -void pcg_atomic_cas(Parser*, MemOrder, MemOrder); -void pcg_fence(Parser*, MemOrder); -void pcg_intrinsic_unary_to_int(Parser*, IntrinKind); -void pcg_intrinsic_void(Parser*, IntrinKind); -void pcg_syscall(Parser*, u32 nargs, const Type* long_ty); -void pcg_frame_or_return_address(Parser*, int is_return, u32 level); -void pcg_readcyclecounter(Parser*); -void pcg_inline_asm(Parser*, const char*, const AsmConstraint*, u32, - const AsmConstraint*, u32, const Sym*, u32); - -#endif diff --git a/lang/c/parse/parse.c b/lang/c/parse/parse.c @@ -565,7 +565,7 @@ FrameSlot make_local_aligned(Parser* p, Sym name, const Type* type, SrcLoc loc, fsd.align = (align_override > nat) ? align_override : nat; fsd.kind = FS_LOCAL; fsd.flags = FSF_NONE; - s = pcg_local(p, &fsd); + s = c_cg_local(p, &fsd); e = scope_define_checked(p, name, SEK_LOCAL, type); e->v.slot = s; sym_set_decl(e, DECL_NONE, DS_AUTO, DL_NONE, DF_NONE, DSTATE_DEFINED); @@ -583,14 +583,14 @@ static FrameSlot make_vla_size_slot(Parser* p) { fsd.size = c_abi_sizeof(p->abi, p->pool, fsd.type); fsd.align = c_abi_alignof(p->abi, p->pool, fsd.type); fsd.kind = FS_LOCAL; - return pcg_local(p, &fsd); + return c_cg_local(p, &fsd); } static void store_top_to_size_slot(Parser* p, FrameSlot slot) { - pcg_push_local_typed(p, slot, ty_size_t(p)); - pcg_swap(p); + c_cg_push_local_typed(p, slot, ty_size_t(p)); + c_cg_swap(p); coerce_top_to_lvalue(p); - pcg_store_void(p); + c_cg_store_void(p); } static void reset_vla_pending(Parser* p) { @@ -635,20 +635,20 @@ static int build_vla_size(Parser* p, const Type* ty, u32* count_idx, if (count_dynamic || elem_dynamic) { FrameSlot byte_slot = make_vla_size_slot(p); - pcg_set_loc(p, loc); + c_cg_set_loc(p, loc); if (count_dynamic) { - pcg_push_local_typed(p, count_slot, ty_size_t(p)); - pcg_load(p); + c_cg_push_local_typed(p, count_slot, ty_size_t(p)); + c_cg_load(p); } else { - pcg_push_int(p, (i64)ty->arr.count, ty_size_t(p)); + c_cg_push_int(p, (i64)ty->arr.count, ty_size_t(p)); } if (elem_dynamic) { - pcg_push_local_typed(p, elem_slot, ty_size_t(p)); - pcg_load(p); + c_cg_push_local_typed(p, elem_slot, ty_size_t(p)); + c_cg_load(p); } else { - pcg_push_int(p, (i64)elem_static_size, ty_size_t(p)); + c_cg_push_int(p, (i64)elem_static_size, ty_size_t(p)); } - pcg_binop(p, BO_IMUL); + c_cg_binop(p, BO_IMUL); store_top_to_size_slot(p, byte_slot); *bounds = add_vla_bound(p, *bounds, ty, byte_slot, count_slot); *out_slot = byte_slot; @@ -962,13 +962,13 @@ static void parse_init_declarator(Parser* p, const DeclSpecs* specs) { bounds = specs->vla_bounds; } ptr_slot = make_local(p, name, ptr_ty, loc); - pcg_set_loc(p, loc); - pcg_push_local_typed(p, byte_slot, ty_size_t(p)); - pcg_load(p); - pcg_alloca(p); - pcg_push_local_typed(p, ptr_slot, ptr_ty); - pcg_swap(p); - pcg_store_void(p); + c_cg_set_loc(p, loc); + c_cg_push_local_typed(p, byte_slot, ty_size_t(p)); + c_cg_load(p); + c_cg_alloca(p); + c_cg_push_local_typed(p, ptr_slot, ptr_ty); + c_cg_swap(p); + c_cg_store_void(p); sym_entry = scope_lookup(p, name); if (sym_entry && sym_entry->kind == SEK_LOCAL) { sym_entry->vla_byte_slot = byte_slot; @@ -995,7 +995,7 @@ static void parse_init_declarator(Parser* p, const DeclSpecs* specs) { e->reg_asm_name = dinfo.asm_label; } } - pcg_set_loc(p, loc); + c_cg_set_loc(p, loc); init_at(p, s, var_ty, 0, var_ty); return; } @@ -1008,7 +1008,7 @@ static void parse_init_declarator(Parser* p, const DeclSpecs* specs) { } } if (accept_punct(p, '=')) { - pcg_set_loc(p, loc); + c_cg_set_loc(p, loc); if ((var_ty->kind == TY_STRUCT || var_ty->kind == TY_UNION) && !is_punct(&p->cur, '{')) { parse_assign_expr(p); @@ -1017,17 +1017,17 @@ static void parse_init_declarator(Parser* p, const DeclSpecs* specs) { var_ty->kind == TY_UNION) { init_at(p, s, var_ty, 0, var_ty); } else { - pcg_push_local_typed(p, s, var_ty); + c_cg_push_local_typed(p, s, var_ty); parse_assign_expr(p); to_rvalue(p); { - const Type* rhs = pcg_top_type(p); + const Type* rhs = c_cg_top_type(p); CSemCheck chk = c_sem_check_assignment(p->pool, var_ty, rhs); if (!chk.ok) perr(p, "%.*s", KIT_SLICE_ARG(kit_slice_cstr(chk.message))); } coerce_top_to_lvalue(p); - pcg_store_void(p); + c_cg_store_void(p); } } else if (p->auto_var_init == KIT_AUTOVAR_ZERO && var_ty && !(var_ty->kind == TY_ARRAY && var_ty->arr.incomplete)) { @@ -1035,7 +1035,7 @@ static void parse_init_declarator(Parser* p, const DeclSpecs* specs) { * initializer is implicitly zeroed (clang/gcc semantics). When the * variable is fully written before any read the store is dead and the * optimizer drops it. VLAs and incomplete arrays are left alone. */ - pcg_set_loc(p, loc); + c_cg_set_loc(p, loc); /* Zero the whole object in one memset rather than recursing to one store * per scalar leaf. The per-leaf path is O(leaves^2) on large aggregates * (each leaf store stashes a fresh frontend temp, growing g->nlocals, @@ -1303,14 +1303,14 @@ static void parse_function_body(Parser* p, ObjSymId fsym, const Type* fn_ty, p->goto_labels = NULL; p->cur_switch = NULL; p->computed_goto_emitted = 0; - pcg_set_loc(p, fname_loc); + c_cg_set_loc(p, fname_loc); /* Record whether this body emits before func_begin (which is itself gated on * emit-enabled): goto-label allocation keys off this, not the momentary * suppress_codegen depth, so a label first referenced inside a constant-false * region still gets a real CG-label id rather than the suppression sentinel. */ - p->cur_func_emits = (u8)pcg_emit_enabled(p); - pcg_func_begin(p, &fd); + p->cur_func_emits = (u8)c_cg_emit_enabled(p); + c_cg_func_begin(p, &fd); for (u16 i = 0; i < nparams; ++i) { FrameSlotDesc fsd; @@ -1324,7 +1324,7 @@ static void parse_function_body(Parser* p, ObjSymId fsym, const Type* fn_ty, fsd.align = c_abi_alignof(p->abi, p->pool, infos[i].type); fsd.kind = FS_PARAM; fsd.flags = FSF_NONE; - s = pcg_param_slot(p, i, &fsd); + s = c_cg_param_slot(p, i, &fsd); pds[i].slot = s; if (infos[i].name) { e = scope_define_checked(p, infos[i].name, SEK_LOCAL, infos[i].type); @@ -1337,10 +1337,10 @@ static void parse_function_body(Parser* p, ObjSymId fsym, const Type* fn_ty, parse_compound_stmt(p); if (fn_ty->fn.ret && fn_ty->fn.ret->kind != TY_VOID && fn_ty->fn.ret->kind != TY_STRUCT && fn_ty->fn.ret->kind != TY_UNION) { - pcg_push_int(p, 0, fn_ty->fn.ret); - pcg_ret(p, 1); + c_cg_push_int(p, 0, fn_ty->fn.ret); + c_cg_ret(p, 1); } else { - pcg_ret(p, 0); + c_cg_ret(p, 0); } for (GotoLabel* gl = p->goto_labels; gl; gl = gl->next) { if (!gl->placed) { @@ -1350,7 +1350,7 @@ static void parse_function_body(Parser* p, ObjSymId fsym, const Type* fn_ty, p->goto_labels = saved_goto_labels; p->cur_switch = saved_switch; p->computed_goto_emitted = saved_computed_goto; - pcg_func_end(p); + c_cg_func_end(p); scope_pop(p); } @@ -1433,10 +1433,10 @@ static void parse_external_decl(Parser* p) { u8 saved_func_emits = p->cur_func_emits; p->cur_func_name = name; p->cur_func_ret = fn_ty->fn.ret; - if (suppress_body_codegen) pcg_codegen_suppress_push(p); + if (suppress_body_codegen) c_cg_codegen_suppress_push(p); parse_function_body(p, fent->v.sym, fn_ty, abi, infos, nparams, loc, fn_section_id, fn_decl_flags); - if (suppress_body_codegen) pcg_codegen_suppress_pop(p); + if (suppress_body_codegen) c_cg_codegen_suppress_pop(p); p->cur_func_name = saved_func_name; p->cur_func_ret = saved_func_ret; p->cur_func_emits = saved_func_emits; @@ -1595,7 +1595,7 @@ static void parse_file_scope_asm(Parser* p) { expect_punct(p, ')', "')' after file-scope asm"); expect_punct(p, ';', "';' after file-scope asm"); if (nbytes > 0) --nbytes; /* drop decode_string_literal's trailing NUL */ - if (pcg_emit_enabled(p)) { + if (c_cg_emit_enabled(p)) { KitSlice asm_src = {{(const char*)bytes}, nbytes}; kit_cg_file_scope_asm(p->cg, asm_src); } diff --git a/lang/c/parse/parse.h b/lang/c/parse/parse.h @@ -2,7 +2,7 @@ #define KIT_LANG_C_PARSE_H #include "decl/decl.h" -#include "parse/cg_adapter.h" +#include "parse/cg.h" #include "pp/pp.h" /* C11 frontend. Reads preprocessed tokens, records C declarations, and drives diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c @@ -9,14 +9,7 @@ static const Type* ty_size_t(Parser* p) { return c_abi_size_type(p->abi, p->pool); } static int type_is_fp(const Type* t); - -static int accept_kw(Parser* p, CKw k) { - if (is_kw(p, &p->cur, k)) { - advance(p); - return 1; - } - return 0; -} +static int cg_const_is_zero(KitCgConstInt v); static int type_is_incomplete(const Type* t) { if (!t) return 1; @@ -63,9 +56,10 @@ static int pointer_pointees_compatible(Parser* p, const Type* lhs, } static int null_pointer_constant(Parser* p, const Type* ty) { - i64 v = 1; - return type_is_int(ty) && (pcg_top_is_null_ptr_const(p) || - (kit_cg_top_const_int(p->cg, &v) && v == 0)); + KitCgConstInt v; + return type_is_int(ty) && + (c_cg_top_is_null_ptr_const(p) || + (kit_cg_top_const_int_ex(p->cg, &v) && cg_const_is_zero(v))); } static void require_scalar(Parser* p, const Type* ty, const char* what) { @@ -98,8 +92,8 @@ static const Type* conditional_pointer_type(Parser* p, const Type* then_ty, * Literal parsing * ============================================================ */ -static u32 cint_bits(Parser* p, const Type* ty); -static int cint_signed(Parser* p, const Type* ty); +static u32 integer_type_bits(Parser* p, const Type* ty); +static int integer_type_signed(Parser* p, const Type* ty); static u64 parse_int_literal_u64(Parser* p, const Tok* t, int* decimal_out) { KitSlice spell_sl = pp_text_slice(p->pp, t); @@ -144,8 +138,8 @@ static u64 parse_int_literal_u64(Parser* p, const Tok* t, int* decimal_out) { } static int uint_fits_type(Parser* p, u64 v, const Type* ty) { - u32 nb = cint_bits(p, ty); - if (cint_signed(p, ty)) { + u32 nb = integer_type_bits(p, ty); + if (integer_type_signed(p, ty)) { if (nb >= 64) return v <= 0x7fffffffffffffffull; return v <= ((1ull << (nb - 1u)) - 1ull); } @@ -447,7 +441,7 @@ u8* decode_string_literal(Parser* p, const Tok* t, size_t* nlen_out) { KitCgSym emit_string_to_rodata(Parser* p, const u8* bytes, size_t n) { const Type* arr_ty = type_array(p->pool, type_prim(p->pool, TY_CHAR), (u32)n, 0); - return kit_cg_const_data(p->cg, bytes, n, 1u, pcg_tid(p, arr_ty)); + return kit_cg_const_data(p->cg, bytes, n, 1u, c_cg_tid(p, arr_ty)); } KitCgSym emit_string_literal_to_rodata(Parser* p, const u8* bytes, @@ -457,556 +451,253 @@ KitCgSym emit_string_literal_to_rodata(Parser* p, const u8* bytes, const Type* arr_ty = type_array(p->pool, elem_ty, count, 0); return kit_cg_const_data(p->cg, bytes, nbytes, c_abi_alignof(p->abi, p->pool, elem_ty), - pcg_tid(p, arr_ty)); + c_cg_tid(p, arr_ty)); } /* ============================================================ - * Constant expression evaluator (cexpr_*) + * Integer conversion helpers + constant-expression guard * ============================================================ */ -static CConstInt cexpr_unary(Parser* p, SrcLoc loc); -static CConstInt cexpr_cond(Parser* p, SrcLoc loc); static const Type* offsetof_designator(Parser* p, const Type* base, u32* off); static const Type* common_fp_type(Parser* p, const Type* a, const Type* b); static void coerce_fp_cmp_operands(Parser* p, const Type* common); -static u32 cint_bits(Parser* p, const Type* ty) { - u32 sz = ty ? c_abi_sizeof(p->abi, p->pool, ty) : 8u; - if (ty && (ty->kind == TY_INT128 || ty->kind == TY_UINT128)) return 128; - if (sz >= 8) return 64; - return sz * 8u; -} - -static int cint_signed(Parser* p, const Type* ty) { - /* signed_ is a pure Type predicate (no ABI lowering); read it directly. */ - (void)p; - if (!ty) return 1; - return type_is_signed_integer(ty) != 0; -} - -static void cint_mask_to_bits(CConstInt* v, u32 bits) { - if (bits < 64) { - v->lo &= (1ull << bits) - 1ull; - v->hi = 0; - } else if (bits < 128) { - v->hi &= (1ull << (bits - 64u)) - 1ull; - } -} +typedef struct CConstGuardMark { + u32 depth; + u32 not_eval; + const char* error; + SrcLoc error_loc; +} CConstGuardMark; -static CConstInt cint_make_u64(Parser* p, const Type* ty, u64 bits) { - CConstInt v; - u32 nb; - if (!ty) ty = ty_int(p); - nb = cint_bits(p, ty); - v.type = ty; - v.lo = bits; - v.hi = 0; - cint_mask_to_bits(&v, nb); - if (ty->kind == TY_BOOL) { - v.lo = (v.lo || v.hi) ? 1u : 0u; - v.hi = 0; - } - return v; +static int c_const_guard_active(Parser* p) { + return p && p->const_guard_depth != 0; } -static CConstInt cint_make_pair(Parser* p, const Type* ty, u64 lo, u64 hi) { - CConstInt v; - if (!ty) ty = ty_int(p); - v.type = ty; - v.lo = lo; - v.hi = hi; - cint_mask_to_bits(&v, cint_bits(p, ty)); - if (ty->kind == TY_BOOL) { - v.lo = (v.lo || v.hi) ? 1u : 0u; - v.hi = 0; +static void c_const_guard_note_at(Parser* p, SrcLoc loc, const char* message) { + if (!p || !p->const_guard_depth || p->const_guard_not_eval || + p->const_guard_error) { + return; } - return v; + p->const_guard_error = message; + p->const_guard_error_loc = loc; } -static int cint_nonzero(CConstInt v) { return v.lo != 0 || v.hi != 0; } - -static int cint_eq(CConstInt a, CConstInt b) { - return a.lo == b.lo && a.hi == b.hi; +static void c_const_guard_note(Parser* p, const char* message) { + c_const_guard_note_at(p, pp_materialize_loc(p->pp, p->cur.loc), message); } -static int cint_cmp_u(CConstInt a, CConstInt b) { - if (a.hi != b.hi) return a.hi < b.hi ? -1 : 1; - if (a.lo != b.lo) return a.lo < b.lo ? -1 : 1; - return 0; +static void c_const_guard_not_eval_push(Parser* p) { + if (p && p->const_guard_depth) ++p->const_guard_not_eval; } -static CConstInt cint_add(Parser* p, const Type* ty, CConstInt a, CConstInt b) { - u64 lo = a.lo + b.lo; - return cint_make_pair(p, ty, lo, a.hi + b.hi + (lo < a.lo)); +static void c_const_guard_not_eval_pop(Parser* p) { + if (!p || !p->const_guard_depth) return; + if (!p->const_guard_not_eval) + perr(p, "internal parser constant guard not-evaluated underflow"); + --p->const_guard_not_eval; } -static CConstInt cint_sub(Parser* p, const Type* ty, CConstInt a, CConstInt b) { - return cint_make_pair(p, ty, a.lo - b.lo, a.hi - b.hi - (a.lo < b.lo)); -} - -static CConstInt cint_shl(Parser* p, const Type* ty, CConstInt a, u32 sh) { - if (sh >= 128) return cint_make_u64(p, ty, 0); - if (sh == 0) return cint_make_pair(p, ty, a.lo, a.hi); - if (sh >= 64) return cint_make_pair(p, ty, 0, a.lo << (sh - 64u)); - return cint_make_pair(p, ty, a.lo << sh, (a.hi << sh) | (a.lo >> (64u - sh))); -} - -static CConstInt cint_shr_u(Parser* p, const Type* ty, CConstInt a, u32 sh) { - if (sh >= 128) return cint_make_u64(p, ty, 0); - if (sh == 0) return cint_make_pair(p, ty, a.lo, a.hi); - if (sh >= 64) return cint_make_pair(p, ty, a.hi >> (sh - 64u), 0); - return cint_make_pair(p, ty, (a.lo >> sh) | (a.hi << (64u - sh)), a.hi >> sh); -} - -static CConstInt cint_neg(Parser* p, const Type* ty, CConstInt a) { - CConstInt zero = cint_make_u64(p, ty, 0); - return cint_sub(p, ty, zero, a); -} - -static CConstInt cint_bnot(Parser* p, const Type* ty, CConstInt a) { - return cint_make_pair(p, ty, ~a.lo, ~a.hi); +static CConstGuardMark c_const_guard_push(Parser* p) { + CConstGuardMark mark; + memset(&mark, 0, sizeof mark); + if (!p) return mark; + mark.depth = p->const_guard_depth; + mark.not_eval = p->const_guard_not_eval; + mark.error = p->const_guard_error; + mark.error_loc = p->const_guard_error_loc; + if (p->const_guard_depth == 0) { + p->const_guard_not_eval = 0; + p->const_guard_error = NULL; + memset(&p->const_guard_error_loc, 0, sizeof p->const_guard_error_loc); + } + ++p->const_guard_depth; + return mark; } -static CConstInt cint_mul(Parser* p, const Type* ty, CConstInt a, CConstInt b) { - CConstInt r = cint_make_u64(p, ty, 0); - CConstInt x = a; - for (u32 i = 0; i < 128; ++i) { - if ((i < 64 ? (b.lo >> i) : (b.hi >> (i - 64u))) & 1ull) - r = cint_add(p, ty, r, x); - x = cint_shl(p, ty, x, 1); +static int c_const_guard_pop(Parser* p, CConstGuardMark mark, + const char** error_out, SrcLoc* loc_out) { + const char* error = p ? p->const_guard_error : NULL; + SrcLoc loc; + memset(&loc, 0, sizeof loc); + if (p) { + loc = p->const_guard_error_loc; + if (!p->const_guard_depth) + perr(p, "internal parser constant guard underflow"); + p->const_guard_depth = mark.depth; + p->const_guard_not_eval = mark.not_eval; + p->const_guard_error = mark.error; + p->const_guard_error_loc = mark.error_loc; } - return r; + if (error_out) *error_out = error; + if (loc_out) *loc_out = loc; + return error == NULL; } -i64 const_int_as_i64(Parser* p, CConstInt v) { - u32 nb = cint_bits(p, v.type); - u64 u = v.lo; - if (cint_signed(p, v.type) && nb < 64) { - u64 mask = (1ull << nb) - 1ull; - u64 sign = 1ull << (nb - 1u); - u &= mask; - if (u & sign) u |= ~mask; - } - return (i64)u; +static u32 integer_type_bits(Parser* p, const Type* ty) { + u32 sz = ty ? c_abi_sizeof(p->abi, p->pool, ty) : 8u; + if (ty && (ty->kind == TY_INT128 || ty->kind == TY_UINT128)) return 128; + if (sz >= 8) return 64; + return sz * 8u; } -static CConstInt cint_cast(Parser* p, CConstInt v, const Type* ty) { - const Type* dst = type_unqual(p->pool, ty); - if (!dst || !type_is_int(dst)) { - perr(p, "integer constant expression cast requires integer type"); - } - return cint_make_pair(p, dst, v.lo, v.hi); +static int integer_type_signed(Parser* p, const Type* ty) { + (void)p; + if (!ty) return 1; + return type_is_signed_integer(ty) != 0; } -static u32 cint_rank(const Type* ty) { +static u32 integer_rank(const Type* ty) { return ty ? type_kind_int_rank((TypeKind)ty->kind) : 0; } -static const Type* cint_unsigned_variant(Parser* p, const Type* ty) { +static const Type* integer_unsigned_variant(Parser* p, const Type* ty) { TypeKind k = ty ? (TypeKind)ty->kind : TY_UINT; return type_prim(p->pool, type_kind_unsigned_variant(k)); } -static const Type* cint_promote_type(Parser* p, const Type* ty) { +static const Type* integer_promote_type(Parser* p, const Type* ty) { const Type* u = type_unqual(p->pool, ty); if (u && u->kind == TY_ENUM) return type_prim(p->pool, TY_INT); return type_promoted(p->pool, u); } -static const Type* cint_common_type(Parser* p, const Type* a, const Type* b) { - const Type* ap = cint_promote_type(p, a); - const Type* bp = cint_promote_type(p, b); - int as = cint_signed(p, ap); - int bs = cint_signed(p, bp); - u32 ar = cint_rank(ap); - u32 br = cint_rank(bp); +static const Type* integer_common_type(Parser* p, const Type* a, + const Type* b) { + const Type* ap = integer_promote_type(p, a); + const Type* bp = integer_promote_type(p, b); + int as = integer_type_signed(p, ap); + int bs = integer_type_signed(p, bp); + u32 ar = integer_rank(ap); + u32 br = integer_rank(bp); if (type_compatible(ap, bp)) return ap; if (as == bs) return ar >= br ? ap : bp; if (!as && ar >= br) return ap; if (!bs && br >= ar) return bp; - if (as && cint_bits(p, ap) > cint_bits(p, bp)) return ap; - if (bs && cint_bits(p, bp) > cint_bits(p, ap)) return bp; - return cint_unsigned_variant(p, as ? ap : bp); + if (as && integer_type_bits(p, ap) > integer_type_bits(p, bp)) return ap; + if (bs && integer_type_bits(p, bp) > integer_type_bits(p, ap)) return bp; + return integer_unsigned_variant(p, as ? ap : bp); } -static CConstInt cint_convert(Parser* p, CConstInt v, const Type* ty) { - return cint_make_pair(p, ty, v.lo, v.hi); +static int cg_const_is_zero(KitCgConstInt v) { + return v.known && v.lo == 0 && v.hi == 0; } -static int cint_truth(Parser* p, CConstInt v) { - (void)p; - return cint_nonzero(v); -} - -static CConstInt cint_bool(Parser* p, int truth) { - return cint_make_u64(p, ty_int(p), truth ? 1u : 0u); +static int cg_top_const_truth(Parser* p, int* truth_out) { + KitCgConstInt v; + if (!kit_cg_top_const_int_ex(p->cg, &v)) return 0; + if (truth_out) *truth_out = (v.lo != 0 || v.hi != 0); + return 1; } -static CConstInt cexpr_mul(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_unary(p, loc); - for (;;) { - int op = 0; - CConstInt r; - const Type* ct; - if (accept_punct(p, '*')) { - op = '*'; - } else if (accept_punct(p, '/')) { - op = '/'; - } else if (accept_punct(p, '%')) { - op = '%'; - } else { - break; - } - r = cexpr_unary(p, loc); - ct = cint_common_type(p, v.type, r.type); - v = cint_convert(p, v, ct); - r = cint_convert(p, r, ct); - if (op == '*') { - v = cint_mul(p, ct, v, r); - } else { - if (!cint_nonzero(r)) - compiler_panic(p->c, loc, - op == '/' ? "division by zero in constant" - : "modulo by zero in constant"); - if (cint_signed(p, ct)) { - i64 lv = const_int_as_i64(p, v); - i64 rv = const_int_as_i64(p, r); - v = cint_make_u64(p, ct, op == '/' ? (u64)(lv / rv) : (u64)(lv % rv)); - } else { - v = cint_make_u64(p, ct, op == '/' ? v.lo / r.lo : v.lo % r.lo); - } - } - } - return v; -} -static CConstInt cexpr_add(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_mul(p, loc); - for (;;) { - int sub = 0; - CConstInt r; - const Type* ct; - if (accept_punct(p, '+')) { - sub = 0; - } else if (accept_punct(p, '-')) { - sub = 1; - } else { - break; - } - r = cexpr_mul(p, loc); - ct = cint_common_type(p, v.type, r.type); - v = cint_convert(p, v, ct); - r = cint_convert(p, r, ct); - v = sub ? cint_sub(p, ct, v, r) : cint_add(p, ct, v, r); - } - return v; -} -static CConstInt cexpr_shift(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_add(p, loc); - for (;;) { - int left = 0; - CConstInt r; - i64 sh; - const Type* vt; - if (accept_punct(p, P_SHL)) { - left = 1; - } else if (accept_punct(p, P_SHR)) { - left = 0; - } else { - break; - } - r = cexpr_add(p, loc); - vt = cint_promote_type(p, v.type); - v = cint_convert(p, v, vt); - sh = const_int_as_i64(p, r); - if (sh < 0 || sh >= (i64)cint_bits(p, vt)) - perr(p, "shift count out of range in constant expression"); - if (left) { - if (cint_signed(p, vt) && const_int_as_i64(p, v) < 0) - perr(p, "left shift of negative value in constant expression"); - v = cint_shl(p, vt, v, (u32)sh); - } else if (cint_signed(p, vt)) { - v = cint_make_u64(p, vt, (u64)(const_int_as_i64(p, v) >> (u32)sh)); - } else { - v = cint_shr_u(p, vt, v, (u32)sh); - } - } - return v; -} -static CConstInt cexpr_rel(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_shift(p, loc); - for (;;) { - int op = 0; - CConstInt r; - const Type* ct; - int res; - if (accept_punct(p, P_LE)) { - op = P_LE; - } else if (accept_punct(p, P_GE)) { - op = P_GE; - } else if (is_punct(&p->cur, '<')) { - advance(p); - op = '<'; - } else if (is_punct(&p->cur, '>')) { - advance(p); - op = '>'; - } else { - break; - } - r = cexpr_shift(p, loc); - ct = cint_common_type(p, v.type, r.type); - v = cint_convert(p, v, ct); - r = cint_convert(p, r, ct); - if (cint_signed(p, ct)) { - i64 lv = const_int_as_i64(p, v); - i64 rv = const_int_as_i64(p, r); - res = op == P_LE ? lv <= rv - : op == P_GE ? lv >= rv - : op == '<' ? lv < rv - : lv > rv; - } else { - int cmp = cint_cmp_u(v, r); - res = op == P_LE ? cmp <= 0 - : op == P_GE ? cmp >= 0 - : op == '<' ? cmp < 0 - : cmp > 0; - } - v = cint_bool(p, res); - } - return v; -} -static CConstInt cexpr_eq(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_rel(p, loc); - for (;;) { - int ne = 0; - CConstInt r; - const Type* ct; - if (accept_punct(p, P_EQ)) { - ne = 0; - } else if (accept_punct(p, P_NE)) { - ne = 1; - } else { - break; - } - r = cexpr_rel(p, loc); - ct = cint_common_type(p, v.type, r.type); - v = cint_convert(p, v, ct); - r = cint_convert(p, r, ct); - v = cint_bool(p, ne ? !cint_eq(v, r) : cint_eq(v, r)); - } - return v; -} -static CConstInt cexpr_band(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_eq(p, loc); - while (is_punct(&p->cur, '&') && !is_punct(&p->cur, P_AND)) { - CConstInt r; - const Type* ct; - advance(p); - r = cexpr_eq(p, loc); - ct = cint_common_type(p, v.type, r.type); - v = cint_convert(p, v, ct); - r = cint_convert(p, r, ct); - v = cint_make_pair(p, ct, v.lo & r.lo, v.hi & r.hi); - } - return v; -} -static CConstInt cexpr_bxor(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_band(p, loc); - while (accept_punct(p, '^')) { - CConstInt r = cexpr_band(p, loc); - const Type* ct = cint_common_type(p, v.type, r.type); - v = cint_convert(p, v, ct); - r = cint_convert(p, r, ct); - v = cint_make_pair(p, ct, v.lo ^ r.lo, v.hi ^ r.hi); +static int cg_top2_const_int(Parser* p, KitCgConstInt* out) { + if (!p || kit_cg_stack_depth(p->cg) < 2u) return 0; + c_cg_swap(p); + if (!kit_cg_top_const_int_ex(p->cg, out)) { + c_cg_swap(p); + return 0; } - return v; + c_cg_swap(p); + return 1; } -static CConstInt cexpr_bor(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_bxor(p, loc); - while (is_punct(&p->cur, '|') && !is_punct(&p->cur, P_OR)) { - CConstInt r; - const Type* ct; - advance(p); - r = cexpr_bxor(p, loc); - ct = cint_common_type(p, v.type, r.type); - v = cint_convert(p, v, ct); - r = cint_convert(p, r, ct); - v = cint_make_pair(p, ct, v.lo | r.lo, v.hi | r.hi); + +static CConstInt cconst_from_cg(Parser* p, const Type* ty, KitCgConstInt v) { + CConstInt r; + memset(&r, 0, sizeof r); + r.type = ty ? type_unqual(p->pool, ty) : ty_int(p); + r.lo = v.lo; + r.hi = v.hi; + if (r.type && r.type->kind == TY_BOOL) { + r.lo = (r.lo || r.hi) ? 1u : 0u; + r.hi = 0; } - return v; + return r; } -static CConstInt cexpr_land(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_bor(p, loc); - while (accept_punct(p, P_AND)) { - CConstInt r = cexpr_bor(p, loc); - v = cint_bool(p, cint_truth(p, v) && cint_truth(p, r)); + +i64 const_int_as_i64(Parser* p, CConstInt v) { + u32 nb = integer_type_bits(p, v.type); + u64 u = v.lo; + if (integer_type_signed(p, v.type) && nb < 64) { + u64 mask = (1ull << nb) - 1ull; + u64 sign = 1ull << (nb - 1u); + u &= mask; + if (u & sign) u |= ~mask; } - return v; + return (i64)u; } -static CConstInt cexpr_lor(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_land(p, loc); - while (accept_punct(p, P_OR)) { - CConstInt r = cexpr_land(p, loc); - v = cint_bool(p, cint_truth(p, v) || cint_truth(p, r)); - } - return v; + +static i64 cg_const_as_i64(Parser* p, const Type* ty, KitCgConstInt v) { + return const_int_as_i64(p, cconst_from_cg(p, ty, v)); } -static CConstInt cexpr_cond(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_lor(p, loc); - if (accept_punct(p, '?')) { - CConstInt then_v = cexpr_cond(p, loc); - CConstInt else_v; - const Type* ct; - expect_punct(p, ':', "':' in constant conditional expression"); - else_v = cexpr_cond(p, loc); - ct = cint_common_type(p, then_v.type, else_v.type); - then_v = cint_convert(p, then_v, ct); - else_v = cint_convert(p, else_v, ct); - v = cint_truth(p, v) ? then_v : else_v; + +static void c_const_check_divrem(Parser* p, BinOp bop, SrcLoc loc) { + KitCgConstInt rhs; + if (!c_const_guard_active(p)) return; + if (bop != BO_SDIV && bop != BO_UDIV && bop != BO_SREM && bop != BO_UREM) + return; + if (kit_cg_top_const_int_ex(p->cg, &rhs) && cg_const_is_zero(rhs)) { + compiler_panic(p->c, loc, + (bop == BO_SDIV || bop == BO_UDIV) + ? "division by zero in constant" + : "modulo by zero in constant"); } - return v; } -static CConstInt cexpr_unary(Parser* p, SrcLoc loc) { - if (accept_punct(p, '+')) return cexpr_unary(p, loc); - if (accept_punct(p, '-')) { - CConstInt v = cexpr_unary(p, loc); - const Type* pt = cint_promote_type(p, v.type); - v = cint_convert(p, v, pt); - return cint_neg(p, pt, v); - } - if (accept_punct(p, '~')) { - CConstInt v = cexpr_unary(p, loc); - const Type* pt = cint_promote_type(p, v.type); - v = cint_convert(p, v, pt); - return cint_bnot(p, pt, v); - } - if (accept_punct(p, '!')) - return cint_bool(p, !cint_truth(p, cexpr_unary(p, loc))); - if (accept_kw(p, KW_SIZEOF)) { - if (is_punct(&p->cur, '(')) { - Tok n = peek1(p); - if (starts_type_name(p, &n)) { - advance(p); - { - const Type* t = parse_type_name(p); - expect_punct(p, ')', "')' after sizeof type-name"); - require_sizeof_type(p, t); - return cint_make_u64(p, ty_size_t(p), - c_abi_sizeof(p->abi, p->pool, t)); - } - } - } - parse_unary(p); - { - const Type* ty = pcg_top_type(p); - if (pcg_top_is_bitfield(p)) perr(p, "sizeof bit-field"); - require_sizeof_type(p, ty); - i64 sz = (i64)c_abi_sizeof(p->abi, p->pool, ty); - pcg_drop(p); - return cint_make_u64(p, ty_size_t(p), (u64)sz); - } - } - if (accept_kw(p, KW_ALIGNOF)) { - if (is_punct(&p->cur, '(')) { - Tok n = peek1(p); - if (starts_type_name(p, &n)) { - advance(p); - { - const Type* t = parse_type_name(p); - expect_punct(p, ')', "')' after _Alignof type-name"); - return cint_make_u64(p, ty_size_t(p), - c_abi_alignof(p->abi, p->pool, t)); - } - } - } - parse_unary(p); - { - const Type* ty = pcg_top_type(p); - i64 al = (i64)c_abi_alignof(p->abi, p->pool, ty); - pcg_drop(p); - return cint_make_u64(p, ty_size_t(p), (u64)al); - } +static void c_const_check_shift(Parser* p, BinOp bop, SrcLoc loc) { + KitCgConstInt lhs; + KitCgConstInt rhs; + const Type* lhs_ty; + i64 sh; + if (!c_const_guard_active(p)) return; + if (bop != BO_SHL && bop != BO_SHR_S && bop != BO_SHR_U) return; + if (!kit_cg_top_const_int_ex(p->cg, &rhs)) return; + lhs_ty = c_cg_top2_type(p); + sh = cg_const_as_i64(p, c_cg_top_type(p), rhs); + if (sh < 0 || sh >= (i64)integer_type_bits(p, lhs_ty)) { + compiler_panic(p->c, loc, + "shift count out of range in constant expression"); } - if (accept_punct(p, '(')) { - if (starts_type_name(p, &p->cur)) { - const Type* t = parse_type_name(p); - expect_punct(p, ')', "')' after cast type-name"); - { - const Type* tu = type_unqual(p->pool, t); - if (p->cur.kind == TOK_FLT) { - double fv; - if (!tu || !type_is_int(tu)) { - perr(p, "integer constant expression cast requires integer type"); - } - fv = parse_float_literal(p, &p->cur); - advance(p); - return cint_make_u64(p, tu, (u64)(i64)fv); - } - CConstInt v = cexpr_unary(p, loc); - return cint_cast(p, v, t); - } - } - { - CConstInt v = cexpr_cond(p, loc); - expect_punct(p, ')', "')' in constant expression"); - return v; - } - } - if (p->cur.kind == TOK_NUM) { - i64 v = parse_int_literal(p, &p->cur); - const Type* ty = int_literal_type(p, &p->cur); - advance(p); - return cint_make_u64(p, ty, (u64)v); - } - if (p->cur.kind == TOK_CHR) { - i64 v = decode_char_literal(p, &p->cur); - const Type* ty = char_literal_type(p, &p->cur); - advance(p); - return cint_make_u64(p, ty, (u64)v); - } - if (p->cur.kind == TOK_IDENT) { - Sym name = tok_ident(&p->cur); - if (name == p->sym_b_offsetof) { - u32 off = 0; - const Type* root; - advance(p); /* IDENT */ - expect_punct(p, '(', "'(' after __builtin_offsetof"); - root = parse_type_name(p); - expect_punct(p, ',', "',' in __builtin_offsetof"); - (void)offsetof_designator(p, root, &off); - expect_punct(p, ')', "')' after __builtin_offsetof"); - return cint_make_u64(p, ty_size_t(p), off); - } - if (name == p->sym_b_constant_p) { - /* In a constant expression too: fold to 1 iff the operand folds to a - * compile-time constant (see parse_builtin_constant_p_call). */ - i64 cval; - int is_const; - advance(p); /* IDENT */ - expect_punct(p, '(', "'(' after __builtin_constant_p"); - parse_assign_expr(p); - is_const = pcg_emit_enabled(p) && kit_cg_top_const_int(p->cg, &cval); - pcg_drop(p); - expect_punct(p, ')', "')' after __builtin_constant_p"); - return cint_bool(p, is_const); - } - { - SymEntry* e = scope_lookup(p, name); - if (e && e->kind == SEK_ENUM_CST) { - advance(p); - return cint_make_u64(p, e->type ? e->type : ty_int(p), - (u64)e->v.enum_value); - } - } - compiler_panic(p->c, loc, "non-constant identifier in constant expression"); + if (bop == BO_SHL && integer_type_signed(p, lhs_ty) && + cg_top2_const_int(p, &lhs) && cg_const_as_i64(p, lhs_ty, lhs) < 0) { + compiler_panic(p->c, loc, + "left shift of negative value in constant expression"); } - compiler_panic(p->c, loc, "expected constant expression"); } CConstInt eval_const_int_typed(Parser* p, SrcLoc loc) { - CConstInt v = cexpr_cond(p, loc); - if (!type_is_int(v.type)) perr(p, "integer constant expression required"); - return v; + CConstGuardMark mark; + KitCgConstInt cg_value; + CConstInt value; + const Type* ty; + const char* guard_error = NULL; + SrcLoc guard_loc; + int has_const; + u32 start_cg_depth; + + memset(&cg_value, 0, sizeof cg_value); + memset(&value, 0, sizeof value); + memset(&guard_loc, 0, sizeof guard_loc); + start_cg_depth = kit_cg_stack_depth(p->cg); + + mark = c_const_guard_push(p); + kit_cg_unevaluated_push(p->cg); + parse_cond_expr(p); + ty = c_cg_top_type(p); + has_const = kit_cg_top_const_int_ex(p->cg, &cg_value); + c_cg_drop_to_depth(p, start_cg_depth); + kit_cg_unevaluated_pop(p->cg); + if (!c_const_guard_pop(p, mark, &guard_error, &guard_loc)) { + compiler_panic(p->c, guard_loc, "%.*s", + KIT_SLICE_ARG(kit_slice_cstr(guard_error))); + } + + if (kit_cg_stack_depth(p->cg) != start_cg_depth) { + compiler_panic(p->c, loc, + "internal parser constant expression stack imbalance"); + } + if (!type_is_int(ty)) perr(p, "integer constant expression required"); + if (!has_const) perr(p, "integer constant expression required"); + value = cconst_from_cg(p, ty, cg_value); + return value; } i64 eval_const_int(Parser* p, SrcLoc loc) { @@ -1018,7 +709,7 @@ i64 eval_const_int(Parser* p, SrcLoc loc) { * ============================================================ */ static void decay_array_to_pointer(Parser* p, const Type* arr_ty) { - pcg_decay_array(p, arr_ty); + c_cg_decay_array(p, arr_ty); } static FrameSlot vla_size_slot_for_type(VLABound* bounds, const Type* ty) { @@ -1029,33 +720,24 @@ static FrameSlot vla_size_slot_for_type(VLABound* bounds, const Type* ty) { } void to_rvalue(Parser* p) { - const Type* t = pcg_top_type(p); - int is_lvalue = pcg_top_is_lvalue(p); + const Type* t = c_cg_top_type(p); + int is_lvalue = c_cg_top_is_lvalue(p); if (t) { if (t->kind == TY_ARRAY) { decay_array_to_pointer(p, t); return; } if (t->kind == TY_FUNC) { - pcg_addr(p); + c_cg_addr(p); return; } if (t->kind == TY_STRUCT || t->kind == TY_UNION) { const Type* uty = type_unqual(p->pool, t); - PcgLvAux* lv = pcg_top_lv_aux(p); - int materialize = - is_lvalue && lv && - (lv->offset != 0 || lv->scale != 0 || lv->is_subobject || - lv->base_kind == PCG_LV_BASE_POINTER_RV); - pcg_retag_keep_flags(p, 0, uty); - if (materialize) { - pcg_addr(p); - pcg_deref(p, uty); - } + c_cg_retag_keep_flags(p, 0, uty); return; } } - if (is_lvalue) pcg_load(p); + if (is_lvalue) c_cg_load(p); } /* ============================================================ @@ -1063,27 +745,27 @@ void to_rvalue(Parser* p) { * ============================================================ */ void coerce_top_to_lvalue(Parser* p) { - const Type* src = pcg_top_type(p); - const Type* dst = pcg_top2_type(p); + const Type* src = c_cg_top_type(p); + const Type* dst = c_cg_top2_type(p); if (!src || !dst || src == dst) return; if (type_is_arith(src) && type_is_arith(dst)) { - pcg_convert(p, dst); + c_cg_convert(p, dst); } else if (type_is_arith(src) && type_is_ptr(dst)) { - pcg_convert(p, dst); + c_cg_convert(p, dst); } else if (type_is_ptr(src) && type_is_ptr(dst)) { - pcg_convert(p, dst); + c_cg_convert(p, dst); } } void coerce_top_to_type(Parser* p, const Type* dst) { - const Type* src = pcg_top_type(p); + const Type* src = c_cg_top_type(p); if (!src || !dst || src == dst) return; if (type_is_arith(src) && type_is_arith(dst)) { - pcg_convert(p, dst); + c_cg_convert(p, dst); } else if (type_is_arith(src) && type_is_ptr(dst)) { - pcg_convert(p, dst); + c_cg_convert(p, dst); } else if (type_is_ptr(src) && type_is_ptr(dst)) { - pcg_convert(p, dst); + c_cg_convert(p, dst); } } @@ -1123,7 +805,7 @@ static const Type* atomic_lock_free_type_for_size(Parser* p, i64 size) { static int atomic_lock_free_for_const_size(Parser* p, i64 size) { if (size <= 0 || size > 16) return 0; const Type* ty = atomic_lock_free_type_for_size(p, size); - return ty ? kit_cg_atomic_is_lock_free(p->c, pcg_mem(p, ty)) : 0; + return ty ? kit_cg_atomic_is_lock_free(p->c, c_cg_mem(p, ty)) : 0; } static KitCgSym builtin_libcall_sym(Parser* p, const char* name, @@ -1134,7 +816,7 @@ static KitCgSym builtin_libcall_sym(Parser* p, const char* name, decl.kind = KIT_CG_DECL_FUNC; decl.display_name = source_name; decl.linkage_name = kit_cg_c_linkage_name(p->c, source_name); - decl.type = pcg_tid(p, fn_ty); + decl.type = c_cg_tid(p, fn_ty); decl.sym.bind = KIT_SB_GLOBAL; decl.sym.visibility = KIT_CG_VIS_DEFAULT; decl.as.func.debug_type = type_cg_debug_in_pool(p->cg, p->c, p->pool, fn_ty); @@ -1212,10 +894,9 @@ static int parse_builtin_mem_call(Parser* p, Sym name, SrcLoc loc) { fn_ty = type_func(p->pool, int_ty, params, 3, 0); } - sym = pcg_emit_enabled(p) ? builtin_libcall_sym(p, libname, fn_ty) - : KIT_CG_SYM_NONE; - pcg_set_loc(p, loc); - pcg_call_symbol(p, sym, 3, fn_ty); + sym = builtin_libcall_sym(p, libname, fn_ty); + c_cg_set_loc(p, loc); + c_cg_call_symbol(p, sym, 3, fn_ty); return 1; } @@ -1247,20 +928,19 @@ static int parse_builtin_clear_cache_call(Parser* p, Sym name, SrcLoc loc) { * The argument expressions are still evaluated for their side effects. The * coherency fact is a backend capability, not an arch identity. */ if (kit_cg_target_backend_features(p->c) & KIT_CG_BACKEND_ICACHE_COHERENT) { - pcg_drop(p); - pcg_drop(p); - pcg_push_int(p, 0, ty_int(p)); + c_cg_drop(p); + c_cg_drop(p); + c_cg_push_int(p, 0, ty_int(p)); return 1; } params[0] = void_ptr_ty; params[1] = void_ptr_ty; fn_ty = type_func(p->pool, void_ty, params, 2, 0); - sym = pcg_emit_enabled(p) ? builtin_libcall_sym(p, "__clear_cache", fn_ty) - : KIT_CG_SYM_NONE; - pcg_set_loc(p, loc); - pcg_call_symbol(p, sym, 2, fn_ty); - pcg_push_int(p, 0, ty_int(p)); + sym = builtin_libcall_sym(p, "__clear_cache", fn_ty); + c_cg_set_loc(p, loc); + c_cg_call_symbol(p, sym, 2, fn_ty); + c_cg_push_int(p, 0, ty_int(p)); return 1; } @@ -1270,7 +950,7 @@ static MemOrder parse_atomic_mem_order(Parser* p) { } parse_assign_expr(p); to_rvalue(p); - pcg_drop(p); + c_cg_drop(p); return MO_SEQ_CST; } @@ -1407,7 +1087,7 @@ static FrameSlot builtin_tmp_slot(Parser* p, const Type* ty) { fsd.size = c_abi_sizeof(p->abi, p->pool, ty); fsd.align = c_abi_alignof(p->abi, p->pool, ty); fsd.kind = FS_LOCAL; - return pcg_local(p, &fsd); + return c_cg_local(p, &fsd); } static int parse_builtin_overflow_call(Parser* p, Sym name, SrcLoc loc) { @@ -1435,7 +1115,7 @@ static int parse_builtin_overflow_call(Parser* p, Sym name, SrcLoc loc) { expect_punct(p, ',', "',' in overflow builtin"); parse_assign_expr(p); to_rvalue(p); - ptr_ty = pcg_top_type(p); + ptr_ty = c_cg_top_type(p); if (!ptr_ty || ptr_ty->kind != TY_PTR) { perr(p, "overflow builtin result argument must be a pointer"); } @@ -1446,32 +1126,28 @@ static int parse_builtin_overflow_call(Parser* p, Sym name, SrcLoc loc) { expect_punct(p, ')', "')' after overflow builtin"); ptr_slot = builtin_tmp_slot(p, ptr_ty); - pcg_push_local_typed(p, ptr_slot, ptr_ty); - pcg_swap(p); - pcg_store_void(p); + c_cg_push_local_typed(p, ptr_slot, ptr_ty); + c_cg_swap(p); + c_cg_store_void(p); - pcg_set_loc(p, loc); - if (pcg_emit_enabled(p)) { - kit_cg_intrinsic(p->cg, info.intrin, 2, pcg_tid(p, op_ty)); - } - pcg_drop_type(p); - pcg_drop_type(p); - pcg_push_type(p, op_ty); - pcg_push_type(p, bool_ty); + c_cg_set_loc(p, loc); + kit_cg_intrinsic(p->cg, info.intrin, 2, c_cg_tid(p, op_ty)); + c_cg_retag_at(p, 1, op_ty, 0); + c_cg_retag_top(p, bool_ty); ov_slot = builtin_tmp_slot(p, bool_ty); - pcg_push_local_typed(p, ov_slot, bool_ty); - pcg_swap(p); - pcg_store_void(p); - - pcg_push_local_typed(p, ptr_slot, ptr_ty); - pcg_load(p); - pcg_deref(p, out_ty); - pcg_swap(p); - pcg_store_void(p); - - pcg_push_local_typed(p, ov_slot, bool_ty); - pcg_load(p); + c_cg_push_local_typed(p, ov_slot, bool_ty); + c_cg_swap(p); + c_cg_store_void(p); + + c_cg_push_local_typed(p, ptr_slot, ptr_ty); + c_cg_load(p); + c_cg_deref(p, out_ty); + c_cg_swap(p); + c_cg_store_void(p); + + c_cg_push_local_typed(p, ov_slot, bool_ty); + c_cg_load(p); return 1; } @@ -1483,15 +1159,15 @@ static int parse_builtin_isnan_call(Parser* p, Sym name, SrcLoc loc) { expect_punct(p, '(', "'(' after __builtin_isnan"); parse_assign_expr(p); to_rvalue(p); - arg_ty = pcg_top_type(p); + arg_ty = c_cg_top_type(p); if (!type_is_fp(arg_ty)) { perr(p, "__builtin_isnan argument must have floating type"); } expect_punct(p, ')', "')' after __builtin_isnan"); - pcg_set_loc(p, loc); - pcg_dup(p); - pcg_cmp(p, CMP_NE); + c_cg_set_loc(p, loc); + c_cg_dup(p); + c_cg_cmp(p, CMP_NE); return 1; } @@ -1528,13 +1204,13 @@ static int parse_builtin_fp_cmp_call(Parser* p, Sym name, SrcLoc loc) { expect_punct(p, '(', "'(' after floating comparison builtin"); parse_assign_expr(p); to_rvalue(p); - if (!type_is_fp(pcg_top_type(p))) { + if (!type_is_fp(c_cg_top_type(p))) { perr(p, "floating comparison builtin requires floating arguments"); } expect_punct(p, ',', "',' between floating comparison builtin arguments"); parse_assign_expr(p); to_rvalue(p); - if (!type_is_fp(pcg_top_type(p))) { + if (!type_is_fp(c_cg_top_type(p))) { perr(p, "floating comparison builtin requires floating arguments"); } expect_punct(p, ')', "')' after floating comparison builtin"); @@ -1542,12 +1218,12 @@ static int parse_builtin_fp_cmp_call(Parser* p, Sym name, SrcLoc loc) { /* Bring both operands to a common floating type so the compare (and the * synthesized self-compares) see matching widths, mirroring the bare * relational path (parse_rel). */ - common = common_fp_type(p, pcg_top2_type(p), pcg_top_type(p)); + common = common_fp_type(p, c_cg_top2_type(p), c_cg_top_type(p)); coerce_fp_cmp_operands(p, common); - pcg_set_loc(p, loc); + c_cg_set_loc(p, loc); if (!is_unordered) { - pcg_cmp(p, cop); + c_cg_cmp(p, cop); return 1; } @@ -1557,23 +1233,23 @@ static int parse_builtin_fp_cmp_call(Parser* p, Sym name, SrcLoc loc) { FrameSlot slot_a = builtin_tmp_slot(p, common); FrameSlot slot_b = builtin_tmp_slot(p, common); /* stack: [a, b] */ - pcg_push_local_typed(p, slot_b, common); - pcg_swap(p); - pcg_store_void(p); + c_cg_push_local_typed(p, slot_b, common); + c_cg_swap(p); + c_cg_store_void(p); /* stack: [a] */ - pcg_push_local_typed(p, slot_a, common); - pcg_swap(p); - pcg_store_void(p); + c_cg_push_local_typed(p, slot_a, common); + c_cg_swap(p); + c_cg_store_void(p); /* stack: [] */ - pcg_push_local_typed(p, slot_a, common); - pcg_load(p); - pcg_dup(p); - pcg_cmp(p, CMP_NE); /* isnan(a): unordered not-equal */ - pcg_push_local_typed(p, slot_b, common); - pcg_load(p); - pcg_dup(p); - pcg_cmp(p, CMP_NE); /* isnan(b) */ - pcg_binop(p, BO_OR); + c_cg_push_local_typed(p, slot_a, common); + c_cg_load(p); + c_cg_dup(p); + c_cg_cmp(p, CMP_NE); /* isnan(a): unordered not-equal */ + c_cg_push_local_typed(p, slot_b, common); + c_cg_load(p); + c_cg_dup(p); + c_cg_cmp(p, CMP_NE); /* isnan(b) */ + c_cg_binop(p, BO_OR); } return 1; } @@ -1602,8 +1278,8 @@ static int parse_builtin_inf_call(Parser* p, Sym name, SrcLoc loc) { advance(p); /* IDENT */ expect_punct(p, '(', "'(' after floating builtin"); expect_punct(p, ')', "')' after floating builtin"); - pcg_set_loc(p, loc); - pcg_push_float(p, __builtin_inf(), ty); + c_cg_set_loc(p, loc); + c_cg_push_float(p, __builtin_inf(), ty); return 1; } @@ -1621,10 +1297,10 @@ static int parse_builtin_nan_call(Parser* p, Sym name, SrcLoc loc) { expect_punct(p, '(', "'(' after __builtin_nan"); parse_assign_expr(p); /* the tag string — evaluated and discarded */ to_rvalue(p); - pcg_drop(p); + c_cg_drop(p); expect_punct(p, ')', "')' after __builtin_nan"); - pcg_set_loc(p, loc); - pcg_push_float(p, __builtin_nan(""), ty); + c_cg_set_loc(p, loc); + c_cg_push_float(p, __builtin_nan(""), ty); return 1; } @@ -1638,17 +1314,19 @@ static int parse_builtin_nan_call(Parser* p, Sym name, SrcLoc loc) { * (so the result — 0 — is still correct). The result is a pushed integer * constant, usable anywhere a constant expression is. */ static int parse_builtin_constant_p_call(Parser* p, Sym name, SrcLoc loc) { - i64 cval; + KitCgConstInt cval; int is_const; if (name != p->sym_b_constant_p) return 0; advance(p); /* IDENT */ expect_punct(p, '(', "'(' after __builtin_constant_p"); + c_const_guard_not_eval_push(p); parse_assign_expr(p); - is_const = pcg_emit_enabled(p) && kit_cg_top_const_int(p->cg, &cval); - pcg_drop(p); + is_const = c_cg_emit_enabled(p) && kit_cg_top_const_int_ex(p->cg, &cval); + c_cg_drop(p); + c_const_guard_not_eval_pop(p); expect_punct(p, ')', "')' after __builtin_constant_p"); - pcg_set_loc(p, loc); - pcg_push_int(p, is_const ? 1 : 0, ty_int(p)); + c_cg_set_loc(p, loc); + c_cg_push_int(p, is_const ? 1 : 0, ty_int(p)); return 1; } @@ -1667,42 +1345,42 @@ static int parse_builtin_fabs_call(Parser* p, Sym name, SrcLoc loc) { expect_punct(p, '(', "'(' after __builtin_fabs"); parse_assign_expr(p); to_rvalue(p); - if (!type_is_fp(pcg_top_type(p))) { + if (!type_is_fp(c_cg_top_type(p))) { perr(p, "__builtin_fabs argument must have floating type"); } coerce_top_to_type(p, ty); expect_punct(p, ')', "')' after __builtin_fabs"); slot = builtin_tmp_slot(p, ty); - pcg_push_local_typed(p, slot, ty); - pcg_swap(p); - pcg_store_void(p); - - pcg_set_loc(p, loc); - pcg_push_local_typed(p, slot, ty); - pcg_load(p); - pcg_push_float(p, 0.0, ty); - pcg_cmp(p, CMP_LT_F); - L_nonneg = pcg_label_new(p); - pcg_branch_false(p, L_nonneg); - pcg_push_local_typed(p, slot, ty); - pcg_push_local_typed(p, slot, ty); - pcg_load(p); - pcg_unop(p, UO_NEG); - pcg_store_void(p); - pcg_label_place(p, L_nonneg); - pcg_push_local_typed(p, slot, ty); - pcg_load(p); - pcg_push_float(p, 0.0, ty); - pcg_cmp(p, CMP_EQ); - L_nonzero = pcg_label_new(p); - pcg_branch_false(p, L_nonzero); - pcg_push_local_typed(p, slot, ty); - pcg_push_float(p, 0.0, ty); - pcg_store_void(p); - pcg_label_place(p, L_nonzero); - pcg_push_local_typed(p, slot, ty); - pcg_load(p); + c_cg_push_local_typed(p, slot, ty); + c_cg_swap(p); + c_cg_store_void(p); + + c_cg_set_loc(p, loc); + c_cg_push_local_typed(p, slot, ty); + c_cg_load(p); + c_cg_push_float(p, 0.0, ty); + c_cg_cmp(p, CMP_LT_F); + L_nonneg = c_cg_label_new(p); + c_cg_branch_false(p, L_nonneg); + c_cg_push_local_typed(p, slot, ty); + c_cg_push_local_typed(p, slot, ty); + c_cg_load(p); + c_cg_unop(p, UO_NEG); + c_cg_store_void(p); + c_cg_label_place(p, L_nonneg); + c_cg_push_local_typed(p, slot, ty); + c_cg_load(p); + c_cg_push_float(p, 0.0, ty); + c_cg_cmp(p, CMP_EQ); + L_nonzero = c_cg_label_new(p); + c_cg_branch_false(p, L_nonzero); + c_cg_push_local_typed(p, slot, ty); + c_cg_push_float(p, 0.0, ty); + c_cg_store_void(p); + c_cg_label_place(p, L_nonzero); + c_cg_push_local_typed(p, slot, ty); + c_cg_load(p); return 1; } @@ -1738,10 +1416,9 @@ static int parse_builtin_abs_call(Parser* p, Sym name, SrcLoc loc) { params[0] = int_ty; fn_ty = type_func(p->pool, int_ty, params, 1, 0); - sym = pcg_emit_enabled(p) ? builtin_libcall_sym(p, libname, fn_ty) - : KIT_CG_SYM_NONE; - pcg_set_loc(p, loc); - pcg_call_symbol(p, sym, 1, fn_ty); + sym = builtin_libcall_sym(p, libname, fn_ty); + c_cg_set_loc(p, loc); + c_cg_call_symbol(p, sym, 1, fn_ty); return 1; } @@ -1772,8 +1449,8 @@ static int parse_kit_syscall_call(Parser* p, Sym name, SrcLoc loc) { } expect_punct(p, ')', "')' after __kit_syscall"); - pcg_set_loc(p, loc); - pcg_syscall(p, nargs, long_ty); + c_cg_set_loc(p, loc); + c_cg_syscall(p, nargs, long_ty); return 1; } @@ -1781,6 +1458,12 @@ static int try_parse_builtin_call(Parser* p) { Sym name = tok_ident(&p->cur); SrcLoc loc = pp_materialize_loc(p->pp, p->cur.loc); + if (c_const_guard_active(p) && name != p->sym_b_offsetof && + name != p->sym_b_constant_p) { + c_const_guard_note_at(p, loc, + "function call in integer constant expression"); + } + if (parse_kit_syscall_call(p, name, loc)) return 1; if (name == p->sym_b_memcpy || name == p->sym_b_memmove || @@ -1825,7 +1508,7 @@ static int try_parse_builtin_call(Parser* p) { u32 off = 0; (void)offsetof_designator(p, root, &off); expect_punct(p, ')', "')' after __builtin_offsetof"); - pcg_push_int(p, (i64)off, ty_size_t(p)); + c_cg_push_int(p, (i64)off, ty_size_t(p)); return 1; } @@ -1834,7 +1517,7 @@ static int try_parse_builtin_call(Parser* p) { to_rvalue(p); expect_punct(p, ',', "',' in __builtin_expect"); parse_assign_expr(p); - pcg_drop(p); + c_cg_drop(p); expect_punct(p, ')', "')' after __builtin_expect"); return 1; } @@ -1843,8 +1526,8 @@ static int try_parse_builtin_call(Parser* p) { parse_assign_expr(p); to_rvalue(p); expect_punct(p, ')', "')' after __builtin_alloca"); - pcg_set_loc(p, loc); - pcg_alloca(p); + c_cg_set_loc(p, loc); + c_cg_alloca(p); return 1; } @@ -1852,8 +1535,8 @@ static int try_parse_builtin_call(Parser* p) { parse_assign_expr(p); to_rvalue(p); expect_punct(p, ')', "')' after __builtin_ctz"); - pcg_set_loc(p, loc); - pcg_intrinsic_unary_to_int(p, INTRIN_CTZ); + c_cg_set_loc(p, loc); + c_cg_intrinsic_unary_to_int(p, INTRIN_CTZ); return 1; } @@ -1861,24 +1544,24 @@ static int try_parse_builtin_call(Parser* p) { parse_assign_expr(p); to_rvalue(p); expect_punct(p, ')', "')' after __builtin_clz"); - pcg_set_loc(p, loc); + c_cg_set_loc(p, loc); /* The operand carries its own type, which drives the sf bit on * aarch64 / REX.W on x64 / sf on rv64. Whether the caller used the * `l` / `ll` suffix only changes the C-level type the user wrote; * kit picks the instruction width from the value type. */ - pcg_intrinsic_unary_to_int(p, INTRIN_CLZ); + c_cg_intrinsic_unary_to_int(p, INTRIN_CLZ); return 1; } if (name == p->sym_b_trap || name == p->sym_b_unreachable) { expect_punct(p, ')', "')' after __builtin_trap/unreachable"); - pcg_set_loc(p, loc); - pcg_intrinsic_void( + c_cg_set_loc(p, loc); + c_cg_intrinsic_void( p, name == p->sym_b_trap ? INTRIN_TRAP : INTRIN_UNREACHABLE); /* Both are noreturn at the C level. Push a dummy `int 0` so callers * that consume an expression value (e.g. ternary, comma) don't see * an empty stack — the dead value will be folded out. */ - pcg_push_int(p, 0, ty_int(p)); + c_cg_push_int(p, 0, ty_int(p)); return 1; } @@ -1891,62 +1574,62 @@ static int try_parse_builtin_call(Parser* p) { if (level < 0) perr(p, "__builtin_%s: level must be non-negative", is_return ? "return_address" : "frame_address"); - pcg_set_loc(p, loc); - pcg_frame_or_return_address(p, is_return, (u32)level); + c_cg_set_loc(p, loc); + c_cg_frame_or_return_address(p, is_return, (u32)level); return 1; } if (name == p->sym_b_readcyclecounter) { expect_punct(p, ')', "')' after __builtin_readcyclecounter"); - pcg_set_loc(p, loc); - pcg_readcyclecounter(p); + c_cg_set_loc(p, loc); + c_cg_readcyclecounter(p); return 1; } if (name == p->sym_b_va_start) { parse_assign_expr(p); - pcg_addr(p); + c_cg_addr(p); expect_punct(p, ',', "',' in __builtin_va_start"); parse_assign_expr(p); - pcg_drop(p); + c_cg_drop(p); expect_punct(p, ')', "')' after __builtin_va_start"); - pcg_set_loc(p, loc); - pcg_va_start(p); - pcg_push_int(p, 0, ty_int(p)); + c_cg_set_loc(p, loc); + c_cg_va_start(p); + c_cg_push_int(p, 0, ty_int(p)); return 1; } if (name == p->sym_b_va_end) { parse_assign_expr(p); - pcg_addr(p); + c_cg_addr(p); expect_punct(p, ')', "')' after __builtin_va_end"); - pcg_set_loc(p, loc); - pcg_va_end(p); - pcg_push_int(p, 0, ty_int(p)); + c_cg_set_loc(p, loc); + c_cg_va_end(p); + c_cg_push_int(p, 0, ty_int(p)); return 1; } if (name == p->sym_b_va_copy) { parse_assign_expr(p); - pcg_addr(p); + c_cg_addr(p); expect_punct(p, ',', "',' in __builtin_va_copy"); parse_assign_expr(p); - pcg_addr(p); + c_cg_addr(p); expect_punct(p, ')', "')' after __builtin_va_copy"); - pcg_set_loc(p, loc); - pcg_va_copy(p); - pcg_push_int(p, 0, ty_int(p)); + c_cg_set_loc(p, loc); + c_cg_va_copy(p); + c_cg_push_int(p, 0, ty_int(p)); return 1; } if (name == p->sym_b_va_arg) { parse_assign_expr(p); - pcg_addr(p); + c_cg_addr(p); expect_punct(p, ',', "',' in __builtin_va_arg"); const Type* ty = parse_type_name(p); expect_punct(p, ')', "')' after __builtin_va_arg"); - pcg_set_loc(p, loc); - pcg_va_arg(p, ty); + c_cg_set_loc(p, loc); + c_cg_va_arg(p, ty); return 1; } @@ -1956,8 +1639,8 @@ static int try_parse_builtin_call(Parser* p) { expect_punct(p, ',', "',' in __atomic_load_n"); MemOrder ord = parse_atomic_mem_order(p); expect_punct(p, ')', "')' after __atomic_load_n"); - pcg_set_loc(p, loc); - pcg_atomic_load(p, ord); + c_cg_set_loc(p, loc); + c_cg_atomic_load(p, ord); return 1; } @@ -1965,7 +1648,7 @@ static int try_parse_builtin_call(Parser* p) { parse_assign_expr(p); to_rvalue(p); const Type* val_ty = - atomic_pointee_type(p, pcg_top_type(p), "__atomic_store_n"); + atomic_pointee_type(p, c_cg_top_type(p), "__atomic_store_n"); expect_punct(p, ',', "',' in __atomic_store_n"); parse_assign_expr(p); to_rvalue(p); @@ -1973,18 +1656,18 @@ static int try_parse_builtin_call(Parser* p) { expect_punct(p, ',', "',' in __atomic_store_n"); MemOrder ord = parse_atomic_mem_order(p); expect_punct(p, ')', "')' after __atomic_store_n"); - pcg_set_loc(p, loc); - pcg_atomic_store(p, ord); - pcg_push_int(p, 0, ty_int(p)); + c_cg_set_loc(p, loc); + c_cg_atomic_store(p, ord); + c_cg_push_int(p, 0, ty_int(p)); return 1; } if (name == p->sym_a_thread_fence || name == p->sym_a_signal_fence) { MemOrder ord = parse_atomic_mem_order(p); expect_punct(p, ')', "')' after atomic fence"); - pcg_set_loc(p, loc); - pcg_fence(p, ord); - pcg_push_int(p, 0, ty_int(p)); + c_cg_set_loc(p, loc); + c_cg_fence(p, ord); + c_cg_push_int(p, 0, ty_int(p)); return 1; } @@ -1993,9 +1676,9 @@ static int try_parse_builtin_call(Parser* p) { */ if (name == p->sym_sync_synchronize) { expect_punct(p, ')', "')' after __sync_synchronize"); - pcg_set_loc(p, loc); - pcg_fence(p, MO_SEQ_CST); - pcg_push_int(p, 0, ty_int(p)); + c_cg_set_loc(p, loc); + c_cg_fence(p, MO_SEQ_CST); + c_cg_push_int(p, 0, ty_int(p)); return 1; } @@ -2004,9 +1687,9 @@ static int try_parse_builtin_call(Parser* p) { expect_punct(p, ',', "',' in atomic lock-free builtin"); parse_assign_expr(p); to_rvalue(p); - pcg_drop(p); + c_cg_drop(p); expect_punct(p, ')', "')' after atomic lock-free builtin"); - pcg_push_int(p, atomic_lock_free_for_const_size(p, size), ty_int(p)); + c_cg_push_int(p, atomic_lock_free_for_const_size(p, size), ty_int(p)); return 1; } @@ -2014,12 +1697,12 @@ static int try_parse_builtin_call(Parser* p) { parse_assign_expr(p); to_rvalue(p); /* ptr */ const Type* obj_ty = - atomic_pointee_type(p, pcg_top_type(p), "__atomic_compare_exchange_n"); + atomic_pointee_type(p, c_cg_top_type(p), "__atomic_compare_exchange_n"); expect_punct(p, ',', "',' in __atomic_compare_exchange_n"); parse_assign_expr(p); to_rvalue(p); /* &expected */ - const Type* eptr_ty = pcg_top_type(p); + const Type* eptr_ty = c_cg_top_type(p); if (!eptr_ty || eptr_ty->kind != TY_PTR) { perr(p, "__atomic_compare_exchange_n: arg 2 must be a pointer"); } @@ -2034,15 +1717,15 @@ static int try_parse_builtin_call(Parser* p) { fsd.size = c_abi_sizeof(p->abi, p->pool, eptr_ty); fsd.align = c_abi_alignof(p->abi, p->pool, eptr_ty); fsd.kind = FS_LOCAL; - FrameSlot eslot = pcg_local(p, &fsd); - pcg_push_local_typed(p, eslot, eptr_ty); - pcg_swap(p); - pcg_store_void(p); + FrameSlot eslot = c_cg_local(p, &fsd); + c_cg_push_local_typed(p, eslot, eptr_ty); + c_cg_swap(p); + c_cg_store_void(p); - pcg_push_local_typed(p, eslot, eptr_ty); - pcg_load(p); - pcg_deref(p, val_ty); - pcg_load(p); + c_cg_push_local_typed(p, eslot, eptr_ty); + c_cg_load(p); + c_cg_deref(p, val_ty); + c_cg_load(p); expect_punct(p, ',', "',' in __atomic_compare_exchange_n"); parse_assign_expr(p); @@ -2057,20 +1740,20 @@ static int try_parse_builtin_call(Parser* p) { MemOrder fail = parse_atomic_mem_order(p); expect_punct(p, ')', "')' after __atomic_compare_exchange_n"); - pcg_set_loc(p, loc); - pcg_atomic_cas(p, succ, fail); + c_cg_set_loc(p, loc); + c_cg_atomic_cas(p, succ, fail); - const Type* ok_ty = pcg_top_type(p); + const Type* ok_ty = c_cg_top_type(p); FrameSlotDesc okd; memset(&okd, 0, sizeof okd); okd.type = ok_ty; okd.size = c_abi_sizeof(p->abi, p->pool, ok_ty); okd.align = c_abi_alignof(p->abi, p->pool, ok_ty); okd.kind = FS_LOCAL; - FrameSlot okslot = pcg_local(p, &okd); - pcg_push_local_typed(p, okslot, ok_ty); - pcg_swap(p); - pcg_store_void(p); + FrameSlot okslot = c_cg_local(p, &okd); + c_cg_push_local_typed(p, okslot, ok_ty); + c_cg_swap(p); + c_cg_store_void(p); FrameSlotDesc pd; memset(&pd, 0, sizeof pd); @@ -2078,25 +1761,25 @@ static int try_parse_builtin_call(Parser* p) { pd.size = c_abi_sizeof(p->abi, p->pool, val_ty); pd.align = c_abi_alignof(p->abi, p->pool, val_ty); pd.kind = FS_LOCAL; - FrameSlot pslot = pcg_local(p, &pd); - pcg_push_local_typed(p, pslot, val_ty); - pcg_swap(p); - pcg_store_void(p); - - pcg_push_local_typed(p, okslot, ok_ty); - pcg_load(p); - CGLabel L_done = pcg_label_new(p); - pcg_branch_true(p, L_done); - pcg_push_local_typed(p, eslot, eptr_ty); - pcg_load(p); - pcg_deref(p, val_ty); - pcg_push_local_typed(p, pslot, val_ty); - pcg_load(p); - pcg_store_void(p); - pcg_label_place(p, L_done); - - pcg_push_local_typed(p, okslot, ok_ty); - pcg_load(p); + FrameSlot pslot = c_cg_local(p, &pd); + c_cg_push_local_typed(p, pslot, val_ty); + c_cg_swap(p); + c_cg_store_void(p); + + c_cg_push_local_typed(p, okslot, ok_ty); + c_cg_load(p); + CGLabel L_done = c_cg_label_new(p); + c_cg_branch_true(p, L_done); + c_cg_push_local_typed(p, eslot, eptr_ty); + c_cg_load(p); + c_cg_deref(p, val_ty); + c_cg_push_local_typed(p, pslot, val_ty); + c_cg_load(p); + c_cg_store_void(p); + c_cg_label_place(p, L_done); + + c_cg_push_local_typed(p, okslot, ok_ty); + c_cg_load(p); return 1; } @@ -2122,7 +1805,7 @@ static int try_parse_builtin_call(Parser* p) { parse_assign_expr(p); to_rvalue(p); const Type* val_ty = - atomic_pointee_type(p, pcg_top_type(p), "__atomic read-modify-write"); + atomic_pointee_type(p, c_cg_top_type(p), "__atomic read-modify-write"); expect_punct(p, ',', "',' in atomic builtin"); parse_assign_expr(p); to_rvalue(p); @@ -2130,8 +1813,8 @@ static int try_parse_builtin_call(Parser* p) { expect_punct(p, ',', "',' in atomic builtin"); MemOrder ord = parse_atomic_mem_order(p); expect_punct(p, ')', "')' after atomic builtin"); - pcg_set_loc(p, loc); - pcg_atomic_rmw(p, op, ord); + c_cg_set_loc(p, loc); + c_cg_atomic_rmw(p, op, ord); return 1; } @@ -2145,14 +1828,16 @@ static void parse_primary(Parser* p) { i64 v = parse_int_literal(p, &t); const Type* lty = int_literal_type(p, &t); advance(p); - pcg_push_int(p, v, lty); + c_cg_push_int(p, v, lty); return; } if (t.kind == TOK_FLT) { double v = parse_float_literal(p, &t); const Type* lty = float_literal_type(p, &t); + c_const_guard_note_at(p, pp_materialize_loc(p->pp, t.loc), + "integer constant expression required"); advance(p); - pcg_push_float(p, v, lty); + c_cg_push_float(p, v, lty); return; } if (is_punct(&t, '(')) { @@ -2182,6 +1867,8 @@ static void parse_primary(Parser* p) { * trailing NUL). */ if (tok_ident(&t) == p->sym_func || tok_ident(&t) == p->sym_func_gcc || tok_ident(&t) == p->sym_pretty_func_gcc) { + c_const_guard_note_at(p, pp_materialize_loc(p->pp, t.loc), + "non-constant identifier in constant expression"); if (p->cur_func_name == 0) { compiler_panic( p->c, pp_materialize_loc(p->pp, t.loc), @@ -2196,14 +1883,16 @@ static void parse_primary(Parser* p) { const char* fn_name = fn_name_sl.s; Heap* h = kit_compiler_context(p->c)->heap; u8* bytes = (u8*)h->alloc(h, nlen + 1u, 1u); + ObjSymId sym; for (size_t i = 0; i < nlen; ++i) bytes[i] = (u8)fn_name[i]; bytes[nlen] = 0; - ObjSymId sym = emit_string_to_rodata(p, bytes, nlen + 1u); + sym = c_cg_emit_enabled(p) ? emit_string_to_rodata(p, bytes, nlen + 1u) + : OBJ_SYM_NONE; h->free(h, bytes, 0); advance(p); const Type* char_ty = type_prim(p->pool, TY_CHAR); const Type* arr_ty = type_array(p->pool, char_ty, (u32)(nlen + 1u), 0); - pcg_push_global(p, sym, arr_ty); + c_cg_push_global(p, sym, arr_ty); return; } e = scope_lookup(p, tok_ident(&t)); @@ -2214,11 +1903,15 @@ static void parse_primary(Parser* p) { compiler_panic(p->c, pp_materialize_loc(p->pp, t.loc), "undeclared identifier '%.*s'", (int)nlen, nm ? nm : "?"); } + if (e->kind != SEK_ENUM_CST) { + c_const_guard_note_at(p, pp_materialize_loc(p->pp, t.loc), + "non-constant identifier in constant expression"); + } advance(p); switch (e->kind) { case SEK_LOCAL: - pcg_push_local_typed(p, e->v.slot, e->type); - if (e->storage == DS_REGISTER) pcg_set_top_register(p); + c_cg_push_local_typed(p, e->v.slot, e->type); + if (e->storage == DS_REGISTER) c_cg_set_top_register(p); if (e->vla_byte_slot != FRAME_SLOT_NONE) { p->last_pushed_vla_slot = e->vla_byte_slot; } @@ -2228,10 +1921,10 @@ static void parse_primary(Parser* p) { return; case SEK_GLOBAL: case SEK_FUNC: - pcg_push_global(p, e->v.sym, e->type); + c_cg_push_global(p, e->v.sym, e->type); return; case SEK_ENUM_CST: - pcg_push_int(p, e->v.enum_value, e->type); + c_cg_push_int(p, e->v.enum_value, e->type); return; case SEK_TYPEDEF: default: @@ -2242,7 +1935,7 @@ static void parse_primary(Parser* p) { i64 v = decode_char_literal(p, &t); const Type* lty = char_literal_type(p, &t); advance(p); - pcg_push_int(p, v, lty); + c_cg_push_int(p, v, lty); return; } if (t.kind == TOK_STR) { @@ -2250,14 +1943,19 @@ static void parse_primary(Parser* p) { u8* bytes = decode_string_literal(p, &t, &n); const Type* elem_ty = string_literal_elem_type(p, &t); u32 elem_size = c_abi_sizeof(p->abi, p->pool, elem_ty); - ObjSymId sym = emit_string_literal_to_rodata(p, bytes, n, elem_ty); + ObjSymId sym = c_cg_emit_enabled(p) + ? emit_string_literal_to_rodata(p, bytes, n, elem_ty) + : OBJ_SYM_NONE; + c_const_guard_note_at( + p, pp_materialize_loc(p->pp, t.loc), + "address constant is not an integer constant expression"); kit_compiler_context(p->c)->heap->free(kit_compiler_context(p->c)->heap, bytes, 0); advance(p); { const Type* arr_ty = type_array(p->pool, elem_ty, elem_size ? (u32)(n / elem_size) : 0, 0); - pcg_push_global(p, sym, arr_ty); + c_cg_push_global(p, sym, arr_ty); } return; } @@ -2320,36 +2018,44 @@ static void parse_postfix(Parser* p) { for (;;) { Tok t = p->cur; if (is_punct(&t, P_INC)) { + c_const_guard_note_at( + p, pp_materialize_loc(p->pp, t.loc), + "increment/decrement in integer constant expression"); advance(p); - if (!pcg_top_is_modifiable_lvalue(p)) { + if (!c_cg_top_is_modifiable_lvalue(p)) { perr(p, "increment/decrement requires modifiable lvalue"); } - if (!c_type_is_scalar(pcg_top_type(p))) { + if (!c_type_is_scalar(c_cg_top_type(p))) { perr(p, "increment/decrement requires scalar operand"); } - pcg_inc_dec(p, BO_IADD, /*post=*/1); + c_cg_inc_dec(p, BO_IADD, /*post=*/1); continue; } if (is_punct(&t, P_DEC)) { + c_const_guard_note_at( + p, pp_materialize_loc(p->pp, t.loc), + "increment/decrement in integer constant expression"); advance(p); - if (!pcg_top_is_modifiable_lvalue(p)) { + if (!c_cg_top_is_modifiable_lvalue(p)) { perr(p, "increment/decrement requires modifiable lvalue"); } - if (!c_type_is_scalar(pcg_top_type(p))) { + if (!c_type_is_scalar(c_cg_top_type(p))) { perr(p, "increment/decrement requires scalar operand"); } - pcg_inc_dec(p, BO_ISUB, /*post=*/1); + c_cg_inc_dec(p, BO_ISUB, /*post=*/1); continue; } if (is_punct(&t, '(')) { - const Type* top = pcg_top_type(p); + const Type* top = c_cg_top_type(p); const Type* fn_type; + c_const_guard_note_at(p, pp_materialize_loc(p->pp, t.loc), + "function call in integer constant expression"); if (top && top->kind == TY_FUNC) { fn_type = top; } else if (top && top->kind == TY_PTR && top->ptr.pointee && top->ptr.pointee->kind == TY_FUNC) { fn_type = top->ptr.pointee; - if (pcg_top_is_lvalue(p)) pcg_load(p); + if (c_cg_top_is_lvalue(p)) c_cg_load(p); } else { perr(p, "called object is not a function"); } @@ -2363,7 +2069,7 @@ static void parse_postfix(Parser* p) { to_rvalue(p); if (param_ty) { CSemCheck chk = - c_sem_check_assignment(p->pool, param_ty, pcg_top_type(p)); + c_sem_check_assignment(p->pool, param_ty, c_cg_top_type(p)); if (!chk.ok) perr(p, "%.*s", KIT_SLICE_ARG(kit_slice_cstr(chk.message))); coerce_top_to_type(p, param_ty); @@ -2379,23 +2085,23 @@ static void parse_postfix(Parser* p) { if (fn_type->fn.variadic && nargs < fn_type->fn.nparams) { perr(p, "too few arguments to variadic function"); } - pcg_call(p, nargs, fn_type); + c_cg_call(p, nargs, fn_type); if (fn_type->fn.ret && fn_type->fn.ret->kind == TY_VOID) { - pcg_push_int(p, 0, ty_int(p)); + c_cg_push_int(p, 0, ty_int(p)); } continue; } if (is_punct(&t, '[')) { - const Type* lt0 = pcg_top_type(p); + const Type* lt0 = c_cg_top_type(p); advance(p); /* '[' */ if (lt0 && lt0->kind == TY_ARRAY) { decay_array_to_pointer(p, lt0); } else if (lt0 && lt0->kind == TY_PTR) { - if (pcg_top_is_lvalue(p)) pcg_load(p); + if (c_cg_top_is_lvalue(p)) c_cg_load(p); } parse_expr(p); { - const Type* it0 = pcg_top_type(p); + const Type* it0 = c_cg_top_type(p); if (it0 && it0->kind == TY_ARRAY) { decay_array_to_pointer(p, it0); } else { @@ -2404,13 +2110,13 @@ static void parse_postfix(Parser* p) { } expect_punct(p, ']', "']' after subscript"); { - const Type* lt = pcg_top2_type(p); - const Type* it = pcg_top_type(p); + const Type* lt = c_cg_top2_type(p); + const Type* it = c_cg_top_type(p); const Type* elem; if (lt && lt->kind == TY_PTR && type_is_int(it)) { elem = lt->ptr.pointee; } else if (it && it->kind == TY_PTR && type_is_int(lt)) { - pcg_swap(p); + c_cg_swap(p); elem = it->ptr.pointee; } else { perr(p, "invalid subscript: needs one pointer and one integer"); @@ -2420,23 +2126,23 @@ static void parse_postfix(Parser* p) { { FrameSlot elem_vla_slot = vla_size_slot_for_type(vla_bounds, elem); if (elem_vla_slot != FRAME_SLOT_NONE) { - pcg_push_local_typed(p, elem_vla_slot, ty_size_t(p)); - pcg_load(p); - pcg_binop(p, BO_IMUL); - pcg_binop(p, BO_IADD); - pcg_deref(p, elem); + c_cg_push_local_typed(p, elem_vla_slot, ty_size_t(p)); + c_cg_load(p); + c_cg_binop(p, BO_IMUL); + c_cg_binop(p, BO_IADD); + c_cg_deref(p, elem); p->last_pushed_vla_slot = elem_vla_slot; p->last_pushed_vla_bounds = vla_bounds; } else { u32 elem_size = c_abi_sizeof(p->abi, p->pool, elem); - pcg_lv_subscript(p, elem_size, elem); + c_cg_lv_subscript(p, elem_size, elem); } } } continue; } if (is_punct(&t, '.')) { - const Type* lt = pcg_top_type(p); + const Type* lt = c_cg_top_type(p); Sym mname; const Type* mty = NULL; i64 off = 0; @@ -2457,7 +2163,7 @@ static void parse_postfix(Parser* p) { if (!find_record_member_path(p, lt, mname, &mty, &off, &bf_off, &bf_w, &bf_ss)) perr(p, "no such member"); - pcg_lv_member(p, off, mty, bf_off, bf_w, bf_ss); + c_cg_lv_member(p, off, mty, bf_off, bf_w, bf_ss); continue; } if (is_punct(&t, P_ARROW)) { @@ -2470,7 +2176,7 @@ static void parse_postfix(Parser* p) { u32 bf_ss = 0; advance(p); /* `->` */ to_rvalue(p); - lt0 = pcg_top_type(p); + lt0 = c_cg_top_type(p); if (!lt0 || lt0->kind != TY_PTR) { perr(p, "'->' requires a pointer operand"); } @@ -2487,8 +2193,8 @@ static void parse_postfix(Parser* p) { if (!find_record_member_path(p, rec_ty, mname, &mty, &off, &bf_off, &bf_w, &bf_ss)) perr(p, "no such member"); - pcg_deref(p, rec_ty); - pcg_lv_member(p, off, mty, bf_off, bf_w, bf_ss); + c_cg_deref(p, rec_ty); + c_cg_lv_member(p, off, mty, bf_off, bf_w, bf_ss); continue; } break; @@ -2518,27 +2224,36 @@ void parse_unary(Parser* p) { fsd.align = c_abi_alignof(p->abi, p->pool, lit_ty); fsd.kind = FS_LOCAL; fsd.flags = FSF_NONE; - slot = pcg_local(p, &fsd); + slot = c_cg_local(p, &fsd); init_at(p, slot, lit_ty, 0, lit_ty); - pcg_push_local_typed(p, slot, lit_ty); + c_cg_push_local_typed(p, slot, lit_ty); return; } + if (c_const_guard_active(p) && p->cur.kind == TOK_FLT) { + const Type* tu = type_unqual(p->pool, dst); + if (tu && type_is_int(tu)) { + double fv = parse_float_literal(p, &p->cur); + advance(p); + c_cg_push_int(p, (i64)fv, tu); + return; + } + } parse_unary(p); to_rvalue(p); - src = pcg_top_type(p); + src = c_cg_top_type(p); if (dst && dst->kind == TY_VOID) { - pcg_drop(p); - pcg_push_int(p, 0, ty_int(p)); + c_cg_drop(p); + c_cg_push_int(p, 0, ty_int(p)); return; } if (!c_type_is_scalar(dst) || !c_type_is_scalar(src)) { perr(p, "cast requires scalar type"); } if (src && src->kind == TY_PTR && dst->kind == TY_PTR) { - pcg_convert(p, dst); + c_cg_convert(p, dst); return; } - pcg_convert(p, dst); + c_cg_convert(p, dst); return; } } @@ -2546,40 +2261,43 @@ void parse_unary(Parser* p) { advance(p); parse_unary(p); to_rvalue(p); - require_arith(p, pcg_top_type(p), "unary '+'"); + require_arith(p, c_cg_top_type(p), "unary '+'"); return; } if (is_punct(&t, '-')) { advance(p); parse_unary(p); to_rvalue(p); - require_arith(p, pcg_top_type(p), "unary '-'"); - pcg_unop(p, UO_NEG); + require_arith(p, c_cg_top_type(p), "unary '-'"); + c_cg_unop(p, UO_NEG); return; } if (is_punct(&t, '!')) { advance(p); parse_unary(p); to_rvalue(p); - require_scalar(p, pcg_top_type(p), "unary '!'"); - pcg_push_int(p, 0, ty_int(p)); - pcg_cmp(p, CMP_EQ); + require_scalar(p, c_cg_top_type(p), "unary '!'"); + c_cg_push_int(p, 0, ty_int(p)); + c_cg_cmp(p, CMP_EQ); return; } if (is_punct(&t, '~')) { advance(p); parse_unary(p); to_rvalue(p); - if (!type_is_int(pcg_top_type(p))) { + if (!type_is_int(c_cg_top_type(p))) { perr(p, "unary '~' requires integer operand"); } - pcg_unop(p, UO_BNOT); + c_cg_unop(p, UO_BNOT); return; } if (is_punct(&t, P_AND)) { /* GNU labels-as-values: `&&label` yields the label's address as void*. */ Sym name; SrcLoc loc; + c_const_guard_note_at( + p, pp_materialize_loc(p->pp, t.loc), + "address constant is not an integer constant expression"); advance(p); /* '&&' */ if (p->cur.kind != TOK_IDENT || ident_kw_inline(p, tok_ident(&p->cur)) != KW_NONE) { @@ -2588,20 +2306,23 @@ void parse_unary(Parser* p) { name = tok_ident(&p->cur); loc = pp_materialize_loc(p->pp, p->cur.loc); advance(p); - pcg_push_label_addr(p, take_label_addr(p, name, loc)); + c_cg_push_label_addr(p, take_label_addr(p, name, loc)); return; } if (is_punct(&t, '&')) { + c_const_guard_note_at( + p, pp_materialize_loc(p->pp, t.loc), + "address constant is not an integer constant expression"); advance(p); parse_unary(p); - if (!pcg_top_is_lvalue(p) && - !(pcg_top_type(p) && pcg_top_type(p)->kind == TY_FUNC)) { + if (!c_cg_top_is_lvalue(p) && + !(c_cg_top_type(p) && c_cg_top_type(p)->kind == TY_FUNC)) { perr(p, "address-of requires lvalue operand"); } - if (pcg_top_is_bitfield(p)) perr(p, "cannot take address of bit-field"); - if (pcg_top_is_register(p)) + if (c_cg_top_is_bitfield(p)) perr(p, "cannot take address of bit-field"); + if (c_cg_top_is_register(p)) perr(p, "cannot take address of register object"); - pcg_addr(p); + c_cg_addr(p); return; } if (is_punct(&t, '*')) { @@ -2610,7 +2331,7 @@ void parse_unary(Parser* p) { advance(p); parse_unary(p); to_rvalue(p); - pty = pcg_top_type(p); + pty = c_cg_top_type(p); if (!pty || pty->kind != TY_PTR) { perr(p, "indirection requires pointer operand"); } @@ -2618,20 +2339,22 @@ void parse_unary(Parser* p) { if (pointee && pointee->kind == TY_VOID) { perr(p, "dereferencing pointer to incomplete type"); } - pcg_deref(p, pointee); + c_cg_deref(p, pointee); return; } if (is_punct(&t, P_INC) || is_punct(&t, P_DEC)) { BinOp bop = is_punct(&t, P_INC) ? BO_IADD : BO_ISUB; + c_const_guard_note_at(p, pp_materialize_loc(p->pp, t.loc), + "increment/decrement in integer constant expression"); advance(p); parse_unary(p); - if (!pcg_top_is_modifiable_lvalue(p)) { + if (!c_cg_top_is_modifiable_lvalue(p)) { perr(p, "increment/decrement requires modifiable lvalue"); } - if (!c_type_is_scalar(pcg_top_type(p))) { + if (!c_type_is_scalar(c_cg_top_type(p))) { perr(p, "increment/decrement requires scalar operand"); } - pcg_inc_dec(p, bop, /*post=*/0); + c_cg_inc_dec(p, bop, /*post=*/0); return; } if (is_kw(p, &t, KW_SIZEOF)) { @@ -2646,42 +2369,48 @@ void parse_unary(Parser* p) { expect_punct(p, ')', "')'"); } else { p->last_pushed_vla_slot = FRAME_SLOT_NONE; - pcg_codegen_suppress_push(p); + c_const_guard_not_eval_push(p); + c_cg_codegen_suppress_push(p); parse_unary(p); - ty = pcg_top_type(p); + ty = c_cg_top_type(p); vla_slot = p->last_pushed_vla_slot; - if (pcg_top_is_bitfield(p)) perr(p, "sizeof bit-field"); - pcg_drop(p); - pcg_codegen_suppress_pop(p); + if (c_cg_top_is_bitfield(p)) perr(p, "sizeof bit-field"); + c_cg_drop(p); + c_cg_codegen_suppress_pop(p); + c_const_guard_not_eval_pop(p); } } else { p->last_pushed_vla_slot = FRAME_SLOT_NONE; - pcg_codegen_suppress_push(p); + c_const_guard_not_eval_push(p); + c_cg_codegen_suppress_push(p); parse_unary(p); - ty = pcg_top_type(p); + ty = c_cg_top_type(p); vla_slot = p->last_pushed_vla_slot; - if (pcg_top_is_bitfield(p)) perr(p, "sizeof bit-field"); - pcg_drop(p); - pcg_codegen_suppress_pop(p); + if (c_cg_top_is_bitfield(p)) perr(p, "sizeof bit-field"); + c_cg_drop(p); + c_cg_codegen_suppress_pop(p); + c_const_guard_not_eval_pop(p); } if (vla_slot != FRAME_SLOT_NONE) { - pcg_push_local_typed(p, vla_slot, ty_size_t(p)); - pcg_load(p); + c_cg_push_local_typed(p, vla_slot, ty_size_t(p)); + c_cg_load(p); } else { require_sizeof_type(p, ty); - pcg_push_int(p, (i64)c_abi_sizeof(p->abi, p->pool, ty), ty_size_t(p)); + c_cg_push_int(p, (i64)c_abi_sizeof(p->abi, p->pool, ty), ty_size_t(p)); } return; } if (is_kw(p, &t, KW_GENERIC)) { advance(p); expect_punct(p, '(', "'('"); - pcg_codegen_suppress_push(p); + c_const_guard_not_eval_push(p); + c_cg_codegen_suppress_push(p); parse_assign_expr(p); to_rvalue(p); - const Type* ctl_ty = pcg_top_type(p); - pcg_drop(p); - pcg_codegen_suppress_pop(p); + const Type* ctl_ty = c_cg_top_type(p); + c_cg_drop(p); + c_cg_codegen_suppress_pop(p); + c_const_guard_not_eval_pop(p); expect_punct(p, ',', "','"); int emitted = 0; Tok* default_buf = NULL; @@ -2837,12 +2566,16 @@ void parse_unary(Parser* p) { if (starts_type_name(p, &p->cur)) { ty = parse_type_name(p); } else { + c_const_guard_not_eval_push(p); + c_cg_codegen_suppress_push(p); parse_unary(p); - ty = pcg_top_type(p); - pcg_drop(p); + ty = c_cg_top_type(p); + c_cg_drop(p); + c_cg_codegen_suppress_pop(p); + c_const_guard_not_eval_pop(p); } expect_punct(p, ')', "')'"); - pcg_push_int(p, (i64)c_abi_alignof(p->abi, p->pool, ty), ty_size_t(p)); + c_cg_push_int(p, (i64)c_abi_alignof(p->abi, p->pool, ty), ty_size_t(p)); return; } parse_postfix(p); @@ -2869,10 +2602,10 @@ static const Type* common_fp_type(Parser* p, const Type* a, const Type* b) { } static void emit_fp_binop(Parser* p, BinOp bop, const Type* common) { - if (pcg_top_type(p) != common) pcg_convert(p, common); - pcg_swap(p); - if (pcg_top_type(p) != common) pcg_convert(p, common); - pcg_swap(p); + if (c_cg_top_type(p) != common) c_cg_convert(p, common); + c_cg_swap(p); + if (c_cg_top_type(p) != common) c_cg_convert(p, common); + c_cg_swap(p); BinOp fop; switch (bop) { case BO_IADD: @@ -2891,22 +2624,22 @@ static void emit_fp_binop(Parser* p, BinOp bop, const Type* common) { perr(p, "operator does not apply to floating types"); return; } - pcg_binop(p, fop); + c_cg_binop(p, fop); } static void coerce_fp_cmp_operands(Parser* p, const Type* common) { - if (pcg_top_type(p) != common) pcg_convert(p, common); - pcg_swap(p); - if (pcg_top_type(p) != common) pcg_convert(p, common); - pcg_swap(p); + if (c_cg_top_type(p) != common) c_cg_convert(p, common); + c_cg_swap(p); + if (c_cg_top_type(p) != common) c_cg_convert(p, common); + c_cg_swap(p); } static void coerce_arith_operands(Parser* p, const Type* common) { if (!common) return; - if (pcg_top_type(p) != common) pcg_convert(p, common); - pcg_swap(p); - if (pcg_top_type(p) != common) pcg_convert(p, common); - pcg_swap(p); + if (c_cg_top_type(p) != common) c_cg_convert(p, common); + c_cg_swap(p); + if (c_cg_top_type(p) != common) c_cg_convert(p, common); + c_cg_swap(p); } static CmpOp unsigned_rel_cmp(CmpOp cop) { @@ -2925,7 +2658,7 @@ static CmpOp unsigned_rel_cmp(CmpOp cop) { } static BinOp int_div_rem_binop(BinOp op, const Type* common) { - if (pcg_type_is_signed(common)) return op; + if (c_cg_type_is_signed(common)) return op; switch (op) { case BO_SDIV: return BO_UDIV; @@ -2940,6 +2673,7 @@ static void parse_mul(Parser* p) { parse_unary(p); for (;;) { Tok t = p->cur; + SrcLoc op_loc; BinOp bop; if (is_punct(&t, '*')) { bop = BO_IMUL; @@ -2950,12 +2684,13 @@ static void parse_mul(Parser* p) { } else { break; } + op_loc = pp_materialize_loc(p->pp, t.loc); advance(p); to_rvalue(p); parse_unary(p); to_rvalue(p); - const Type* lt = pcg_top2_type(p); - const Type* rt = pcg_top_type(p); + const Type* lt = c_cg_top2_type(p); + const Type* rt = c_cg_top_type(p); const Type* common = common_fp_type(p, lt, rt); if (bop == BO_SREM) { if (!type_is_int(lt) || !type_is_int(rt)) @@ -2966,9 +2701,12 @@ static void parse_mul(Parser* p) { if (common) { emit_fp_binop(p, bop, common); } else { - const Type* icommon = cint_common_type(p, lt, rt); + const Type* icommon = integer_common_type(p, lt, rt); + BinOp ibop; coerce_arith_operands(p, icommon); - pcg_binop(p, int_div_rem_binop(bop, icommon)); + ibop = int_div_rem_binop(bop, icommon); + c_const_check_divrem(p, ibop, op_loc); + c_cg_binop(p, ibop); } } } @@ -2977,14 +2715,14 @@ static void scale_pointer_index(Parser* p, u32 elem_size) { const Type* idx_ty = c_abi_ptrdiff_type(p->abi, p->pool); coerce_top_to_type(p, idx_ty); if (elem_size != 1) { - pcg_push_int(p, (i64)elem_size, idx_ty); - pcg_binop(p, BO_IMUL); + c_cg_push_int(p, (i64)elem_size, idx_ty); + c_cg_binop(p, BO_IMUL); } } static void emit_add_or_sub(Parser* p, BinOp bop) { - const Type* lt = pcg_top2_type(p); - const Type* rt = pcg_top_type(p); + const Type* lt = c_cg_top2_type(p); + const Type* rt = c_cg_top_type(p); int l_is_ptr = lt && lt->kind == TY_PTR; int r_is_ptr = rt && rt->kind == TY_PTR; if (bop == BO_IADD) { @@ -2996,16 +2734,16 @@ static void emit_add_or_sub(Parser* p, BinOp bop) { perr(p, "pointer arithmetic on void pointer"); u32 esz = c_abi_sizeof(p->abi, p->pool, lt->ptr.pointee); scale_pointer_index(p, esz); - pcg_binop(p, BO_IADD); + c_cg_binop(p, BO_IADD); return; } if (r_is_ptr && type_is_int(lt)) { if (rt->ptr.pointee && rt->ptr.pointee->kind == TY_VOID) perr(p, "pointer arithmetic on void pointer"); - pcg_swap(p); + c_cg_swap(p); u32 esz = c_abi_sizeof(p->abi, p->pool, rt->ptr.pointee); scale_pointer_index(p, esz); - pcg_binop(p, BO_IADD); + c_cg_binop(p, BO_IADD); return; } } else { /* BO_ISUB */ @@ -3014,7 +2752,7 @@ static void emit_add_or_sub(Parser* p, BinOp bop) { perr(p, "pointer arithmetic on void pointer"); u32 esz = c_abi_sizeof(p->abi, p->pool, lt->ptr.pointee); scale_pointer_index(p, esz); - pcg_binop(p, BO_ISUB); + c_cg_binop(p, BO_ISUB); return; } if (l_is_ptr && r_is_ptr) { @@ -3022,12 +2760,12 @@ static void emit_add_or_sub(Parser* p, BinOp bop) { perr(p, "subtraction of incompatible pointer types"); } u32 esz = c_abi_sizeof(p->abi, p->pool, lt->ptr.pointee); - pcg_binop(p, BO_ISUB); + c_cg_binop(p, BO_ISUB); if (esz != 1) { - pcg_push_int(p, (i64)esz, ty_size_t(p)); - pcg_binop(p, BO_SDIV); + c_cg_push_int(p, (i64)esz, ty_size_t(p)); + c_cg_binop(p, BO_SDIV); } - pcg_convert(p, c_abi_ptrdiff_type(p->abi, p->pool)); + c_cg_convert(p, c_abi_ptrdiff_type(p->abi, p->pool)); return; } } @@ -3042,9 +2780,9 @@ static void emit_add_or_sub(Parser* p, BinOp bop) { emit_fp_binop(p, bop, common); return; } - common = cint_common_type(p, lt, rt); + common = integer_common_type(p, lt, rt); coerce_arith_operands(p, common); - pcg_binop(p, bop); + c_cg_binop(p, bop); } static void parse_add(Parser* p) { @@ -3071,6 +2809,7 @@ static void parse_shift(Parser* p) { parse_add(p); for (;;) { Tok t = p->cur; + SrcLoc op_loc; BinOp bop; if (is_punct(&t, P_SHL)) { bop = BO_SHL; @@ -3079,27 +2818,29 @@ static void parse_shift(Parser* p) { } else { break; } + op_loc = pp_materialize_loc(p->pp, t.loc); advance(p); to_rvalue(p); { - const Type* lt = pcg_top_type(p); - const Type* lp = cint_promote_type(p, lt); + const Type* lt = c_cg_top_type(p); + const Type* lp = integer_promote_type(p, lt); if (!type_is_int(lt)) perr(p, "shift operator requires integer operands"); - if (pcg_top_type(p) != lp) pcg_convert(p, lp); + if (c_cg_top_type(p) != lp) c_cg_convert(p, lp); if (bop == BO_SHR_S && !type_is_signed_integer(lp)) bop = BO_SHR_U; } parse_add(p); to_rvalue(p); { - const Type* rt = pcg_top_type(p); - const Type* rp = cint_promote_type(p, rt); + const Type* rt = c_cg_top_type(p); + const Type* rp = integer_promote_type(p, rt); if (!type_is_int(rt)) perr(p, "shift operator requires integer operands"); - if (pcg_top_type(p) != rp) pcg_convert(p, rp); + if (c_cg_top_type(p) != rp) c_cg_convert(p, rp); } - if (!type_is_int(pcg_top2_type(p)) || !type_is_int(pcg_top_type(p))) { + if (!type_is_int(c_cg_top2_type(p)) || !type_is_int(c_cg_top_type(p))) { perr(p, "shift operator requires integer operands"); } - pcg_binop(p, bop); + c_const_check_shift(p, bop, op_loc); + c_cg_binop(p, bop); } } @@ -3124,8 +2865,8 @@ static void parse_rel(Parser* p) { parse_shift(p); to_rvalue(p); { - const Type* lt = pcg_top2_type(p); - const Type* rt = pcg_top_type(p); + const Type* lt = c_cg_top2_type(p); + const Type* rt = c_cg_top_type(p); const Type* common = common_fp_type(p, lt, rt); if (lt && lt->kind == TY_PTR && rt && rt->kind == TY_PTR) { if (!pointer_pointees_compatible(p, lt, rt)) { @@ -3155,12 +2896,12 @@ static void parse_rel(Parser* p) { break; } } else if (type_is_arith(lt) && type_is_arith(rt)) { - common = cint_common_type(p, lt, rt); + common = integer_common_type(p, lt, rt); coerce_arith_operands(p, common); - if (!cint_signed(p, common)) cop = unsigned_rel_cmp(cop); + if (!integer_type_signed(p, common)) cop = unsigned_rel_cmp(cop); } } - pcg_cmp(p, cop); + c_cg_cmp(p, cop); } } @@ -3176,14 +2917,14 @@ static void parse_eq(Parser* p) { } else { break; } - int lhs_null = null_pointer_constant(p, pcg_top_type(p)); + int lhs_null = null_pointer_constant(p, c_cg_top_type(p)); advance(p); to_rvalue(p); parse_rel(p); to_rvalue(p); { - const Type* lt = pcg_top2_type(p); - const Type* rt = pcg_top_type(p); + const Type* lt = c_cg_top2_type(p); + const Type* rt = c_cg_top_type(p); const Type* common = common_fp_type(p, lt, rt); int lnull = lhs_null; int rnull = null_pointer_constant(p, rt); @@ -3200,11 +2941,11 @@ static void parse_eq(Parser* p) { if (common) coerce_fp_cmp_operands(p, common); else if (type_is_arith(lt) && type_is_arith(rt)) { - common = cint_common_type(p, lt, rt); + common = integer_common_type(p, lt, rt); coerce_arith_operands(p, common); } } - pcg_cmp(p, cop); + c_cg_cmp(p, cop); } } @@ -3215,12 +2956,12 @@ static void parse_band(Parser* p) { to_rvalue(p); parse_eq(p); to_rvalue(p); - if (!type_is_int(pcg_top2_type(p)) || !type_is_int(pcg_top_type(p))) { + if (!type_is_int(c_cg_top2_type(p)) || !type_is_int(c_cg_top_type(p))) { perr(p, "bitwise operator requires integer operands"); } coerce_arith_operands( - p, cint_common_type(p, pcg_top2_type(p), pcg_top_type(p))); - pcg_binop(p, BO_AND); + p, integer_common_type(p, c_cg_top2_type(p), c_cg_top_type(p))); + c_cg_binop(p, BO_AND); } } @@ -3231,12 +2972,12 @@ static void parse_bxor(Parser* p) { to_rvalue(p); parse_band(p); to_rvalue(p); - if (!type_is_int(pcg_top2_type(p)) || !type_is_int(pcg_top_type(p))) { + if (!type_is_int(c_cg_top2_type(p)) || !type_is_int(c_cg_top_type(p))) { perr(p, "bitwise operator requires integer operands"); } coerce_arith_operands( - p, cint_common_type(p, pcg_top2_type(p), pcg_top_type(p))); - pcg_binop(p, BO_XOR); + p, integer_common_type(p, c_cg_top2_type(p), c_cg_top_type(p))); + c_cg_binop(p, BO_XOR); } } @@ -3247,23 +2988,15 @@ static void parse_bor(Parser* p) { to_rvalue(p); parse_bxor(p); to_rvalue(p); - if (!type_is_int(pcg_top2_type(p)) || !type_is_int(pcg_top_type(p))) { + if (!type_is_int(c_cg_top2_type(p)) || !type_is_int(c_cg_top_type(p))) { perr(p, "bitwise operator requires integer operands"); } coerce_arith_operands( - p, cint_common_type(p, pcg_top2_type(p), pcg_top_type(p))); - pcg_binop(p, BO_OR); + p, integer_common_type(p, c_cg_top2_type(p), c_cg_top_type(p))); + c_cg_binop(p, BO_OR); } } -static int cg_top_const_truth(Parser* p, int* truth_out) { - i64 v = 0; - if (!pcg_emit_enabled(p)) return 0; - if (!kit_cg_top_const_int(p->cg, &v)) return 0; - *truth_out = v != 0; - return 1; -} - static FrameSlot ll_tmp_slot(Parser* p, const Type* ty) { FrameSlotDesc fsd; memset(&fsd, 0, sizeof fsd); @@ -3272,20 +3005,20 @@ static FrameSlot ll_tmp_slot(Parser* p, const Type* ty) { fsd.align = c_abi_alignof(p->abi, p->pool, ty); fsd.kind = FS_LOCAL; fsd.flags = FSF_NONE; - return pcg_local(p, &fsd); + return c_cg_local(p, &fsd); } static void ll_store_const(Parser* p, FrameSlot tmp, const Type* ty, i64 v) { - pcg_push_local_typed(p, tmp, ty); - pcg_push_int(p, v, ty); - pcg_store_void(p); + c_cg_push_local_typed(p, tmp, ty); + c_cg_push_int(p, v, ty); + c_cg_store_void(p); } static void parse_land(Parser* p) { parse_bor(p); while (is_punct(&p->cur, P_AND)) { - CGLabel L_false = pcg_label_new(p); - CGLabel L_end = pcg_label_new(p); + CGLabel L_false = c_cg_label_new(p); + CGLabel L_end = c_cg_label_new(p); const Type* result_ty = ty_int(p); FrameSlot tmp = ll_tmp_slot(p, result_ty); int lhs_known; @@ -3293,43 +3026,45 @@ static void parse_land(Parser* p) { int rhs_truth = 0; advance(p); to_rvalue(p); - require_scalar(p, pcg_top_type(p), "logical '&&'"); + require_scalar(p, c_cg_top_type(p), "logical '&&'"); lhs_known = cg_top_const_truth(p, &lhs_truth); if (lhs_known && !lhs_truth) { - pcg_drop(p); - pcg_codegen_suppress_push(p); + c_cg_drop(p); + c_const_guard_not_eval_push(p); + c_cg_codegen_suppress_push(p); parse_bor(p); to_rvalue(p); - require_scalar(p, pcg_top_type(p), "logical '&&'"); - pcg_drop(p); - pcg_codegen_suppress_pop(p); - pcg_push_int(p, 0, result_ty); + require_scalar(p, c_cg_top_type(p), "logical '&&'"); + c_cg_drop(p); + c_cg_codegen_suppress_pop(p); + c_const_guard_not_eval_pop(p); + c_cg_push_int(p, 0, result_ty); continue; } - pcg_branch_false(p, L_false); + c_cg_branch_false(p, L_false); parse_bor(p); to_rvalue(p); - require_scalar(p, pcg_top_type(p), "logical '&&'"); + require_scalar(p, c_cg_top_type(p), "logical '&&'"); if (lhs_known && lhs_truth && cg_top_const_truth(p, &rhs_truth)) { - pcg_drop(p); - pcg_push_int(p, rhs_truth ? 1 : 0, result_ty); + c_cg_drop(p); + c_cg_push_int(p, rhs_truth ? 1 : 0, result_ty); continue; } - pcg_branch_false(p, L_false); + c_cg_branch_false(p, L_false); ll_store_const(p, tmp, result_ty, 1); - pcg_jump(p, L_end); - pcg_label_place(p, L_false); + c_cg_jump(p, L_end); + c_cg_label_place(p, L_false); ll_store_const(p, tmp, result_ty, 0); - pcg_label_place(p, L_end); - pcg_push_local_typed(p, tmp, result_ty); + c_cg_label_place(p, L_end); + c_cg_push_local_typed(p, tmp, result_ty); } } static void parse_lor(Parser* p) { parse_land(p); while (is_punct(&p->cur, P_OR)) { - CGLabel L_true = pcg_label_new(p); - CGLabel L_end = pcg_label_new(p); + CGLabel L_true = c_cg_label_new(p); + CGLabel L_end = c_cg_label_new(p); const Type* result_ty = ty_int(p); FrameSlot tmp = ll_tmp_slot(p, result_ty); int lhs_known; @@ -3337,125 +3072,182 @@ static void parse_lor(Parser* p) { int rhs_truth = 0; advance(p); to_rvalue(p); - require_scalar(p, pcg_top_type(p), "logical '||'"); + require_scalar(p, c_cg_top_type(p), "logical '||'"); lhs_known = cg_top_const_truth(p, &lhs_truth); if (lhs_known && lhs_truth) { - pcg_drop(p); - pcg_codegen_suppress_push(p); + c_cg_drop(p); + c_const_guard_not_eval_push(p); + c_cg_codegen_suppress_push(p); parse_land(p); to_rvalue(p); - require_scalar(p, pcg_top_type(p), "logical '||'"); - pcg_drop(p); - pcg_codegen_suppress_pop(p); - pcg_push_int(p, 1, result_ty); + require_scalar(p, c_cg_top_type(p), "logical '||'"); + c_cg_drop(p); + c_cg_codegen_suppress_pop(p); + c_const_guard_not_eval_pop(p); + c_cg_push_int(p, 1, result_ty); continue; } - pcg_branch_true(p, L_true); + c_cg_branch_true(p, L_true); parse_land(p); to_rvalue(p); - require_scalar(p, pcg_top_type(p), "logical '||'"); + require_scalar(p, c_cg_top_type(p), "logical '||'"); if (lhs_known && !lhs_truth && cg_top_const_truth(p, &rhs_truth)) { - pcg_drop(p); - pcg_push_int(p, rhs_truth ? 1 : 0, result_ty); + c_cg_drop(p); + c_cg_push_int(p, rhs_truth ? 1 : 0, result_ty); continue; } - pcg_branch_true(p, L_true); + c_cg_branch_true(p, L_true); ll_store_const(p, tmp, result_ty, 0); - pcg_jump(p, L_end); - pcg_label_place(p, L_true); + c_cg_jump(p, L_end); + c_cg_label_place(p, L_true); ll_store_const(p, tmp, result_ty, 1); - pcg_label_place(p, L_end); - pcg_push_local_typed(p, tmp, result_ty); + c_cg_label_place(p, L_end); + c_cg_push_local_typed(p, tmp, result_ty); } } static void parse_ternary(Parser* p) { parse_lor(p); if (!is_punct(&p->cur, '?')) return; - CGLabel L_else = pcg_label_new(p); - CGLabel L_end = pcg_label_new(p); + CGLabel L_else = c_cg_label_new(p); + CGLabel L_then = c_cg_label_new(p); + CGLabel L_end = c_cg_label_new(p); const Type* result_ty = ty_int(p); + const Type* then_store_ty; int then_null = 0; - FrameSlot tmp; + FrameSlot then_tmp; FrameSlotDesc fsd; - const Type* then_store_ty; advance(p); /* '?' */ to_rvalue(p); - require_scalar(p, pcg_top_type(p), "conditional operator"); - pcg_branch_false(p, L_else); + require_scalar(p, c_cg_top_type(p), "conditional operator"); + { + int cond_truth = 0; + if (c_const_guard_active(p) && cg_top_const_truth(p, &cond_truth)) { + const Type* selected_ty; + const Type* other_ty; + const Type* common; + c_cg_drop(p); + if (cond_truth) { + parse_expr(p); + to_rvalue(p); + selected_ty = c_cg_top_type(p); + expect_punct(p, ':', "':' in ternary"); + c_const_guard_not_eval_push(p); + c_cg_codegen_suppress_push(p); + parse_assign_expr(p); + to_rvalue(p); + other_ty = c_cg_top_type(p); + c_cg_drop(p); + c_cg_codegen_suppress_pop(p); + c_const_guard_not_eval_pop(p); + } else { + c_const_guard_not_eval_push(p); + c_cg_codegen_suppress_push(p); + parse_expr(p); + to_rvalue(p); + other_ty = c_cg_top_type(p); + c_cg_drop(p); + c_cg_codegen_suppress_pop(p); + c_const_guard_not_eval_pop(p); + expect_punct(p, ':', "':' in ternary"); + parse_assign_expr(p); + to_rvalue(p); + selected_ty = c_cg_top_type(p); + } + common = common_fp_type(p, selected_ty, other_ty); + if (!common && type_is_int(selected_ty) && type_is_int(other_ty)) + common = integer_common_type(p, selected_ty, other_ty); + if (common && c_cg_top_type(p) != common) c_cg_convert(p, common); + return; + } + } + c_cg_branch_false(p, L_else); parse_expr(p); to_rvalue(p); - result_ty = pcg_top_type(p); + result_ty = c_cg_top_type(p); then_null = null_pointer_constant(p, result_ty); if (!result_ty) result_ty = ty_int(p); if (type_is_int(result_ty)) { result_ty = type_promoted(p->pool, result_ty); - if (pcg_top_type(p) != result_ty) pcg_convert(p, result_ty); + if (c_cg_top_type(p) != result_ty) c_cg_convert(p, result_ty); } - then_store_ty = - then_null ? type_ptr(p->pool, type_prim(p->pool, TY_VOID)) : result_ty; + then_store_ty = result_ty; memset(&fsd, 0, sizeof fsd); fsd.type = then_store_ty; fsd.size = c_abi_sizeof(p->abi, p->pool, then_store_ty); fsd.align = c_abi_alignof(p->abi, p->pool, then_store_ty); fsd.kind = FS_LOCAL; fsd.flags = FSF_NONE; - tmp = pcg_local(p, &fsd); - if (pcg_top_type(p) != then_store_ty) pcg_convert(p, then_store_ty); - pcg_push_local_typed(p, tmp, then_store_ty); - pcg_swap(p); - pcg_store_void(p); - pcg_jump(p, L_end); - pcg_label_place(p, L_else); + then_tmp = c_cg_local(p, &fsd); + if (c_cg_top_type(p) != then_store_ty) c_cg_convert(p, then_store_ty); + c_cg_push_local_typed(p, then_tmp, then_store_ty); + c_cg_swap(p); + c_cg_store_void(p); + c_cg_jump(p, L_then); + c_cg_label_place(p, L_else); expect_punct(p, ':', "':' in ternary"); parse_assign_expr(p); to_rvalue(p); - const Type* else_ty = pcg_top_type(p); - const Type* common = common_fp_type(p, result_ty, else_ty); - const Type* ptr_result = conditional_pointer_type( - p, result_ty, then_null, else_ty, null_pointer_constant(p, else_ty)); - if ((result_ty && result_ty->kind == TY_PTR) || - (else_ty && else_ty->kind == TY_PTR)) { - if (!ptr_result) perr(p, "conditional operator pointer type mismatch"); - result_ty = ptr_result; - } else if (type_is_arith(result_ty) && type_is_arith(else_ty)) { - /* Usual arithmetic conversions are handled by the existing `common` - * materialization below; keep the first temporary's type stable here. */ - } else if (!type_compatible(result_ty, else_ty)) { - perr(p, "conditional operator type mismatch"); - } - if (pcg_top_type(p) != result_ty) { - pcg_convert(p, result_ty); - } - pcg_push_local_typed(p, tmp, result_ty); - pcg_swap(p); - pcg_store_void(p); - pcg_label_place(p, L_end); - if (common && common != result_ty) { - FrameSlotDesc cfsd; - FrameSlot ctmp; - memset(&cfsd, 0, sizeof cfsd); - cfsd.type = common; - cfsd.size = c_abi_sizeof(p->abi, p->pool, common); - cfsd.align = c_abi_alignof(p->abi, p->pool, common); - cfsd.kind = FS_LOCAL; - cfsd.flags = FSF_NONE; - ctmp = pcg_local(p, &cfsd); - pcg_push_local_typed(p, tmp, result_ty); - pcg_load(p); - pcg_convert(p, common); - pcg_push_local_typed(p, ctmp, common); - pcg_swap(p); - pcg_store_void(p); - pcg_push_local_typed(p, ctmp, common); + { + const Type* else_ty = c_cg_top_type(p); + int else_null = null_pointer_constant(p, else_ty); + const Type* common = common_fp_type(p, then_store_ty, else_ty); + const Type* ptr_result = conditional_pointer_type( + p, then_store_ty, then_null, else_ty, else_null); + const Type* final_ty = then_store_ty; + FrameSlot final_tmp = then_tmp; + int use_final_tmp = 0; + + if (!common && type_is_int(then_store_ty) && type_is_int(else_ty)) + common = integer_common_type(p, then_store_ty, else_ty); + if ((then_store_ty && then_store_ty->kind == TY_PTR) || + (else_ty && else_ty->kind == TY_PTR)) { + if (!ptr_result) perr(p, "conditional operator pointer type mismatch"); + final_ty = ptr_result; + } else if (type_is_arith(then_store_ty) && type_is_arith(else_ty)) { + if (common) final_ty = common; + } else if (!type_compatible(then_store_ty, else_ty)) { + perr(p, "conditional operator type mismatch"); + } + + if (final_ty != then_store_ty) { + FrameSlotDesc ffsd; + memset(&ffsd, 0, sizeof ffsd); + ffsd.type = final_ty; + ffsd.size = c_abi_sizeof(p->abi, p->pool, final_ty); + ffsd.align = c_abi_alignof(p->abi, p->pool, final_ty); + ffsd.kind = FS_LOCAL; + ffsd.flags = FSF_NONE; + final_tmp = c_cg_local(p, &ffsd); + use_final_tmp = 1; + } + + if (c_cg_top_type(p) != final_ty) c_cg_convert(p, final_ty); + c_cg_push_local_typed(p, final_tmp, final_ty); + c_cg_swap(p); + c_cg_store_void(p); + c_cg_jump(p, L_end); + + c_cg_label_place(p, L_then); + if (use_final_tmp) { + c_cg_push_local_typed(p, then_tmp, then_store_ty); + c_cg_load(p); + if (c_cg_top_type(p) != final_ty) c_cg_convert(p, final_ty); + c_cg_push_local_typed(p, final_tmp, final_ty); + c_cg_swap(p); + c_cg_store_void(p); + } + + c_cg_label_place(p, L_end); + c_cg_push_local_typed(p, final_tmp, final_ty); return; } - pcg_push_local_typed(p, tmp, result_ty); } void parse_assign_expr(Parser* p) { parse_ternary(p); Tok t = p->cur; + SrcLoc op_loc = pp_materialize_loc(p->pp, t.loc); BinOp compound; int is_simple_assign; if (is_punct(&t, '=')) { @@ -3494,11 +3286,12 @@ void parse_assign_expr(Parser* p) { } else { return; } - if (!pcg_top_is_modifiable_lvalue(p)) { + c_const_guard_note_at(p, op_loc, "assignment in integer constant expression"); + if (!c_cg_top_is_modifiable_lvalue(p)) { perr(p, "assignment requires modifiable lvalue"); } advance(p); - const Type* lhs = pcg_top_type(p); + const Type* lhs = c_cg_top_type(p); if (compound == BO_SHR_S && !type_is_signed_integer(lhs)) compound = BO_SHR_U; { if (lhs && (lhs->qual & Q_CONST)) { @@ -3506,45 +3299,23 @@ void parse_assign_expr(Parser* p) { } } if (is_simple_assign) { - /* Aggregate destinations keep any member/index displacement in the lvalue - * aux rather than a scalar EA the store can fold. Collapse a non-trivial - * EA into a plain pointer-deref lvalue (mirrors the source-side handling in - * to_rvalue) so the aggregate copy lands at the right address. */ - if (lhs && (lhs->kind == TY_STRUCT || lhs->kind == TY_UNION)) { - PcgLvAux* dlv = pcg_top_lv_aux(p); - if (pcg_top_is_lvalue(p) && dlv && - (dlv->offset != 0 || dlv->scale != 0)) { - const Type* uty = type_unqual(p->pool, lhs); - pcg_addr(p); - pcg_deref(p, uty); - } - } parse_assign_expr(p); to_rvalue(p); { - const Type* rhs = pcg_top_type(p); + const Type* rhs = c_cg_top_type(p); CSemCheck chk = c_sem_check_assignment(p->pool, lhs, rhs); if (!chk.ok) perr(p, "%.*s", KIT_SLICE_ARG(kit_slice_cstr(chk.message))); } coerce_top_to_lvalue(p); - pcg_store(p); + c_cg_store(p); return; } - { - PcgLvAux* lv = pcg_top_lv_aux(p); - if (pcg_emit_enabled(p)) { - if (lv && lv->scale != 0) - kit_cg_dup2(p->cg); - else - kit_cg_dup(p->cg); - } - pcg_dup_type(p); - } - pcg_load(p); + c_cg_dup(p); + c_cg_load(p); parse_assign_expr(p); to_rvalue(p); { - const Type* rhs = pcg_top_type(p); + const Type* rhs = c_cg_top_type(p); int op = '+'; switch (compound) { case BO_IADD: @@ -3590,29 +3361,30 @@ void parse_assign_expr(Parser* p) { if (compound == BO_IADD || compound == BO_ISUB) { emit_add_or_sub(p, compound); } else { - const Type* lt = pcg_top2_type(p); - const Type* rt = pcg_top_type(p); + const Type* lt = c_cg_top2_type(p); + const Type* rt = c_cg_top_type(p); const Type* common = common_fp_type(p, lt, rt); if (common) { if (compound == BO_SREM) perr(p, "operator '%%=' requires integer operands"); emit_fp_binop(p, compound, common); } else { - const Type* icommon = cint_common_type(p, lt, rt); + const Type* icommon = integer_common_type(p, lt, rt); coerce_arith_operands(p, icommon); - pcg_binop(p, int_div_rem_binop(compound, icommon)); + c_cg_binop(p, int_div_rem_binop(compound, icommon)); } } coerce_top_to_type(p, lhs); - pcg_retag_keep_flags(p, 1, lhs); - pcg_store(p); + c_cg_retag_keep_flags(p, 1, lhs); + c_cg_store(p); } void parse_expr(Parser* p) { parse_assign_expr(p); while (is_punct(&p->cur, ',')) { + c_const_guard_note(p, "comma operator in integer constant expression"); advance(p); - pcg_drop(p); + c_cg_drop(p); parse_assign_expr(p); } } diff --git a/lang/c/parse/parse_init.c b/lang/c/parse/parse_init.c @@ -130,7 +130,7 @@ static int try_init_aggregate_from_expr(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, const Type* ty) { const Type* expr_ty; - u32 save_sp; + u32 save_depth; int compatible; if (!ty || (ty->kind != TY_STRUCT && ty->kind != TY_UNION)) return 0; @@ -141,21 +141,21 @@ static int try_init_aggregate_from_expr(Parser* p, FrameSlot slot, record_initializer_expr_for_replay(p); - save_sp = p->cg_type_sp; - pcg_codegen_suppress_push(p); + save_depth = c_cg_stack_depth(p); + c_cg_codegen_suppress_push(p); parse_assign_expr(p); - expr_ty = pcg_top_type(p); + expr_ty = c_cg_top_type(p); compatible = type_compatible(type_unqual(p->pool, ty), type_unqual(p->pool, expr_ty)); - p->cg_type_sp = save_sp; - pcg_codegen_suppress_pop(p); + c_cg_drop_to_depth(p, save_depth); + c_cg_codegen_suppress_pop(p); replay_recorded_initializer_expr(p); if (!compatible) return 0; parse_assign_expr(p); if (!type_compatible(type_unqual(p->pool, ty), - type_unqual(p->pool, pcg_top_type(p)))) { + type_unqual(p->pool, c_cg_top_type(p)))) { perr(p, "incompatible aggregate initializer"); } emit_struct_copy_into_slot(p, slot, arr_ty, offset, ty); @@ -166,22 +166,19 @@ static int try_init_aggregate_from_expr(Parser* p, FrameSlot slot, * local `slot` (whose type is `arr_ty`), with element type `elem_ty`. */ void push_subobject_lv(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, const Type* elem_ty) { - pcg_push_local_typed(p, slot, arr_ty); + c_cg_push_local_typed(p, slot, arr_ty); /* Fold the byte offset onto the local lvalue's aux; the next * load/store/addr will bake it into the memop's ea.offset. The result is an * lvalue of elem_ty backed by the frame slot. */ - pcg_lv_member(p, (i64)offset, elem_ty, /*bf_off=*/0, /*bf_w=*/0, /*ss=*/0); + c_cg_lv_member(p, (i64)offset, elem_ty, /*bf_off=*/0, /*bf_w=*/0, /*ss=*/0); } void zero_object_bytes_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, const Type* ty) { - KitCgMemAccess access = pcg_mem(p, ty); + KitCgMemAccess access = c_cg_mem(p, ty); push_subobject_lv(p, slot, arr_ty, offset, ty); - pcg_addr(p); - if (pcg_emit_enabled(p)) { - kit_cg_memset(p->cg, 0, c_abi_sizeof(p->abi, p->pool, ty), access); - } - pcg_drop_type(p); + c_cg_addr(p); + kit_cg_memset(p->cg, 0, c_abi_sizeof(p->abi, p->pool, ty), access); } static void push_record_field_lv(Parser* p, FrameSlot slot, const Type* arr_ty, @@ -199,7 +196,7 @@ static void push_record_field_lv(Parser* p, FrameSlot slot, const Type* arr_ty, bf_w = L->fields[field_index].bit_width; bf_ss = L->fields[field_index].storage_size; } - pcg_lv_member(p, (i64)foff, f->type, bf_off, bf_w, bf_ss); + c_cg_lv_member(p, (i64)foff, f->type, bf_off, bf_w, bf_ss); } /* Emit a load+store for one scalar leaf. */ @@ -208,15 +205,15 @@ static void emit_copy_leaf(Parser* p, FrameSlot dst_slot, FrameSlot src_ptr_slot, const Type* src_ptr_ty, u32 src_off, const Type* leaf_ty) { push_subobject_lv(p, dst_slot, dst_arr_ty, dst_off, leaf_ty); - pcg_push_local_typed(p, src_ptr_slot, src_ptr_ty); - pcg_load(p); + c_cg_push_local_typed(p, src_ptr_slot, src_ptr_ty); + c_cg_load(p); /* TOS is now a pointer rvalue (the loaded source pointer). Retag as a * C-language lvalue with POINTER_RV base, then fold the source byte offset - * onto its aux. The next pcg_load consumes the EA into the memop. */ - pcg_deref(p, leaf_ty); - pcg_lv_member(p, (i64)src_off, leaf_ty, 0, 0, 0); - pcg_load(p); - pcg_store_void(p); + * onto its aux. The next c_cg_load consumes the EA into the memop. */ + c_cg_deref(p, leaf_ty); + c_cg_lv_member(p, (i64)src_off, leaf_ty, 0, 0, 0); + c_cg_load(p); + c_cg_store_void(p); } /* Walk a (possibly nested) aggregate, emitting a leaf load+store for each @@ -267,17 +264,17 @@ void emit_struct_copy_into_slot(Parser* p, FrameSlot dst_slot, const Type* ptr_ty = type_ptr(p->pool, ty); FrameSlotDesc fsd; FrameSlot src_ptr_slot; - pcg_addr(p); + c_cg_addr(p); memset(&fsd, 0, sizeof fsd); fsd.type = ptr_ty; fsd.size = c_abi_sizeof(p->abi, p->pool, ptr_ty); fsd.align = c_abi_alignof(p->abi, p->pool, ptr_ty); fsd.kind = FS_LOCAL; fsd.flags = FSF_NONE; - src_ptr_slot = pcg_local(p, &fsd); - pcg_push_local_typed(p, src_ptr_slot, ptr_ty); - pcg_swap(p); - pcg_store_void(p); + src_ptr_slot = c_cg_local(p, &fsd); + c_cg_push_local_typed(p, src_ptr_slot, ptr_ty); + c_cg_swap(p); + c_cg_store_void(p); emit_walk_copy(p, dst_slot, dst_arr_ty, dst_off, src_ptr_slot, ptr_ty, 0, ty); } @@ -298,8 +295,8 @@ void zero_init_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, if (f->flags & FIELD_ZERO_WIDTH) continue; if (f->flags & FIELD_BITFIELD) { push_record_field_lv(p, slot, arr_ty, offset, ty, i); - pcg_push_int(p, 0, f->type); - pcg_store_void(p); + c_cg_push_int(p, 0, f->type); + c_cg_store_void(p); continue; } { @@ -320,8 +317,8 @@ void zero_init_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, return; } push_subobject_lv(p, slot, arr_ty, offset, ty); - pcg_push_int(p, 0, ty); - pcg_store_void(p); + c_cg_push_int(p, 0, ty); + c_cg_store_void(p); } static void init_field_at(Parser* p, FrameSlot slot, const Type* arr_ty, @@ -334,12 +331,12 @@ static void init_field_at(Parser* p, FrameSlot slot, const Type* arr_ty, parse_assign_expr(p); to_rvalue(p); { - const Type* rhs = pcg_top_type(p); + const Type* rhs = c_cg_top_type(p); CSemCheck chk = c_sem_check_assignment(p->pool, f->type, rhs); if (!chk.ok) perr(p, "%.*s", KIT_SLICE_ARG(kit_slice_cstr(chk.message))); } coerce_top_to_lvalue(p); - pcg_store_void(p); + c_cg_store_void(p); return; } init_at(p, slot, arr_ty, rec_offset + L->fields[field_index].offset, f->type); @@ -358,14 +355,14 @@ static void init_string_at(Parser* p, FrameSlot slot, const Type* arr_ty, if (copy > count) copy = count; for (i = 0; i < copy; ++i) { push_subobject_lv(p, slot, arr_ty, offset + (u32)i * elem_size, elem_ty); - pcg_push_int(p, (i64)decode_lit_unit_le(bytes + i * elem_size, elem_size), - elem_ty); - pcg_store_void(p); + c_cg_push_int(p, (i64)decode_lit_unit_le(bytes + i * elem_size, elem_size), + elem_ty); + c_cg_store_void(p); } for (; i < count; ++i) { push_subobject_lv(p, slot, arr_ty, offset + (u32)i * elem_size, elem_ty); - pcg_push_int(p, 0, elem_ty); - pcg_store_void(p); + c_cg_push_int(p, 0, elem_ty); + c_cg_store_void(p); } kit_compiler_context(p->c)->heap->free(kit_compiler_context(p->c)->heap, bytes, 0); @@ -522,8 +519,8 @@ static u32 init_struct_fields(Parser* p, FrameSlot slot, const Type* arr_ty, if (zf->flags & FIELD_BITFIELD) { if (!(zf->flags & FIELD_ZERO_WIDTH)) { push_record_field_lv(p, slot, arr_ty, offset, ty, zero_lo); - pcg_push_int(p, 0, zf->type); - pcg_store_void(p); + c_cg_push_int(p, 0, zf->type); + c_cg_store_void(p); } } else { u32 zoff = offset + L->fields[zero_lo].offset; @@ -575,8 +572,8 @@ static u32 init_struct_fields(Parser* p, FrameSlot slot, const Type* arr_ty, if (f->flags & FIELD_BITFIELD) { if (!(f->flags & FIELD_ZERO_WIDTH)) { push_record_field_lv(p, slot, arr_ty, offset, ty, j); - pcg_push_int(p, 0, f->type); - pcg_store_void(p); + c_cg_push_int(p, 0, f->type); + c_cg_store_void(p); } } else { u32 foff = offset + L->fields[j].offset; @@ -618,8 +615,8 @@ static void init_aggregate_remainder(Parser* p, FrameSlot slot, if (f->flags & FIELD_BITFIELD) { if (!(f->flags & FIELD_ZERO_WIDTH)) { push_record_field_lv(p, slot, arr_ty, offset, ty, i); - pcg_push_int(p, 0, f->type); - pcg_store_void(p); + c_cg_push_int(p, 0, f->type); + c_cg_store_void(p); } } else { const Type* fty = init_field_type_at(ty, i); @@ -750,12 +747,12 @@ void init_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, parse_assign_expr(p); to_rvalue(p); { - const Type* rhs = pcg_top_type(p); + const Type* rhs = c_cg_top_type(p); CSemCheck chk = c_sem_check_assignment(p->pool, ty, rhs); if (!chk.ok) perr(p, "%.*s", KIT_SLICE_ARG(kit_slice_cstr(chk.message))); } coerce_top_to_lvalue(p); - pcg_store_void(p); + c_cg_store_void(p); if (had_brace) { accept_punct(p, ','); expect_punct(p, '}', "'}' after scalar initializer"); diff --git a/lang/c/parse/parse_priv.h b/lang/c/parse/parse_priv.h @@ -247,10 +247,6 @@ typedef struct Parser { u8 default_visibility; /* SymVis */ u8 auto_var_init; /* KitAutoVarInit: implicit init for uninit locals */ - PcgSlot* cg_slot_stack; - u32 cg_type_sp; - u32 cg_type_cap; - Tok cur; Tok next; int has_next; @@ -390,6 +386,11 @@ typedef struct Parser { u8 param_vla_bound_len; u32 suppress_codegen; + u32 const_guard_depth; + u32 const_guard_not_eval; + const char* const_guard_error; + SrcLoc const_guard_error_loc; + u32 static_local_counter; u32 compound_literal_counter; diff --git a/lang/c/parse/parse_stmt.c b/lang/c/parse/parse_stmt.c @@ -22,9 +22,9 @@ static int accept_kw_stmt(Parser* p, CKw k) { } static void parse_stmt_suppressed(Parser* p) { - pcg_codegen_suppress_push(p); + c_cg_codegen_suppress_push(p); parse_stmt(p); - pcg_codegen_suppress_pop(p); + c_cg_codegen_suppress_pop(p); } /* ============================================================ @@ -38,13 +38,13 @@ static void parse_if_stmt(Parser* p) { expect_punct(p, '(', "'('"); parse_expr(p); to_rvalue(p); - if (!c_type_is_scalar(pcg_top_type(p))) { + if (!c_type_is_scalar(c_cg_top_type(p))) { perr(p, "if condition requires scalar type"); } cond_known = kit_cg_top_const_int(p->cg, &cond); expect_punct(p, ')', "')'"); if (cond_known) { - pcg_drop(p); + c_cg_drop(p); if (cond) { parse_stmt(p); if (accept_kw_stmt(p, KW_ELSE)) parse_stmt_suppressed(p); @@ -60,7 +60,7 @@ static void parse_if_stmt(Parser* p) { * * Under codegen suppression no cond is on the SValue stack, so emit * nothing and just walk the body for the diagnostics-only pass. */ - if (!pcg_emit_enabled(p)) { + if (!c_cg_emit_enabled(p)) { parse_stmt(p); if (accept_kw_stmt(p, KW_ELSE)) parse_stmt(p); return; @@ -76,7 +76,7 @@ static void parse_while_stmt(Parser* p) { /* Drive the structured-CF API so the C-source target can lower this to * `for (;;) { … break; … continue; }` instead of goto soup. The labels * the scope mints are reused as cur_break/cur_continue, so parse_break - * /parse_continue/etc. keep using their existing raw `pcg_jump` calls — + * /parse_continue/etc. keep using their existing raw `c_cg_jump` calls — * the C target recognizes the labels as the innermost scope's * boundaries and emits the structured keywords on its own. */ CGLabel saved_break = p->cur_break; @@ -84,7 +84,7 @@ static void parse_while_stmt(Parser* p) { KitCgScope scope; CGLabel L_top; CGLabel L_end; - int emit = pcg_emit_enabled(p); + int emit = c_cg_emit_enabled(p); if (emit) { scope = kit_cg_scope_begin(p->cg); L_top = kit_cg_scope_continue_label(p->cg, scope); @@ -97,12 +97,12 @@ static void parse_while_stmt(Parser* p) { expect_punct(p, '(', "'('"); parse_expr(p); to_rvalue(p); - if (!c_type_is_scalar(pcg_top_type(p))) { + if (!c_type_is_scalar(c_cg_top_type(p))) { perr(p, "while condition requires scalar type"); } expect_punct(p, ')', "')'"); if (!emit) { - pcg_drop(p); + c_cg_drop(p); p->cur_break = L_end; p->cur_continue = L_top; parse_stmt(p); @@ -110,20 +110,20 @@ static void parse_while_stmt(Parser* p) { p->cur_continue = saved_continue; return; } - pcg_branch_false(p, L_end); + c_cg_branch_false(p, L_end); p->cur_break = L_end; p->cur_continue = L_top; parse_stmt(p); p->cur_break = saved_break; p->cur_continue = saved_continue; - pcg_jump(p, L_top); + c_cg_jump(p, L_top); kit_cg_scope_end(p->cg, scope); } static void parse_for_stmt(Parser* p) { - CGLabel L_top = pcg_label_new(p); - CGLabel L_step = pcg_label_new(p); - CGLabel L_end = pcg_label_new(p); + CGLabel L_top = c_cg_label_new(p); + CGLabel L_step = c_cg_label_new(p); + CGLabel L_end = c_cg_label_new(p); CGLabel saved_break = p->cur_break; CGLabel saved_continue = p->cur_continue; @@ -137,33 +137,33 @@ static void parse_for_stmt(Parser* p) { parse_local_decl(p, &specs); } else { parse_expr(p); - pcg_drop(p); + c_cg_drop(p); expect_punct(p, ';', "';'"); } } - pcg_label_place(p, L_top); + c_cg_label_place(p, L_top); if (!is_punct(&p->cur, ';')) { parse_expr(p); to_rvalue(p); - if (!c_type_is_scalar(pcg_top_type(p))) { + if (!c_type_is_scalar(c_cg_top_type(p))) { perr(p, "for condition requires scalar type"); } - pcg_branch_false(p, L_end); + c_cg_branch_false(p, L_end); } expect_punct(p, ';', "';'"); { - CGLabel L_body = pcg_label_new(p); - pcg_jump(p, L_body); - pcg_label_place(p, L_step); + CGLabel L_body = c_cg_label_new(p); + c_cg_jump(p, L_body); + c_cg_label_place(p, L_step); if (!is_punct(&p->cur, ')')) { parse_expr(p); - pcg_drop(p); + c_cg_drop(p); } - pcg_jump(p, L_top); + c_cg_jump(p, L_top); expect_punct(p, ')', "')'"); - pcg_label_place(p, L_body); + c_cg_label_place(p, L_body); p->cur_break = L_end; p->cur_continue = L_step; @@ -171,8 +171,8 @@ static void parse_for_stmt(Parser* p) { p->cur_break = saved_break; p->cur_continue = saved_continue; - pcg_jump(p, L_step); - pcg_label_place(p, L_end); + c_cg_jump(p, L_step); + c_cg_label_place(p, L_end); } scope_pop(p); } @@ -182,7 +182,7 @@ static void parse_return_stmt(Parser* p) { if (p->cur_func_ret && p->cur_func_ret->kind != TY_VOID) { perr(p, "return with no value in non-void function"); } - pcg_ret(p, 0); + c_cg_ret(p, 0); return; } if (p->cur_func_ret && p->cur_func_ret->kind == TY_VOID) { @@ -191,57 +191,57 @@ static void parse_return_stmt(Parser* p) { parse_expr(p); to_rvalue(p); { - const Type* rhs = pcg_top_type(p); + const Type* rhs = c_cg_top_type(p); CSemCheck chk = c_sem_check_assignment(p->pool, p->cur_func_ret, rhs); if (!chk.ok) perr(p, "%.*s", KIT_SLICE_ARG(kit_slice_cstr(chk.message))); } /* Convert the value to the function return type, as `return` performs the * equivalent of assignment to an object of the return type (§6.8.6.4). - * pcg_ret expects the value already in the return type; without this a + * c_cg_ret expects the value already in the return type; without this a * narrower value (e.g. a _Bool call result returned from an int function) * would be reloaded at the wider return width and read adjacent bytes. */ coerce_top_to_type(p, p->cur_func_ret); expect_punct(p, ';', "';' after return value"); - pcg_ret(p, 1); + c_cg_ret(p, 1); } static void parse_break_stmt(Parser* p) { if (p->cur_break == 0) perr(p, "'break' outside of loop or switch"); - pcg_jump(p, p->cur_break); + c_cg_jump(p, p->cur_break); expect_punct(p, ';', "';' after break"); } static void parse_continue_stmt(Parser* p) { if (p->cur_continue == 0) perr(p, "'continue' outside of loop"); - pcg_jump(p, p->cur_continue); + c_cg_jump(p, p->cur_continue); expect_punct(p, ';', "';' after continue"); } static void parse_do_stmt(Parser* p) { - CGLabel L_top = pcg_label_new(p); - CGLabel L_cond = pcg_label_new(p); - CGLabel L_end = pcg_label_new(p); + CGLabel L_top = c_cg_label_new(p); + CGLabel L_cond = c_cg_label_new(p); + CGLabel L_end = c_cg_label_new(p); CGLabel saved_break = p->cur_break; CGLabel saved_continue = p->cur_continue; - pcg_label_place(p, L_top); + c_cg_label_place(p, L_top); p->cur_break = L_end; p->cur_continue = L_cond; parse_stmt(p); p->cur_break = saved_break; p->cur_continue = saved_continue; - pcg_label_place(p, L_cond); + c_cg_label_place(p, L_cond); if (!is_kw(p, &p->cur, KW_WHILE)) perr(p, "expected 'while' after do-body"); advance(p); /* while */ expect_punct(p, '(', "'('"); parse_expr(p); to_rvalue(p); - if (!c_type_is_scalar(pcg_top_type(p))) { + if (!c_type_is_scalar(c_cg_top_type(p))) { perr(p, "do-while condition requires scalar type"); } expect_punct(p, ')', "')' after do-while condition"); expect_punct(p, ';', "';' after do-while"); - pcg_branch_true(p, L_top); - pcg_label_place(p, L_end); + c_cg_branch_true(p, L_top); + c_cg_label_place(p, L_end); } GotoLabel* label_get_or_create(Parser* p, Sym name, SrcLoc loc) { @@ -257,10 +257,10 @@ GotoLabel* label_get_or_create(Parser* p, Sym name, SrcLoc loc) { * `LABEL:` placement and its `goto LABEL` references can straddle a * constant-false (suppressed) region. Key allocation off whether the function * emits at all, not the transient suppress depth — otherwise a label first - * mentioned inside a suppressed `goto` would cache pcg_label_new's + * mentioned inside a suppressed `goto` would cache c_cg_label_new's * suppression sentinel and later alias the function's first real label * ("placed twice"). */ - gl->label = p->cur_func_emits ? kit_cg_label_new(p->cg) : pcg_label_new(p); + gl->label = p->cur_func_emits ? kit_cg_label_new(p->cg) : c_cg_label_new(p); gl->placed = 0; gl->first_use = loc; gl->min_forward_vla_mark = p->vla_mark; @@ -295,7 +295,7 @@ static void parse_computed_goto(Parser* p) { advance(p); /* '*' */ parse_expr(p); to_rvalue(p); - if (!type_is_ptr(pcg_top_type(p))) { + if (!type_is_ptr(c_cg_top_type(p))) { perr(p, "computed goto requires a pointer operand"); } expect_punct(p, ';', "';' after computed goto"); @@ -313,7 +313,7 @@ static void parse_computed_goto(Parser* p) { if (gl->addr_taken) targets[i++] = gl->label; } p->computed_goto_emitted = 1; - pcg_computed_goto(p, targets, ntargets); + c_cg_computed_goto(p, targets, ntargets); } static void parse_goto_stmt(Parser* p) { @@ -340,7 +340,7 @@ static void parse_goto_stmt(Parser* p) { } else if (p->vla_mark < gl->min_forward_vla_mark) { gl->min_forward_vla_mark = p->vla_mark; } - pcg_jump(p, gl->label); + c_cg_jump(p, gl->label); } static void parse_label_stmt(Parser* p) { @@ -356,7 +356,7 @@ static void parse_label_stmt(Parser* p) { } gl->placed = 1; gl->label_vla_mark = p->vla_mark; - pcg_label_place(p, gl->label); + c_cg_label_place(p, gl->label); parse_stmt(p); } @@ -371,8 +371,8 @@ static void parse_case_stmt(Parser* p) { if (ce->value == v) perr(p, "duplicate case value"); } expect_punct(p, ':', "':' after case constant"); - L = pcg_label_new(p); - pcg_label_place(p, L); + L = c_cg_label_new(p); + c_cg_label_place(p, L); ce = arena_new(p->pool->arena, CaseEntry); if (!ce) perr(p, "out of memory in parse_case_stmt"); ce->value = v; @@ -387,8 +387,8 @@ static void parse_default_stmt(Parser* p) { if (!p->cur_switch) perr(p, "'default' label not in switch statement"); expect_punct(p, ':', "':' after default"); if (p->cur_switch->default_label != 0) perr(p, "duplicate 'default' label"); - L = pcg_label_new(p); - pcg_label_place(p, L); + L = c_cg_label_new(p); + c_cg_label_place(p, L); p->cur_switch->default_label = L; parse_stmt(p); } @@ -408,7 +408,7 @@ static void parse_switch_stmt(Parser* p) { KitCgScope scope; CGLabel L_dispatch; CGLabel L_end; - int emit = pcg_emit_enabled(p); + int emit = c_cg_emit_enabled(p); FrameSlotDesc fsd; const Type* vty; CaseEntry* it; @@ -417,7 +417,7 @@ static void parse_switch_stmt(Parser* p) { if (emit) { scope = kit_cg_scope_begin(p->cg); - L_dispatch = pcg_label_new(p); + L_dispatch = c_cg_label_new(p); L_end = kit_cg_scope_break_label(p->cg, scope); } else { scope = 0; @@ -428,7 +428,7 @@ static void parse_switch_stmt(Parser* p) { expect_punct(p, '(', "'('"); parse_expr(p); to_rvalue(p); - vty = pcg_top_type(p); + vty = c_cg_top_type(p); if (!vty) vty = type_prim(p->pool, TY_INT); if (!type_is_int(vty)) perr(p, "switch expression requires integer type"); /* C99 6.8.4.2: the integer promotions are performed on the controlling @@ -442,14 +442,14 @@ static void parse_switch_stmt(Parser* p) { else prom = type_promoted(p->pool, prom); if (prom && prom != vty) { - pcg_convert(p, prom); + c_cg_convert(p, prom); vty = prom; } } expect_punct(p, ')', "')' after switch expression"); if (!emit) { - pcg_drop(p); + c_cg_drop(p); memset(&ctx, 0, sizeof ctx); ctx.parent = saved_switch; p->cur_switch = &ctx; @@ -466,15 +466,15 @@ static void parse_switch_stmt(Parser* p) { fsd.size = c_abi_sizeof(p->abi, p->pool, vty); fsd.align = c_abi_alignof(p->abi, p->pool, vty); fsd.kind = FS_LOCAL; - ctx.value_slot = pcg_local(p, &fsd); + ctx.value_slot = c_cg_local(p, &fsd); ctx.value_type = vty; ctx.parent = saved_switch; - pcg_push_local_typed(p, ctx.value_slot, vty); - pcg_swap(p); - pcg_store_void(p); + c_cg_push_local_typed(p, ctx.value_slot, vty); + c_cg_swap(p); + c_cg_store_void(p); - pcg_jump(p, L_dispatch); + c_cg_jump(p, L_dispatch); p->cur_switch = &ctx; p->cur_break = L_end; @@ -482,9 +482,9 @@ static void parse_switch_stmt(Parser* p) { p->cur_break = saved_break; p->cur_switch = saved_switch; - pcg_jump(p, L_end); + c_cg_jump(p, L_end); - pcg_label_place(p, L_dispatch); + c_cg_label_place(p, L_dispatch); /* Reverse cases into source order; CaseEntry list grows at the head * during parsing so iteration here is LIFO without the flip. */ prev = NULL; @@ -514,12 +514,12 @@ static void parse_switch_stmt(Parser* p) { i++; } } - pcg_push_local_typed(p, ctx.value_slot, vty); - pcg_load(p); + c_cg_push_local_typed(p, ctx.value_slot, vty); + c_cg_load(p); { KitCgSwitch sw; memset(&sw, 0, sizeof sw); - sw.selector_type = pcg_tid(p, vty); + sw.selector_type = c_cg_tid(p, vty); sw.default_label = ctx.default_label ? (KitCgLabel)ctx.default_label : (KitCgLabel)L_end; sw.cases = cases; @@ -572,17 +572,17 @@ typedef struct AsmOutLValue { static void asm_out_lvalue_push(Parser* p, const AsmOutLValue* lv) { if (lv->direct_local) { - pcg_push_local_typed(p, lv->value_slot, lv->val_ty); + c_cg_push_local_typed(p, lv->value_slot, lv->val_ty); return; } - pcg_push_local_typed(p, lv->addr_slot, lv->ptr_ty); - pcg_load(p); - pcg_deref(p, lv->val_ty); + c_cg_push_local_typed(p, lv->addr_slot, lv->ptr_ty); + c_cg_load(p); + c_cg_deref(p, lv->val_ty); } static void asm_out_value_push(Parser* p, const AsmOutLValue* lv) { asm_out_lvalue_push(p, lv); - pcg_load(p); + c_cg_load(p); } static Sym parse_asm_operand_name(Parser* p) { @@ -687,26 +687,26 @@ static void parse_asm_stmt(Parser* p) { expect_punct(p, '(', "'(' before asm output lvalue"); c.reg = asm_operand_pinned_reg(p, &pinned_slot); parse_assign_expr(p); - val_ty = pcg_top_type(p); + val_ty = c_cg_top_type(p); if (!val_ty) perr(p, "asm output: cannot determine lvalue type"); c.type = val_ty; if (c.reg && pinned_slot != FRAME_SLOT_NONE) { - pcg_drop(p); + c_cg_drop(p); lv.direct_local = 1; lv.value_slot = pinned_slot; } else { - pcg_addr(p); - ptr_ty = pcg_top_type(p); + c_cg_addr(p); + ptr_ty = c_cg_top_type(p); if (!ptr_ty) perr(p, "asm output: cannot take address"); memset(&fsd, 0, sizeof fsd); fsd.type = ptr_ty; fsd.size = 8; fsd.align = 8; fsd.kind = FS_LOCAL; - slot = pcg_local(p, &fsd); - pcg_push_local_typed(p, slot, ptr_ty); - pcg_swap(p); - pcg_store_void(p); + slot = c_cg_local(p, &fsd); + c_cg_push_local_typed(p, slot, ptr_ty); + c_cg_swap(p); + c_cg_store_void(p); lv.addr_slot = slot; lv.ptr_ty = ptr_ty; } @@ -746,7 +746,7 @@ static void parse_asm_stmt(Parser* p) { c.reg = asm_operand_pinned_reg(p, NULL); parse_assign_expr(p); to_rvalue(p); - c.type = pcg_top_type(p); + c.type = c_cg_top_type(p); expect_punct(p, ')', "')' after asm input expression"); if (nin == cap_in) { u32 nc = cap_in * 2; @@ -837,16 +837,16 @@ static void parse_asm_stmt(Parser* p) { } } - pcg_set_loc(p, loc); - pcg_inline_asm(p, tmpl, outs, nout, ins, nin, clobbers, nclob); + c_cg_set_loc(p, loc); + c_cg_inline_asm(p, tmpl, outs, nout, ins, nin, clobbers, nclob); if (nout > 0) { u32 i; for (i = nout; i-- > 0;) { AsmOutLValue* lv = &out_lvs[i]; asm_out_lvalue_push(p, lv); - pcg_swap(p); - pcg_store_void(p); + c_cg_swap(p); + c_cg_store_void(p); } } } @@ -876,14 +876,14 @@ void parse_compound_stmt(Parser* p) { /* Statement boundary: the value stack is back to empty, so every transient * compiler temp minted by this statement is dead. Recycle their frame homes * for the next statement, bounding the -O0 frame. */ - pcg_reclaim_temps(p); + c_cg_reclaim_temps(p); } expect_punct(p, '}', "'}'"); scope_pop(p); } void parse_stmt(Parser* p) { - pcg_set_loc(p, tok_loc_stmt(p, &p->cur)); + c_cg_set_loc(p, tok_loc_stmt(p, &p->cur)); if (p->cur.kind == TOK_IDENT && ident_kw_inline(p, tok_ident(&p->cur)) == KW_NONE) { Tok n = peek1(p); @@ -962,6 +962,6 @@ void parse_stmt(Parser* p) { } /* Expression statement. */ parse_expr(p); - pcg_drop(p); + c_cg_drop(p); expect_punct(p, ';', "';' after expression"); } diff --git a/lang/c/parse/parse_type.c b/lang/c/parse/parse_type.c @@ -1447,7 +1447,7 @@ int parse_decl_suffix(Parser* p, DeclSuffix* out) { * file-scope bounds through eval_const_int below, so a non-constant bound * becomes a clean "non-constant ... in constant expression" diagnostic * instead of entering the VLA codegen path — which assumes an active - * function frame (pcg_local/alloca/branch) and, with none at file scope, + * function frame (c_cg_local/alloca/branch) and, with none at file scope, * silently miscompiles or hangs. */ int is_const_start = p->cur_func_name == 0 || t.kind == TOK_NUM || t.kind == TOK_CHR; @@ -1480,13 +1480,13 @@ int parse_decl_suffix(Parser* p, DeclSuffix* out) { fsd.size = c_abi_sizeof(p->abi, p->pool, fsd.type); fsd.align = c_abi_alignof(p->abi, p->pool, fsd.type); fsd.kind = FS_LOCAL; - out->vla_count_slot = pcg_local(p, &fsd); + out->vla_count_slot = c_cg_local(p, &fsd); parse_assign_expr(p); to_rvalue(p); - pcg_push_local_typed(p, out->vla_count_slot, fsd.type); - pcg_swap(p); + c_cg_push_local_typed(p, out->vla_count_slot, fsd.type); + c_cg_swap(p); coerce_top_to_lvalue(p); - pcg_store_void(p); + c_cg_store_void(p); p->vla_pending = 1; ++p->vla_mark; p->vla_pending_count_slot = out->vla_count_slot; diff --git a/src/cg/control.c b/src/cg/control.c @@ -3,7 +3,8 @@ KitCgLabel kit_cg_label_new(KitCg* g) { if (!g) return KIT_CG_LABEL_NONE; - if (api_unevaluated(g)) return KIT_CG_LABEL_NONE; + if (api_unevaluated(g) && g->fn_ret_type == KIT_CG_TYPE_NONE) + return KIT_CG_LABEL_NONE; return (KitCgLabel)g->target->label_new(g->target); } diff --git a/src/cg/memory.c b/src/cg/memory.c @@ -508,7 +508,12 @@ static void api_cg_store_impl(KitCg* g, KitCgMemAccess access, access_ty = ty; if (api_unevaluated(g)) { api_release(g, &base); - api_release(g, &rv); + if (keep_result) { + api_push(g, rv); + api_const_copy_top_from(g, rv_const); + } else { + api_release(g, &rv); + } return; } diff --git a/test/asm/hostas_cross.sh b/test/asm/hostas_cross.sh @@ -64,13 +64,11 @@ ENFORCE_CLANG="${KIT_HOSTAS_ENFORCE_CLANG:-1}" EXEC_SMOKE_TIMEOUT="${KIT_HOSTAS_EXEC_TIMEOUT:-45}" PAR="${KIT_HOSTAS_PARALLEL:-1}" -# Opt axis (see hostas_toy.sh): map a KIT_TEST_OPTS override of O-spellings -# onto the engine's bare opt levels; default 0 1. -OPT_LEVELS="0 1" -if [ -n "${KIT_TEST_OPTS:-}" ]; then - OPT_LEVELS="" - for _o in $KIT_TEST_OPTS; do OPT_LEVELS="$OPT_LEVELS ${_o#O}"; done -fi +# Opt axis. KIT_TEST_OPTS carries "O"-prefixed levels (e.g. "O0 O1"); the +# corpus engine expects bare levels (KIT_OPT_LEVELS="0 1"). Strip the prefix. +OPTS="${KIT_TEST_OPTS:-O0 O1}" +OPT_LEVELS="" +for _o in $OPTS; do OPT_LEVELS="$OPT_LEVELS ${_o#O}"; done # "tag:triple" — tag is exec_target.sh's <arch>-<os> spelling. All three ELF # targets are in the gating default (each SKIPs cleanly if its clang cross diff --git a/test/asm/hostas_toy.sh b/test/asm/hostas_toy.sh @@ -50,14 +50,11 @@ PAR="${KIT_HOSTAS_PARALLEL:-1}" # (just the known 141 case) never gated. Keep that: SKIP must not fail the run. KIT_SKIP_IS_FAILURE=0 -# Opt axis: the original took O0/O1 spellings via KIT_TEST_OPTS; the corpus -# engine expands bare levels (KIT_OPT_LEVELS="0 1"). Map a KIT_TEST_OPTS -# override (e.g. "O0 O1" or "O0") onto bare levels to preserve the env knob. -OPT_LEVELS="0 1" -if [ -n "${KIT_TEST_OPTS:-}" ]; then - OPT_LEVELS="" - for _o in $KIT_TEST_OPTS; do OPT_LEVELS="$OPT_LEVELS ${_o#O}"; done -fi +# Opt axis. KIT_TEST_OPTS carries "O"-prefixed levels (e.g. "O0 O1"); the +# corpus engine expects bare levels (KIT_OPT_LEVELS="0 1"). Strip the prefix. +OPTS="${KIT_TEST_OPTS:-O0 O1}" +OPT_LEVELS="" +for _o in $OPTS; do OPT_LEVELS="$OPT_LEVELS ${_o#O}"; done # Filter ($1) preserved -> the engine filters discovery by KIT_TEST_FILTER. FILTER="${1:-${KIT_TEST_FILTER:-}}" diff --git a/test/ecosystem/README.md b/test/ecosystem/README.md @@ -30,7 +30,7 @@ Or directly, with finer control: scripts/ecosystem.sh status # what's provisioned scripts/ecosystem.sh fetch sqlite # fetch one project test/ecosystem/run.sh sqlite lua # run only these -KIT_ECO_OPT=O0 test/ecosystem/run.sh # one opt level +KIT_TEST_OPTS=O0 test/ecosystem/run.sh # one opt level test/ecosystem/run.sh --update # refresh goldens (from clang) ``` diff --git a/test/ecosystem/known_bugs/yyjson-write-label.md b/test/ecosystem/known_bugs/yyjson-write-label.md @@ -3,7 +3,7 @@ **Status:** FIXED. yyjson compiles, links, and runs at both -O0 and -O1 and matches clang. The defect was in the **front end**, not the emitter: a goto label whose first mention is inside a constant-false (codegen-suppressed) -region was assigned `pcg_label_new`'s suppression sentinel instead of a real +region was assigned `c_cg_label_new`'s suppression sentinel instead of a real CG-label id, so it later aliased the function's first real label and the -O0 native emitter aborted placing that id twice. `parse_function_body` now keys goto-label allocation off whether the function emits at all diff --git a/test/ecosystem/run.sh b/test/ecosystem/run.sh @@ -22,7 +22,7 @@ # (from the clang build, else the kit build) # env: # KIT kit binary (default build/kit) -# KIT_ECO_OPT space-separated opt levels (default "O0 O1") +# KIT_TEST_OPTS space-separated opt levels (default "O0 O1") # KIT_ECO_NO_CLANG=1 skip the clang differential even if clang is present set -u @@ -34,7 +34,7 @@ RECIPE_DIR="$SCRIPT_DIR/recipes" ECOSYS="$REPO_ROOT/scripts/ecosystem.sh" KIT="${KIT:-$REPO_ROOT/build/kit}" -OPTS="${KIT_ECO_OPT:-O0 O1}" +OPTS="${KIT_TEST_OPTS:-O0 O1}" UPDATE=0 SEL="" diff --git a/test/parse/CORPUS.md b/test/parse/CORPUS.md @@ -152,6 +152,7 @@ here for completeness once they're real cases. | `6_5_14_ternary` | ★ | `return (5>3) ? 42 : 7;` | 42 | | `6_5_15_comma` | ★ | `int x; return (x=1, x=42, x);` | 42 | | `6_5_16_assign` | ★ | `int x; x = 42; return x;` | 42 | +| `6_5_16_08_suppressed_assign` | ★ | `if (0) { x = 13; }` must parse suppressed assignment without touching live value | 42 | | `6_5_17_compound_assign` | ★ | `int x = 40; x += 2; return x;` | 42 | | `6_5_18_pre_inc` | ★ | `int x = 41; return ++x;` | 42 | | `6_5_19_post_inc` | ★ | `int x = 42; x++; return x;` | 43; reads as 43 | diff --git a/test/parse/cases/6_5_15_06_cond_struct_qualifier_mismatch.c b/test/parse/cases/6_5_15_06_cond_struct_qualifier_mismatch.c @@ -3,7 +3,7 @@ * qualification — e.g. a `const` struct lvalue reached through a `const T*` * (`*cp`) versus a plain struct subobject (`s->n.f`). * - * Regression: the parser reconciled the two arms with pcg_convert, which — + * Regression: the parser reconciled the two arms with c_cg_convert, which — * having no aggregate case — fell into the scalar bitcast path and pushed the * struct as a value. The CG rejects that ("aggregate must be a place, not a * value"); on aarch64 it instead surfaced as "scalar too large". The trigger @@ -35,9 +35,9 @@ int test_main(void) { P p = {3, 4}; P q = {7, 8}; struct S s = {{{5, 6}}}; - int viaptr = pick(&p, &s, 1); /* *cp -> 34 */ - int viamem = pick(&p, &s, 0); /* s->n.f -> 56 */ - int c = pick2(&p, &q, 1); /* *cp -> 304 */ - int n = pick2(&p, &q, 0); /* *np -> 708 */ + int viaptr = pick(&p, &s, 1); /* *cp -> 34 */ + int viamem = pick(&p, &s, 0); /* s->n.f -> 56 */ + int c = pick2(&p, &q, 1); /* *cp -> 304 */ + int n = pick2(&p, &q, 0); /* *np -> 708 */ return (viaptr == 34 && viamem == 56 && c == 304 && n == 708) ? 0 : 1; } diff --git a/test/parse/cases/6_5_16_08_suppressed_assign.c b/test/parse/cases/6_5_16_08_suppressed_assign.c @@ -0,0 +1,7 @@ +int test_main(void) { + int x = 42; + if (0) { + x = 13; + } + return x; +} diff --git a/test/parse/cases/6_5_16_08_suppressed_assign.expected b/test/parse/cases/6_5_16_08_suppressed_assign.expected @@ -0,0 +1 @@ +42 diff --git a/test/parse/cases/gnu_inline_control_flow.c b/test/parse/cases/gnu_inline_control_flow.c @@ -1,7 +1,7 @@ /* A C99 `extern inline` definition is parsed and semantically validated but * emits no out-of-line code (codegen suppressed). Control-flow statements in * such a body must do their break/continue/case bookkeeping without opening a - * CG function — regression for pcg_label_new and the while/switch suppressed + * CG function — regression for c_cg_label_new and the while/switch suppressed * paths, which mint dummy labels under suppression instead of calling the CG * label ops. The suppressed body is intentionally not referenced (an extern * inline has no external definition to link against); test_main stands alone.