issue-0036: 16-byte integer-only struct by value trips LLVM verifier

Surfaced while writing the ffi-02-small-struct.sx baseline. The sx
#foreign decl lowers `{ s64, s64 }` (and other 16-byte integer-only
shapes like `{ s32, s32, s32, s32 }`) to `[2 x i64]` for the small-
struct register-pair ABI on AAPCS64 / SysV AMD64, but the call site
loads the struct as `{ i64, i64 }`. The two types must agree for the
LLVM verifier to accept the call:

  Call parameter type does not match function signature!
    %load = load { i64, i64 }, ptr %alloca, align 8
  [2 x i64]  %call = call [2 x i64] @issue0036_swap({ i64, i64 } %load)

Float-only 16-byte aggregates (e.g. Vec4f) work because they route
through the HFA path which keeps the struct representation. See
examples/ffi-02-small-struct.sx for the working cases.

Phase 1's #foreign lowering rework is the natural place to unify
these representations; check there before fixing inline.
This commit is contained in:
agra
2026-05-19 11:22:56 +03:00
parent 84b3fc8866
commit 36e929101b
4 changed files with 51 additions and 0 deletions

38
examples/issue-0036.sx Normal file
View File

@@ -0,0 +1,38 @@
// 16-byte integer-only struct by value through `#foreign` trips
// the LLVM verifier:
//
// Call parameter type does not match function signature!
// %loadN = load { i64, i64 }, ptr %allocaM, align 8
// [2 x i64] %callN = call [2 x i64] @issue0036_swap({ i64, i64 } %load...)
//
// The foreign-decl side lowers `Pair64` to `[2 x i64]` (ABI-coercion
// for the small-struct register-pair return on AAPCS64 / SysV AMD64),
// while the call site loads the sx struct as `{ i64, i64 }`. The two
// representations need to agree.
//
// Float-only aggregates of the same total size (e.g. Vec4f, 4×f32 =
// 16 B) work today because they're routed through the HFA path which
// keeps the struct representation. See `examples/ffi-02-small-struct.sx`
// for the working cases.
//
// Expected once fixed: print "ok = true" with exit 0. Today this
// example fails at codegen with a non-zero exit; the snapshot captures
// that failure so the test suite stays honest.
#import "modules/std.sx";
#import c {
#source "vendors/issue_0036/issue_0036.c";
};
Pair64 :: struct { a: s64; b: s64; }
issue0036_swap :: (p: Pair64) -> Pair64 #foreign;
main :: () -> s32 {
p : Pair64 = .{ a = 1, b = 2 };
q := issue0036_swap(p);
print("swap = ({}, {})\n", q.a, q.b);
print("ok = {}\n", q.a == 2 and q.b == 1);
0;
}