commit ebb03c558f3b1dd575aa9eaa4bbcd9b77f73d41d
parent ef0ffdfc2b24e392e8a4d6e9dac6f6f74ca0f1b1
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 26 May 2026 15:41:22 -0700
cg: add IR alias support, semantic label tables, switch hint, store relax
- Add CgIrAlias to CgIrModule and wire the IR recorder to record aliases
through the CGTarget.alias hook.
- Add data_label_addr_unsupported_msg callback to CgIrRecorderConfig so
targets can customize the diagnostic when label addresses aren't supported.
- Implement api_emit_label_table using the semantic local_static_data
begin/write/label_addr/end surface, producing a read-only pointer array
of label addresses.
- Allow the CFREE_CG_SWITCH_JUMP_TABLE hint to bypass the O0 single-pass
switch lowering, so tests and frontends can exercise the semantic
label-table path without enabling O1.
- Relax the store aggregate type/size assertion to only check access-size
consistency, removing over-strict lvalue/rvalue checks.
Diffstat:
7 files changed, 105 insertions(+), 16 deletions(-)
diff --git a/src/cg/control.c b/src/cg/control.c
@@ -320,10 +320,12 @@ void cfree_cg_switch(CfreeCg* g, CfreeCgSwitch sw) {
desc.cases = cases;
}
- /* The C-source target overrides switch_ to emit a native `switch`
- * statement and forces opt_level=0; route everything to it
- * unchanged. Native + opt targets both flow through the planner. */
- native_switch_override = (g->target->switch_ && g->opt_level == 0);
+ /* Direct O0 targets may override switch_ for a single-pass branch-chain
+ * lowering. Still honor an explicit jump-table hint so tests and frontends
+ * can exercise the semantic label-table path without enabling O1. */
+ native_switch_override =
+ (g->target->switch_ && g->opt_level == 0 &&
+ desc.hint != CFREE_CG_SWITCH_JUMP_TABLE);
plan = native_switch_override ? (CGSwitchPlan){CG_SWITCH_PLAN_CHAIN, 0, 0}
: cg_plan_switch(g, &desc);
diff --git a/src/cg/data.c b/src/cg/data.c
@@ -606,12 +606,62 @@ void cfree_cg_data_end(CfreeCg* g) {
* The old machine-code jump-table path is intentionally not part of the
* semantic CgTarget cutover. */
ObjSymId api_emit_label_table(CfreeCg* g, const Label* labels, u32 n) {
- (void)labels;
- (void)n;
- if (g) {
+ Compiler* c;
+ ObjSymId sym;
+ CfreeCgTypeId void_ptr_ty;
+ CfreeCgTypeId arr_ty;
+ CGLocalStaticDataDesc desc;
+ CfreeCgDataDefAttrs attrs;
+ CfreeCgDecl decl;
+ char name_buf[32];
+ StrBuf name_sb;
+ Sym name;
+ if (!g || !labels || !n) return OBJ_SYM_NONE;
+ if (!g->target || !g->target->local_static_data_begin ||
+ !g->target->local_static_data_write ||
+ !g->target->local_static_data_label_addr ||
+ !g->target->local_static_data_end) {
compiler_panic(g->c, g->cur_loc,
"api_emit_label_table: target does not support semantic "
"label tables");
}
- return OBJ_SYM_NONE;
+ c = g->c;
+ strbuf_init(&name_sb, name_buf, sizeof name_buf);
+ strbuf_put_slice(&name_sb, SLICE_LIT(".Lcfree_jt."));
+ strbuf_put_u64(&name_sb, g->rodata_counter++);
+ name = pool_intern_slice(
+ c->global,
+ (Slice){.s = strbuf_cstr(&name_sb), .len = strbuf_len(&name_sb)});
+ sym = obj_symbol(g->obj, name, SB_LOCAL, SK_OBJ, OBJ_SEC_NONE, 0,
+ (u64)n * (u64)c->target.ptr_size);
+ if (sym == OBJ_SYM_NONE)
+ compiler_panic(c, g->cur_loc, "api_emit_label_table: symbol failed");
+
+ void_ptr_ty = cg_type_ptr_to(c, builtin_id(CFREE_CG_BUILTIN_VOID));
+ arr_ty = cfree_cg_type_array((CfreeCompiler*)c, void_ptr_ty, n);
+ memset(&decl, 0, sizeof decl);
+ decl.kind = CFREE_CG_DECL_OBJECT;
+ decl.sym.bind = CFREE_SB_LOCAL;
+ decl.sym.visibility = CFREE_CG_VIS_DEFAULT;
+ decl.as.object.flags = CFREE_CG_OBJ_READONLY;
+ api_remember_sym(g, sym, arr_ty, decl);
+
+ memset(&attrs, 0, sizeof attrs);
+ attrs.flags = CFREE_CG_DATADEF_FUNCTION_LOCAL | CFREE_CG_DATADEF_READONLY;
+ attrs.align = (u32)c->target.ptr_align;
+ memset(&desc, 0, sizeof desc);
+ desc.sym = sym;
+ desc.type = arr_ty;
+ desc.attrs = attrs;
+ desc.align = attrs.align;
+ if (!g->target->local_static_data_begin(g->target, &desc)) {
+ compiler_panic(c, g->cur_loc,
+ "api_emit_label_table: target rejected label table");
+ }
+ for (u32 i = 0; i < n; ++i) {
+ g->target->local_static_data_label_addr(g->target, labels[i], 0,
+ (u32)c->target.ptr_size, 0);
+ }
+ g->target->local_static_data_end(g->target);
+ return sym;
}
diff --git a/src/cg/ir.c b/src/cg/ir.c
@@ -33,6 +33,19 @@ static void module_grow(CgIrModule* m, u32 want) {
m->funcs_cap = cap;
}
+static void module_alias_grow(CgIrModule* m, u32 want) {
+ CgIrAlias* next;
+ u32 cap;
+ if (m->aliases_cap >= want) return;
+ cap = m->aliases_cap ? m->aliases_cap : 8u;
+ while (cap < want) cap *= 2u;
+ next = ir_zalloc_or_panic(m->c, m->arena, sizeof(*next) * cap,
+ _Alignof(CgIrAlias));
+ if (m->aliases) memcpy(next, m->aliases, sizeof(*next) * m->naliases);
+ m->aliases = next;
+ m->aliases_cap = cap;
+}
+
CgIrModule* cg_ir_module_new(Compiler* c) {
CgIrModule* m = arena_znew(c->tu, CgIrModule);
if (!m) return NULL;
@@ -47,6 +60,18 @@ void cg_ir_module_add_func(CgIrModule* m, CgIrFunc* f) {
m->funcs[m->nfuncs++] = f;
}
+void cg_ir_module_add_alias(CgIrModule* m, ObjSymId alias_sym,
+ ObjSymId target_sym, CfreeCgTypeId type) {
+ CgIrAlias* a;
+ if (!m || alias_sym == OBJ_SYM_NONE || target_sym == OBJ_SYM_NONE) return;
+ module_alias_grow(m, m->naliases + 1u);
+ a = &m->aliases[m->naliases++];
+ memset(a, 0, sizeof *a);
+ a->alias_sym = alias_sym;
+ a->target_sym = target_sym;
+ a->type = type;
+}
+
static CGFuncDesc dup_func_desc(Arena* a, const CGFuncDesc* in) {
CGFuncDesc out = *in;
if (in->nresults) {
diff --git a/src/cg/ir.h b/src/cg/ir.h
@@ -221,17 +221,28 @@ typedef struct CgIrFunc {
u8 pad[3];
} CgIrFunc;
+typedef struct CgIrAlias {
+ ObjSymId alias_sym;
+ ObjSymId target_sym;
+ CfreeCgTypeId type;
+} CgIrAlias;
+
typedef struct CgIrModule {
Arena* arena;
Compiler* c;
CgIrFunc** funcs;
u32 nfuncs;
u32 funcs_cap;
+ CgIrAlias* aliases;
+ u32 naliases;
+ u32 aliases_cap;
} CgIrModule;
CgIrModule* cg_ir_module_new(Compiler*);
CgIrFunc* cg_ir_func_new(Compiler*, const CGFuncDesc*);
void cg_ir_module_add_func(CgIrModule*, CgIrFunc*);
+void cg_ir_module_add_alias(CgIrModule*, ObjSymId alias_sym,
+ ObjSymId target_sym, CfreeCgTypeId type);
CGLocal cg_ir_func_add_local(CgIrFunc*, const CGLocalDesc*, int is_param,
u32 param_index);
diff --git a/src/cg/ir_recorder.c b/src/cg/ir_recorder.c
@@ -12,6 +12,7 @@ struct CgIrRecorder {
void (*finalize_recorded)(void*, const CgIrModule*);
void (*destroy_user)(void*);
int (*local_static_data_begin)(void*, const CGLocalStaticDataDesc*);
+ const char* (*data_label_addr_unsupported_msg)(void*);
const char* (*tail_call_unrealizable_reason)(void*, const CGFuncDesc*,
const CGCallDesc*);
void* user;
@@ -66,10 +67,8 @@ static void rec_func_end(CgTarget* t) {
static void rec_alias(CgTarget* t, ObjSymId alias_sym, ObjSymId target_sym,
CfreeCgTypeId type) {
- (void)t;
- (void)alias_sym;
- (void)target_sym;
- (void)type;
+ CgIrRecorder* r = rec_of(t);
+ cg_ir_module_add_alias(r->module, alias_sym, target_sym, type);
}
static CGLocal rec_local(CgTarget* t, const CGLocalDesc* desc) {
@@ -218,7 +217,9 @@ static void rec_local_static_data_end(CgTarget* t) {
}
static const char* rec_data_label_addr_unsupported_msg(CgTarget* t) {
- (void)t;
+ CgIrRecorder* r = rec_of(t);
+ if (r->data_label_addr_unsupported_msg)
+ return r->data_label_addr_unsupported_msg(r->user);
return "IR recorder supports function-local label address data";
}
@@ -565,6 +566,7 @@ CgTarget* cg_ir_recorder_new(Compiler* c, ObjBuilder* obj,
r->finalize_recorded = cfg->finalize;
r->destroy_user = cfg->destroy;
r->local_static_data_begin = cfg->local_static_data_begin;
+ r->data_label_addr_unsupported_msg = cfg->data_label_addr_unsupported_msg;
r->tail_call_unrealizable_reason = cfg->tail_call_unrealizable_reason;
r->user = cfg->user;
}
diff --git a/src/cg/ir_recorder.h b/src/cg/ir_recorder.h
@@ -10,6 +10,7 @@ typedef struct CgIrRecorderConfig {
void (*finalize)(void* user, const CgIrModule* module);
void (*destroy)(void* user);
int (*local_static_data_begin)(void* user, const CGLocalStaticDataDesc* desc);
+ const char* (*data_label_addr_unsupported_msg)(void* user);
const char* (*tail_call_unrealizable_reason)(void* user,
const CGFuncDesc* caller,
const CGCallDesc* call);
diff --git a/src/cg/memory.c b/src/cg/memory.c
@@ -689,9 +689,7 @@ void cfree_cg_store(CfreeCg* g, CfreeCgMemAccess access, CfreeCgEffAddr ea) {
compiler_panic(g->c, g->cur_loc,
"CfreeCg: aggregate store source is not an lvalue");
}
- if ((is_lvalue && !cg_type_is_aggregate(g->c, api_sv_type(&base))) ||
- (!src_ptr_rvalue && !cg_type_is_aggregate(g->c, api_sv_type(&rv))) ||
- access_size != dst_size || access_size != src_size) {
+ if (access_size != dst_size || access_size != src_size) {
compiler_panic(g->c, g->cur_loc,
"CfreeCg: store aggregate type/size mismatch: access "
"size %u, destination size %u, value size %u",