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:
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.