kit

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

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:
Minclude/kit/cg.h | 25++++++++++---------------
Mlang/c/type/type.c | 9++-------
Mlang/toy/decls.c | 7++-----
Mlang/toy/parser_core.c | 5++---
Mlang/toy/types.c | 9++-------
Mlang/wasm/cg.c | 25++++---------------------
Msrc/arch/aa64/native.c | 49++++++++++++++++++++++++-------------------------
Msrc/arch/c_target/c_emit.c | 199+++++++++++++------------------------------------------------------------------
Msrc/arch/c_target/c_emit.h | 2+-
Msrc/arch/c_target/ir_emit.c | 2+-
Msrc/arch/check_target.c | 5++---
Msrc/arch/native_target.h | 6++++--
Msrc/arch/riscv/emu.c | 3+--
Msrc/arch/riscv/native.c | 49++++++++++++++++++++++++-------------------------
Msrc/arch/wasm/ir_emit.c | 15+++++----------
Msrc/arch/x64/native.c | 49++++++++++++++++++++++++-------------------------
Msrc/cg/atomic.c | 5++---
Msrc/cg/call.c | 98+++++++++++++++++++++++++------------------------------------------------------
Msrc/cg/cgtarget.h | 12++++++------
Msrc/cg/debug.c | 4++--
Msrc/cg/internal.h | 8+-------
Msrc/cg/ir.c | 6------
Msrc/cg/ir.h | 4++--
Msrc/cg/ir_dump.c | 20++++++++++----------
Msrc/cg/ir_recorder.c | 6+++---
Msrc/cg/native_direct_target.c | 23+++++++++++++----------
Msrc/cg/native_direct_target.h | 3++-
Msrc/cg/session.c | 16+---------------
Msrc/cg/type.c | 96+++++++++++++++++++++----------------------------------------------------------
Msrc/cg/type.h | 12+++++-------
Msrc/cg/wide.c | 3+--
Msrc/emu/cpu.c | 3+--
Msrc/opt/cg_ir_lower.c | 11+++++------
Msrc/opt/ir.h | 3+--
Msrc/opt/pass_native_emit.c | 7+++----
Mtest/api/abi_classify_test.c | 6++----
Mtest/api/cg_fp_cmp_test.c | 3+--
Mtest/api/cg_switch_test.c | 3+--
Mtest/api/cg_type_test.c | 44+++++++++++++++-----------------------------
Mtest/cg/ir_recorder_test.c | 21++++++++-------------
Mtest/cg/native_direct_target_test.c | 38+++++++++++++++++---------------------
Mtest/cg/strength_reduce_test.c | 3+--
Mtest/interp/interp_smoke_test.c | 17++++-------------
Mtest/opt/cg_ir_lower_test.c | 10+++-------
Mtest/opt/tiny_inline_test.c | 15+++++----------
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 = &param; 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 = &param; 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 = &param_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 = &param_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 = &param_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 = &param_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 = &param_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 = &param_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 = &param_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; }