commit 0f85365d6934d1507a8da340e71118dc91b8ce4a
parent 619a06df5b377adca4306a10108923d8cb3365ef
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 14 May 2026 12:20:19 -0700
Add internal arch registration descriptor
Diffstat:
10 files changed, 156 insertions(+), 78 deletions(-)
diff --git a/doc/arch-registration-plan.md b/doc/arch-registration-plan.md
@@ -0,0 +1,20 @@
+# Architecture Registration Plan
+
+## Checklist
+
+- [x] Introduce one internal arch descriptor and registry lookup.
+- [x] Route existing arch dispatchers through that descriptor without changing behavior.
+- [ ] Move ABI selection behind the arch descriptor.
+- [ ] Move object-format relocation translators behind the arch descriptor.
+- [ ] Move linker-only arch constants and stub emitters fully behind the descriptor.
+- [ ] Move assembler/disassembler/register helpers behind arch-owned implementation files.
+- [ ] Make `MCEmitter` delegate label fixup encoding to the arch descriptor.
+- [ ] Consolidate files into `src/arch/{aa64,rv64,x64}/` with one exposed implementation object per arch.
+- [ ] Teach the build to honor `CFREE_ARCHS` and compile only selected arch subtrees.
+- [ ] Add targeted subset-build tests for `aa64`, `x64`, `rv64`, and mixed subsets.
+
+## Phase 1
+
+Phase 1 is a refactor-only step. It adds the shared descriptor boundary and
+rewires existing centralized dispatchers to use it while all currently supported
+architectures remain compiled in by default.
diff --git a/src/api/arch_regs.c b/src/api/arch_regs.c
@@ -1,49 +1,33 @@
-/* Public arch register name API.
- *
- * Stateless dispatch onto the per-arch register table. v1 wires aarch64
- * only; other arches return zero / unknown. */
+/* Public arch register name API. */
#include <cfree.h>
-
#include <stddef.h>
-#include "arch/aa64_regs.h"
+#include "arch/arch.h"
const char* cfree_arch_register_name(CfreeArchKind arch, uint32_t dwarf_idx) {
- switch (arch) {
- case CFREE_ARCH_ARM_64:
- return aa64_register_name(dwarf_idx);
- default:
- return NULL;
- }
+ const ArchImpl* impl = arch_lookup(arch);
+ if (!impl || !impl->register_name) return NULL;
+ return impl->register_name(dwarf_idx);
}
int cfree_arch_register_index(CfreeArchKind arch, const char* name,
uint32_t* idx_out) {
- switch (arch) {
- case CFREE_ARCH_ARM_64:
- return aa64_register_index(name, idx_out);
- default:
- return 1;
- }
+ const ArchImpl* impl = arch_lookup(arch);
+ if (!impl || !impl->register_index) return 1;
+ return impl->register_index(name, idx_out);
}
uint32_t cfree_arch_register_count(CfreeArchKind arch) {
- switch (arch) {
- case CFREE_ARCH_ARM_64:
- return aa64_register_iter_size();
- default:
- return 0;
- }
+ const ArchImpl* impl = arch_lookup(arch);
+ if (!impl || !impl->register_count) return 0;
+ return impl->register_count();
}
int cfree_arch_register_at(CfreeArchKind arch, uint32_t idx,
CfreeArchReg* out) {
if (!out) return 1;
- switch (arch) {
- case CFREE_ARCH_ARM_64:
- return aa64_register_iter_get(idx, &out->dwarf_idx, &out->name);
- default:
- return 1;
- }
+ const ArchImpl* impl = arch_lookup(arch);
+ if (!impl || !impl->register_at) return 1;
+ return impl->register_at(idx, out);
}
diff --git a/src/arch/aarch64/arch.c b/src/arch/aarch64/arch.c
@@ -0,0 +1,23 @@
+#include "arch/arch.h"
+
+#include "arch/aa64.h"
+#include "arch/aa64_disasm.h"
+#include "arch/aa64_regs.h"
+#include "link/link_arch.h"
+
+static int aa64_register_at_public(uint32_t idx, CfreeArchReg* out) {
+ if (!out) return 1;
+ return aa64_register_iter_get(idx, &out->dwarf_idx, &out->name);
+}
+
+const ArchImpl arch_impl_aa64 = {
+ .kind = CFREE_ARCH_ARM_64,
+ .name = "aa64",
+ .cgtarget_new = aa64_cgtarget_new,
+ .disasm_new = aa64_disasm_new,
+ .link = &link_arch_aa64,
+ .register_name = aa64_register_name,
+ .register_index = aa64_register_index,
+ .register_count = aa64_register_iter_size,
+ .register_at = aa64_register_at_public,
+};
diff --git a/src/arch/arch.h b/src/arch/arch.h
@@ -700,6 +700,26 @@ struct ArchDisasm {
void (*destroy)(ArchDisasm*);
};
+typedef struct LinkArchDesc LinkArchDesc;
+
+typedef struct ArchImpl {
+ CfreeArchKind kind;
+ const char* name;
+
+ CGTarget* (*cgtarget_new)(Compiler*, ObjBuilder*, MCEmitter*);
+ ArchDisasm* (*disasm_new)(Compiler*);
+
+ const LinkArchDesc* link;
+
+ const char* (*register_name)(uint32_t dwarf_idx);
+ int (*register_index)(const char* name, uint32_t* idx_out);
+ uint32_t (*register_count)(void);
+ int (*register_at)(uint32_t idx, CfreeArchReg* out);
+} ArchImpl;
+
+const ArchImpl* arch_lookup(CfreeArchKind);
+const ArchImpl* arch_for_compiler(const Compiler*);
+
ArchDisasm* arch_disasm_new(Compiler*);
u32 arch_disasm_decode(ArchDisasm*, const u8* bytes, size_t len, u64 vaddr,
CfreeInsn* out);
diff --git a/src/arch/cgtarget.c b/src/arch/cgtarget.c
@@ -1,28 +1,19 @@
-/* Public CGTarget constructor — dispatches by Compiler.target.arch.
+/* Public CGTarget constructor — dispatches through the registered arch impl.
*
- * Per-arch constructors live in their own files (aa64.c, x64.c, rv64.c).
- * The lifecycle helpers (cgtarget_finalize, cgtarget_free) are arch-
- * agnostic shims over the vtable. */
+ * The lifecycle helpers (cgtarget_finalize, cgtarget_free) are arch-agnostic
+ * shims over the vtable. */
-#include "arch/aa64.h"
#include "arch/arch.h"
-#include "arch/rv64.h"
-#include "arch/x64.h"
CGTarget* cgtarget_new(Compiler* c, ObjBuilder* o, MCEmitter* m) {
- switch (c->target.arch) {
- case CFREE_ARCH_ARM_64:
- return aa64_cgtarget_new(c, o, m);
- case CFREE_ARCH_X86_64:
- return x64_cgtarget_new(c, o, m);
- case CFREE_ARCH_RV64:
- return rv64_cgtarget_new(c, o, m);
- default: {
- SrcLoc loc = {0, 0, 0};
- compiler_panic(c, loc,
- "cgtarget_new: unsupported target arch %d",
- (int)c->target.arch);
- }
+ const ArchImpl* arch = arch_for_compiler(c);
+ if (arch && arch->cgtarget_new) {
+ return arch->cgtarget_new(c, o, m);
+ }
+ {
+ SrcLoc loc = {0, 0, 0};
+ compiler_panic(c, loc, "cgtarget_new: unsupported target arch %d",
+ (int)c->target.arch);
}
}
diff --git a/src/arch/disasm.c b/src/arch/disasm.c
@@ -1,23 +1,16 @@
-/* Disassembler dispatcher — peer of src/arch/cgtarget.c.
- *
- * Per-arch ArchDisasm constructors live in their own files. v1 wires
- * aarch64 only; x86_64 / rv64 panic with a clean diagnostic so a build
- * that asks for those targets dies loudly instead of returning NULL
- * silently. arch_disasm_decode / arch_disasm_free are vtable thunks. */
+/* Disassembler dispatcher — peer of src/arch/cgtarget.c. */
-#include "arch/aa64_disasm.h"
#include "arch/arch.h"
ArchDisasm* arch_disasm_new(Compiler* c) {
- switch (c->target.arch) {
- case CFREE_ARCH_ARM_64:
- return aa64_disasm_new(c);
- default: {
- SrcLoc loc = {0, 0, 0};
- compiler_panic(c, loc,
- "arch_disasm_new: unsupported target arch %d",
- (int)c->target.arch);
- }
+ const ArchImpl* arch = arch_for_compiler(c);
+ if (arch && arch->disasm_new) {
+ return arch->disasm_new(c);
+ }
+ {
+ SrcLoc loc = {0, 0, 0};
+ compiler_panic(c, loc, "arch_disasm_new: unsupported target arch %d",
+ (int)c->target.arch);
}
}
diff --git a/src/arch/registry.c b/src/arch/registry.c
@@ -0,0 +1,23 @@
+#include "arch/arch.h"
+
+extern const ArchImpl arch_impl_aa64;
+extern const ArchImpl arch_impl_rv64;
+extern const ArchImpl arch_impl_x64;
+
+const ArchImpl* arch_lookup(CfreeArchKind arch) {
+ switch (arch) {
+ case CFREE_ARCH_ARM_64:
+ return &arch_impl_aa64;
+ case CFREE_ARCH_X86_64:
+ return &arch_impl_x64;
+ case CFREE_ARCH_RV64:
+ return &arch_impl_rv64;
+ default:
+ return NULL;
+ }
+}
+
+const ArchImpl* arch_for_compiler(const Compiler* c) {
+ if (!c) return NULL;
+ return arch_lookup(c->target.arch);
+}
diff --git a/src/arch/rv64/arch.c b/src/arch/rv64/arch.c
@@ -0,0 +1,16 @@
+#include "arch/arch.h"
+
+#include "arch/rv64.h"
+#include "link/link_arch.h"
+
+const ArchImpl arch_impl_rv64 = {
+ .kind = CFREE_ARCH_RV64,
+ .name = "rv64",
+ .cgtarget_new = rv64_cgtarget_new,
+ .disasm_new = NULL,
+ .link = &link_arch_rv64,
+ .register_name = NULL,
+ .register_index = NULL,
+ .register_count = NULL,
+ .register_at = NULL,
+};
diff --git a/src/arch/x64/arch.c b/src/arch/x64/arch.c
@@ -0,0 +1,16 @@
+#include "arch/arch.h"
+
+#include "arch/x64.h"
+#include "link/link_arch.h"
+
+const ArchImpl arch_impl_x64 = {
+ .kind = CFREE_ARCH_X86_64,
+ .name = "x64",
+ .cgtarget_new = x64_cgtarget_new,
+ .disasm_new = NULL,
+ .link = &link_arch_x64,
+ .register_name = NULL,
+ .register_index = NULL,
+ .register_count = NULL,
+ .register_at = NULL,
+};
diff --git a/src/link/link_arch.c b/src/link/link_arch.c
@@ -1,19 +1,11 @@
-/* Per-arch link descriptor dispatcher. See link_arch.h for the
- * struct contract. Concrete descriptors live in link_arch_<arch>.c. */
+/* Per-arch link descriptor dispatcher. */
#include "link/link_arch.h"
+#include "arch/arch.h"
#include "core/core.h"
const LinkArchDesc* link_arch_desc_for(const Compiler* c) {
- switch (c->target.arch) {
- case CFREE_ARCH_ARM_64:
- return &link_arch_aa64;
- case CFREE_ARCH_X86_64:
- return &link_arch_x64;
- case CFREE_ARCH_RV64:
- return &link_arch_rv64;
- default:
- return NULL;
- }
+ const ArchImpl* arch = arch_for_compiler(c);
+ return arch ? arch->link : NULL;
}