kit

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

commit 39ee5e11fb5963cf7b89f2f43f7abf2c163899c6
parent d0785fcb8283dadb957a8afb00dd246b4f59e461
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 25 May 2026 04:00:40 -0700

Add ABI registry gating

Diffstat:
MMakefile | 42+++++++++++++++++++++++++++++++++++++++---
Mdoc/REGISTRY.md | 40+++++++++++++++++++++-------------------
Msrc/abi/abi.c | 17+++++++----------
Msrc/abi/abi_internal.h | 2++
Asrc/abi/registry.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/arch/aa64/arch.c | 14--------------
Msrc/arch/arch.h | 2--
Msrc/arch/registry.c | 8++++----
Msrc/arch/rv64/arch.c | 8--------
Msrc/arch/x64/arch.c | 14--------------
10 files changed, 118 insertions(+), 74 deletions(-)

diff --git a/Makefile b/Makefile @@ -27,8 +27,8 @@ DRIVER_CFLAGS = $(CFLAGS_COMMON) -Iinclude -Ilang include mk/config.mk -# Non-arch lib sources: everything under src/ except per-arch backend -# directories. Shared arch infrastructure (src/arch/*.c) stays unconditional. +# Non-arch lib sources: everything under src/ except gated component +# directories/files. Shared arch/obj/abi infrastructure stays unconditional. LIB_SRCS_NONARCH = $(shell find src -name '*.c' \ -not -path 'src/arch/aa64/*' \ -not -path 'src/arch/x64/*' \ @@ -36,7 +36,8 @@ LIB_SRCS_NONARCH = $(shell find src -name '*.c' \ -not -path 'src/arch/c_target/*' \ -not -path 'src/obj/elf/*' \ -not -path 'src/obj/macho/*' \ - -not -path 'src/obj/coff/*') + -not -path 'src/obj/coff/*' \ + -not -path 'src/abi/abi_*.c') LIB_SRCS_ARCH_AA64 = $(shell find src/arch/aa64 -name '*.c' 2>/dev/null) LIB_SRCS_ARCH_X64 = $(shell find src/arch/x64 -name '*.c' 2>/dev/null) @@ -47,6 +48,14 @@ LIB_SRCS_OBJ_ELF = $(shell find src/obj/elf -name '*.c' 2>/dev/null) LIB_SRCS_OBJ_MACHO = $(shell find src/obj/macho -name '*.c' 2>/dev/null) LIB_SRCS_OBJ_COFF = $(shell find src/obj/coff -name '*.c' 2>/dev/null) +LIB_SRC_ABI_AAPCS64 = src/abi/abi_aapcs64.c +LIB_SRC_ABI_APPLE_ARM64 = src/abi/abi_apple_arm64.c +LIB_SRC_ABI_AAPCS64_WINDOWS = src/abi/abi_aapcs64_windows.c +LIB_SRC_ABI_SYSV_X64 = src/abi/abi_sysv_x64.c +LIB_SRC_ABI_APPLE_X64 = src/abi/abi_apple_x64.c +LIB_SRC_ABI_WIN64_X64 = src/abi/abi_win64_x64.c +LIB_SRC_ABI_RV64 = src/abi/abi_rv64.c + LIB_SRCS = $(LIB_SRCS_NONARCH) ifeq ($(CFREE_ARCH_AA64_ENABLED),1) LIB_SRCS += $(LIB_SRCS_ARCH_AA64) @@ -69,6 +78,33 @@ endif ifeq ($(CFREE_OBJ_COFF_ENABLED),1) LIB_SRCS += $(LIB_SRCS_OBJ_COFF) endif +ifeq ($(CFREE_ARCH_AA64_ENABLED),1) +ifneq ($(filter 1,$(CFREE_OBJ_ELF_ENABLED) $(CFREE_OBJ_MACHO_ENABLED) $(CFREE_OBJ_COFF_ENABLED)),) +LIB_SRCS += $(LIB_SRC_ABI_AAPCS64) +endif +ifeq ($(CFREE_OBJ_MACHO_ENABLED),1) +LIB_SRCS += $(LIB_SRC_ABI_APPLE_ARM64) +endif +ifeq ($(CFREE_OBJ_COFF_ENABLED),1) +LIB_SRCS += $(LIB_SRC_ABI_AAPCS64_WINDOWS) +endif +endif +ifeq ($(CFREE_ARCH_X64_ENABLED),1) +ifneq ($(filter 1,$(CFREE_OBJ_ELF_ENABLED) $(CFREE_OBJ_MACHO_ENABLED)),) +LIB_SRCS += $(LIB_SRC_ABI_SYSV_X64) +endif +ifeq ($(CFREE_OBJ_MACHO_ENABLED),1) +LIB_SRCS += $(LIB_SRC_ABI_APPLE_X64) +endif +ifeq ($(CFREE_OBJ_COFF_ENABLED),1) +LIB_SRCS += $(LIB_SRC_ABI_WIN64_X64) +endif +endif +ifeq ($(CFREE_ARCH_RV64_ENABLED),1) +ifeq ($(CFREE_OBJ_ELF_ENABLED),1) +LIB_SRCS += $(LIB_SRC_ABI_RV64) +endif +endif # Per-frontend source sets. Each is gated by its CFREE_LANG_*_ENABLED flag # from mk/config.mk so the matching `#if` in src/api/lang_registry.c and diff --git a/doc/REGISTRY.md b/doc/REGISTRY.md @@ -61,8 +61,8 @@ one arch and at least one obj format are enabled. **Status: vtable, registry, and source gating are done.** - **Vtable**: `ArchImpl` (`src/arch/arch.h`). Carries `dwarf`/`dbg` - hooks, the `link` arch descriptor, the per-OS `abi_vtable` - dispatcher, and the codegen/assembler/disassembler factories. + hooks, the `link` arch descriptor, and the codegen/assembler/ + disassembler factories. ABI selection lives under `src/abi/`. - **Registry**: `src/arch/registry.c` already holds a static `arch_impls[]` array and exposes `arch_lookup`. - **Build**: each `src/arch/<name>/` source group is gated by the @@ -153,21 +153,22 @@ back only when the matching `CFREE_OBJ_*_ENABLED` flag is enabled. ## Axis 3: ABIs (derived) -**Status: vtable and dispatch already exist; only gating is new.** - -- **Vtable**: `ABIVtable`, selected by `arch->abi_vtable(c, os)` - (`src/abi/abi.c:177`). -- **Selection**: each arch's dispatch function maps `CfreeOSKind` to one - of its supported `ABIVtable`s. Today every (arch × OS) cell that - cfree supports is wired in unconditionally. -- **Change**: gate per-OS entries inside the dispatch by the - corresponding format flag — `#if CFREE_OBJ_COFF` drops the - `win64_x64` / `aapcs64_windows` entries; `#if CFREE_OBJ_MACHO` drops - `apple_arm64` / `apple_x64`; etc. The format flag is used as a proxy - for "the OS family that uses this format." -- **Build**: per-ABI `.c` files in `src/abi/` opt in via the same - combined flags (e.g. `abi_win64_x64.c` only when - `CFREE_ARCH_X64 && CFREE_OBJ_COFF`). +**Status: vtable, registry, and source gating are done.** + +- **Vtable**: `ABIVtable` (`src/abi/abi_internal.h`). It carries the + function classifier and ABI-owned `va_list` layout facts. +- **Registry**: `src/abi/registry.c` maps `(CfreeArchKind, + CfreeObjFmt)` to the active `ABIVtable`. `abi_init()` now performs a + single `abi_vtable_lookup(target.arch, target.obj)` instead of asking + the arch implementation to dispatch by OS. +- **Gating**: registry entries are gated by the combined arch + object + format flags. ELF selects SysV/AAPCS-style ABIs, Mach-O selects Apple + variants, and COFF selects Windows variants. +- **Build**: per-ABI `.c` files in `src/abi/` are excluded from the + broad lib source glob and re-added only for enabled arch/format cells. + A few ABI variants share a base classifier implementation, so the + Makefile also keeps those base classifier TUs when a derived variant + delegates to them. | Arch | ELF (SysV-ish) | Mach-O (Apple) | PE/COFF (Windows) | |----------|-------------------|-----------------|----------------------| @@ -214,7 +215,7 @@ back only when the matching `CFREE_OBJ_*_ENABLED` flag is enabled. |------------|-----------------------|---------------------------|-------------------------------------------------| | Arch | `ArchImpl` | `src/arch/registry.c` | none for registry/source gating | | Obj format | `ObjFormatImpl` | `src/obj/registry.c` | move small policy checks into vtable as needed | -| ABI | `ABIVtable` | per-arch `abi_dispatch` | gate per-OS dispatch entries by obj-format flag | +| ABI | `ABIVtable` | `src/abi/registry.c` | none for registry/source gating | | Frontend | `CfreeFrontendVTable` | `src/api/lang_registry.c` | none for registry/source gating | ## Implementation order @@ -224,7 +225,8 @@ back only when the matching `CFREE_OBJ_*_ENABLED` flag is enabled. 2. Done: gate arch registry entries and arch Makefile sources. 3. Done: extract `ObjFormatImpl`, move format code under `src/obj/{elf,macho,coff}/`, and gate those directories. -4. Remaining: gate per-ABI sources and per-OS dispatch entries. +4. Done: add `src/abi/registry.c`, move ABI selection out of `ArchImpl`, + and gate per-ABI sources. 5. Done: add `src/api/lang_registry.c`, expose `cfree_<lang>_frontend_vtable` externs, and fold frontends into `libcfree.a`. diff --git a/src/abi/abi.c b/src/abi/abi.c @@ -5,16 +5,15 @@ * before calling into this layer. * * Per-ABI bits (function classification, __va_list shape) live in - * abi_aapcs64.c, abi_sysv_x64.c, ... The active arch descriptor selects the - * ABI vtable for (target.arch, target.os). The C-standard-driven scalar - * profile and record layout stay here so all ABIs share one impl. */ + * abi_aapcs64.c, abi_sysv_x64.c, ... The ABI registry selects the vtable + * for (target.arch, target.obj). The C-standard-driven scalar profile and + * record layout stay here so all ABIs share one impl. */ #include "abi/abi.h" #include <string.h> #include "abi/abi_internal.h" -#include "arch/arch.h" #include "cg/type.h" #include "core/arena.h" #include "core/core.h" @@ -174,14 +173,12 @@ ABITypeInfo abi_va_list_info(TargetABI* a) { return a->vt->va_list_info; } /* ---- lifecycle ---- */ static const ABIVtable* select_vtable(Compiler* c) { - const ArchImpl* arch = arch_for_compiler(c); - if (arch && arch->abi_vtable) { - return arch->abi_vtable(c, c->target.os); - } + const ABIVtable* vt = abi_vtable_lookup(c->target.arch, c->target.obj); + if (vt) return vt; { SrcLoc loc = {0, 0, 0}; - compiler_panic(c, loc, "abi_init: unsupported target arch %d", - (int)c->target.arch); + compiler_panic(c, loc, "abi_init: unsupported target arch/obj %d/%d", + (int)c->target.arch, (int)c->target.obj); } } diff --git a/src/abi/abi_internal.h b/src/abi/abi_internal.h @@ -28,6 +28,8 @@ extern const ABIVtable apple_x64_vtable; extern const ABIVtable win64_x64_vtable; extern const ABIVtable aapcs64_windows_vtable; +const ABIVtable* abi_vtable_lookup(CfreeArchKind arch, CfreeObjFmt obj); + /* Shared TargetABI internals. The struct definition is here so each ABI * TU can reach into the per-TU caches via TargetABI*. abi.c owns the * cache plumbing; the per-ABI TUs only allocate ABIFuncInfo / record diff --git a/src/abi/registry.c b/src/abi/registry.c @@ -0,0 +1,45 @@ +#include <cfree/config.h> + +#include "abi/abi_internal.h" + +/* Derived ABI registry. ABIs are not user-configurable; an entry exists only + * when both the machine arch and the object/OS-family format are enabled. */ +typedef struct ABIImpl { + CfreeArchKind arch; + CfreeObjFmt obj; + const ABIVtable* vt; +} ABIImpl; + +static const ABIImpl abi_impls[] = { +#if CFREE_ARCH_AA64_ENABLED && CFREE_OBJ_ELF_ENABLED + {CFREE_ARCH_ARM_64, CFREE_OBJ_ELF, &aapcs64_vtable}, +#endif +#if CFREE_ARCH_AA64_ENABLED && CFREE_OBJ_MACHO_ENABLED + {CFREE_ARCH_ARM_64, CFREE_OBJ_MACHO, &apple_arm64_vtable}, +#endif +#if CFREE_ARCH_AA64_ENABLED && CFREE_OBJ_COFF_ENABLED + {CFREE_ARCH_ARM_64, CFREE_OBJ_COFF, &aapcs64_windows_vtable}, +#endif +#if CFREE_ARCH_X64_ENABLED && CFREE_OBJ_ELF_ENABLED + {CFREE_ARCH_X86_64, CFREE_OBJ_ELF, &sysv_x64_vtable}, +#endif +#if CFREE_ARCH_X64_ENABLED && CFREE_OBJ_MACHO_ENABLED + {CFREE_ARCH_X86_64, CFREE_OBJ_MACHO, &apple_x64_vtable}, +#endif +#if CFREE_ARCH_X64_ENABLED && CFREE_OBJ_COFF_ENABLED + {CFREE_ARCH_X86_64, CFREE_OBJ_COFF, &win64_x64_vtable}, +#endif +#if CFREE_ARCH_RV64_ENABLED && CFREE_OBJ_ELF_ENABLED + {CFREE_ARCH_RV64, CFREE_OBJ_ELF, &rv64_vtable}, +#endif + {CFREE_ARCH_WASM, CFREE_OBJ_WASM, NULL}, +}; + +const ABIVtable* abi_vtable_lookup(CfreeArchKind arch, CfreeObjFmt obj) { + u32 i; + for (i = 0; i < (u32)(sizeof abi_impls / sizeof abi_impls[0]); ++i) { + if (abi_impls[i].arch == arch && abi_impls[i].obj == obj) + return abi_impls[i].vt; + } + return NULL; +} diff --git a/src/arch/aa64/arch.c b/src/arch/aa64/arch.c @@ -1,6 +1,5 @@ #include "arch/arch.h" -#include "abi/abi_internal.h" #include "arch/aa64/aa64.h" #include "arch/aa64/asm.h" #include "arch/aa64/disasm.h" @@ -13,18 +12,6 @@ extern const LinkArchDesc link_arch_aa64; extern const ArchDbgOps aa64_dbg_ops; -static const ABIVtable* aa64_abi_vtable(Compiler* c, CfreeOSKind os) { - (void)c; - switch (os) { - case CFREE_OS_MACOS: - return &apple_arm64_vtable; - case CFREE_OS_WINDOWS: - return &aapcs64_windows_vtable; - default: - return &aapcs64_vtable; - } -} - static int aa64_register_at_public(uint32_t idx, CfreeArchReg* out) { const char* nm = NULL; int rc; @@ -153,7 +140,6 @@ const ArchImpl arch_impl_aa64 = { .backend = {.name = "aa64", .make = aa64_backend_make}, .kind = CFREE_ARCH_ARM_64, .name = "aa64", - .abi_vtable = aa64_abi_vtable, .cgtarget_new = aa64_cgtarget_new, .asm_new = aa64_arch_asm_new, .disasm_new = aa64_disasm_new, diff --git a/src/arch/arch.h b/src/arch/arch.h @@ -1045,7 +1045,6 @@ struct ArchDisasm { }; typedef struct LinkArchDesc LinkArchDesc; -typedef struct ABIVtable ABIVtable; typedef struct ArchDwarfOps { /* DWARF .debug_line minimum instruction length and maximum operations per @@ -1101,7 +1100,6 @@ typedef struct ArchImpl { CfreeArchKind kind; const char* name; - const ABIVtable* (*abi_vtable)(Compiler*, CfreeOSKind os); /* Low-level CGTarget constructor: caller supplies the MCEmitter. Tests use * this directly via the cgtarget_new() wrapper; the arch's `backend.make` * also calls it after creating an MCEmitter internally. */ diff --git a/src/arch/registry.c b/src/arch/registry.c @@ -3,8 +3,8 @@ * This file is the *only* place in the codebase that checks * CFREE_ARCH_*_ENABLED. Everything downstream operates on the registry's * outputs — `const CGBackend*` for session-level code emission, and - * `const ArchImpl*` for arch-specific metadata (ABI selection, DWARF, - * debugger hooks, register file, etc.). + * `const ArchImpl*` for arch-specific metadata (DWARF, debugger hooks, + * register file, etc.). * * Conceptually: * - A CGBackend is "something that can build a CGTarget from a Compiler + @@ -32,8 +32,8 @@ extern const CGBackend cg_backend_c_target; #endif /* Arch-metadata roster. The arch_lookup_* helpers iterate this list when a - * caller needs ABI/debug/etc. metadata — answers only come from backends - * that have an ArchImpl, so c_target is intentionally absent. + * caller needs machine-arch metadata — answers only come from backends that + * have an ArchImpl, so c_target is intentionally absent. * cg_backend_for_session() picks the CGBackend (which is &impl->backend or * &cg_backend_c_target) without consulting this list. */ static const ArchImpl* const arch_impls[] = { diff --git a/src/arch/rv64/arch.c b/src/arch/rv64/arch.c @@ -1,6 +1,5 @@ #include "arch/arch.h" -#include "abi/abi_internal.h" #include "arch/rv64/asm.h" #include "arch/rv64/disasm.h" #include "arch/rv64/regs.h" @@ -12,12 +11,6 @@ extern const LinkArchDesc link_arch_rv64; extern const ArchDbgOps rv64_dbg_ops; -static const ABIVtable* rv64_abi_vtable(Compiler* c, CfreeOSKind os) { - (void)c; - (void)os; - return &rv64_vtable; -} - static const ArchDwarfOps rv64_dwarf_ops = { .min_inst_len = 4u, .max_ops_per_inst = 1u, @@ -147,7 +140,6 @@ const ArchImpl arch_impl_rv64 = { .backend = {.name = "rv64", .make = rv64_backend_make}, .kind = CFREE_ARCH_RV64, .name = "rv64", - .abi_vtable = rv64_abi_vtable, .cgtarget_new = rv64_cgtarget_new, .asm_new = rv64_arch_asm_new, .disasm_new = rv64_disasm_new, diff --git a/src/arch/x64/arch.c b/src/arch/x64/arch.c @@ -1,6 +1,5 @@ #include "arch/arch.h" -#include "abi/abi_internal.h" #include "arch/x64/asm.h" #include "arch/x64/disasm.h" #include "arch/x64/regs.h" @@ -12,18 +11,6 @@ extern const LinkArchDesc link_arch_x64; extern const ArchDbgOps x64_dbg_ops; -static const ABIVtable* x64_abi_vtable(Compiler* c, CfreeOSKind os) { - (void)c; - switch (os) { - case CFREE_OS_MACOS: - return &apple_x64_vtable; - case CFREE_OS_WINDOWS: - return &win64_x64_vtable; - default: - return &sysv_x64_vtable; - } -} - static const ArchDwarfOps x64_dwarf_ops = { .min_inst_len = 1u, .max_ops_per_inst = 1u, @@ -78,7 +65,6 @@ const ArchImpl arch_impl_x64 = { .backend = {.name = "x64", .make = x64_backend_make}, .kind = CFREE_ARCH_X86_64, .name = "x64", - .abi_vtable = x64_abi_vtable, .cgtarget_new = x64_cgtarget_new, .asm_new = x64_arch_asm_new, .disasm_new = x64_disasm_new,