commit 106b8cb6c48dabf72df6804e4900cbdb39e836ca
parent 1541d1cfc7b9638a91946f78a7d4ccae1630b644
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 8 Jun 2026 12:39:29 -0700
Remove multi-value from the CG API (single result)
Multi-result functions (KitCgFuncSig.results[]/nresults, nresults>1) were an
advertise-but-ignore facade: the public API accepted up to 16 results, but no
frontend produced more than one, there were zero tests, and every native
backend panicked ("multiple returns unsupported") while the -O1 opt path and
wasm silently dropped extras. Per CODEGEN.md Principles 2/3, collapse the whole
surface to a single optional result (void == KIT_CG_TYPE_NONE) instead of
implementing ~1000 LOC nothing uses.
- Public: KitCgFuncSig.result (was results[]/nresults); kit_cg_type_func_result
drops its index; removed kit_cg_type_func_nresults.
- Type system stores a single KitCgFuncResult; cg_type_func_result_id returns
the raw result type (NONE for void) alongside cg_type_func_ret_id.
- CGFuncDesc.result_type / CGCallDesc.result (single CGLocal, CG_LOCAL_NONE =
void); CgIrRetAux/value+present; OptCGFuncDesc.result_type. Removed
API_CG_MAX_RESULTS, fn_result_types[], the call/ret loops.
- ret hooks collapse to a single value: CgTarget.ret(CGLocal),
NativeTarget.plan_ret(value), NativeDirectTarget.emit_ret(CGLocal). Backend
"multiple returns unsupported" panics removed. NativeCallDesc kept as a
bounded (<=1) array internally.
- c_target: dropped the tuple typedef machinery; single-result signatures/calls.
- Frontends (c/toy/wasm) build a single sig.result; wasm-spec multi-value
(src/wasm, WasmType) is untouched.
Green: lib/bin/rt, cg-api, opt, toy 1382/0, parse 3784 + parse-err 128/0,
interp, abi-classify, ir-recorder, native-direct-target, tiny-inline, isa,
link, elf, smoke x64/rv64; bootstrap reproduces byte-identical (stage2==stage3).
Diffstat:
45 files changed, 312 insertions(+), 647 deletions(-)
diff --git a/include/kit/cg.h b/include/kit/cg.h
@@ -98,17 +98,15 @@ typedef struct KitCgFuncParam {
KitCgAbiAttrs attrs;
} KitCgFuncParam;
-/* Symmetric with KitCgFuncParam. A function declares 0..N results; these are
- * semantic multi-results (e.g. wasm multi-value), not ABI-split parts of one
- * value. nresults == 0 is void. */
+/* Symmetric with KitCgFuncParam. A function returns a single value or nothing;
+ * result.type == KIT_CG_TYPE_NONE means void. */
typedef struct KitCgFuncResult {
KitCgTypeId type;
KitCgAbiAttrs attrs;
} KitCgFuncResult;
typedef struct KitCgFuncSig {
- const KitCgFuncResult* results; /* nresults entries; NULL/0 == void */
- uint32_t nresults;
+ KitCgFuncResult result; /* result.type == KIT_CG_TYPE_NONE == void */
const KitCgFuncParam* params;
uint32_t nparams;
KitCgCallConv call_conv;
@@ -184,9 +182,8 @@ KIT_API uint32_t kit_cg_type_ptr_address_space(KitCompiler*, KitCgTypeId);
KIT_API KitCgTypeId kit_cg_type_array_elem(KitCompiler*, KitCgTypeId);
KIT_API uint64_t kit_cg_type_array_count(KitCompiler*, KitCgTypeId);
-KIT_API uint32_t kit_cg_type_func_nresults(KitCompiler*, KitCgTypeId);
-KIT_API KitCgFuncResult kit_cg_type_func_result(KitCompiler*, KitCgTypeId,
- uint32_t index);
+/* The function's result; result.type == KIT_CG_TYPE_NONE for a void function. */
+KIT_API KitCgFuncResult kit_cg_type_func_result(KitCompiler*, KitCgTypeId);
KIT_API uint32_t kit_cg_type_func_nparams(KitCompiler*, KitCgTypeId);
KIT_API KitCgFuncParam kit_cg_type_func_param(KitCompiler*, KitCgTypeId,
uint32_t index);
@@ -950,17 +947,15 @@ typedef struct KitCgCallAttrs {
* tail call whose callee return type is incompatible with the enclosing
* function's return type is a frontend bug and aborts under any policy.
*
- * Results: a non-tail call pushes the callee's nresults values in declaration
- * order, so the last result ends up on top of stack (TOS). A void callee
- * (nresults == 0) pushes nothing. */
+ * Results: a non-tail call to a value-returning callee pushes the callee's
+ * single result onto the stack (TOS). A void callee pushes nothing. */
KIT_API void kit_cg_call(KitCg*, uint32_t nargs, KitCgTypeId fn_type,
KitCgCallAttrs attrs);
KIT_API void kit_cg_call_symbol(KitCg*, KitCgSym sym, uint32_t nargs,
KitCgCallAttrs attrs);
-/* Returns from the current function. Pops the enclosing function's nresults
- * values, expecting them in declaration order with the last result on top of
- * stack (the same order kit_cg_call pushes them). A void function
- * (nresults == 0) pops nothing — this is the single return entry point. */
+/* Returns from the current function. Pops the enclosing function's single
+ * result value from TOS, or nothing for a void function. This is the single
+ * return entry point. */
KIT_API void kit_cg_ret(KitCg*);
/* ============================================================
diff --git a/lang/c/type/type.c b/lang/c/type/type.c
@@ -686,15 +686,10 @@ static KitCgTypeId type_cg_lower(TypeCgLower* l, const Type* t,
t->arr.count);
case TY_FUNC: {
KitCgFuncParam* params = NULL;
- KitCgFuncResult result;
KitCgFuncSig sig;
memset(&sig, 0, sizeof sig);
- if (t->fn.ret->kind != TY_VOID) {
- memset(&result, 0, sizeof result);
- result.type = type_cg_lower(l, t->fn.ret, TYPE_CG_VALUE);
- sig.results = &result;
- sig.nresults = 1;
- }
+ if (t->fn.ret->kind != TY_VOID)
+ sig.result.type = type_cg_lower(l, t->fn.ret, TYPE_CG_VALUE);
sig.nparams = t->fn.nparams;
sig.abi_variadic = t->fn.variadic;
sig.call_conv = KIT_CG_CC_TARGET_C;
diff --git a/lang/toy/decls.c b/lang/toy/decls.c
@@ -425,13 +425,10 @@ int toy_parse_fn(ToyParser* p, int is_extern, int is_pub) {
sig_params[i].type = param_types[i];
sig_params[i].attrs = param_attrs[i];
}
- KitCgFuncResult sig_result;
memset(&sig, 0, sizeof sig);
if (kit_cg_type_kind(p->c, ret_type) != KIT_CG_TYPE_VOID) {
- sig_result.type = ret_type;
- sig_result.attrs = ret_attrs;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result.type = ret_type;
+ sig.result.attrs = ret_attrs;
}
sig.params = sig_params;
sig.nparams = (uint32_t)nparams;
diff --git a/lang/toy/parser_core.c b/lang/toy/parser_core.c
@@ -9,9 +9,8 @@ KitCgTypeId toy_builtin_type(ToyParser* p, KitCgBuiltinType ty) {
}
KitCgTypeId toy_cg_func_ret(ToyParser* p, KitCgTypeId fn_ty) {
- if (kit_cg_type_func_nresults(p->c, fn_ty) == 0)
- return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
- return kit_cg_type_func_result(p->c, fn_ty, 0).type;
+ KitCgTypeId rt = kit_cg_type_func_result(p->c, fn_ty).type;
+ return rt ? rt : toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
}
void* toy_parser_zalloc(ToyParser* p, size_t count, size_t elem_size,
diff --git a/lang/toy/types.c b/lang/toy/types.c
@@ -158,14 +158,9 @@ KitCgTypeId toy_parse_type(ToyParser* p) {
ret_toy_type = p->last_type;
for (i = 0; i < nparams; i++) sig_params[i].type = param_types[i];
- KitCgFuncResult sig_result;
memset(&sig, 0, sizeof sig);
- if (kit_cg_type_kind(p->c, ret_type) != KIT_CG_TYPE_VOID) {
- memset(&sig_result, 0, sizeof sig_result);
- sig_result.type = ret_type;
- sig.results = &sig_result;
- sig.nresults = 1;
- }
+ if (kit_cg_type_kind(p->c, ret_type) != KIT_CG_TYPE_VOID)
+ sig.result.type = ret_type;
sig.params = sig_params;
sig.nparams = (uint32_t)nparams;
sig.call_conv = KIT_CG_CC_TARGET_C;
diff --git a/lang/wasm/cg.c b/lang/wasm/cg.c
@@ -2086,14 +2086,8 @@ void wasm_emit_cg_into(KitCompiler* c, KitCg* cg, const WasmModule* m) {
for (j = 0; j < f->nparams; ++j) {
cg_params[j + 1u].type = wasm_cg_type(c, b, f->params[j]);
}
- KitCgFuncResult sig_result;
memset(&sig, 0, sizeof sig);
- if (f->nresults) {
- memset(&sig_result, 0, sizeof sig_result);
- sig_result.type = wasm_cg_type(c, b, f->results[0]);
- sig.results = &sig_result;
- sig.nresults = 1;
- }
+ if (f->nresults) sig.result.type = wasm_cg_type(c, b, f->results[0]);
sig.params = cg_params;
sig.nparams = f->nparams + 1u;
sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -2630,14 +2624,9 @@ void wasm_emit_cg_into(KitCompiler* c, KitCg* cg, const WasmModule* m) {
indirect_params[0].type = rt.instance_ptr_ty;
for (uint32_t p = 0; p < t->nparams; ++p)
indirect_params[p + 1u].type = wasm_cg_type(c, b, t->params[p]);
- KitCgFuncResult indirect_result;
memset(&indirect_sig, 0, sizeof indirect_sig);
- if (t->nresults) {
- memset(&indirect_result, 0, sizeof indirect_result);
- indirect_result.type = wasm_cg_type(c, b, t->results[0]);
- indirect_sig.results = &indirect_result;
- indirect_sig.nresults = 1;
- }
+ if (t->nresults)
+ indirect_sig.result.type = wasm_cg_type(c, b, t->results[0]);
indirect_sig.params = indirect_params;
indirect_sig.nparams = t->nparams + 1u;
indirect_sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -2807,14 +2796,8 @@ void wasm_emit_cg_into(KitCompiler* c, KitCg* cg, const WasmModule* m) {
ref_params[0].type = rt.instance_ptr_ty;
for (uint32_t p = 0; p < t->nparams; ++p)
ref_params[p + 1u].type = wasm_cg_type(c, b, t->params[p]);
- KitCgFuncResult ref_result;
memset(&ref_sig, 0, sizeof ref_sig);
- if (t->nresults) {
- memset(&ref_result, 0, sizeof ref_result);
- ref_result.type = wasm_cg_type(c, b, t->results[0]);
- ref_sig.results = &ref_result;
- ref_sig.nresults = 1;
- }
+ if (t->nresults) ref_sig.result.type = wasm_cg_type(c, b, t->results[0]);
ref_sig.params = ref_params;
ref_sig.nparams = t->nparams + 1u;
ref_sig.call_conv = KIT_CG_CC_TARGET_C;
diff --git a/src/arch/aa64/native.c b/src/arch/aa64/native.c
@@ -3005,14 +3005,13 @@ static void aa_emit_call(NativeTarget* t, const NativeCallPlan* plan) {
}
static void aa_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
- const NativeLoc* values, u32 nvalues,
+ const NativeLoc* value,
NativeCallPlanRet** out_rets, u32* out_nrets) {
const ABIFuncInfo* abi = abi_cg_func_info(t->c->abi, fd->fn_type);
NativeCallPlanRet* rets = NULL;
u32 nr = 0;
- if (nvalues > 1u) aa_panic(aa_of(t), "multiple returns unsupported");
- if (nvalues) rets = arena_zarray(t->c->tu, NativeCallPlanRet, 4);
- if (nvalues && abi && abi->ret.kind == ABI_ARG_INDIRECT) {
+ if (value) rets = arena_zarray(t->c->tu, NativeCallPlanRet, 4);
+ if (value && abi && abi->ret.kind == ABI_ARG_INDIRECT) {
AANativeTarget* a = aa_of(t);
/* Hold the sret destination pointer in x8, not AA_TMP1: aa_copy_bytes
* materializes out-of-range source/dest frame offsets into AA_TMP1, which
@@ -3027,29 +3026,29 @@ static void aa_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
memset(&dst_addr, 0, sizeof dst_addr);
dst_addr.base_kind = NATIVE_ADDR_BASE_REG;
dst_addr.base.reg = AA_X8;
- dst_addr.base_type = values[0].type;
- src_addr = aa_loc_addr(a, values[0], 0);
- src_addr.base_type = values[0].type;
+ dst_addr.base_type = value->type;
+ src_addr = aa_loc_addr(a, *value, 0);
+ src_addr.base_type = value->type;
memset(&access, 0, sizeof access);
- access.type = values[0].type;
- access.size = (u32)cg_type_size(t->c, values[0].type);
- access.align = type_align32(t, values[0].type);
+ access.type = value->type;
+ access.size = (u32)cg_type_size(t->c, value->type);
+ access.align = type_align32(t, value->type);
aa_copy_bytes(t, dst_addr, src_addr, access);
*out_rets = NULL;
*out_nrets = 0;
return;
}
- if (nvalues && abi && abi->ret.kind == ABI_ARG_DIRECT) {
+ if (value && abi && abi->ret.kind == ABI_ARG_DIRECT) {
u32 ni = 0, nf = 0;
for (u32 p = 0; p < abi->ret.nparts; ++p) {
const ABIArgPart* part = &abi->ret.parts[p];
NativeAllocClass cls =
part->cls == ABI_CLASS_FP ? NATIVE_REG_FP : NATIVE_REG_INT;
KitCgTypeId pty = aa_part_scalar_type(part);
- rets[nr].src = values[0];
+ rets[nr].src = *value;
if (rets[nr].src.kind == NATIVE_LOC_FRAME)
rets[nr].src =
- native_loc_stack(pty, values[0].v.frame, (i32)part->src_offset);
+ native_loc_stack(pty, value->v.frame, (i32)part->src_offset);
else if (rets[nr].src.kind == NATIVE_LOC_STACK) {
rets[nr].src.v.stack.offset += (i32)part->src_offset;
rets[nr].src.type = pty;
@@ -3062,10 +3061,10 @@ static void aa_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
rets[nr].mem = aa_mem_for_type(t, pty, part->size);
nr++;
}
- } else if (nvalues) {
- rets[0].src = values[0];
- rets[0].dst = native_loc_reg(values[0].type, NATIVE_REG_INT, 0);
- rets[0].mem = aa_mem_for_type(t, values[0].type, 0);
+ } else if (value) {
+ rets[0].src = *value;
+ rets[0].dst = native_loc_reg(value->type, NATIVE_REG_INT, 0);
+ rets[0].mem = aa_mem_for_type(t, value->type, 0);
nr = 1;
}
*out_rets = rets;
@@ -4100,26 +4099,26 @@ static const char* aa_no_tail(NativeDirectTarget* d, const CGCallDesc* call) {
NativeLoc* results = NULL;
u32 stack;
memset(&nd, 0, sizeof nd);
+ u32 nresults = call->result != CG_LOCAL_NONE ? 1u : 0u;
if (call->nargs) args = arena_zarray(d->base.c->tu, NativeLoc, call->nargs);
- if (call->nresults)
- results = arena_zarray(d->base.c->tu, NativeLoc, call->nresults);
+ if (nresults) results = arena_zarray(d->base.c->tu, NativeLoc, nresults);
for (u32 i = 0; i < call->nargs; ++i) {
args[i].kind = NATIVE_LOC_FRAME;
args[i].type = d->locals[call->args[i] - 1u].type;
args[i].cls = d->locals[call->args[i] - 1u].cls;
args[i].v.frame = d->locals[call->args[i] - 1u].home;
}
- for (u32 i = 0; i < call->nresults; ++i) {
- results[i].kind = NATIVE_LOC_FRAME;
- results[i].type = d->locals[call->results[i] - 1u].type;
- results[i].cls = d->locals[call->results[i] - 1u].cls;
- results[i].v.frame = d->locals[call->results[i] - 1u].home;
+ if (nresults) {
+ results[0].kind = NATIVE_LOC_FRAME;
+ results[0].type = d->locals[call->result - 1u].type;
+ results[0].cls = d->locals[call->result - 1u].cls;
+ results[0].v.frame = d->locals[call->result - 1u].home;
}
nd.fn_type = call->fn_type;
nd.args = args;
nd.results = results;
nd.nargs = call->nargs;
- nd.nresults = call->nresults;
+ nd.nresults = nresults;
stack = aa_call_stack_size(d->native, &nd);
if (stack > aa_of(d->native)->incoming_stack_size)
return "aarch64 tail call: stack argument area too small";
diff --git a/src/arch/c_target/c_emit.c b/src/arch/c_target/c_emit.c
@@ -32,10 +32,6 @@ static void c_ensure_typedef(CTarget* t, KitCgTypeId tid);
static const char* c_typedef_name(CTarget* t, KitCgTypeId tid);
static const char* c_typename(CTarget* t, KitCgTypeId type);
static KitCgTypeId c_local_type_or_panic(CTarget* t, CGLocal local);
-static void c_ensure_tuple_typedef(CTarget* t, const KitCgTypeId* types,
- u32 ntypes);
-static void c_emit_tuple_type_name(CTarget* t, CBuf* b,
- const KitCgTypeId* types, u32 ntypes);
static Operand c_op_local(CGLocal local, KitCgTypeId type);
static int c_type_is_aggregate(CTarget* t, KitCgTypeId type);
static int c_type_is_bool(CTarget* t, KitCgTypeId type);
@@ -45,10 +41,6 @@ static void c_emit_addr_deref(CTarget* t, Operand addr,
KitCgTypeId access_type);
static void c_emit_copy_addr(CTarget* t, Operand addr);
CGLocal c_emit_local(CTarget* t, const CGLocalDesc* d);
-static void c_ensure_forward_decl_with_results(CTarget* t, ObjSymId sym,
- KitCgTypeId fn_type,
- const KitCgTypeId* result_types,
- u32 nresults);
/* Private accessor on ObjBuilder (defined in obj/obj.c, not in obj.h).
* Same forward-decl trick as obj_tls.c uses. */
ObjSymId obj_tlv_bootstrap_get(const ObjBuilder*);
@@ -351,39 +343,6 @@ static KitCgTypeId c_local_type_or_panic(CTarget* t, CGLocal local) {
return KIT_CG_TYPE_NONE;
}
-static void c_emit_tuple_type_name(CTarget* t, CBuf* b,
- const KitCgTypeId* types, u32 ntypes) {
- (void)t;
- cbuf_puts(b, "__kit_tuple");
- cbuf_put_u64(b, (u64)ntypes);
- for (u32 i = 0; i < ntypes; ++i) {
- cbuf_putc(b, '_');
- cbuf_put_u64(b, (u64)api_unalias_type(t->c, types[i]));
- }
-}
-
-static void c_ensure_tuple_typedef(CTarget* t, const KitCgTypeId* types,
- u32 ntypes) {
- if (ntypes <= 1) return;
- cbuf_puts(&t->typedefs, "#ifndef __kit_tuple_guard_");
- c_emit_tuple_type_name(t, &t->typedefs, types, ntypes);
- cbuf_puts(&t->typedefs, "\n#define __kit_tuple_guard_");
- c_emit_tuple_type_name(t, &t->typedefs, types, ntypes);
- cbuf_puts(&t->typedefs, "\ntypedef struct ");
- c_emit_tuple_type_name(t, &t->typedefs, types, ntypes);
- cbuf_puts(&t->typedefs, " {");
- for (u32 i = 0; i < ntypes; ++i) {
- cbuf_putc(&t->typedefs, ' ');
- c_emit_type(t, &t->typedefs, types[i]);
- cbuf_puts(&t->typedefs, " f");
- cbuf_put_u64(&t->typedefs, (u64)i);
- cbuf_putc(&t->typedefs, ';');
- }
- cbuf_puts(&t->typedefs, " } ");
- c_emit_tuple_type_name(t, &t->typedefs, types, ntypes);
- cbuf_puts(&t->typedefs, ";\n#endif\n");
-}
-
static Operand c_op_local(CGLocal local, KitCgTypeId type) {
Operand op;
memset(&op, 0, sizeof op);
@@ -978,26 +937,17 @@ void c_emit_prologue(CTarget* t) {
/* Write `RetT name(P0, P1, ...)` (without trailing `;` or `{`) to `b`. */
static void c_emit_func_signature(CTarget* t, CBuf* b, const char* name,
- KitCgTypeId fn_type,
- const KitCgTypeId* result_types,
- u32 nresults) {
+ KitCgTypeId fn_type) {
KitCgTypeId ret_type = cg_type_func_ret_id(t->c, fn_type);
const CgType* fty = cg_type_get(t->c, api_unalias_type(t->c, fn_type));
SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
if (!fty || fty->kind != KIT_CG_TYPE_FUNC) {
compiler_panic(t->c, loc, "C target: fn_type is not a function type");
}
- if (!result_types) {
- nresults = cg_type_is_void(t->c, ret_type) ? 0u : 1u;
- result_types = &ret_type;
- }
- if (nresults == 0) {
+ if (cg_type_is_void(t->c, ret_type)) {
cbuf_puts(b, "void");
- } else if (nresults == 1) {
- c_emit_type(t, b, result_types[0]);
} else {
- c_ensure_tuple_typedef(t, result_types, nresults);
- c_emit_tuple_type_name(t, b, result_types, nresults);
+ c_emit_type(t, b, ret_type);
}
cbuf_puts(b, " ");
cbuf_puts(b, name);
@@ -1040,19 +990,14 @@ void c_emit_func_begin(CTarget* t, const CGFuncDesc* fd) {
/* Forward-declare so out-of-order callers and same-TU references find the
* prototype regardless of definition order. */
- c_ensure_forward_decl_with_results(t, fd->sym, fd->fn_type, fd->result_types,
- fd->nresults);
+ c_ensure_forward_decl(t, fd->sym, fd->fn_type);
- c_emit_func_signature(t, &t->body, name, fd->fn_type, fd->result_types,
- fd->nresults);
+ c_emit_func_signature(t, &t->body, name, fd->fn_type);
cbuf_puts(&t->body, " {\n");
t->fn_body_start = t->body.len;
}
-static void c_ensure_forward_decl_with_results(CTarget* t, ObjSymId sym,
- KitCgTypeId fn_type,
- const KitCgTypeId* result_types,
- u32 nresults) {
+void c_ensure_forward_decl(CTarget* t, ObjSymId sym, KitCgTypeId fn_type) {
Heap* h = t->c->ctx->heap;
if ((u32)sym >= t->sym_forwarded_cap) {
u32 newcap = t->sym_forwarded_cap ? t->sym_forwarded_cap : 16;
@@ -1071,8 +1016,7 @@ static void c_ensure_forward_decl_with_results(CTarget* t, ObjSymId sym,
const char* name = c_sym_name(t, sym);
const ObjSym* os = obj_symbol_get(t->obj, sym);
if ((os && (os->kind == SK_FUNC || os->kind == SK_IFUNC)) || fn_type != 0) {
- c_emit_func_signature(t, &t->forwards, name, fn_type, result_types,
- nresults);
+ c_emit_func_signature(t, &t->forwards, name, fn_type);
cbuf_puts(&t->forwards, ";\n");
} else {
if (os && os->bind == SB_LOCAL)
@@ -1091,10 +1035,6 @@ static void c_ensure_forward_decl_with_results(CTarget* t, ObjSymId sym,
}
}
-void c_ensure_forward_decl(CTarget* t, ObjSymId sym, KitCgTypeId fn_type) {
- c_ensure_forward_decl_with_results(t, sym, fn_type, NULL, 0);
-}
-
void c_emit_func_end(CTarget* t) {
size_t splice_at = t->fn_body_start;
size_t body_after = t->body.len;
@@ -2226,37 +2166,17 @@ static int c_try_emit_ti_intrinsic(CTarget* t, const CgType* fty,
return 0;
}
-static void c_emit_call_expr(CTarget* t, const CgType* fty, const CGCallDesc* d,
- const KitCgTypeId* result_types) {
+static void c_emit_call_expr(CTarget* t, const CgType* fty,
+ const CGCallDesc* d) {
if (c_try_emit_ti_intrinsic(t, fty, d)) return;
if (d->callee.kind == OPK_GLOBAL) {
- c_ensure_forward_decl_with_results(t, d->callee.v.global.sym, d->fn_type,
- d->nresults > 1 ? result_types : NULL,
- d->nresults);
+ c_ensure_forward_decl(t, d->callee.v.global.sym, d->fn_type);
cbuf_puts(&t->body, c_sym_name(t, d->callee.v.global.sym));
} else if (d->callee.kind == OPK_LOCAL) {
+ const char* fp = c_typedef_name(t, d->fn_type);
cbuf_puts(&t->body, "((");
- if (d->nresults > 1) {
- c_emit_tuple_type_name(t, &t->body, result_types, d->nresults);
- cbuf_puts(&t->body, " (*)(");
- if (fty->func.nparams == 0 && !fty->func.abi_variadic) {
- cbuf_puts(&t->body, "void");
- } else {
- for (u32 i = 0; i < fty->func.nparams; ++i) {
- if (i > 0) cbuf_puts(&t->body, ", ");
- c_emit_type(t, &t->body, fty->func.params[i].type);
- }
- if (fty->func.abi_variadic) {
- if (fty->func.nparams > 0) cbuf_puts(&t->body, ", ");
- cbuf_puts(&t->body, "...");
- }
- }
- cbuf_puts(&t->body, ")");
- } else {
- const char* fp = c_typedef_name(t, d->fn_type);
- c_ensure_typedef(t, d->fn_type);
- cbuf_puts(&t->body, fp);
- }
+ c_ensure_typedef(t, d->fn_type);
+ cbuf_puts(&t->body, fp);
cbuf_puts(&t->body, ")");
c_emit_operand(t, d->callee);
cbuf_puts(&t->body, ")");
@@ -2308,7 +2228,6 @@ const char* c_emit_tail_call_unrealizable_reason_for(
void c_emit_call(CTarget* t, const CGCallDesc* d) {
SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0};
- Heap* h = t->c->ctx->heap;
const CgType* fty = cg_type_get(t->c, api_unalias_type(t->c, d->fn_type));
if (!fty || fty->kind != KIT_CG_TYPE_FUNC) {
@@ -2316,54 +2235,22 @@ void c_emit_call(CTarget* t, const CGCallDesc* d) {
}
KitCgTypeId ret_type = cg_func_ret_type(fty);
int is_tail = (d->flags & CG_CALL_TAIL) != 0;
- KitCgTypeId* result_types = NULL;
- if (d->nresults > 1) {
- result_types = (KitCgTypeId*)h->alloc(h, sizeof(KitCgTypeId) * d->nresults,
- _Alignof(KitCgTypeId));
- if (!result_types) {
- compiler_panic(t->c, loc, "C target: out of memory");
- return;
- }
- for (u32 i = 0; i < d->nresults; ++i)
- result_types[i] = c_local_type_or_panic(t, d->results[i]);
- c_ensure_tuple_typedef(t, result_types, d->nresults);
- }
if (is_tail) {
cbuf_puts(&t->body, " __attribute__((musttail)) return ");
- c_emit_call_expr(t, fty, d, result_types);
+ c_emit_call_expr(t, fty, d);
cbuf_puts(&t->body, ";\n");
t->last_was_terminator = 1;
- } else if (d->nresults == 0) {
+ } else if (d->result == CG_LOCAL_NONE) {
cbuf_puts(&t->body, " ");
- c_emit_call_expr(t, fty, d, result_types);
+ c_emit_call_expr(t, fty, d);
cbuf_puts(&t->body, ";\n");
- } else if (d->nresults == 1) {
- c_ensure_local(t, d->results[0], ret_type);
- c_emit_local_assign_open(t, d->results[0], ret_type);
- c_emit_call_expr(t, fty, d, result_types);
- c_emit_local_assign_close(t);
} else {
- char tmp[32];
- u32 tmp_id = t->next_tmp++;
- snprintf(tmp, sizeof tmp, "__kit_call_%u", (unsigned)tmp_id);
- cbuf_puts(&t->body, " ");
- c_emit_tuple_type_name(t, &t->body, result_types, d->nresults);
- cbuf_putc(&t->body, ' ');
- cbuf_puts(&t->body, tmp);
- cbuf_puts(&t->body, " = ");
- c_emit_call_expr(t, fty, d, result_types);
- cbuf_puts(&t->body, ";\n");
- for (u32 i = 0; i < d->nresults; ++i) {
- c_ensure_local(t, d->results[i], result_types[i]);
- c_emit_local_assign_open(t, d->results[i], result_types[i]);
- cbuf_puts(&t->body, tmp);
- cbuf_puts(&t->body, ".f");
- cbuf_put_u64(&t->body, (u64)i);
- c_emit_local_assign_close(t);
- }
+ c_ensure_local(t, d->result, ret_type);
+ c_emit_local_assign_open(t, d->result, ret_type);
+ c_emit_call_expr(t, fty, d);
+ c_emit_local_assign_close(t);
}
- if (result_types) h->free(h, result_types, sizeof(KitCgTypeId) * d->nresults);
}
/* === load / store === */
@@ -2408,55 +2295,35 @@ void c_emit_addr_of(CTarget* t, Operand dst, Operand lv) {
c_emit_local_assign_close(t);
}
-void c_emit_ret(
- CTarget* t, const CGLocal* values,
- u32 nvalues) { /* Already-terminated block: this ret is unreachable (the
- * frontend's defensive `return 0;` epilogue lands here right
- * after a user return). */
+void c_emit_ret(CTarget* t, CGLocal value) {
+ /* Already-terminated block: this ret is unreachable (the frontend's
+ * defensive `return 0;` epilogue lands here right after a user return). */
if (t->last_was_terminator) return;
- /* CG emits a defensive ret_void epilogue at the end of every function. For
+ /* CG emits a defensive void-return epilogue at the end of every function. For
* a non-void function that's unreachable; emitting a bare `return;` would
* trip -Wreturn-type. Spell it as `__builtin_unreachable()` so the host C
* compiler sees the path is dead without us inventing a fake value. */
- if (nvalues == 0 && t->cur_fn) {
- if (t->cur_fn->nresults != 0) {
+ if (value == CG_LOCAL_NONE && t->cur_fn) {
+ if (t->cur_fn->result_type != KIT_CG_TYPE_NONE) {
cbuf_puts(&t->body, " __builtin_unreachable();\n");
t->last_was_terminator = 1;
return;
}
}
cbuf_puts(&t->body, " return");
- if (nvalues == 1) {
+ if (value != CG_LOCAL_NONE) {
cbuf_puts(&t->body, " ");
- KitCgTypeId ret_type =
- t->cur_fn ? t->cur_fn->result_types[0] : (KitCgTypeId)0;
+ KitCgTypeId ret_type = t->cur_fn ? t->cur_fn->result_type : (KitCgTypeId)0;
const CgType* rty =
ret_type ? cg_type_get(t->c, api_unalias_type(t->c, ret_type)) : NULL;
int is_aggregate = rty && (rty->kind == KIT_CG_TYPE_RECORD ||
rty->kind == KIT_CG_TYPE_ARRAY);
if (ret_type && !is_aggregate) {
- KitCgTypeId value_ty = c_local_type_or_panic(t, values[0]);
- c_emit_operand_as(t, c_op_local(values[0], value_ty), ret_type);
+ KitCgTypeId value_ty = c_local_type_or_panic(t, value);
+ c_emit_operand_as(t, c_op_local(value, value_ty), ret_type);
} else {
- c_emit_operand(t, c_op_local(values[0], ret_type));
- }
- } else if (nvalues > 1) {
- const KitCgTypeId* result_types =
- t->cur_fn ? t->cur_fn->result_types : NULL;
- if (!result_types || t->cur_fn->nresults != nvalues) {
- compiler_panic(t->c, t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0},
- "C target: multi-return shape mismatch");
- return;
- }
- c_ensure_tuple_typedef(t, result_types, nvalues);
- cbuf_puts(&t->body, " (");
- c_emit_tuple_type_name(t, &t->body, result_types, nvalues);
- cbuf_puts(&t->body, "){");
- for (u32 i = 0; i < nvalues; ++i) {
- if (i > 0) cbuf_puts(&t->body, ", ");
- c_emit_operand(t, c_op_local(values[i], result_types[i]));
+ c_emit_operand(t, c_op_local(value, ret_type));
}
- cbuf_puts(&t->body, "}");
}
cbuf_puts(&t->body, ";\n");
t->last_was_terminator = 1;
@@ -2513,7 +2380,7 @@ void c_emit_alias(CTarget* t, ObjSymId alias_sym, ObjSymId target_sym,
if (!fmt || !fmt->alias_via_thunk) {
/* Attribute form. Works for both function and object aliases on ELF
* and PE/COFF. */
- c_emit_func_signature(t, &t->forwards, alias_name, type, NULL, 0);
+ c_emit_func_signature(t, &t->forwards, alias_name, type);
cbuf_puts(&t->forwards, " __attribute__((alias(\"");
cbuf_puts(&t->forwards, target_name);
cbuf_puts(&t->forwards, "\")));\n");
@@ -2530,7 +2397,7 @@ void c_emit_alias(CTarget* t, ObjSymId alias_sym, ObjSymId target_sym,
* via c_func_begin). Also dedup that. */
c_ensure_forward_decl(t, target_sym, type);
/* `static`? No — alias must be externally visible. */
- c_emit_func_signature(t, &t->forwards, alias_name, type, NULL, 0);
+ c_emit_func_signature(t, &t->forwards, alias_name, type);
cbuf_puts(&t->forwards, " { ");
KitCgTypeId ret_type = cg_type_func_ret_id(t->c, type);
if (!cg_type_is_void(t->c, ret_type)) cbuf_puts(&t->forwards, "return ");
diff --git a/src/arch/c_target/c_emit.h b/src/arch/c_target/c_emit.h
@@ -193,7 +193,7 @@ void c_emit_func_end(CTarget*);
void c_emit_alias(CTarget*, ObjSymId, ObjSymId, KitCgTypeId);
/* Re-emit a file-scope `__asm__("...")` block verbatim at TU scope. */
void c_emit_file_scope_asm(CTarget*, const char* src, size_t len);
-void c_emit_ret(CTarget*, const CGLocal*, u32);
+void c_emit_ret(CTarget*, CGLocal value);
void c_emit_unreachable(CTarget*);
void c_emit_load_imm(CTarget*, Operand, i64);
void c_emit_load_const(CTarget*, Operand, ConstBytes);
diff --git a/src/arch/c_target/ir_emit.c b/src/arch/c_target/ir_emit.c
@@ -126,7 +126,7 @@ static void ir_emit_inst(CIrEmitter* e, const CgIrInst* in) {
}
case CG_IR_RET: {
const CgIrRetAux* aux = (const CgIrRetAux*)in->extra.aux;
- c_emit_ret(t, aux->values, aux->nvalues);
+ c_emit_ret(t, aux->present ? aux->value : CG_LOCAL_NONE);
return;
}
case CG_IR_UNREACHABLE:
diff --git a/src/arch/check_target.c b/src/arch/check_target.c
@@ -223,10 +223,9 @@ static void check_call(CgTarget* t, const CGCallDesc* d) {
(void)d;
}
-static void check_ret(CgTarget* t, const CGLocal* values, u32 nvalues) {
+static void check_ret(CgTarget* t, CGLocal value) {
(void)t;
- (void)values;
- (void)nvalues;
+ (void)value;
}
static void check_unreachable(CgTarget* t) { (void)t; }
diff --git a/src/arch/native_target.h b/src/arch/native_target.h
@@ -497,8 +497,10 @@ struct NativeTarget {
void (*plan_call)(NativeTarget*, const NativeCallDesc*, NativeCallPlan*);
void (*emit_call)(NativeTarget*, const NativeCallPlan*);
- void (*plan_ret)(NativeTarget*, const CGFuncDesc*, const NativeLoc* values,
- u32 nvalues, NativeCallPlanRet** out_rets, u32* out_nrets);
+ /* `value` is the single returned local's location, or NULL for a void
+ * return. out_rets/out_nrets describe the ABI parts of that one value. */
+ void (*plan_ret)(NativeTarget*, const CGFuncDesc*, const NativeLoc* value,
+ NativeCallPlanRet** out_rets, u32* out_nrets);
void (*ret)(NativeTarget*);
void (*atomic_load)(NativeTarget*, NativeLoc dst, NativeAddr addr, MemAccess,
diff --git a/src/arch/riscv/emu.c b/src/arch/riscv/emu.c
@@ -65,8 +65,7 @@ static KitCgTypeId rv64_emu_func_type(KitCompiler* c, KitCgTypeId ret,
memset(&sig, 0, sizeof(sig));
memset(&result, 0, sizeof(result));
result.type = ret;
- sig.results = &result;
- sig.nresults = 1;
+ sig.result = result;
sig.params = p;
sig.nparams = nparams;
sig.call_conv = KIT_CG_CC_TARGET_C;
diff --git a/src/arch/riscv/native.c b/src/arch/riscv/native.c
@@ -2436,15 +2436,14 @@ static void rv_emit_call(NativeTarget* t, const NativeCallPlan* plan) {
}
static void rv_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
- const NativeLoc* values, u32 nvalues,
+ const NativeLoc* value,
NativeCallPlanRet** out_rets, u32* out_nrets) {
RvNativeTarget* a = rv_of(t);
const ABIFuncInfo* abi = abi_cg_func_info(t->c->abi, fd->fn_type);
NativeCallPlanRet* rets = NULL;
u32 nr = 0;
- if (nvalues > 1u) rv_panic(a, "multiple returns unsupported");
- if (nvalues) rets = arena_zarray(t->c->tu, NativeCallPlanRet, 4);
- if (nvalues && abi && abi->ret.kind == ABI_ARG_INDIRECT) {
+ if (value) rets = arena_zarray(t->c->tu, NativeCallPlanRet, 4);
+ if (value && abi && abi->ret.kind == ABI_ARG_INDIRECT) {
KitCgTypeId i64t = builtin_id(KIT_CG_BUILTIN_I64);
NativeLoc dstp = native_loc_reg(i64t, NATIVE_REG_INT, RV_TMP1);
NativeLoc saved = native_loc_stack(i64t, a->sret_ptr_slot, 0);
@@ -2454,19 +2453,19 @@ static void rv_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
memset(&dst_addr, 0, sizeof dst_addr);
dst_addr.base_kind = NATIVE_ADDR_BASE_REG;
dst_addr.base.reg = RV_TMP1;
- dst_addr.base_type = values[0].type;
- src_addr = rv_loc_addr(a, values[0], 0);
- src_addr.base_type = values[0].type;
+ dst_addr.base_type = value->type;
+ src_addr = rv_loc_addr(a, *value, 0);
+ src_addr.base_type = value->type;
memset(&access, 0, sizeof access);
- access.type = values[0].type;
- access.size = (u32)cg_type_size(t->c, values[0].type);
- access.align = native_type_align(t, values[0].type);
+ access.type = value->type;
+ access.size = (u32)cg_type_size(t->c, value->type);
+ access.align = native_type_align(t, value->type);
rv_copy_bytes(t, dst_addr, src_addr, access);
*out_rets = NULL;
*out_nrets = 0;
return;
}
- if (nvalues && abi && abi->ret.kind == ABI_ARG_DIRECT) {
+ if (value && abi && abi->ret.kind == ABI_ARG_DIRECT) {
u32 ni = 0, nf = 0, p;
for (p = 0; p < abi->ret.nparts; ++p) {
const ABIArgPart* part = &abi->ret.parts[p];
@@ -2474,10 +2473,10 @@ static void rv_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
part->cls == ABI_CLASS_FP ? NATIVE_REG_FP : NATIVE_REG_INT;
KitCgTypeId pty = rv_part_scalar_type(part);
Reg rreg = cls == NATIVE_REG_FP ? RV_FA0 + nf++ : RV_A0 + ni++;
- rets[nr].src = values[0];
+ rets[nr].src = *value;
if (rets[nr].src.kind == NATIVE_LOC_FRAME)
rets[nr].src =
- native_loc_stack(pty, values[0].v.frame, (i32)part->src_offset);
+ native_loc_stack(pty, value->v.frame, (i32)part->src_offset);
else if (rets[nr].src.kind == NATIVE_LOC_STACK) {
rets[nr].src.v.stack.offset += (i32)part->src_offset;
rets[nr].src.type = pty;
@@ -2486,10 +2485,10 @@ static void rv_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
rets[nr].mem = native_mem_for_type(t, pty, part->size);
nr++;
}
- } else if (nvalues) {
- rets[0].src = values[0];
- rets[0].dst = native_loc_reg(values[0].type, NATIVE_REG_INT, RV_A0);
- rets[0].mem = native_mem_for_type(t, values[0].type, 0);
+ } else if (value) {
+ rets[0].src = *value;
+ rets[0].dst = native_loc_reg(value->type, NATIVE_REG_INT, RV_A0);
+ rets[0].mem = native_mem_for_type(t, value->type, 0);
nr = 1;
}
*out_rets = rets;
@@ -4019,26 +4018,26 @@ static const char* rv_no_tail(NativeDirectTarget* d, const CGCallDesc* call) {
if (a->frame.ncallee_saves)
return "rv64 tail call: callee-saved registers in use";
memset(&nd, 0, sizeof nd);
+ u32 nresults = call->result != CG_LOCAL_NONE ? 1u : 0u;
if (call->nargs) args = arena_zarray(d->base.c->tu, NativeLoc, call->nargs);
- if (call->nresults)
- results = arena_zarray(d->base.c->tu, NativeLoc, call->nresults);
+ if (nresults) results = arena_zarray(d->base.c->tu, NativeLoc, nresults);
for (i = 0; i < call->nargs; ++i) {
args[i].kind = NATIVE_LOC_FRAME;
args[i].type = d->locals[call->args[i] - 1u].type;
args[i].cls = d->locals[call->args[i] - 1u].cls;
args[i].v.frame = d->locals[call->args[i] - 1u].home;
}
- for (i = 0; i < call->nresults; ++i) {
- results[i].kind = NATIVE_LOC_FRAME;
- results[i].type = d->locals[call->results[i] - 1u].type;
- results[i].cls = d->locals[call->results[i] - 1u].cls;
- results[i].v.frame = d->locals[call->results[i] - 1u].home;
+ if (nresults) {
+ results[0].kind = NATIVE_LOC_FRAME;
+ results[0].type = d->locals[call->result - 1u].type;
+ results[0].cls = d->locals[call->result - 1u].cls;
+ results[0].v.frame = d->locals[call->result - 1u].home;
}
nd.fn_type = call->fn_type;
nd.args = args;
nd.results = results;
nd.nargs = call->nargs;
- nd.nresults = call->nresults;
+ nd.nresults = nresults;
stack = rv_call_stack_size(d->native, &nd);
if (stack > a->incoming_stack_size)
return "rv64 tail call: stack argument area too small";
diff --git a/src/arch/wasm/ir_emit.c b/src/arch/wasm/ir_emit.c
@@ -357,11 +357,9 @@ static void wasm_ir_emit_call(WasmIrEmitter* e, const CgIrInst* in) {
}
}
d.args = args;
- if (src->nresults > 1)
- wasm_ir_fail(e, in->loc, "wasm: multiple call results not yet supported");
- if (src->nresults == 1) {
+ if (src->result != CG_LOCAL_NONE) {
d.ret =
- wasm_ir_abi_value(e, src->results[0], ret_type, abi ? &abi->ret : NULL,
+ wasm_ir_abi_value(e, src->result, ret_type, abi ? &abi->ret : NULL,
abi && abi->has_sret, 0, in->loc);
}
wasm_call((CGTarget*)&e->target->base, &d);
@@ -374,13 +372,11 @@ static void wasm_ir_emit_ret(WasmIrEmitter* e, const CgIrFunc* f,
const ABIFuncInfo* abi = abi_cg_func_info(e->target->c->abi, f->desc.fn_type);
KitCgTypeId ret_type = cg_type_func_ret_id(e->target->c, f->desc.fn_type);
CGABIValue ret;
- if (!aux || aux->nvalues == 0) {
+ if (!aux || !aux->present) {
wasm_ret((CGTarget*)&e->target->base, NULL);
return;
}
- if (aux->nvalues > 1)
- wasm_ir_fail(e, in->loc, "wasm: multiple return values not yet supported");
- ret = wasm_ir_abi_value(e, aux->values[0], ret_type, abi ? &abi->ret : NULL,
+ ret = wasm_ir_abi_value(e, aux->value, ret_type, abi ? &abi->ret : NULL,
abi && abi->has_sret, 1, in->loc);
wasm_ret((CGTarget*)&e->target->base, &ret);
}
@@ -868,8 +864,7 @@ static void wasm_ir_emit_func(WTarget* t, const CgIrFunc* f) {
fd.text_section_id = f->desc.text_section_id;
fd.group_id = f->desc.group_id;
fd.fn_type = f->desc.fn_type;
- fd.result_types = f->desc.result_types;
- fd.nresults = f->desc.nresults;
+ fd.result_type = f->desc.result_type;
fd.nparams = f->desc.nparams;
fd.loc = f->desc.loc;
fd.flags = f->desc.flags;
diff --git a/src/arch/x64/native.c b/src/arch/x64/native.c
@@ -2722,15 +2722,14 @@ static void x64_emit_call(NativeTarget* t, const NativeCallPlan* plan) {
}
static void x64_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
- const NativeLoc* values, u32 nvalues,
+ const NativeLoc* value,
NativeCallPlanRet** out_rets, u32* out_nrets) {
X64NativeTarget* a = x64_of(t);
const ABIFuncInfo* abi = abi_cg_func_info(t->c->abi, fd->fn_type);
NativeCallPlanRet* rets = NULL;
u32 nr = 0;
- if (nvalues > 1u) x64_panic(a, "multiple returns unsupported");
- if (nvalues) rets = arena_zarray(t->c->tu, NativeCallPlanRet, 4);
- if (nvalues && abi && abi->ret.kind == ABI_ARG_INDIRECT) {
+ if (value) rets = arena_zarray(t->c->tu, NativeCallPlanRet, 4);
+ if (value && abi && abi->ret.kind == ABI_ARG_INDIRECT) {
/* sret: reload destination pointer (spilled at entry) into r11, memcpy the
* source aggregate into [r11], and convention-return the pointer in rax. */
KitCgTypeId i64t = builtin_id(KIT_CG_BUILTIN_I64);
@@ -2742,13 +2741,13 @@ static void x64_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
memset(&dst_addr, 0, sizeof dst_addr);
dst_addr.base_kind = NATIVE_ADDR_BASE_REG;
dst_addr.base.reg = X64_R11;
- dst_addr.base_type = values[0].type;
- src_addr = x64_loc_addr(a, values[0], 0);
- src_addr.base_type = values[0].type;
+ dst_addr.base_type = value->type;
+ src_addr = x64_loc_addr(a, *value, 0);
+ src_addr.base_type = value->type;
memset(&access, 0, sizeof access);
- access.type = values[0].type;
- access.size = (u32)cg_type_size(t->c, values[0].type);
- access.align = native_type_align(t, values[0].type);
+ access.type = value->type;
+ access.size = (u32)cg_type_size(t->c, value->type);
+ access.align = native_type_align(t, value->type);
x64_copy_bytes(t, dst_addr, src_addr, access);
/* rax = sret pointer. Reload it (copy_bytes clobbered r11/rax). */
x64_load_part(t, native_loc_reg(i64t, NATIVE_REG_INT, X64_RAX), saved, 0,
@@ -2757,7 +2756,7 @@ static void x64_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
*out_nrets = 0;
return;
}
- if (nvalues && abi && abi->ret.kind == ABI_ARG_DIRECT) {
+ if (value && abi && abi->ret.kind == ABI_ARG_DIRECT) {
u32 ni = 0, nf = 0;
static const u32 ret_int_regs[2] = {X64_RAX, X64_RDX};
u16 p;
@@ -2768,10 +2767,10 @@ static void x64_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
KitCgTypeId pty = x64_part_scalar_type(part);
Reg rreg = cls == NATIVE_REG_FP ? (Reg)(X64_XMM0 + nf++)
: (Reg)ret_int_regs[ni++];
- rets[nr].src = values[0];
+ rets[nr].src = *value;
if (rets[nr].src.kind == NATIVE_LOC_FRAME)
rets[nr].src =
- native_loc_stack(pty, values[0].v.frame, (i32)part->src_offset);
+ native_loc_stack(pty, value->v.frame, (i32)part->src_offset);
else if (rets[nr].src.kind == NATIVE_LOC_STACK) {
rets[nr].src.v.stack.offset += (i32)part->src_offset;
rets[nr].src.type = pty;
@@ -2780,10 +2779,10 @@ static void x64_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
rets[nr].mem = native_mem_for_type(t, pty, part->size);
nr++;
}
- } else if (nvalues) {
- rets[0].src = values[0];
- rets[0].dst = native_loc_reg(values[0].type, NATIVE_REG_INT, X64_RAX);
- rets[0].mem = native_mem_for_type(t, values[0].type, 0);
+ } else if (value) {
+ rets[0].src = *value;
+ rets[0].dst = native_loc_reg(value->type, NATIVE_REG_INT, X64_RAX);
+ rets[0].mem = native_mem_for_type(t, value->type, 0);
nr = 1;
}
*out_rets = rets;
@@ -4353,26 +4352,26 @@ static const char* x64_no_tail(NativeDirectTarget* d, const CGCallDesc* call) {
if (a->frame.ncallee_saves)
return "x64 tail call: callee-saved registers in use";
memset(&nd, 0, sizeof nd);
+ u32 nresults = call->result != CG_LOCAL_NONE ? 1u : 0u;
if (call->nargs) args = arena_zarray(d->base.c->tu, NativeLoc, call->nargs);
- if (call->nresults)
- results = arena_zarray(d->base.c->tu, NativeLoc, call->nresults);
+ if (nresults) results = arena_zarray(d->base.c->tu, NativeLoc, nresults);
for (i = 0; i < call->nargs; ++i) {
args[i].kind = NATIVE_LOC_FRAME;
args[i].type = d->locals[call->args[i] - 1u].type;
args[i].cls = d->locals[call->args[i] - 1u].cls;
args[i].v.frame = d->locals[call->args[i] - 1u].home;
}
- for (i = 0; i < call->nresults; ++i) {
- results[i].kind = NATIVE_LOC_FRAME;
- results[i].type = d->locals[call->results[i] - 1u].type;
- results[i].cls = d->locals[call->results[i] - 1u].cls;
- results[i].v.frame = d->locals[call->results[i] - 1u].home;
+ if (nresults) {
+ results[0].kind = NATIVE_LOC_FRAME;
+ results[0].type = d->locals[call->result - 1u].type;
+ results[0].cls = d->locals[call->result - 1u].cls;
+ results[0].v.frame = d->locals[call->result - 1u].home;
}
nd.fn_type = call->fn_type;
nd.args = args;
nd.results = results;
nd.nargs = call->nargs;
- nd.nresults = call->nresults;
+ nd.nresults = nresults;
stack = x64_call_stack_size(d->native, &nd);
/* x64_call_stack_size includes the shadow-space prefix; the caller's incoming
* window has the same prefix, so compare against incoming_stack_size + it. */
diff --git a/src/cg/atomic.c b/src/cg/atomic.c
@@ -104,9 +104,8 @@ static KitCgSym cg_atomic_runtime_sym(KitCg* g, const char* name,
for (u32 i = 0; i < nparams; ++i) ps[i].type = params[i];
memset(&sig, 0, sizeof sig);
memset(&result, 0, sizeof result);
- result.type = ret;
- sig.results = ret ? &result : NULL;
- sig.nresults = ret ? 1u : 0u;
+ result.type = ret; /* ret == KIT_CG_TYPE_NONE -> void result */
+ sig.result = result;
sig.params = ps;
sig.nparams = nparams;
sig.call_conv = KIT_CG_CC_TARGET_C;
diff --git a/src/cg/call.c b/src/cg/call.c
@@ -109,15 +109,9 @@ static void api_call_clobber_boundary(KitCg* g, const CGCallDesc* d) {
}
static int api_tail_ret_compatible(KitCg* g, KitCgTypeId callee_fn_type) {
- u32 cn = cg_type_func_nresults(g->c, callee_fn_type);
- if (cn != g->fn_nresults) return 0;
- for (u32 i = 0; i < cn; ++i) {
- KitCgTypeId cr = cg_type_func_result_id(g->c, callee_fn_type, i);
- if (api_unalias_type(g->c, g->fn_result_types[i]) !=
- api_unalias_type(g->c, cr))
- return 0;
- }
- return 1;
+ KitCgTypeId cr = cg_type_func_result_id(g->c, callee_fn_type);
+ return api_unalias_type(g->c, g->fn_desc.result_type) ==
+ api_unalias_type(g->c, cr);
}
static int api_tail_decide(KitCg* g, const CGCallDesc* desc,
@@ -153,13 +147,13 @@ static void api_finish_call(KitCg* g, CGCallDesc* desc, CGLocal* args,
if (callee && callee->op.kind != OPK_GLOBAL) {
api_release_temp_local(g, callee_op.v.local);
}
- /* Push results in declaration order so the last result ends up on TOS. */
- for (u32 i = 0; i < desc->nresults; ++i) {
- KitCgTypeId rty = cg_type_func_result_id(g->c, desc->fn_type, i);
- api_push_call_result(g, desc->results[i], rty);
+ /* Push the single result (if any) onto the stack. */
+ if (desc->result != CG_LOCAL_NONE) {
+ KitCgTypeId rty = cg_type_func_result_id(g->c, desc->fn_type);
+ api_push_call_result(g, desc->result, rty);
}
/* ALLOWED tail call that degraded to an ordinary call: synthesize the
- * caller's return of the just-pushed result(s). */
+ * caller's return of the just-pushed result. */
if (want_tail && !emit_tail) kit_cg_ret(g);
}
@@ -167,9 +161,8 @@ void kit_cg_call(KitCg* g, uint32_t nargs, KitCgTypeId fn_type,
KitCgCallAttrs attrs) {
CgTarget* T;
KitCgTypeId fty;
- u32 nresults;
+ KitCgTypeId result_type;
CGLocal* args;
- CGLocal* results = NULL;
CGCallDesc desc;
ApiSValue callee;
Operand callee_op;
@@ -217,20 +210,9 @@ void kit_cg_call(KitCg* g, uint32_t nargs, KitCgTypeId fn_type,
emit_tail = want_tail ? api_tail_decide(g, &desc, attrs.tail) : 0;
desc.flags = emit_tail ? CG_CALL_TAIL : CG_CALL_NONE;
- nresults = emit_tail ? 0u : cg_type_func_nresults(g->c, fty);
- if (nresults > API_CG_MAX_RESULTS) {
- compiler_panic(g->c, g->cur_loc, "KitCg: call has more than %u results",
- API_CG_MAX_RESULTS);
- nresults = API_CG_MAX_RESULTS;
- }
- if (nresults) {
- results = arena_array(g->c->tu, CGLocal, nresults);
- for (u32 i = 0; i < nresults; ++i)
- results[i] =
- api_alloc_call_result(g, cg_type_func_result_id(g->c, fty, i));
- desc.results = results;
- desc.nresults = nresults;
- }
+ result_type = emit_tail ? KIT_CG_TYPE_NONE : cg_type_func_result_id(g->c, fty);
+ if (result_type != KIT_CG_TYPE_NONE)
+ desc.result = api_alloc_call_result(g, result_type);
(void)T;
api_finish_call(g, &desc, args, nargs, callee_op, &callee, want_tail,
@@ -240,9 +222,8 @@ void kit_cg_call(KitCg* g, uint32_t nargs, KitCgTypeId fn_type,
void api_call_symbol_common(KitCg* g, KitCgSym sym, uint32_t nargs,
KitCgCallAttrs attrs) {
KitCgTypeId fty;
- u32 nresults;
+ KitCgTypeId result_type;
CGLocal* args;
- CGLocal* results = NULL;
CGCallDesc desc;
Operand callee_op;
KitCgInlinePolicy inline_policy;
@@ -280,20 +261,9 @@ void api_call_symbol_common(KitCg* g, KitCgSym sym, uint32_t nargs,
emit_tail = want_tail ? api_tail_decide(g, &desc, attrs.tail) : 0;
desc.flags = emit_tail ? CG_CALL_TAIL : CG_CALL_NONE;
- nresults = emit_tail ? 0u : cg_type_func_nresults(g->c, fty);
- if (nresults > API_CG_MAX_RESULTS) {
- compiler_panic(g->c, g->cur_loc, "KitCg: call has more than %u results",
- API_CG_MAX_RESULTS);
- nresults = API_CG_MAX_RESULTS;
- }
- if (nresults) {
- results = arena_array(g->c->tu, CGLocal, nresults);
- for (u32 i = 0; i < nresults; ++i)
- results[i] =
- api_alloc_call_result(g, cg_type_func_result_id(g->c, fty, i));
- desc.results = results;
- desc.nresults = nresults;
- }
+ result_type = emit_tail ? KIT_CG_TYPE_NONE : cg_type_func_result_id(g->c, fty);
+ if (result_type != KIT_CG_TYPE_NONE)
+ desc.result = api_alloc_call_result(g, result_type);
api_finish_call(g, &desc, args, nargs, callee_op, NULL, want_tail, emit_tail);
}
@@ -303,31 +273,25 @@ void kit_cg_call_symbol(KitCg* g, KitCgSym sym, uint32_t nargs,
}
void kit_cg_ret(KitCg* g) {
- ApiSValue popped[API_CG_MAX_RESULTS];
- CGLocal values[API_CG_MAX_RESULTS];
- u32 n;
+ KitCgTypeId rty;
+ ApiSValue v;
+ CGLocal value;
if (!g) return;
- n = g->fn_nresults;
- if (n == 0) {
- g->target->ret(g->target, NULL, 0);
+ rty = g->fn_desc.result_type;
+ if (rty == KIT_CG_TYPE_NONE) {
+ g->target->ret(g->target, CG_LOCAL_NONE);
return;
}
- /* TOS holds the last result; pop in reverse so values[] is declaration
- * order, matching the order kit_cg_call pushes results. */
- for (u32 k = 0; k < n; ++k) {
- u32 idx = n - 1u - k;
- KitCgTypeId rty = g->fn_result_types[idx];
- ApiSValue v = api_pop(g);
- if (cg_type_is_aggregate(g->c, rty)) {
- values[idx] = api_materialize_call_local(g, &v, rty);
- } else {
- Operand ret_op = api_force_local(g, &v, rty);
- values[idx] = ret_op.v.local;
- }
- popped[idx] = v;
+ /* TOS holds the single result value. */
+ v = api_pop(g);
+ if (cg_type_is_aggregate(g->c, rty)) {
+ value = api_materialize_call_local(g, &v, rty);
+ } else {
+ Operand ret_op = api_force_local(g, &v, rty);
+ value = ret_op.v.local;
}
- g->target->ret(g->target, values, n);
- for (u32 i = 0; i < n; ++i) api_release(g, &popped[i]);
+ g->target->ret(g->target, value);
+ api_release(g, &v);
}
/* ============================================================
diff --git a/src/cg/cgtarget.h b/src/cg/cgtarget.h
@@ -351,9 +351,8 @@ typedef struct CGFuncDesc {
ObjSecId text_section_id;
ObjGroupId group_id; /* OBJ_GROUP_NONE if none */
KitCgTypeId fn_type;
- const KitCgTypeId* result_types;
+ KitCgTypeId result_type; /* KIT_CG_TYPE_NONE/void == no result */
const CGParamDesc* params;
- u32 nresults;
u32 nparams;
SrcLoc loc;
u32 flags; /* CGFuncDescFlag */
@@ -384,9 +383,8 @@ typedef struct CGCallDesc {
KitCgTypeId fn_type;
Operand callee;
const CGLocal* args;
- const CGLocal* results;
+ CGLocal result; /* CG_LOCAL_NONE == void callee (no result) */
u32 nargs;
- u32 nresults;
u16 flags; /* CGCallFlag */
u8 tail_policy; /* KitCgTailPolicy; meaningful when CG_CALL_TAIL is set.
* The opt recorder accepts every tail and preserves this so
@@ -671,7 +669,7 @@ struct CgTarget {
* local arguments, and local result destinations. The semantic target does
* not expose calling-convention lowering; native targets derive physical
* argument/return placement from fn_type and local metadata internally.
- * Multiple results are semantic multi-results, not ABI split parts. */
+ * `result` is the single local destination, or CG_LOCAL_NONE for void. */
void (*call)(CgTarget*, const CGCallDesc*);
/* Pure query: can `d` be emitted as a sibling (tail) call on this target,
* given the current target state? Returns NULL if yes; otherwise a short,
@@ -689,7 +687,9 @@ struct CgTarget {
* CG_CALL_TAIL can emit the sibling call. May itself be NULL, meaning the
* target supports no tail calls at all. */
const char* (*tail_call_unrealizable_reason)(CgTarget*, const CGCallDesc*);
- void (*ret)(CgTarget*, const CGLocal* values, u32 nvalues);
+ /* Return from the function. `value` is the single returned local, or
+ * CG_LOCAL_NONE for a void return. */
+ void (*ret)(CgTarget*, CGLocal value);
/* Control terminator marking statically-unreachable code (the C
* __builtin_unreachable point). Like ret/jump it ends the current basic
* block: no fall-through successor is implied. Backends typically emit a
diff --git a/src/cg/debug.c b/src/cg/debug.c
@@ -43,8 +43,8 @@ DebugTypeId api_debug_type(KitCg* g, KitCgTypeId id) {
}
case KIT_CG_TYPE_FUNC: {
Heap* h = (Heap*)g->c->ctx->heap;
- DebugTypeId ret = ty->func.nresults
- ? api_debug_type(g, ty->func.results[0].type)
+ DebugTypeId ret = ty->func.result.type
+ ? api_debug_type(g, ty->func.result.type)
: DEBUG_TYPE_NONE;
DebugTypeId* params = NULL;
DebugTypeId fn;
diff --git a/src/cg/internal.h b/src/cg/internal.h
@@ -90,10 +90,6 @@ typedef struct ApiSValue {
#define API_CG_STACK_INITIAL 16u
-/* Upper bound on a function's declared results (semantic multi-results, e.g.
- * wasm multi-value). Real counts are tiny; declaring more is rejected. */
-#define API_CG_MAX_RESULTS 16u
-
/* Largest scalar the codegen lowers as a native (lock-free) atomic. All
* current targets — aa64, x64, rv64, wasm32 — provide 8-byte (i64-width)
* atomics, so this is both the legality ceiling and the lock-free ceiling.
@@ -156,9 +152,7 @@ struct KitCg {
u32 nlocals;
u32 locals_cap;
- KitCgTypeId fn_ret_type;
- KitCgTypeId fn_result_types[API_CG_MAX_RESULTS];
- u32 fn_nresults;
+ KitCgTypeId fn_ret_type; /* KIT_CG_TYPE_NONE/void == no result */
SrcLoc cur_loc;
CGFuncDesc fn_desc;
diff --git a/src/cg/ir.c b/src/cg/ir.c
@@ -130,11 +130,6 @@ void cg_ir_module_add_file_scope_asm(CgIrModule* m, const char* src,
static CGFuncDesc dup_func_desc(Arena* a, const CGFuncDesc* in) {
CGFuncDesc out = *in;
- if (in->nresults) {
- KitCgTypeId* r = arena_array(a, KitCgTypeId, in->nresults);
- memcpy(r, in->result_types, sizeof(*r) * in->nresults);
- out.result_types = r;
- }
if (in->nparams) {
CGParamDesc* p = arena_array(a, CGParamDesc, in->nparams);
memcpy(p, in->params, sizeof(*p) * in->nparams);
@@ -338,7 +333,6 @@ ConstBytes cg_ir_dup_const_bytes(Arena* a, ConstBytes in) {
CGCallDesc cg_ir_dup_call_desc(Arena* a, const CGCallDesc* in) {
CGCallDesc out = *in;
out.args = cg_ir_dup_locals(a, in->args, in->nargs);
- out.results = cg_ir_dup_locals(a, in->results, in->nresults);
return out;
}
diff --git a/src/cg/ir.h b/src/cg/ir.h
@@ -105,8 +105,8 @@ typedef struct CgIrCallAux {
} CgIrCallAux;
typedef struct CgIrRetAux {
- CGLocal* values;
- u32 nvalues;
+ CGLocal value; /* CG_LOCAL_NONE when present == 0 (void return) */
+ u8 present;
} CgIrRetAux;
typedef struct CgIrCmpBranchAux {
diff --git a/src/cg/ir_dump.c b/src/cg/ir_dump.c
@@ -371,13 +371,13 @@ static void put_inst_extra(StrBuf* sb, const CgIrFunc* f, const CgIrInst* in) {
strbuf_putc(sb, 'L');
strbuf_put_u64(sb, (u64)aux->desc.args[i]);
}
- strbuf_put_slice(sb, SLICE_LIT("] results=["));
- for (u32 i = 0; i < aux->desc.nresults; ++i) {
- if (i) strbuf_putc(sb, ' ');
+ strbuf_put_slice(sb, SLICE_LIT("] result="));
+ if (aux->desc.result != CG_LOCAL_NONE) {
strbuf_putc(sb, 'L');
- strbuf_put_u64(sb, (u64)aux->desc.results[i]);
+ strbuf_put_u64(sb, (u64)aux->desc.result);
+ } else {
+ strbuf_putc(sb, '-');
}
- strbuf_putc(sb, ']');
if (aux->desc.flags & CG_CALL_TAIL)
strbuf_put_slice(sb, SLICE_LIT(" tail"));
}
@@ -386,13 +386,13 @@ static void put_inst_extra(StrBuf* sb, const CgIrFunc* f, const CgIrInst* in) {
case CG_IR_RET: {
const CgIrRetAux* aux = (const CgIrRetAux*)in->extra.aux;
if (aux) {
- strbuf_put_slice(sb, SLICE_LIT(" values=["));
- for (u32 i = 0; i < aux->nvalues; ++i) {
- if (i) strbuf_putc(sb, ' ');
+ strbuf_put_slice(sb, SLICE_LIT(" value="));
+ if (aux->present) {
strbuf_putc(sb, 'L');
- strbuf_put_u64(sb, (u64)aux->values[i]);
+ strbuf_put_u64(sb, (u64)aux->value);
+ } else {
+ strbuf_putc(sb, '-');
}
- strbuf_putc(sb, ']');
}
break;
}
diff --git a/src/cg/ir_recorder.c b/src/cg/ir_recorder.c
@@ -437,12 +437,12 @@ static const char* rec_tail_call_unrealizable_reason(CgTarget* t,
return NULL;
}
-static void rec_ret(CgTarget* t, const CGLocal* values, u32 nvalues) {
+static void rec_ret(CgTarget* t, CGLocal value) {
CgIrRecorder* r = rec_of(t);
CgIrInst* in = emit(r, CG_IR_RET);
CgIrRetAux* aux = AUX_NEW(r, CgIrRetAux);
- aux->values = cg_ir_dup_locals(require_func(r)->arena, values, nvalues);
- aux->nvalues = nvalues;
+ aux->value = value;
+ aux->present = value != CG_LOCAL_NONE;
in->extra.aux = aux;
}
diff --git a/src/cg/native_direct_target.c b/src/cg/native_direct_target.c
@@ -1630,12 +1630,12 @@ static void nd_call(CgTarget* t, const CGCallDesc* desc) {
memset(&plan, 0, sizeof plan);
memset(&nd, 0, sizeof nd);
memset(&callee_tmp, 0, sizeof callee_tmp);
+ u32 nresults = desc->result != CG_LOCAL_NONE ? 1u : 0u;
args = nd_loc_buf(d, d->argbuf, ND_ARG_BUF, desc->nargs);
- results = nd_loc_buf(d, d->retbuf, ND_RET_BUF, desc->nresults);
+ results = nd_loc_buf(d, d->retbuf, ND_RET_BUF, nresults);
for (u32 i = 0; i < desc->nargs; ++i)
args[i] = nd_loc_frame(d, desc->args[i], 0);
- for (u32 i = 0; i < desc->nresults; ++i)
- results[i] = nd_loc_frame(d, desc->results[i], 0);
+ if (nresults) results[0] = nd_loc_frame(d, desc->result, 0);
nd.fn_type = desc->fn_type;
nd.callee = nd_loc_operand(d, desc->callee);
if (nd.callee.kind == NATIVE_LOC_FRAME) {
@@ -1648,7 +1648,7 @@ static void nd_call(CgTarget* t, const CGCallDesc* desc) {
nd.args = args;
nd.results = results;
nd.nargs = desc->nargs;
- nd.nresults = desc->nresults;
+ nd.nresults = nresults;
nd.flags = desc->flags;
nd.tail_policy = desc->tail_policy;
nd.inline_policy = desc->inline_policy;
@@ -1684,20 +1684,23 @@ static const char* nd_tail_call_unrealizable_reason(CgTarget* t,
return "target does not expose direct tail-call lowering";
}
-static void nd_ret(CgTarget* t, const CGLocal* values, u32 nvalues) {
+static void nd_ret(CgTarget* t, CGLocal value) {
NativeDirectTarget* d = nd_of(t);
- NativeLoc* locs = NULL;
+ NativeLoc loc;
+ const NativeLoc* locp = NULL;
NativeCallPlanRet* rets = NULL;
u32 nrets = 0;
nd_flush_all(d);
if (d->ops && d->ops->emit_ret) {
- d->ops->emit_ret(d, values, nvalues);
+ d->ops->emit_ret(d, value);
return;
}
- locs = nd_loc_buf(d, d->argbuf, ND_ARG_BUF, nvalues);
- for (u32 i = 0; i < nvalues; ++i) locs[i] = nd_loc_frame(d, values[i], 0);
+ if (value != CG_LOCAL_NONE) {
+ loc = nd_loc_frame(d, value, 0);
+ locp = &loc;
+ }
ND_REQUIRE_NATIVE(d, plan_ret, "target does not plan returns");
- d->native->plan_ret(d->native, d->func, locs, nvalues, &rets, &nrets);
+ d->native->plan_ret(d->native, d->func, locp, &rets, &nrets);
for (u32 i = 0; i < nrets; ++i)
nd_write_loc(d, rets[i].dst, rets[i].src, rets[i].mem);
ND_REQUIRE_NATIVE(d, ret, "target does not emit returns");
diff --git a/src/cg/native_direct_target.h b/src/cg/native_direct_target.h
@@ -74,7 +74,8 @@ struct NativeOps {
const char* (*tail_call_unrealizable_reason)(NativeDirectTarget*,
const CGCallDesc*);
void (*emit_call)(NativeDirectTarget*, const NativeCallPlan*);
- void (*emit_ret)(NativeDirectTarget*, const CGLocal* values, u32 nvalues);
+ /* `value` is the single returned local, or CG_LOCAL_NONE for void. */
+ void (*emit_ret)(NativeDirectTarget*, CGLocal value);
void (*va_start_)(NativeDirectTarget*, Operand ap_addr);
void (*va_arg_)(NativeDirectTarget*, Operand dst, Operand ap_addr,
diff --git a/src/cg/session.c b/src/cg/session.c
@@ -49,8 +49,6 @@ static void cg_free_obj_state(KitCg* g) {
g->locals_cap = 0;
g->sym_cap = 0;
g->fn_ret_type = 0;
- g->fn_result_types[0] = 0;
- g->fn_nresults = 0;
memset(&g->fn_desc, 0, sizeof(g->fn_desc));
memset(g->fn_params, 0, sizeof(g->fn_params));
memset(g->scopes, 0, sizeof(g->scopes));
@@ -386,7 +384,7 @@ void kit_cg_func_begin_attrs(KitCg* g, KitCgSym cg_sym,
g->fn_desc.text_section_id = text_sec;
g->fn_desc.group_id = OBJ_GROUP_NONE;
g->fn_desc.fn_type = fty;
- g->fn_desc.result_types = g->fn_result_types;
+ g->fn_desc.result_type = cg_type_func_result_id(c, fty);
g->fn_desc.loc = g->cur_loc;
g->fn_desc.sym_bind = api_map_bind(attrs.sym.bind);
g->fn_desc.sym_kind = SK_FUNC;
@@ -402,16 +400,6 @@ void kit_cg_func_begin_attrs(KitCg* g, KitCgSym cg_sym,
g->fn_desc.flags |= CGFD_NORETURN;
g->fn_ret_type = cg_type_func_ret_id(c, fty);
- g->fn_nresults = cg_type_func_nresults(c, fty);
- if (g->fn_nresults > API_CG_MAX_RESULTS) {
- compiler_panic(c, g->cur_loc,
- "KitCg: function declares more than %u results",
- API_CG_MAX_RESULTS);
- g->fn_nresults = API_CG_MAX_RESULTS;
- }
- for (u32 i = 0; i < g->fn_nresults; ++i)
- g->fn_result_types[i] = cg_type_func_result_id(c, fty, i);
- g->fn_desc.nresults = g->fn_nresults;
g->nlocals = 0;
g->sp = 0;
@@ -486,8 +474,6 @@ void kit_cg_func_end(KitCg* g) {
api_debug_emit_source_locals(g);
if (g->debug) debug_func_end(g->debug);
g->fn_ret_type = KIT_CG_TYPE_NONE;
- g->fn_result_types[0] = KIT_CG_TYPE_NONE;
- g->fn_nresults = 0;
g->nscopes = 0;
memset(g->scopes, 0, sizeof g->scopes);
}
diff --git a/src/cg/type.c b/src/cg/type.c
@@ -22,8 +22,7 @@ typedef struct CgApiType {
const KitCgField* fields;
const KitCgEnumValue* values;
const KitCgFuncParam* params;
- const KitCgFuncResult* results;
- u32 nresults;
+ KitCgFuncResult result; /* result.type == KIT_CG_TYPE_NONE == void */
KitCgCallConv call_conv;
u8 kind;
u8 abi_variadic;
@@ -266,29 +265,21 @@ KitCgTypeId cg_type_func_ret_id(Compiler* c, KitCgTypeId id) {
if (ty && ty->kind == KIT_CG_TYPE_ALIAS)
return cg_type_func_ret_id(c, ty->alias.base);
if (!ty || ty->kind != KIT_CG_TYPE_FUNC) return KIT_CG_TYPE_NONE;
- return ty->func.nresults ? ty->func.results[0].type
- : builtin_id(KIT_CG_BUILTIN_VOID);
+ return ty->func.result.type ? ty->func.result.type
+ : builtin_id(KIT_CG_BUILTIN_VOID);
}
KitCgTypeId cg_func_ret_type(const CgType* fnty) {
- return fnty->func.nresults ? fnty->func.results[0].type
- : builtin_id(KIT_CG_BUILTIN_VOID);
+ return fnty->func.result.type ? fnty->func.result.type
+ : builtin_id(KIT_CG_BUILTIN_VOID);
}
-uint32_t cg_type_func_nresults(Compiler* c, KitCgTypeId id) {
+KitCgTypeId cg_type_func_result_id(Compiler* c, KitCgTypeId id) {
const CgType* ty = cg_type_get(c, id);
if (ty && ty->kind == KIT_CG_TYPE_ALIAS)
- return cg_type_func_nresults(c, ty->alias.base);
- return ty && ty->kind == KIT_CG_TYPE_FUNC ? ty->func.nresults : 0;
-}
-
-KitCgTypeId cg_type_func_result_id(Compiler* c, KitCgTypeId id, u32 index) {
- const CgType* ty = cg_type_get(c, id);
- if (ty && ty->kind == KIT_CG_TYPE_ALIAS)
- return cg_type_func_result_id(c, ty->alias.base, index);
- if (!ty || ty->kind != KIT_CG_TYPE_FUNC || index >= ty->func.nresults)
- return KIT_CG_TYPE_NONE;
- return ty->func.results[index].type;
+ return cg_type_func_result_id(c, ty->alias.base);
+ if (!ty || ty->kind != KIT_CG_TYPE_FUNC) return KIT_CG_TYPE_NONE;
+ return ty->func.result.type;
}
KitCgTypeId cg_type_func_param_id(Compiler* c, KitCgTypeId id, u32 index) {
@@ -354,14 +345,9 @@ static int cg_params_eq(const KitCgFuncParam* a, const KitCgFuncParam* b,
return 1;
}
-static int cg_results_eq(const KitCgFuncResult* a, const KitCgFuncResult* b,
- u32 n) {
- for (u32 i = 0; i < n; ++i)
- if (a[i].type != b[i].type ||
- memcmp(&a[i].attrs, &b[i].attrs, sizeof(a[i].attrs)) != 0) {
- return 0;
- }
- return 1;
+static int cg_result_eq(const KitCgFuncResult* a, const KitCgFuncResult* b) {
+ return a->type == b->type &&
+ memcmp(&a->attrs, &b->attrs, sizeof(a->attrs)) == 0;
}
static KitCgTypeId find_func_type_id(Compiler* c, KitCgFuncSig sig) {
@@ -373,12 +359,10 @@ static KitCgTypeId find_func_type_id(Compiler* c, KitCgFuncSig sig) {
for (u32 i = 0; i < n; ++i) {
CgApiType* e = CgApiTypes_at(&s->types, i);
if (!e || e->kind != CG_API_TYPE_FUNC) continue;
- if (e->nresults != sig.nresults || e->count != sig.nparams) continue;
+ if (e->count != sig.nparams) continue;
if (e->abi_variadic != (sig.abi_variadic != 0)) continue;
if (e->call_conv != sig.call_conv) continue;
- if (sig.nresults && !cg_results_eq(e->results, sig.results, sig.nresults)) {
- continue;
- }
+ if (!cg_result_eq(&e->result, &sig.result)) continue;
if (sig.nparams && !cg_params_eq(e->params, sig.params, sig.nparams)) {
continue;
}
@@ -424,17 +408,6 @@ static KitCgFuncParam* copy_cg_params(Compiler* c, const KitCgFuncParam* src,
return dst;
}
-static KitCgFuncResult* copy_cg_results(Compiler* c, const KitCgFuncResult* src,
- u32 n) {
- KitCgFuncResult* dst;
- if (!n) return NULL;
- if (!src) return NULL;
- dst = arena_array(&c->global->arena, KitCgFuncResult, n);
- if (!dst) return NULL;
- memcpy(dst, src, sizeof(*dst) * n);
- return dst;
-}
-
static CgTypeField* copy_cg_fields(Compiler* c, const KitCgField* src, u32 n) {
CgTypeField* dst;
if (!n) return NULL;
@@ -625,10 +598,8 @@ static int cg_type_set_enum(Compiler* c, CgApiType* e, KitSym tag,
}
static int cg_type_set_func(Compiler* c, CgApiType* e, KitCgFuncSig sig,
- KitCgFuncParam* params, KitCgFuncResult* results) {
- for (u32 i = 0; i < sig.nresults; ++i) {
- if (!cg_type_get(c, sig.results[i].type)) return 0;
- }
+ KitCgFuncParam* params) {
+ if (sig.result.type && !cg_type_get(c, sig.result.type)) return 0;
for (u32 i = 0; i < sig.nparams; ++i) {
if (!cg_type_get(c, sig.params[i].type)) return 0;
}
@@ -636,8 +607,7 @@ static int cg_type_set_func(Compiler* c, CgApiType* e, KitCgFuncSig sig,
e->cg.kind = KIT_CG_TYPE_FUNC;
e->cg.size = 1;
e->cg.align = 1;
- e->cg.func.results = results;
- e->cg.func.nresults = sig.nresults;
+ e->cg.func.result = sig.result;
e->cg.func.params = params;
e->cg.func.nparams = sig.nparams;
e->cg.func.call_conv = sig.call_conv;
@@ -771,22 +741,15 @@ KitCgTypeId kit_cg_type_enum(KitCompiler* c, KitSym tag, KitCgTypeId base,
KitCgTypeId kit_cg_type_func(KitCompiler* c, KitCgFuncSig sig) {
KitCgFuncParam* copied = NULL;
- KitCgFuncResult* copied_results = NULL;
KitCgTypeId id;
CgApiType* e;
- if (!c || (sig.nresults && !sig.results) || (sig.nparams && !sig.params) ||
- sig.nparams > UINT16_MAX || sig.nresults > UINT16_MAX) {
+ if (!c || (sig.nparams && !sig.params) || sig.nparams > UINT16_MAX) {
return KIT_CG_TYPE_NONE;
}
- for (u32 i = 0; i < sig.nresults; ++i) {
- if (!cg_type_get(c, sig.results[i].type)) return KIT_CG_TYPE_NONE;
- }
+ if (sig.result.type && !cg_type_get(c, sig.result.type))
+ return KIT_CG_TYPE_NONE;
id = find_func_type_id(c, sig);
if (id != KIT_CG_TYPE_NONE) return id;
- if (sig.nresults) {
- copied_results = copy_cg_results(c, sig.results, sig.nresults);
- if (!copied_results) return KIT_CG_TYPE_NONE;
- }
if (sig.nparams) {
copied = copy_cg_params(c, sig.params, sig.nparams);
if (!copied) return KIT_CG_TYPE_NONE;
@@ -798,12 +761,11 @@ KitCgTypeId kit_cg_type_func(KitCompiler* c, KitCgFuncSig sig) {
if (!e) return KIT_CG_TYPE_NONE;
e->count = sig.nparams;
e->params = copied;
- e->results = copied_results;
- e->nresults = sig.nresults;
+ e->result = sig.result;
e->call_conv = sig.call_conv;
e->abi_variadic = sig.abi_variadic != 0;
e->kind = CG_API_TYPE_FUNC;
- if (!cg_type_set_func(c, e, sig, copied, copied_results)) {
+ if (!cg_type_set_func(c, e, sig, copied)) {
return KIT_CG_TYPE_NONE;
}
return id;
@@ -869,20 +831,12 @@ uint64_t kit_cg_type_array_count(KitCompiler* c, KitCgTypeId id) {
return (ty && ty->kind == KIT_CG_TYPE_ARRAY) ? ty->array.count : 0u;
}
-uint32_t kit_cg_type_func_nresults(KitCompiler* c, KitCgTypeId id) {
- const CgType* ty = cg_type_get(c, id);
- return (ty && ty->kind == KIT_CG_TYPE_FUNC) ? ty->func.nresults : 0;
-}
-
-KitCgFuncResult kit_cg_type_func_result(KitCompiler* c, KitCgTypeId id,
- uint32_t index) {
+KitCgFuncResult kit_cg_type_func_result(KitCompiler* c, KitCgTypeId id) {
const CgType* ty = cg_type_get(c, id);
KitCgFuncResult empty;
memset(&empty, 0, sizeof(empty));
- if (!ty || ty->kind != KIT_CG_TYPE_FUNC || index >= ty->func.nresults) {
- return empty;
- }
- return ty->func.results[index];
+ if (!ty || ty->kind != KIT_CG_TYPE_FUNC) return empty;
+ return ty->func.result;
}
uint32_t kit_cg_type_func_nparams(KitCompiler* c, KitCgTypeId id) {
diff --git a/src/cg/type.h b/src/cg/type.h
@@ -39,8 +39,7 @@ typedef struct CgType {
u64 count;
} array;
struct {
- KitCgFuncResult* results;
- u32 nresults;
+ KitCgFuncResult result; /* result.type == KIT_CG_TYPE_NONE == void */
KitCgFuncParam* params;
u32 nparams;
KitCgCallConv call_conv;
@@ -90,12 +89,11 @@ int cg_type_is_void(Compiler*, KitCgTypeId);
int cg_type_is_aggregate(Compiler*, KitCgTypeId);
KitCgTypeId cg_type_ptr_to(Compiler*, KitCgTypeId);
KitCgTypeId cg_type_pointee(Compiler*, KitCgTypeId);
-/* The first result's type, or VOID when the function has no results. Kept for
- * the common single-result paths (tail-return compatibility, source backends);
- * multi-result callers use cg_type_func_nresults + cg_type_func_result_id. */
+/* The function's result type, or the VOID builtin when it returns nothing. */
KitCgTypeId cg_type_func_ret_id(Compiler*, KitCgTypeId);
-uint32_t cg_type_func_nresults(Compiler*, KitCgTypeId);
-KitCgTypeId cg_type_func_result_id(Compiler*, KitCgTypeId, uint32_t);
+/* The function's raw result type, or KIT_CG_TYPE_NONE when it returns nothing
+ * (the "has a result" predicate; cg_type_func_ret_id maps none -> VOID). */
+KitCgTypeId cg_type_func_result_id(Compiler*, KitCgTypeId);
/* First-result type (or VOID) given a resolved KIT_CG_TYPE_FUNC CgType. For
* the single-result/void consumers (ABI classification, source backends) that
* already hold the CgType. */
diff --git a/src/cg/wide.c b/src/cg/wide.c
@@ -307,8 +307,7 @@ KitCgSym api_runtime_helper(KitCg* g, const char* name, KitCgTypeId ret,
/* Runtime helpers always return a single value. */
memset(&result, 0, sizeof result);
result.type = ret;
- sig.results = &result;
- sig.nresults = 1;
+ sig.result = result;
sig.params = ps;
sig.nparams = nparams;
sig.call_conv = KIT_CG_CC_TARGET_C;
diff --git a/src/emu/cpu.c b/src/emu/cpu.c
@@ -135,8 +135,7 @@ KitCgTypeId emu_block_fn_type(Compiler* c) {
memset(&sig, 0, sizeof(sig));
memset(&result, 0, sizeof(result));
result.type = bi.id[KIT_CG_BUILTIN_I64];
- sig.results = &result;
- sig.nresults = 1;
+ sig.result = result;
sig.params = ¶m;
sig.nparams = 1;
sig.call_conv = KIT_CG_CC_TARGET_C;
diff --git a/src/opt/cg_ir_lower.c b/src/opt/cg_ir_lower.c
@@ -63,8 +63,7 @@ static OptCGFuncDesc lower_func_desc(Arena* a, const struct CGFuncDesc* in) {
out.text_section_id = in->text_section_id;
out.group_id = in->group_id;
out.fn_type = in->fn_type;
- out.result_types = in->result_types;
- out.nresults = in->nresults;
+ out.result_type = in->result_type;
out.nparams = in->nparams;
out.loc = in->loc;
out.flags = in->flags;
@@ -781,8 +780,8 @@ static void lower_call(CgIrLower* l, Inst* out, const CgIrInst* in) {
for (u32 i = 0; i < src->desc.nargs; ++i)
aux->desc.args[i] = abi_value_for_local(l, src->desc.args[i], in->loc);
}
- if (src->desc.nresults) {
- aux->desc.ret = abi_value_for_local(l, src->desc.results[0], in->loc);
+ if (src->desc.result != CG_LOCAL_NONE) {
+ aux->desc.ret = abi_value_for_local(l, src->desc.result, in->loc);
set_inst_def(out, &aux->desc.ret.storage);
}
out->type = src->desc.fn_type;
@@ -792,9 +791,9 @@ static void lower_call(CgIrLower* l, Inst* out, const CgIrInst* in) {
static void lower_ret(CgIrLower* l, Inst* out, const CgIrInst* in) {
const CgIrRetAux* src = (const CgIrRetAux*)in->extra.aux;
IRRetAux* aux = arena_znew(l->f->arena, IRRetAux);
- if (src && src->nvalues) {
+ if (src && src->present) {
aux->present = 1;
- aux->val = abi_value_for_local(l, src->values[0], in->loc);
+ aux->val = abi_value_for_local(l, src->value, in->loc);
}
out->extra.aux = aux;
}
diff --git a/src/opt/ir.h b/src/opt/ir.h
@@ -202,9 +202,8 @@ typedef struct OptCGFuncDesc {
ObjSecId text_section_id;
ObjGroupId group_id;
KitCgTypeId fn_type;
- const KitCgTypeId* result_types;
+ KitCgTypeId result_type; /* KIT_CG_TYPE_NONE/void == no result */
const CGParamDesc* params;
- u32 nresults;
u32 nparams;
SrcLoc loc;
u32 flags;
diff --git a/src/opt/pass_native_emit.c b/src/opt/pass_native_emit.c
@@ -552,8 +552,7 @@ static CGFuncDesc semantic_func_desc(NativeEmitCtx* e) {
out.text_section_id = in->text_section_id;
out.group_id = in->group_id;
out.fn_type = in->fn_type;
- out.result_types = in->result_types;
- out.nresults = in->nresults;
+ out.result_type = in->result_type;
out.nparams = in->nparams;
out.loc = in->loc;
out.flags = in->flags;
@@ -728,7 +727,7 @@ static void emit_call(NativeEmitCtx* e, Inst* in) {
static void emit_ret(NativeEmitCtx* e, Inst* in, const CGFuncDesc* fd) {
IRRetAux* aux = (IRRetAux*)in->extra.aux;
NativeLoc value = loc_none();
- NativeLoc* values = NULL;
+ const NativeLoc* values = NULL;
NativeCallPlanRet* rets = NULL;
u32 nrets = 0;
if (aux && aux->present) {
@@ -741,7 +740,7 @@ static void emit_ret(NativeEmitCtx* e, Inst* in, const CGFuncDesc* fd) {
value = abi_storage_loc(e, &aux->val, in->loc);
values = &value;
}
- e->target->plan_ret(e->target, fd, values, values ? 1u : 0u, &rets, &nrets);
+ e->target->plan_ret(e->target, fd, values, &rets, &nrets);
for (u32 i = 0; i < nrets; ++i)
write_loc(e, rets[i].dst, rets[i].src, rets[i].mem, in->loc);
/* Skip the trailing branch-to-epilogue when this IR_RET is the very last
diff --git a/test/api/abi_classify_test.c b/test/api/abi_classify_test.c
@@ -64,8 +64,7 @@ static const ABIFuncInfo* classify_fn(KitCompiler* c, KitCgTypeId ret_ty,
if (ret_ty != kit_cg_builtin_types(c).id[KIT_CG_BUILTIN_VOID]) {
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = ret_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
}
sig.params = ¶m;
sig.nparams = 1;
@@ -358,8 +357,7 @@ static const ABIFuncInfo* classify_fn_n(KitCompiler* c, KitCgTypeId ret_ty,
if (ret_ty != kit_cg_builtin_types(c).id[KIT_CG_BUILTIN_VOID]) {
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = ret_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
}
sig.params = params;
sig.nparams = nargs;
diff --git a/test/api/cg_fp_cmp_test.c b/test/api/cg_fp_cmp_test.c
@@ -128,8 +128,7 @@ static void build_cmp_fn(KitCompiler* c, KitCg* cg, const char* name,
memset(&result, 0, sizeof result);
result.type = i32;
memset(&sig, 0, sizeof sig);
- sig.results = &result;
- sig.nresults = 1;
+ sig.result = result;
sig.params = params;
sig.nparams = 2;
sig.call_conv = KIT_CG_CC_TARGET_C;
diff --git a/test/api/cg_switch_test.c b/test/api/cg_switch_test.c
@@ -101,8 +101,7 @@ static void build_switch_fn(KitCompiler* c, KitCgTypeId i32_ty,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.params = ¶m_desc;
sig.nparams = 1;
sig.call_conv = KIT_CG_CC_TARGET_C;
diff --git a/test/api/cg_type_test.c b/test/api/cg_type_test.c
@@ -115,8 +115,7 @@ static void exercise_cg_handles(KitCompiler* c, KitCgTypeId i32_ty,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.params = ¶m_desc;
sig.nparams = 1;
sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -195,8 +194,7 @@ static void exercise_cg_scalar_local(KitCompiler* c, KitCgTypeId i32_ty,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.call_conv = KIT_CG_CC_TARGET_C;
snprintf(name_buf, sizeof name_buf, "cg_scalar_local_o%d", opt_level);
@@ -265,8 +263,7 @@ static void exercise_cg_late_local_addr(KitCompiler* c, KitCgTypeId i32_ty,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.call_conv = KIT_CG_CC_TARGET_C;
snprintf(name_buf, sizeof name_buf, "cg_late_local_addr_o%d", opt_level);
@@ -412,8 +409,7 @@ static KitCgSym begin_i32_func(KitCompiler* c, KitCg* cg, KitCgTypeId i32_ty,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.call_conv = KIT_CG_CC_TARGET_C;
memset(&decl, 0, sizeof decl);
@@ -529,8 +525,7 @@ static uint32_t cg_emit_delayed_chain(KitCompiler* c, KitCgTypeId i32_ty,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.params = ¶m_desc;
sig.nparams = 1;
sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -603,8 +598,7 @@ static uint32_t cg_emit_unary_chain(KitCompiler* c, KitCgTypeId i32_ty,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.params = ¶m_desc;
sig.nparams = 1;
sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -672,8 +666,7 @@ static uint32_t cg_emit_local_shadow(KitCompiler* c, KitCgTypeId i32_ty,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.call_conv = KIT_CG_CC_TARGET_C;
memset(&decl, 0, sizeof decl);
@@ -745,8 +738,7 @@ static uint32_t cg_emit_delayed_cmp(KitCompiler* c, KitCgTypeId i32_ty,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.params = ¶m_desc;
sig.nparams = 1;
sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -822,8 +814,7 @@ static uint32_t cg_emit_delayed_store(KitCompiler* c, KitCgTypeId i32_ty,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.params = ¶m_desc;
sig.nparams = 1;
sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -904,8 +895,7 @@ static uint32_t cg_emit_delayed_pressure(KitCompiler* c, KitCgTypeId i32_ty,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.params = param_desc;
sig.nparams = NPARAMS;
sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -995,8 +985,7 @@ static uint32_t cg_emit_local_shadow_boundary(KitCompiler* c,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.call_conv = KIT_CG_CC_TARGET_C;
memset(&decl, 0, sizeof decl);
@@ -1102,8 +1091,7 @@ static uint32_t cg_emit_local_shadow_partial_store(KitCompiler* c,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i32_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.call_conv = KIT_CG_CC_TARGET_C;
memset(&decl, 0, sizeof decl);
@@ -1402,8 +1390,7 @@ static void exercise_cg_free_does_not_finish(KitCompiler* c,
KitCgFuncResult result;
memset(&result, 0, sizeof result);
result.type = i32_ty;
- sig.results = &result;
- sig.nresults = 1;
+ sig.result = result;
sig.call_conv = KIT_CG_CC_TARGET_C;
memset(&decl, 0, sizeof decl);
@@ -1579,8 +1566,7 @@ int main(void) {
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = i64_ty;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.params = params;
sig.nparams = 2;
sig.abi_variadic = 1;
@@ -1588,7 +1574,7 @@ int main(void) {
fn = kit_cg_type_func(c, sig);
EXPECT(fn != KIT_CG_TYPE_NONE, "function type failed");
EXPECT(kit_cg_type_func(c, sig) == fn, "function type id should be interned");
- EXPECT(kit_cg_type_func_result(c, fn, 0).type == i64_ty,
+ EXPECT(kit_cg_type_func_result(c, fn).type == i64_ty,
"function return mismatch");
EXPECT(kit_cg_type_func_nparams(c, fn) == 2, "function param count mismatch");
EXPECT(kit_cg_type_func_param(c, fn, 1).type == ptr_i32,
diff --git a/test/cg/ir_recorder_test.c b/test/cg/ir_recorder_test.c
@@ -87,8 +87,7 @@ static CGFuncDesc fn_desc(TestCtx* tc) {
memset(&sig, 0, sizeof sig);
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = tc->i32;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.call_conv = KIT_CG_CC_TARGET_C;
fd.fn_type = kit_cg_type_func(tc->c, sig);
fd.loc.line = 3;
@@ -144,7 +143,7 @@ static void test_records_basic_function_shape(void) {
t->load_imm(t, op_local(b, tc.i32), 2);
t->binop(t, BO_IADD, op_local(dst, tc.i32), op_local(a, tc.i32),
op_local(b, tc.i32));
- t->ret(t, &dst, 1);
+ t->ret(t, dst);
t->func_end(t);
m = cg_ir_recorder_module(t);
@@ -164,8 +163,8 @@ static void test_records_basic_function_shape(void) {
EXPECT(f->insts[2].loc.file_id == 9 && f->insts[2].loc.line == 7,
"sticky source location should be stamped on insts");
ret = (CgIrRetAux*)f->insts[3].extra.aux;
- EXPECT(ret && ret->nvalues == 1 && ret->values[0] == dst,
- "return should preserve semantic result locals");
+ EXPECT(ret && ret->present && ret->value == dst,
+ "return should preserve semantic result local");
tc_fini(&tc);
}
@@ -177,7 +176,6 @@ static void test_deep_copies_call_switch_and_const_payloads(void) {
CGLocal arg, result;
CGCallDesc call;
CGLocal call_args[1];
- CGLocal call_results[1];
CGSwitchDesc sw;
CGSwitchCase cases[2];
u8 bytes[4] = {1, 2, 3, 4};
@@ -198,16 +196,13 @@ static void test_deep_copies_call_switch_and_const_payloads(void) {
memset(&call, 0, sizeof call);
call_args[0] = arg;
- call_results[0] = result;
call.fn_type = fd.fn_type;
call.callee = op_global(12, tc.ptr);
call.args = call_args;
- call.results = call_results;
+ call.result = result;
call.nargs = 1;
- call.nresults = 1;
t->call(t, &call);
call_args[0] = 999;
- call_results[0] = 1000;
memset(&sw, 0, sizeof sw);
cases[0].value = 4;
@@ -237,7 +232,7 @@ static void test_deep_copies_call_switch_and_const_payloads(void) {
EXPECT(f->ninsts == 3, "expected call, switch, load_const");
call_aux = (CgIrCallAux*)f->insts[0].extra.aux;
EXPECT(call_aux && call_aux->desc.args[0] == arg &&
- call_aux->desc.results[0] == result,
+ call_aux->desc.result == result,
"call descriptor should be deep-copied");
switch_aux = (CgIrSwitchAux*)f->insts[1].extra.aux;
EXPECT(switch_aux && switch_aux->ncases == 2 &&
@@ -339,7 +334,7 @@ static void test_func_dump_renders_text(void) {
t->load_imm(t, op_local(b, tc.i32), 2);
t->binop(t, BO_IADD, op_local(dst, tc.i32), op_local(a, tc.i32),
op_local(b, tc.i32));
- t->ret(t, &dst, 1);
+ t->ret(t, dst);
t->func_end(t);
f = cg_ir_recorder_module(t)->funcs[0];
@@ -357,7 +352,7 @@ static void test_func_dump_renders_text(void) {
EXPECT(strstr(s, "= 40") != NULL, "should print immediate value");
EXPECT(strstr(s, "binop") != NULL, "should print binop op");
EXPECT(strstr(s, "iadd") != NULL, "should name the binop kind");
- EXPECT(strstr(s, "ret values=[") != NULL, "should print ret values");
+ EXPECT(strstr(s, "ret value=L") != NULL, "should print ret value");
kit_writer_close(w);
tc_fini(&tc);
}
diff --git a/test/cg/native_direct_target_test.c b/test/cg/native_direct_target_test.c
@@ -196,24 +196,24 @@ static void mock_emit_call(NativeTarget* t, const NativeCallPlan* plan) {
}
static void mock_plan_ret(NativeTarget* t, const CGFuncDesc* fd,
- const NativeLoc* values, u32 nvalues,
+ const NativeLoc* value,
NativeCallPlanRet** out_rets, u32* out_nrets) {
NativeCallPlanRet* r;
(void)fd;
- r = arena_zarray(t->c->tu, NativeCallPlanRet, nvalues ? nvalues : 1u);
- for (u32 i = 0; i < nvalues; ++i) {
- r[i].src = values[i];
- r[i].dst.kind = NATIVE_LOC_REG;
- r[i].dst.cls = NATIVE_REG_INT;
- r[i].dst.type = values[i].type;
- r[i].dst.v.reg = i;
- r[i].mem.type = values[i].type;
- r[i].mem.size = 4;
- r[i].mem.align = 4;
+ r = arena_zarray(t->c->tu, NativeCallPlanRet, 1);
+ if (value) {
+ r[0].src = *value;
+ r[0].dst.kind = NATIVE_LOC_REG;
+ r[0].dst.cls = NATIVE_REG_INT;
+ r[0].dst.type = value->type;
+ r[0].dst.v.reg = 0;
+ r[0].mem.type = value->type;
+ r[0].mem.size = 4;
+ r[0].mem.align = 4;
}
*out_rets = r;
- *out_nrets = nvalues;
- ev(mock_of(t), EV_PLAN_RET, 0, nvalues, 0);
+ *out_nrets = value ? 1u : 0u;
+ ev(mock_of(t), EV_PLAN_RET, 0, value ? 1u : 0u, 0);
}
static void mock_ret(NativeTarget* t) { ev(mock_of(t), EV_RET, 0, 0, 0); }
@@ -360,11 +360,10 @@ static CGFuncDesc fn_desc(TestCtx* tc) {
memset(&sig, 0, sizeof sig);
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = tc->i32;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.call_conv = KIT_CG_CC_TARGET_C;
fd.fn_type = kit_cg_type_func(tc->c, sig);
- fd.nresults = 1;
+ fd.result_type = tc->i32;
return fd;
}
@@ -409,7 +408,7 @@ static void test_frame_locals_scratch_storeback_and_branches(void) {
t->cmp_branch(t, CMP_EQ, op_local(sum, tc.i32), op_imm(16, tc.i32), done);
t->jump(t, done);
t->label_place(t, done);
- t->ret(t, &sum, 1);
+ t->ret(t, sum);
t->func_end(t);
EXPECT(count_event(&native, EV_LOAD_IMM) == 3,
@@ -440,7 +439,6 @@ static void test_call_barrier_storeback_and_max_outgoing(void) {
CGLocal arg, fnptr, result;
CGCallDesc call;
CGLocal args[1];
- CGLocal results[1];
NativeDirectTarget* nd;
tc_init(&tc);
t = make_target(&tc, &native);
@@ -454,13 +452,11 @@ static void test_call_barrier_storeback_and_max_outgoing(void) {
memset(&call, 0, sizeof call);
args[0] = arg;
- results[0] = result;
call.fn_type = fd.fn_type;
call.callee = op_local(fnptr, tc.ptr);
call.args = args;
- call.results = results;
+ call.result = result;
call.nargs = 1;
- call.nresults = 1;
t->call(t, &call);
nd = (NativeDirectTarget*)t;
diff --git a/test/cg/strength_reduce_test.c b/test/cg/strength_reduce_test.c
@@ -70,8 +70,7 @@ static KitStatus emit_binop_fn(KitCompiler* c, void* user) {
memset(&sig, 0, sizeof sig);
sig.params = ¶m_desc;
sig.nparams = 1;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.call_conv = KIT_CG_CC_TARGET_C;
memset(&decl, 0, sizeof decl);
diff --git a/test/interp/interp_smoke_test.c b/test/interp/interp_smoke_test.c
@@ -102,22 +102,17 @@ static KitInterpStatus run_leaf(TestCtx* tc, CgIrFunc* cg, int64_t* out) {
static CgIrFunc* new_func(TestCtx* tc, KitCgTypeId ret_type) {
CGFuncDesc fd;
- KitCgTypeId* rt;
memset(&fd, 0, sizeof fd);
- rt = arena_array(tc->c->tu, KitCgTypeId, 1);
- rt[0] = ret_type;
fd.fn_type = ret_type;
- fd.result_types = rt;
- fd.nresults = 1;
+ fd.result_type = ret_type;
return cg_ir_func_new(tc->c, &fd);
}
static void ret_local(CgIrFunc* cg, CGLocal v) {
CgIrRetAux* aux = arena_znew(cg->arena, CgIrRetAux);
- CGLocal rv = v;
CgIrInst* ret;
- aux->values = cg_ir_dup_locals(cg->arena, &rv, 1);
- aux->nvalues = 1;
+ aux->value = v;
+ aux->present = 1;
ret = cg_ir_emit(cg, CG_IR_RET, (SrcLoc){0, 0, 0});
ret->extra.aux = aux;
}
@@ -220,13 +215,10 @@ static CgIrFunc* new_func_p(TestCtx* tc, KitCgTypeId ret,
const KitCgTypeId* ptypes, u32 np,
CGLocal* out_params) {
CGFuncDesc fd;
- KitCgTypeId* rt;
CGParamDesc* pds;
CgIrFunc* f;
u32 i;
memset(&fd, 0, sizeof fd);
- rt = arena_array(tc->c->tu, KitCgTypeId, 1);
- rt[0] = ret;
pds = np ? arena_array(tc->c->tu, CGParamDesc, np) : NULL;
for (i = 0; i < np; ++i) {
memset(&pds[i], 0, sizeof pds[i]);
@@ -236,8 +228,7 @@ static CgIrFunc* new_func_p(TestCtx* tc, KitCgTypeId ret,
pds[i].align = ty_align(tc, ptypes[i]);
}
fd.fn_type = ret;
- fd.result_types = rt;
- fd.nresults = 1;
+ fd.result_type = ret;
fd.params = pds;
fd.nparams = np;
f = cg_ir_func_new(tc->c, &fd);
diff --git a/test/opt/cg_ir_lower_test.c b/test/opt/cg_ir_lower_test.c
@@ -85,11 +85,8 @@ static void converter_builds_cfg_and_pregs(void) {
CGFuncDesc fd;
memset(&fd, 0, sizeof fd);
- KitCgTypeId result_types[1];
- result_types[0] = tc.i32;
fd.fn_type = tc.i32;
- fd.result_types = result_types;
- fd.nresults = 1;
+ fd.result_type = tc.i32;
CgIrFunc* cg = cg_ir_func_new(tc.c, &fd);
CGLocal a = add_local(cg, tc.i32, "a");
CGLocal b = add_local(cg, tc.i32, "b");
@@ -118,9 +115,8 @@ static void converter_builds_cfg_and_pregs(void) {
li3->extra.imm = 3;
CgIrRetAux* ret_aux = arena_znew(cg->arena, CgIrRetAux);
- CGLocal retv = b;
- ret_aux->values = cg_ir_dup_locals(cg->arena, &retv, 1);
- ret_aux->nvalues = 1;
+ ret_aux->value = b;
+ ret_aux->present = 1;
CgIrInst* ret = cg_ir_emit(cg, CG_IR_RET, (SrcLoc){0, 0, 0});
ret->extra.aux = ret_aux;
diff --git a/test/opt/tiny_inline_test.c b/test/opt/tiny_inline_test.c
@@ -58,8 +58,7 @@ static void tc_init(TestCtx* tc) {
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = tc->i32;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.params = params;
sig.nparams = 1;
sig.call_conv = KIT_CG_CC_TARGET_C;
@@ -131,7 +130,7 @@ static CgIrFunc* build_callee(TestCtx* tc, ObjSymId sym, u32 nbinops,
for (u32 i = 0; i < nbinops; ++i)
t->binop(t, BO_IADD, op_local(acc, tc->i32),
op_local(i == 0 ? x : acc, tc->i32), op_local(x, tc->i32));
- t->ret(t, &acc, 1);
+ t->ret(t, acc);
t->func_end(t);
return cap.func;
}
@@ -145,7 +144,6 @@ static CgIrFunc* build_caller(TestCtx* tc, ObjSymId callee_sym,
CGCallDesc call;
CGLocal arg, res;
CGLocal cargs[1];
- CGLocal cres[1];
KitCgFuncSig sig;
memset(&cap, 0, sizeof cap);
t = make_recorder(tc, &cap);
@@ -155,8 +153,7 @@ static CgIrFunc* build_caller(TestCtx* tc, ObjSymId callee_sym,
KitCgFuncResult sig_result;
memset(&sig_result, 0, sizeof sig_result);
sig_result.type = tc->i32;
- sig.results = &sig_result;
- sig.nresults = 1;
+ sig.result = sig_result;
sig.call_conv = KIT_CG_CC_TARGET_C;
fd.fn_type = kit_cg_type_func((KitCompiler*)tc->c, sig);
t->func_begin(t, &fd);
@@ -165,16 +162,14 @@ static CgIrFunc* build_caller(TestCtx* tc, ObjSymId callee_sym,
t->load_imm(t, op_local(arg, tc->i32), 41);
memset(&call, 0, sizeof call);
cargs[0] = arg;
- cres[0] = res;
call.fn_type = tc->fn1;
call.callee = op_global(callee_sym, tc->ptr);
call.args = cargs;
call.nargs = 1;
- call.results = cres;
- call.nresults = 1;
+ call.result = res;
call.inline_policy = call_policy;
t->call(t, &call);
- t->ret(t, &res, 1);
+ t->ret(t, res);
t->func_end(t);
return cap.func;
}