// Phase 1 step 1.8 (PLAN-FFI.md): >16 B non-HFA struct returns // through `#objc_call`. AAPCS64 routes these through the indirect- // return convention: caller allocates the result slot, passes its // pointer as a hidden `x8` arg with the `sret()` attribute, // callee writes through it and returns void. // // 1.6's `objc_msg_send` lowering hands the IR struct type straight // to LLVMBuildCall2 — works for ≤16 B aggregates and HFAs of any // size (since those stay register-resident) but breaks >16 B int // aggregates: LLVM accepts the signature but the AArch64 backend // expects the result in x0/x1, the runtime stub doesn't populate // those for sret-shaped returns, and the upper fields come back // as garbage. // // 1.8a (this commit): xfail. Snapshot shows garbage in the third // field — pins the broken behavior. // 1.8b (next commit): emit_llvm.zig applies the sret transform // (ret type collapses to void, prepend ptr sret param, alloca // slot at call site, load result post-call) for non-HFA >16 B // returns. Snapshot flips to all-zeros (Obj-C runtime contract). #import "modules/std.sx"; #import "modules/compiler.sx"; // 24 B non-HFA integer aggregate. Distinct from any HFA path — // no all-float / all-double check fires. Triple :: struct { a: s64; b: s64; c: s64; } main :: () -> s32 { inline if OS == .macos { t := #objc_call(Triple)(null, "tripleValue"); // Per the [nil structReturn] = 0 contract, all three fields // should be 0 after 1.8b lands. Today: a/b correct, c is // whatever the callee-saves left in the high register. print("triple = ({}, {}, {})\n", t.a, t.b, t.c); } inline if OS != .macos { print("skipped (not macos)\n"); } 0; }