kit

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

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:
Msrc/cg/control.c | 10++++++----
Msrc/cg/data.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/cg/ir.c | 25+++++++++++++++++++++++++
Msrc/cg/ir.h | 11+++++++++++
Msrc/cg/ir_recorder.c | 12+++++++-----
Msrc/cg/ir_recorder.h | 1+
Msrc/cg/memory.c | 4+---
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",