commit 324f8448f5eb6f6259e3870d714024379995936d
parent b25fcf61b2c648bb5c0376b5d6d7d04976239499
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 18 May 2026 13:26:27 -0700
fix parse aggregate and VLA lowering
Diffstat:
4 files changed, 93 insertions(+), 126 deletions(-)
diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c
@@ -540,12 +540,17 @@ i64 eval_const_int(Parser* p, SrcLoc loc) { return cexpr_bor(p, loc); }
* to_rvalue
* ============================================================ */
+static void decay_array_to_pointer(Parser* p, const Type* arr_ty) {
+ const Type* ptr_ty = type_ptr(p->pool, arr_ty->arr.elem);
+ cfree_cg_addr_offset(p->cg, 0, pcg_tid(p->c, ptr_ty));
+ pcg_retag_top(p, ptr_ty);
+}
+
void to_rvalue(Parser* p) {
const Type* t = cg_top_type(p->cg);
if (t) {
if (t->kind == TY_ARRAY) {
- cg_addr(p->cg);
- cg_retag_top(p->cg, type_ptr(p->pool, t->arr.elem));
+ decay_array_to_pointer(p, t);
return;
}
if (t->kind == TY_FUNC) {
@@ -567,6 +572,8 @@ void coerce_top_to_lvalue(Parser* p) {
if (!src || !dst || src == dst) return;
if (type_is_arith(src) && type_is_arith(dst)) {
cg_convert(p->cg, dst);
+ } else if (type_is_arith(src) && type_is_ptr(dst)) {
+ cg_convert(p->cg, dst);
}
}
@@ -1050,6 +1057,49 @@ static void parse_primary(Parser* p) {
perr(p, "expected expression");
}
+static int find_record_member_path(Parser* p, const Type* rec_ty, Sym mname,
+ const Type** out_ty, u32 path[2],
+ u32* out_depth) {
+ const ABIRecordLayout* L;
+ if (!rec_ty || (rec_ty->kind != TY_STRUCT && rec_ty->kind != TY_UNION))
+ return 0;
+ L = c_abi_record_layout(p->abi, p->pool, rec_ty);
+ if (!L) return 0;
+ for (u16 i = 0; i < rec_ty->rec.nfields; ++i) {
+ const Field* f = &rec_ty->rec.fields[i];
+ if (f->name == mname && mname != 0) {
+ *out_ty = f->type;
+ path[0] = i;
+ *out_depth = 1;
+ return 1;
+ }
+ if ((f->flags & FIELD_ANON) &&
+ (f->type->kind == TY_STRUCT || f->type->kind == TY_UNION)) {
+ const ABIRecordLayout* IL = c_abi_record_layout(p->abi, p->pool, f->type);
+ if (!IL) continue;
+ for (u16 j = 0; j < f->type->rec.nfields; ++j) {
+ const Field* ff = &f->type->rec.fields[j];
+ if (ff->name == mname && mname != 0) {
+ *out_ty = ff->type;
+ path[0] = i;
+ path[1] = j;
+ *out_depth = 2;
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static void cg_record_member_path(Parser* p, const Type* member_ty,
+ const u32* path, u32 depth) {
+ for (u32 i = 0; i < depth; ++i) {
+ cfree_cg_field(p->cg, path[i]);
+ }
+ pcg_retag_top(p, member_ty);
+}
+
static void parse_postfix(Parser* p) {
parse_primary(p);
for (;;) {
@@ -1103,8 +1153,7 @@ static void parse_postfix(Parser* p) {
const Type* lt0 = cg_top_type(p->cg);
advance(p); /* '[' */
if (lt0 && lt0->kind == TY_ARRAY) {
- cg_addr(p->cg);
- cg_retag_top(p->cg, type_ptr(p->pool, lt0->arr.elem));
+ decay_array_to_pointer(p, lt0);
} else if (lt0 && lt0->kind == TY_PTR) {
cg_load(p->cg);
}
@@ -1112,8 +1161,7 @@ static void parse_postfix(Parser* p) {
{
const Type* it0 = cg_top_type(p->cg);
if (it0 && it0->kind == TY_ARRAY) {
- cg_addr(p->cg);
- cg_retag_top(p->cg, type_ptr(p->pool, it0->arr.elem));
+ decay_array_to_pointer(p, it0);
} else {
to_rvalue(p);
}
@@ -1146,8 +1194,8 @@ static void parse_postfix(Parser* p) {
const Type* lt = cg_top_type(p->cg);
Sym mname;
const Type* mty = NULL;
- u32 moff = 0;
- const Field* mf = NULL;
+ u32 path[2];
+ u32 depth = 0;
advance(p); /* '.' */
if (!lt || (lt->kind != TY_STRUCT && lt->kind != TY_UNION)) {
perr(p,
@@ -1158,57 +1206,9 @@ static void parse_postfix(Parser* p) {
}
mname = p->cur.v.ident;
advance(p);
- {
- const ABIRecordLayout* L = c_abi_record_layout(p->abi, p->pool, lt);
- if (!L) perr(p, "no such member");
- int found = 0;
- for (u16 i = 0; i < lt->rec.nfields; ++i) {
- const Field* f = <->rec.fields[i];
- if (f->name == mname && mname != 0) {
- mty = f->type;
- moff = L->fields[i].offset;
- mf = f;
- found = 1;
- break;
- }
- /* anonymous member flattening */
- if ((f->flags & FIELD_ANON) &&
- (f->type->kind == TY_STRUCT || f->type->kind == TY_UNION)) {
- const Type* inner_ty = NULL;
- u32 inner_off = 0;
- const Field* inner_f = NULL;
- const ABIRecordLayout* IL =
- c_abi_record_layout(p->abi, p->pool, f->type);
- if (IL) {
- for (u16 j = 0; j < f->type->rec.nfields; ++j) {
- const Field* ff = &f->type->rec.fields[j];
- if (ff->name == mname && mname != 0) {
- inner_ty = ff->type;
- inner_off = IL->fields[j].offset;
- inner_f = ff;
- break;
- }
- }
- }
- if (inner_ty) {
- mty = inner_ty;
- moff = L->fields[i].offset + inner_off;
- mf = inner_f;
- found = 1;
- break;
- }
- }
- }
- if (!found) perr(p, "no such member");
- }
- (void)mf;
- cg_addr(p->cg);
- cg_retag_top(p->cg, type_ptr(p->pool, mty));
- if (moff > 0) {
- cg_push_int(p->cg, (i64)moff, ty_size_t(p));
- cg_binop(p->cg, BO_IADD);
- }
- cg_deref(p->cg, mty);
+ if (!find_record_member_path(p, lt, mname, &mty, path, &depth))
+ perr(p, "no such member");
+ cg_record_member_path(p, mty, path, depth);
continue;
}
if (is_punct(&t, P_ARROW)) {
@@ -1216,8 +1216,8 @@ static void parse_postfix(Parser* p) {
const Type* rec_ty;
Sym mname;
const Type* mty = NULL;
- u32 moff = 0;
- const Field* mf = NULL;
+ u32 path[2];
+ u32 depth = 0;
advance(p); /* `->` */
to_rvalue(p);
lt0 = cg_top_type(p->cg);
@@ -1233,46 +1233,10 @@ static void parse_postfix(Parser* p) {
}
mname = p->cur.v.ident;
advance(p);
- {
- const ABIRecordLayout* L = c_abi_record_layout(p->abi, p->pool, rec_ty);
- if (!L) perr(p, "no such member");
- int found = 0;
- for (u16 i = 0; i < rec_ty->rec.nfields; ++i) {
- const Field* f = &rec_ty->rec.fields[i];
- if (f->name == mname && mname != 0) {
- mty = f->type;
- moff = L->fields[i].offset;
- mf = f;
- found = 1;
- break;
- }
- if ((f->flags & FIELD_ANON) &&
- (f->type->kind == TY_STRUCT || f->type->kind == TY_UNION)) {
- const ABIRecordLayout* IL =
- c_abi_record_layout(p->abi, p->pool, f->type);
- if (IL) {
- for (u16 j = 0; j < f->type->rec.nfields; ++j) {
- const Field* ff = &f->type->rec.fields[j];
- if (ff->name == mname && mname != 0) {
- mty = ff->type;
- moff = L->fields[i].offset + IL->fields[j].offset;
- mf = ff;
- found = 1;
- break;
- }
- }
- }
- if (found) break;
- }
- }
- if (!found) perr(p, "no such member");
- }
- (void)mf;
- if (moff > 0) {
- cg_push_int(p->cg, (i64)moff, ty_size_t(p));
- cg_binop(p->cg, BO_IADD);
- }
- cg_deref(p->cg, mty);
+ if (!find_record_member_path(p, rec_ty, mname, &mty, path, &depth))
+ perr(p, "no such member");
+ cg_deref(p->cg, rec_ty);
+ cg_record_member_path(p, mty, path, depth);
continue;
}
break;
diff --git a/lang/c/parse/parse_init.c b/lang/c/parse/parse_init.c
@@ -16,10 +16,6 @@
* File-local helpers
* ============================================================ */
-static const Type* ty_size_t_init(Parser* p) {
- return c_abi_size_type(p->abi, p->pool);
-}
-
static SrcLoc tok_loc_init(const Tok* t) { return t->loc; }
static CKw ident_kw_init(const Parser* p, Sym name) {
@@ -55,13 +51,10 @@ static u32 init_elided(Parser* p, FrameSlot slot, const Type* arr_ty,
* 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) {
+ const Type* elem_ptr_ty = type_ptr(p->pool, elem_ty);
cg_push_local_typed(p->cg, slot, arr_ty);
- cg_addr(p->cg);
- cg_retag_top(p->cg, type_ptr(p->pool, elem_ty));
- if (offset > 0) {
- cg_push_int(p->cg, (i64)offset, ty_size_t_init(p));
- cg_binop(p->cg, BO_IADD);
- }
+ cfree_cg_addr_offset(p->cg, (i64)offset, pcg_tid(p->c, elem_ptr_ty));
+ pcg_retag_top(p, elem_ptr_ty);
cg_deref(p->cg, elem_ty);
}
@@ -70,14 +63,12 @@ static void emit_copy_leaf(Parser* p, FrameSlot dst_slot,
const Type* dst_arr_ty, u32 dst_off,
FrameSlot src_ptr_slot, const Type* src_ptr_ty,
u32 src_off, const Type* leaf_ty) {
+ const Type* leaf_ptr_ty = type_ptr(p->pool, leaf_ty);
push_subobject_lv(p, dst_slot, dst_arr_ty, dst_off, leaf_ty);
cg_push_local_typed(p->cg, src_ptr_slot, src_ptr_ty);
cg_load(p->cg);
- cg_retag_top(p->cg, type_ptr(p->pool, leaf_ty));
- if (src_off > 0) {
- cg_push_int(p->cg, (i64)src_off, ty_size_t_init(p));
- cg_binop(p->cg, BO_IADD);
- }
+ cfree_cg_addr_offset(p->cg, (i64)src_off, pcg_tid(p->c, leaf_ptr_ty));
+ pcg_retag_top(p, leaf_ptr_ty);
cg_deref(p->cg, leaf_ty);
cg_load(p->cg);
cg_store(p->cg);
@@ -352,6 +343,7 @@ static u32 init_elided(Parser* p, FrameSlot slot, const Type* arr_ty,
push_subobject_lv(p, slot, arr_ty, offset, ty);
parse_assign_expr(p);
to_rvalue(p);
+ coerce_top_to_lvalue(p);
cg_store(p->cg);
cg_drop(p->cg);
if (had_brace) {
diff --git a/lang/c/parse/parse_type.c b/lang/c/parse/parse_type.c
@@ -1032,6 +1032,7 @@ int parse_decl_suffix(Parser* p, DeclSuffix* out) {
to_rvalue(p);
cg_push_local_typed(p->cg, out->vla_count_slot, fsd.type);
cg_swap(p->cg);
+ coerce_top_to_lvalue(p);
cg_store(p->cg);
cg_drop(p->cg);
p->vla_pending = 1;
diff --git a/src/api/cg.c b/src/api/cg.c
@@ -3421,41 +3421,51 @@ void cfree_cg_addr_offset(CfreeCg *g, int64_t byte_offset,
Operand base;
Operand result;
Reg rr;
+ int want_ptr;
+ int base_is_lvalue;
+ int free_base = 0;
if (!g)
return;
rty = resolve_type(g->c, result_type);
if (!rty)
return;
v = api_pop(g);
+ want_ptr = cg_type_is_ptr(g->c, rty);
+ base_is_lvalue = api_is_lvalue_sv(&v);
if (v.source_local != CFREE_CG_LOCAL_NONE)
api_local_const_clear(api_local_from_handle(g, v.source_local));
api_ensure_reg(g, &v);
if (v.op.kind == OPK_GLOBAL) {
result = api_op_global(v.op.v.global.sym,
v.op.v.global.addend + byte_offset, rty);
- api_push(g, cg_type_is_ptr(g->c, rty) ? api_make_sv(result, rty)
- : api_make_lv(result, rty));
+ api_push(g, want_ptr ? api_make_sv(result, rty) : api_make_lv(result, rty));
return;
}
- if (v.op.kind == OPK_INDIRECT) {
+ if (!want_ptr && v.op.kind == OPK_INDIRECT) {
i64 ofs = (i64)v.op.v.ind.ofs + byte_offset;
if (ofs >= INT32_MIN && ofs <= INT32_MAX) {
result = api_op_indirect(v.op.v.ind.base, (i32)ofs, rty);
- api_push(g, cg_type_is_ptr(g->c, rty) ? api_make_sv(result, rty)
- : api_make_lv(result, rty));
+ api_push(g, api_make_lv(result, rty));
return;
}
}
- ptr_ty = cg_type_is_ptr(g->c, api_sv_type(&v)) ? api_sv_type(&v)
- : cg_type_ptr_to(g->c, rty);
- base = api_is_lvalue_sv(&v) ? api_force_reg(g, &v, ptr_ty)
- : api_force_reg(g, &v, ptr_ty);
+ ptr_ty = want_ptr ? rty : cg_type_ptr_to(g->c, rty);
+ if (!base_is_lvalue && cg_type_is_ptr(g->c, api_sv_type(&v)))
+ ptr_ty = api_sv_type(&v);
+ if (base_is_lvalue) {
+ base = api_lvalue_addr(g, &v, ptr_ty);
+ free_base = 1;
+ } else {
+ base = api_force_reg(g, &v, ptr_ty);
+ }
rr = api_alloc_reg_or_spill(g, RC_INT, ptr_ty);
result = api_op_reg(rr, ptr_ty);
g->target->binop(g->target, BO_IADD, result, base,
api_op_imm(byte_offset, ptr_ty));
+ if (free_base)
+ api_free_reg(g, base.v.reg, RC_INT);
api_release(g, &v);
- if (cg_type_is_ptr(g->c, rty)) {
+ if (want_ptr) {
result.type = rty;
api_push(g, api_make_sv(result, rty));
} else {