From 2f39bd797d4873cf59606d1e5a721d761e11312f Mon Sep 17 00:00:00 2001 From: kalsprite Date: Mon, 10 Aug 2026 20:57:47 -0700 Subject: [PATCH 1/3] riscv abi bug --- src/llvm_abi.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 528ddca88..36397ce71 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1764,49 +1764,53 @@ namespace lbAbiRiscv64 { // Flatten down the type so it is easier to check all the ABI conditions. // Note that we also need to remove all implicit padding fields Odin adds so we keep ABI // compatibility for struct declarations. + // The flattened form is for the floating-point rules, which are about the MEMBERS; the + // integer fallback below is about the OBJECT, so `size` stays the size of the original. + LLVMTypeRef fp_type = type; + LLVMTypeKind fp_kind = kind; + i64 fp_size = size; if (kind == LLVMStructTypeKind && size <= gb_max(2*xlen, 2*flen)) { Array fields = array_make(temporary_allocator(), 0, LLVMCountStructElementTypes(type)); flatten(m, &fields, type, false); if (fields.count == 1) { - type = fields[0]; + fp_type = fields[0]; } else { - type = LLVMStructTypeInContext(c, fields.data, cast(unsigned)fields.count, false); + fp_type = LLVMStructTypeInContext(c, fields.data, cast(unsigned)fields.count, false); } - kind = LLVMGetTypeKind(type); - size = lb_sizeof(type); - GB_ASSERT_MSG(size == lb_sizeof(orig_type), "flattened: %s of size %d, original: %s of size %d", LLVMPrintTypeToString(type), size, LLVMPrintTypeToString(orig_type), lb_sizeof(orig_type)); + fp_kind = LLVMGetTypeKind(fp_type); + fp_size = lb_sizeof(fp_type); } - if (is_float(type) && size <= flen && *fprs_left >= 1) { + if (is_float(fp_type) && fp_size <= flen && *fprs_left >= 1) { *fprs_left -= 1; return non_struct(c, orig_type); } - if (kind == LLVMStructTypeKind && size <= 2*flen) { - unsigned elem_count = LLVMCountStructElementTypes(type); + if (fp_kind == LLVMStructTypeKind && fp_size <= 2*flen) { + unsigned elem_count = LLVMCountStructElementTypes(fp_type); if (elem_count == 2) { - LLVMTypeRef ty1 = LLVMStructGetTypeAtIndex(type, 0); + LLVMTypeRef ty1 = LLVMStructGetTypeAtIndex(fp_type, 0); i64 ty1s = lb_sizeof(ty1); - LLVMTypeRef ty2 = LLVMStructGetTypeAtIndex(type, 1); + LLVMTypeRef ty2 = LLVMStructGetTypeAtIndex(fp_type, 1); i64 ty2s = lb_sizeof(ty2); if (is_float(ty1) && is_float(ty2) && ty1s <= flen && ty2s <= flen && *fprs_left >= 2) { *fprs_left -= 2; - return lb_arg_type_direct(orig_type, type, nullptr, nullptr); + return lb_arg_type_direct(orig_type, fp_type, nullptr, nullptr); } if (is_float(ty1) && is_register(ty2) && ty1s <= flen && ty2s <= xlen && *fprs_left >= 1 && *gprs_left >= 1) { *fprs_left -= 1; *gprs_left -= 1; - return lb_arg_type_direct(orig_type, type, nullptr, nullptr); + return lb_arg_type_direct(orig_type, fp_type, nullptr, nullptr); } if (is_register(ty1) && is_float(ty2) && ty1s <= xlen && ty2s <= flen && *gprs_left >= 1 && *fprs_left >= 1) { *fprs_left -= 1; *gprs_left -= 1; - return lb_arg_type_direct(orig_type, type, nullptr, nullptr); + return lb_arg_type_direct(orig_type, fp_type, nullptr, nullptr); } } } From a50660b5bb672738049dcccb77733ca4de54b9f8 Mon Sep 17 00:00:00 2001 From: kalsprite Date: Mon, 10 Aug 2026 21:11:43 -0700 Subject: [PATCH 2/3] apply the FP rules to over-aligned structs --- src/llvm_abi.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 36397ce71..e06aa9244 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1766,10 +1766,12 @@ namespace lbAbiRiscv64 { // compatibility for struct declarations. // The flattened form is for the floating-point rules, which are about the MEMBERS; the // integer fallback below is about the OBJECT, so `size` stays the size of the original. + // The rules are stated over the members alone, so the aggregate's size does not gate + // them: over-alignment grows a struct without changing any member type. LLVMTypeRef fp_type = type; LLVMTypeKind fp_kind = kind; i64 fp_size = size; - if (kind == LLVMStructTypeKind && size <= gb_max(2*xlen, 2*flen)) { + if (kind == LLVMStructTypeKind) { Array fields = array_make(temporary_allocator(), 0, LLVMCountStructElementTypes(type)); flatten(m, &fields, type, false); @@ -1785,6 +1787,11 @@ namespace lbAbiRiscv64 { if (is_float(fp_type) && fp_size <= flen && *fprs_left >= 1) { *fprs_left -= 1; + if (fp_type != orig_type) { + // A struct that flattened to a single float has to be coerced to that float; + // handing back the original sends an over-aligned one to integer registers. + return lb_arg_type_direct(orig_type, fp_type, nullptr, nullptr); + } return non_struct(c, orig_type); } From aec971197a727cfe06e9013d3b250750a5c65880 Mon Sep 17 00:00:00 2001 From: kalsprite Date: Mon, 10 Aug 2026 21:34:08 -0700 Subject: [PATCH 3/3] riscv64: a pointer is not an integer in the FP calling convention --- src/llvm_abi.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index e06aa9244..06a083a2f 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1738,6 +1738,13 @@ namespace lbAbiRiscv64 { } } + // The psABI's rule is "one floating-point real and one integer (or bitfield)", and a pointer + // is not an integer. `is_register` admits pointers and keeps that meaning for its other + // callers, so the floating-point arms need their own predicate. + gb_internal bool is_int_member(LLVMTypeRef type) { + return LLVMGetTypeKind(type) == LLVMIntegerTypeKind && lb_sizeof(type) > 0; + } + gb_internal lbArgType compute_arg_type(lbModule *m, LLVMTypeRef type, int *gprs_left, int *fprs_left, Type *odin_type) { LLVMContextRef c = m->ctx; @@ -1808,13 +1815,13 @@ namespace lbAbiRiscv64 { return lb_arg_type_direct(orig_type, fp_type, nullptr, nullptr); } - if (is_float(ty1) && is_register(ty2) && ty1s <= flen && ty2s <= xlen && *fprs_left >= 1 && *gprs_left >= 1) { + if (is_float(ty1) && is_int_member(ty2) && ty1s <= flen && ty2s <= xlen && *fprs_left >= 1 && *gprs_left >= 1) { *fprs_left -= 1; *gprs_left -= 1; return lb_arg_type_direct(orig_type, fp_type, nullptr, nullptr); } - if (is_register(ty1) && is_float(ty2) && ty1s <= xlen && ty2s <= flen && *gprs_left >= 1 && *fprs_left >= 1) { + if (is_int_member(ty1) && is_float(ty2) && ty1s <= xlen && ty2s <= flen && *gprs_left >= 1 && *fprs_left >= 1) { *fprs_left -= 1; *gprs_left -= 1; return lb_arg_type_direct(orig_type, fp_type, nullptr, nullptr);