Files
sx/examples/ffi-objc-call-07-fp-hfa-return.sx
agra 794a49e938 ffi 1.9: 4×f64 HFA round-trip through #objc_call (UIEdgeInsets shape)
105/105 regression tests pass (+ffi-objc-call-07-fp-hfa-return).

Same round-trip pattern as 1.8 — register an Obj-C class at
runtime with class_addMethod, IMP returns specific non-zero values,
#objc_call reads them back — but for an all-double 32 B HFA
instead of a 24 B int aggregate.

Locks in the f32-vs-f64 landmine that bit us when we first
wrote safeAreaInsets in uikit.sx: the homogeneous-float-aggregate
ABI routes 1..4 f32 or f64 fields through v0..v3 (AAPCS64) /
xmm0..xmm3 (SysV AMD64) WITHOUT integer coercion. As long as the
LLVM call-site function type carries the precise struct (which
our `objc_msg_send` arm does), the backend lowers it correctly.

This is the smaller cousin of 1.8 — 1.8 needed an emit_llvm code
change to make the sret transform work; 1.9 needs no codegen
change because HFAs of any size up to v0..v3 stay register-resident.
The test just pins that path with a real, value-bearing IMP so a
future ABI-rule shake-up has a regression net.
2026-05-19 18:51:56 +03:00

47 lines
1.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Phase 1 step 1.9 (PLAN-FFI.md): all-double HFA returns through
// `#objc_call`. 4×f64 = 32 B, stays in v0..v3 on AAPCS64 and
// xmm0..xmm3 on SysV AMD64 — same shape as UIEdgeInsets / NSRect /
// CGRect, the f32-vs-f64 landmine that bit us when we first wrote
// `safeAreaInsets` in uikit.sx.
//
// Nominally covered by ffi-objc-call-05's nil-recv NSRect case,
// but that only checks that zeros come back. Here we install a
// real IMP that returns specific non-zero values and verify each
// field comes through the float-register file intact.
#import "modules/std.sx";
#import "modules/compiler.sx";
#import "modules/std/objc.sx";
UIEdgeInsets :: struct {
top: f64;
left: f64;
bottom: f64;
right: f64;
}
insets_imp :: (self: *void, _cmd: *void) -> UIEdgeInsets callconv(.c) {
UIEdgeInsets.{ top = 1.5, left = 2.5, bottom = 3.5, right = 4.5 };
}
main :: () -> s32 {
inline if OS == .macos {
ns_object := objc_getClass("NSObject".ptr);
my_cls := objc_allocateClassPair(ns_object, "SxInsetsProbe".ptr, 0);
sel := sel_registerName("safeAreaInsets".ptr);
// Method type encoding: {UIEdgeInsets=dddd}@: → returns 4×f64,
// implicit (self: id, _cmd: SEL). `d` = double.
ok := class_addMethod(my_cls, sel, xx insets_imp, "{UIEdgeInsets=dddd}@:".ptr);
print("addMethod = {}\n", ok);
objc_registerClassPair(my_cls);
instance := class_createInstance(my_cls, 0);
ins := #objc_call(UIEdgeInsets)(instance, "safeAreaInsets");
print("insets = ({}, {}, {}, {})\n", ins.top, ins.left, ins.bottom, ins.right);
}
inline if OS != .macos {
print("skipped (not macos)\n");
}
0;
}