commit 13beba6de38f1389ec3217e78f27ddacfabb57bc
parent db75a878a607b07c93cb48829fec994292984aa5
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 10 Jun 2026 08:23:02 -0700
cleanup: I.1 — collapse parser shadow stack into one PcgSlot array
The parser's typed shadow stack was three parallel arrays (cg_type_stack /
cg_value_flags / cg_lv_aux) shuffled in lockstep by grow/push/dup/swap/rot3.
Collapse them into a single PcgSlot{type; aux; flags} array (cg_slot_stack),
so the structural ops become single whole-slot copies/swaps and growth is one
allocation. All ~70 accessors in cg_adapter.c now index one array; no callers
outside cg_adapter.c touch the stack (I.3 already routed the parse_expr.c
pokes through pcg_retag_keep_flags).
Pure data-layout refactor, no behavior change: test-parse 3880+128/0,
test-toy 1392/0/35, test-cg-api 211/0 — identical to baseline.
Diffstat:
4 files changed, 69 insertions(+), 97 deletions(-)
diff --git a/doc/plan/TODO.md b/doc/plan/TODO.md
@@ -89,10 +89,6 @@ Add new deferred fixes below as they are discovered.
(`cc_default_obj_path_for_name`, `cc_dep_default_target`, `build_default_obj_name`); its
home is the driver util layer (`driver/`), not `link_inputs.c`. (The link pipeline +
flag-parse blocks were assessed and are not byte-identical — different option structs.)
-- **I.1 — C parser shadow stack** is a 3-array struct-of-arrays
- (`lang/c/parse/cg_adapter.c:53-141`) whose dup/swap/rot3/grow shuffle 3 lanes in
- lockstep; collapse into one `PcgSlot{type;flags;aux;}` array. (Touches the parser↔CG
- bridge — higher risk; needs care.)
- **I.2 — two parallel C-6.7.9 initializer walkers** (`lang/c/parse/parse_init.c:648-777`)
re-encode the same traversal grammar. Factor a single grammar driver + leaf vtable.
- **I.4 — opt passes re-hand-roll the centralized operand-walk.** (The `ranges_overlap`
diff --git a/lang/c/parse/cg_adapter.c b/lang/c/parse/cg_adapter.c
@@ -51,39 +51,26 @@ static void pcg_aux_clear(PcgLvAux* a) {
}
static void pcg_stack_grow(Parser* p, u32 want) {
- const Type** ns;
- u8* nf;
- PcgLvAux* na;
+ 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_array(p->pool->arena, const Type*, nc);
- if (!ns) perr(p, "out of memory in CG type stack");
- nf = arena_zarray(p->pool->arena, u8, nc);
- if (!nf) perr(p, "out of memory in CG value stack");
- na = arena_zarray(p->pool->arena, PcgLvAux, nc);
- if (!na) perr(p, "out of memory in CG lvalue aux stack");
- if (p->cg_type_stack && p->cg_type_sp) {
- memcpy(ns, p->cg_type_stack, sizeof(*ns) * p->cg_type_sp);
- }
- if (p->cg_value_flags && p->cg_type_sp) {
- memcpy(nf, p->cg_value_flags, sizeof(*nf) * p->cg_type_sp);
- }
- if (p->cg_lv_aux && p->cg_type_sp) {
- memcpy(na, p->cg_lv_aux, sizeof(*na) * p->cg_type_sp);
- }
- p->cg_type_stack = ns;
- p->cg_value_flags = nf;
- p->cg_lv_aux = na;
+ 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);
- p->cg_type_stack[p->cg_type_sp] = ty;
- p->cg_value_flags[p->cg_type_sp] = 0;
- pcg_aux_clear(&p->cg_lv_aux[p->cg_type_sp]);
+ s = &p->cg_slot_stack[p->cg_type_sp];
+ s->type = ty;
+ s->flags = 0;
+ pcg_aux_clear(&s->aux);
++p->cg_type_sp;
}
@@ -92,51 +79,33 @@ void pcg_drop_type(Parser* p) {
}
void pcg_dup_type(Parser* p) {
- const Type* ty = pcg_top_type(p);
- u8 flags = p->cg_type_sp ? p->cg_value_flags[p->cg_type_sp - 1u] : 0;
- PcgLvAux aux;
- if (p->cg_type_sp)
- aux = p->cg_lv_aux[p->cg_type_sp - 1u];
- else
- pcg_aux_clear(&aux);
- pcg_push_type(p, ty);
+ PcgSlot top;
if (p->cg_type_sp) {
- p->cg_value_flags[p->cg_type_sp - 1u] = flags;
- p->cg_lv_aux[p->cg_type_sp - 1u] = aux;
+ top = p->cg_slot_stack[p->cg_type_sp - 1u];
+ } else {
+ top.type = NULL;
+ top.flags = 0;
+ pcg_aux_clear(&top.aux);
}
+ /* Copy the slot out before push: pcg_stack_grow may reallocate. */
+ 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) {
- const Type* a = p->cg_type_stack[p->cg_type_sp - 1u];
- u8 af = p->cg_value_flags[p->cg_type_sp - 1u];
- PcgLvAux ax = p->cg_lv_aux[p->cg_type_sp - 1u];
- p->cg_type_stack[p->cg_type_sp - 1u] = p->cg_type_stack[p->cg_type_sp - 2u];
- p->cg_value_flags[p->cg_type_sp - 1u] =
- p->cg_value_flags[p->cg_type_sp - 2u];
- p->cg_lv_aux[p->cg_type_sp - 1u] = p->cg_lv_aux[p->cg_type_sp - 2u];
- p->cg_type_stack[p->cg_type_sp - 2u] = a;
- p->cg_value_flags[p->cg_type_sp - 2u] = af;
- p->cg_lv_aux[p->cg_type_sp - 2u] = ax;
+ 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) {
- const Type* a = p->cg_type_stack[p->cg_type_sp - 3u];
- u8 af = p->cg_value_flags[p->cg_type_sp - 3u];
- PcgLvAux ax = p->cg_lv_aux[p->cg_type_sp - 3u];
- p->cg_type_stack[p->cg_type_sp - 3u] = p->cg_type_stack[p->cg_type_sp - 2u];
- p->cg_value_flags[p->cg_type_sp - 3u] =
- p->cg_value_flags[p->cg_type_sp - 2u];
- p->cg_lv_aux[p->cg_type_sp - 3u] = p->cg_lv_aux[p->cg_type_sp - 2u];
- p->cg_type_stack[p->cg_type_sp - 2u] = p->cg_type_stack[p->cg_type_sp - 1u];
- p->cg_value_flags[p->cg_type_sp - 2u] =
- p->cg_value_flags[p->cg_type_sp - 1u];
- p->cg_lv_aux[p->cg_type_sp - 2u] = p->cg_lv_aux[p->cg_type_sp - 1u];
- p->cg_type_stack[p->cg_type_sp - 1u] = a;
- p->cg_value_flags[p->cg_type_sp - 1u] = af;
- p->cg_lv_aux[p->cg_type_sp - 1u] = ax;
+ 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;
}
}
@@ -159,27 +128,27 @@ void pcg_drop(Parser* p) {
}
PcgLvAux* pcg_top_lv_aux(Parser* p) {
- return p->cg_type_sp ? &p->cg_lv_aux[p->cg_type_sp - 1u] : NULL;
+ 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_lv_aux[p->cg_type_sp - 1u - 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_type_stack[p->cg_type_sp - 1u] : NULL;
+ 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_type_stack[p->cg_type_sp - 2u] : NULL;
+ 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) {
- p->cg_type_stack[p->cg_type_sp - 1u] = ty;
- p->cg_value_flags[p->cg_type_sp - 1u] = 0;
- pcg_aux_clear(&p->cg_lv_aux[p->cg_type_sp - 1u]);
+ p->cg_slot_stack[p->cg_type_sp - 1u].type = 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);
}
}
@@ -189,49 +158,49 @@ void pcg_retag_top(Parser* p, const Type* ty) {
* 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)
- p->cg_type_stack[p->cg_type_sp - 1u - depth] = ty;
+ p->cg_slot_stack[p->cg_type_sp - 1u - depth].type = ty;
}
int pcg_top_is_bitfield(Parser* p) {
return p->cg_type_sp &&
- (p->cg_value_flags[p->cg_type_sp - 1u] & PCG_VALUE_BITFIELD) != 0;
+ (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_value_flags[p->cg_type_sp - 1u] |= PCG_VALUE_BITFIELD;
+ 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_value_flags[p->cg_type_sp - 1u] & PCG_VALUE_REGISTER) != 0;
+ (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_value_flags[p->cg_type_sp - 1u] |= PCG_VALUE_REGISTER;
+ 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_value_flags[p->cg_type_sp - 1u] & PCG_VALUE_LVALUE) != 0;
+ (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_value_flags[p->cg_type_sp - 1u] &
+ 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_value_flags[p->cg_type_sp - 1u] &
+ 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_value_flags[p->cg_type_sp - 1u] = pcg_lvalue_flags_for_type(ty);
+ 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; }
@@ -449,7 +418,7 @@ void pcg_push_int(Parser* p, i64 v, const Type* ty) {
}
pcg_push_type(p, ty);
if (v == 0 && p->cg_type_sp) {
- p->cg_value_flags[p->cg_type_sp - 1u] |= PCG_VALUE_NULL_PTR_CONST;
+ p->cg_slot_stack[p->cg_type_sp - 1u].flags |= PCG_VALUE_NULL_PTR_CONST;
}
}
@@ -488,8 +457,8 @@ 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_value_flags[p->cg_type_sp - 1u] = pcg_lvalue_flags_for_type(ty);
- p->cg_lv_aux[p->cg_type_sp - 1u].base_kind = PCG_LV_BASE_LOCAL;
+ 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;
}
}
@@ -501,8 +470,8 @@ void pcg_push_global(Parser* p, ObjSymId sym, const Type* ty) {
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_value_flags[p->cg_type_sp - 1u] = pcg_lvalue_flags_for_type(ty);
- p->cg_lv_aux[p->cg_type_sp - 1u].base_kind = PCG_LV_BASE_POINTER_RV;
+ 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;
}
}
@@ -527,9 +496,9 @@ void pcg_load(Parser* p) {
kit_cg_load(p->cg, access);
}
if (was_lvalue && p->cg_type_sp) {
- p->cg_type_stack[p->cg_type_sp - 1u] = ty;
- p->cg_value_flags[p->cg_type_sp - 1u] = 0;
- pcg_aux_clear(&p->cg_lv_aux[p->cg_type_sp - 1u]);
+ p->cg_slot_stack[p->cg_type_sp - 1u].type = 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);
}
}
@@ -736,8 +705,8 @@ void pcg_deref(Parser* p, const Type* pointee) {
* pointer stays on the CG stack untouched. */
pcg_retag_top(p, pointee);
if (p->cg_type_sp) {
- p->cg_value_flags[p->cg_type_sp - 1u] = pcg_lvalue_flags_for_type(pointee);
- p->cg_lv_aux[p->cg_type_sp - 1u].base_kind = PCG_LV_BASE_POINTER_RV;
+ 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;
}
}
@@ -764,7 +733,7 @@ void pcg_lv_member(Parser* p, i64 byte_offset, const Type* field_ty,
if (was_lvalue) {
pcg_set_top_lvalue(p);
} else if (base_is_rvalue_agg && p->cg_type_sp) {
- p->cg_value_flags[p->cg_type_sp - 1u] = PCG_VALUE_LVALUE;
+ 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. */
{
@@ -783,7 +752,7 @@ void pcg_lv_member(Parser* p, i64 byte_offset, const Type* field_ty,
lv_after->is_subobject = 1u;
}
if (bf_width && p->cg_type_sp)
- p->cg_value_flags[p->cg_type_sp - 1u] |= PCG_VALUE_BITFIELD;
+ p->cg_slot_stack[p->cg_type_sp - 1u].flags |= PCG_VALUE_BITFIELD;
}
}
@@ -797,7 +766,7 @@ void pcg_lv_subscript(Parser* p, u32 elem_size, const Type* elem_ty) {
i64 saved_offset = base_lv ? base_lv->offset : 0;
u8 base_is_lvalue =
(p->cg_type_sp >= 2u &&
- (p->cg_value_flags[p->cg_type_sp - 2u] & PCG_VALUE_LVALUE) != 0);
+ (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);
diff --git a/lang/c/parse/cg_adapter.h b/lang/c/parse/cg_adapter.h
@@ -5,8 +5,8 @@
*
* 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_type_stack / cg_value_flags / cg_lv_aux in struct
- * Parser) in lockstep with the public CG stack and folds C lvalue chains into
+ * 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. */
@@ -22,7 +22,7 @@ typedef KitCgLocal FrameSlot;
#define FRAME_SLOT_NONE KIT_CG_LOCAL_NONE
#define OBJ_GROUP_NONE 0u
-/* Lvalue auxiliary state, carried parallel to cg_type_stack / cg_value_flags.
+/* 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
@@ -59,6 +59,15 @@ typedef struct PcgLvAux {
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. */
+typedef struct PcgSlot {
+ const Type* type;
+ PcgLvAux aux;
+ u8 flags;
+} PcgSlot;
+
typedef enum BinOp {
BO_IADD,
BO_ISUB,
diff --git a/lang/c/parse/parse_priv.h b/lang/c/parse/parse_priv.h
@@ -214,9 +214,7 @@ typedef struct Parser {
u8 default_visibility; /* SymVis */
u8 auto_var_init; /* KitAutoVarInit: implicit init for uninit locals */
- const Type** cg_type_stack;
- u8* cg_value_flags;
- PcgLvAux* cg_lv_aux;
+ PcgSlot* cg_slot_stack;
u32 cg_type_sp;
u32 cg_type_cap;