Files
sx/examples/101-ffi-medium-struct.sx
agra 7d2c2fb062 emit_llvm: bridge struct<->array ABI for 9..16-byte foreign structs
Resolves issue-0036 (LLVM verifier failure on 16-byte integer-only
struct by value through #foreign). The mismatch:

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

`abiCoerceParamType` had already chosen `[2 x i64]` for 9..16-byte
non-HFA structs (the AAPCS64 / SysV AMD64 register-pair ABI slot for
that size class) on the foreign-decl side, but `coerceArg` only knew
how to bridge struct<->integer (the ≤8 B case) — not struct<->array.
LLVM's verifier rejects type-mismatched call args, so the call site
never landed.

Added the symmetric branches in coerceArg:
  - Struct -> Array : alloca <array>; store <struct>; load <array>
  - Array -> Struct : alloca <array>; store <array>;  load <struct>

Both use the LLVM opaque-pointer memory-bitcast pattern already in
place for the integer case. They're paired with the existing
i64 <-> small-struct bridge so all four (≤8 B int, 9..16 B int,
16 B HFA, >16 B byval) ABI slots round-trip cleanly through
emit_llvm now.

File mechanics: promotes the issue-0036 repro to a focused feature
example per CLAUDE.md's issue-resolution workflow:

  examples/issue-0036.sx              -> examples/101-ffi-medium-struct.sx
  tests/expected/issue-0036.{txt,exit} -> tests/expected/101-ffi-medium-struct.{txt,exit}
  vendors/issue_0036/issue_0036.c     -> vendors/ffi_medium_struct/ffi_medium_struct.c

Snapshot updated to the passing output. 89/89 regression tests pass;
chess Android build still clean.
2026-05-19 11:31:04 +03:00

34 lines
1.1 KiB
Plaintext

// 16-byte integer-only struct passed by value through `#foreign`.
//
// emit_llvm.zig's `abiCoerceParamType` routes 9..16-byte non-HFA
// structs through `[2 x i64]` for register-pair passing on AAPCS64 /
// SysV AMD64. The call-site side loads the sx struct as `{ i64, i64 }`,
// so the verifier needs a memory-bitcast bridge in both directions
// (`abi.struct2arr` on the way in, `abi.arr2struct` on the way back).
//
// Different ABI path from 8-byte register-pair structs (coerce to
// i64), HFAs at the same total size (Vec4f stays a struct), and
// >16-byte aggregates (passed via pointer + byval).
//
// This file was originally filed as issue-0036 (LLVM verifier
// failure repro). Promoted to a focused feature example once the
// emit_llvm.zig coerceArg branches landed.
#import "modules/std.sx";
#import c {
#source "vendors/ffi_medium_struct/ffi_medium_struct.c";
};
Pair64 :: struct { a: s64; b: s64; }
ffi_pair64_swap :: (p: Pair64) -> Pair64 #foreign;
main :: () -> s32 {
p : Pair64 = .{ a = 1, b = 2 };
q := ffi_pair64_swap(p);
print("swap = ({}, {})\n", q.a, q.b);
print("ok = {}\n", q.a == 2 and q.b == 1);
0;
}