commit fe074537ddf0db080abc3cc1080993eaf382cc7d
parent 3cd976f9a92c23fee94ad15a98803084674e8990
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 25 May 2026 20:18:25 -0700
Disable native backends for C target cutover
Diffstat:
7 files changed, 74 insertions(+), 28 deletions(-)
diff --git a/Makefile b/Makefile
@@ -222,7 +222,7 @@ endif
ifeq ($(CFREE_OBJ_COFF_ENABLED),1)
LIB_SRCS += $(LIB_SRCS_OBJ_COFF)
endif
-ifeq ($(CFREE_ARCH_AA64_ENABLED),1)
+ifneq ($(filter 1,$(CFREE_ARCH_AA64_ENABLED) $(CFREE_ARCH_C_TARGET_ENABLED)),)
ifneq ($(filter 1,$(CFREE_OBJ_ELF_ENABLED) $(CFREE_OBJ_MACHO_ENABLED) $(CFREE_OBJ_COFF_ENABLED)),)
LIB_SRCS += $(LIB_SRC_ABI_AAPCS64)
endif
@@ -233,7 +233,7 @@ 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_ARCH_X64_ENABLED) $(CFREE_ARCH_C_TARGET_ENABLED)),)
ifneq ($(filter 1,$(CFREE_OBJ_ELF_ENABLED) $(CFREE_OBJ_MACHO_ENABLED)),)
LIB_SRCS += $(LIB_SRC_ABI_SYSV_X64)
endif
@@ -244,7 +244,7 @@ ifeq ($(CFREE_OBJ_COFF_ENABLED),1)
LIB_SRCS += $(LIB_SRC_ABI_WIN64_X64)
endif
endif
-ifeq ($(CFREE_ARCH_RV64_ENABLED),1)
+ifneq ($(filter 1,$(CFREE_ARCH_RV64_ENABLED) $(CFREE_ARCH_C_TARGET_ENABLED)),)
ifeq ($(CFREE_OBJ_ELF_ENABLED),1)
LIB_SRCS += $(LIB_SRC_ABI_RV64)
endif
diff --git a/include/cfree/config.h b/include/cfree/config.h
@@ -24,10 +24,10 @@
*/
/* Backend architectures. */
-#define CFREE_ARCH_AA64_ENABLED 1
-#define CFREE_ARCH_X64_ENABLED 1
-#define CFREE_ARCH_RV64_ENABLED 1
-#define CFREE_ARCH_WASM_ENABLED 1
+#define CFREE_ARCH_AA64_ENABLED 0
+#define CFREE_ARCH_X64_ENABLED 0
+#define CFREE_ARCH_RV64_ENABLED 0
+#define CFREE_ARCH_WASM_ENABLED 0
#define CFREE_ARCH_C_TARGET_ENABLED 1
/* Object/image formats. Each gates emit + read + link-image paths and
@@ -54,7 +54,7 @@
/* Optimizer pipeline. -O0/direct codegen is always available; -O1 and above
* require this flag and the matching src/opt sources. */
-#define CFREE_OPT_ENABLED 1
+#define CFREE_OPT_ENABLED 0
/* Optional library subsystems. These are kept separate from driver tool flags:
* libcfree embedders care mostly about whether a public subsystem and its
diff --git a/src/abi/registry.c b/src/abi/registry.c
@@ -10,31 +10,42 @@ typedef struct ABIImpl {
const ABIVtable* vt;
} ABIImpl;
+#define CFREE_ABI_AA64_ENABLED \
+ (CFREE_ARCH_AA64_ENABLED || CFREE_ARCH_C_TARGET_ENABLED)
+#define CFREE_ABI_X64_ENABLED \
+ (CFREE_ARCH_X64_ENABLED || CFREE_ARCH_C_TARGET_ENABLED)
+#define CFREE_ABI_RV64_ENABLED \
+ (CFREE_ARCH_RV64_ENABLED || CFREE_ARCH_C_TARGET_ENABLED)
+
static const ABIImpl abi_impls[] = {
-#if CFREE_ARCH_AA64_ENABLED && CFREE_OBJ_ELF_ENABLED
+#if CFREE_ABI_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
+#if CFREE_ABI_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
+#if CFREE_ABI_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
+#if CFREE_ABI_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
+#if CFREE_ABI_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
+#if CFREE_ABI_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
+#if CFREE_ABI_RV64_ENABLED && CFREE_OBJ_ELF_ENABLED
{CFREE_ARCH_RV64, CFREE_OBJ_ELF, &rv64_vtable},
#endif
#if CFREE_ARCH_WASM_ENABLED && CFREE_OBJ_WASM_ENABLED
{CFREE_ARCH_WASM, CFREE_OBJ_WASM, &wasm32_vtable},
#endif
+#if !CFREE_ABI_AA64_ENABLED && !CFREE_ABI_X64_ENABLED && \
+ !CFREE_ABI_RV64_ENABLED && !CFREE_ARCH_WASM_ENABLED
+ {CFREE_ARCH_WASM, CFREE_OBJ_WASM, NULL},
+#endif
};
const ABIVtable* abi_vtable_lookup(CfreeArchKind arch, CfreeObjFmt obj) {
@@ -45,3 +56,7 @@ const ABIVtable* abi_vtable_lookup(CfreeArchKind arch, CfreeObjFmt obj) {
}
return NULL;
}
+
+#undef CFREE_ABI_AA64_ENABLED
+#undef CFREE_ABI_X64_ENABLED
+#undef CFREE_ABI_RV64_ENABLED
diff --git a/src/arch/c_target/emit.c b/src/arch/c_target/emit.c
@@ -2059,6 +2059,35 @@ static void c_emit_call_arg(CTarget* t, const CGABIValue* v) {
c_emit_operand(t, op);
}
+const char* c_tail_call_unrealizable_reason(CGTarget* T,
+ const CGCallDesc* d) {
+ CTarget* t = (CTarget*)T;
+ SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
+ const CgType* fty = cg_type_get(t->c, api_unalias_type(t->c, d->fn_type));
+ if (!fty || fty->kind != CFREE_CG_TYPE_FUNC) {
+ compiler_panic(t->c, loc, "C target: tail call: bad fn_type");
+ }
+ const CgType* caller =
+ t->cur_fn
+ ? cg_type_get(t->c, api_unalias_type(t->c, t->cur_fn->fn_type))
+ : NULL;
+ if (!caller || caller->kind != CFREE_CG_TYPE_FUNC) {
+ compiler_panic(t->c, loc, "C target: tail call outside function");
+ }
+ if (caller->func.abi_variadic) {
+ return "C target: caller variadic tail call not yet supported by clang "
+ "musttail";
+ }
+ if (fty->func.abi_variadic) {
+ return "C target: variadic tail call not yet supported by clang musttail";
+ }
+ if (caller->func.nparams != fty->func.nparams) {
+ return "C target: tail call with differing parameter counts not yet "
+ "supported by clang musttail";
+ }
+ return NULL;
+}
+
void c_call(CGTarget* T, const CGCallDesc* d) {
CTarget* t = (CTarget*)T;
SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
@@ -2070,15 +2099,13 @@ void c_call(CGTarget* T, const CGCallDesc* d) {
CfreeCgTypeId ret_type = fty->func.ret;
int fn_returns = !cg_type_is_void(t->c, ret_type);
int is_tail = (d->flags & CG_CALL_TAIL) != 0;
- /* When a non-void function is tail-called, CG sets ret.storage to a void
- * IMM (the caller will not consume the result). We still need to forward
- * the callee's value with a `return` so the enclosing function returns
- * something — gcc will tail-call-optimize it on its own. */
+ /* When tail-called, CG sets ret.storage to a void IMM and will not emit a
+ * separate ret. Clang's C statement attribute gives us a checked musttail
+ * lowering while still spelling the source-level value forwarding exactly. */
/* === Emit the LHS / open the call statement === */
if (is_tail) {
- cbuf_puts(&t->body, " ");
- if (fn_returns) cbuf_puts(&t->body, "return ");
+ cbuf_puts(&t->body, " __attribute__((musttail)) return ");
} else if (fn_returns) {
Operand rs = d->ret.storage;
if (rs.kind == OPK_REG) {
@@ -2140,12 +2167,6 @@ void c_call(CGTarget* T, const CGCallDesc* d) {
c_emit_reg_assign_close(t);
} else {
cbuf_puts(&t->body, ";\n");
- if (is_tail && !fn_returns) {
- /* Void tail call. After this CG won't call ret, so add an explicit
- * return so any non-void enclosing function still has a terminating
- * statement. (For void-returning enclosing fns this is a no-op.) */
- cbuf_puts(&t->body, " return;\n");
- }
}
if (is_tail) t->last_was_terminator = 1;
}
diff --git a/src/arch/c_target/target.c b/src/arch/c_target/target.c
@@ -24,6 +24,7 @@ void c_unop(CGTarget*, UnOp, Operand, Operand);
void c_cmp(CGTarget*, CmpOp, Operand, Operand, Operand);
void c_convert(CGTarget*, ConvKind, Operand, Operand);
void c_call(CGTarget*, const CGCallDesc*);
+const char* c_tail_call_unrealizable_reason(CGTarget*, const CGCallDesc*);
void c_load(CGTarget*, Operand, Operand, MemAccess);
void c_store(CGTarget*, Operand, Operand, MemAccess);
void c_addr_of(CGTarget*, Operand, Operand);
@@ -271,6 +272,7 @@ CGTarget* c_cgtarget_new(Compiler* c, ObjBuilder* o, CfreeWriter* w) {
/* ---- calls / return ---- */
t->call = c_call;
+ t->tail_call_unrealizable_reason = c_tail_call_unrealizable_reason;
t->plan_call = c_unimpl_plan_call;
t->load_call_arg = c_unimpl_load_call_arg;
t->store_call_arg = c_unimpl_store_call_arg;
diff --git a/src/arch/registry.c b/src/arch/registry.c
@@ -53,6 +53,10 @@ static const ArchImpl* const arch_impls[] = {
#if CFREE_ARCH_WASM_ENABLED
&arch_impl_wasm,
#endif
+#if !CFREE_ARCH_AA64_ENABLED && !CFREE_ARCH_X64_ENABLED && \
+ !CFREE_ARCH_RV64_ENABLED && !CFREE_ARCH_WASM_ENABLED
+ NULL,
+#endif
};
static u32 arch_impls_count(void) {
@@ -61,7 +65,7 @@ static u32 arch_impls_count(void) {
const ArchImpl* arch_lookup(CfreeArchKind arch) {
for (u32 i = 0; i < arch_impls_count(); ++i) {
- if (arch_impls[i]->kind == arch) return arch_impls[i];
+ if (arch_impls[i] && arch_impls[i]->kind == arch) return arch_impls[i];
}
return NULL;
}
diff --git a/src/cg/session.c b/src/cg/session.c
@@ -6,7 +6,9 @@
#include "opt/opt.h"
#endif
+#if CFREE_ARCH_WASM_ENABLED
#include "arch/wasm/wasm_imports.h"
+#endif
static void cg_free_obj_state(CfreeCg* g) {
Heap* h;
@@ -202,9 +204,11 @@ CfreeCgSym cfree_cg_decl(CfreeCg* g, CfreeCgDecl decl) {
* for other backends is one heap allocation that no one reads. */
if (decl.kind == CFREE_CG_DECL_FUNC &&
(decl.as.func.wasm_import_module || decl.as.func.wasm_import_name)) {
+#if CFREE_ARCH_WASM_ENABLED
wasm_imports_set(ob, (Sym)decl.linkage_name,
(Sym)decl.as.func.wasm_import_module,
(Sym)decl.as.func.wasm_import_name);
+#endif
}
return (CfreeCgSym)sym;
}