abi: pass >16B aggregates by ptr-in-next-reg (Apple ARM64 ABI) + Path B for fn-ptr casts
Three stacked compiler bugs were causing iOS-sim chess to crash inside [MTLTexture replaceRegion:...]. Fixing them lets every replaceRegion call site succeed (1×1 RGBA8, 1MB R8 atlas, 440×440 chess pieces). Path B for callconv(.c) fn-pointer casts: - FunctionInfo now carries call_conv: CallConv (TypeInfo.CallConv) so function-type interning distinguishes sx-CC from C-CC. Inst.zig's Function.CallingConvention aliases the same enum. - Parser accepts an optional `callconv(.c)` suffix on fn-pointer type spellings (factored into parseOptionalCallConv() shared with parseFnDecl and parseLambda). - resolveFunctionType passes the parsed CC through functionTypeCC(). - .call_indirect reads fp.call_conv == .c and applies the C-ABI alloca+materialize for >16B aggregate args (Path A's behaviour at .call). Apple ARM64 ABI (drop LLVM byval): - Side-by-side asm diff vs clang's emission for the equivalent C call site showed LLVM's `byval` attribute lowers Apple-arm64 byval on the stack, while clang passes the struct via a pointer in the next int register (x2 for replaceRegion:). The runtime objc_msgSend dispatch path expects clang's convention. - Dropped the byval attribute from the function-signature emission and from both call sites (.call and .call_indirect). The materialize-into- alloca + pass-plain-ptr pattern stays — the call site now matches clang's `mov x2, sp` exactly. - Path A's sx-to-sx case continues to work since both ends use plain ptr (caller does alloca+store+pass, callee loads from the ptr in prologue). Protocol dispatch (emitProtocolDispatch): - Untargeted `null` lowers as const_null with type .void (per target_type orelse .void). The "wrap-value-in-alloca-pass-pointer" branch alloca'd a void slot, which LLVM's IRBuilder asserts on — EXC_BREAKPOINT in getTypeSizeInBits, manifesting as exit 133 / SIGTRAP when building the chess game. Fixed by re-emitting as constNull(void_ptr) when arg_ty == .void && expected_ty == void_ptr. - is_pointer_ty only recognized .pointer, so [*]T (many_pointer) was alloca-wrapped — the heap pixels pointer from stbi_load was stored into a stack slot and the slot's address was passed as the *void arg. Fixed by extending the check to `.pointer or .many_pointer`. metal.sx call sites + lifecycle guards: - msg_replace (replaceRegion:, MTLRegion = 48B) and the two setScissorRect: sites (MTLScissorRect = 32B) now spell their fn-pointer types with by-value params + callconv(.c) — the *MTLRegion/@local workaround is gone. - metal_begin_frame_ios bails before nextDrawable when pixel_w/h are 0 (drawableSize 0×0 makes nextDrawable abort via XPC). - metal_init_ios only sets drawableSize when dims are positive. - begin_frame's encoder/cmd_buffer failure paths now clear self.drawable so a partial failure doesn't leak a drawable back into the pool. Examples + tests: - examples/86-callconv-c-fnptr-large-aggregate.sx — new, covers Path B with C-CC fn-ptr cast. - examples/87-fnptr-cast-large-aggregate.sx — renamed from issue-0025.sx, covers Path B with default sx-CC (the negative case). - examples/85-cc-c-large-aggregate.sx — from Session 60, covers Path A. - examples/issue-0014.sx, issue-0024.sx, issue-0025.sx — removed (resolved earlier this work). 71 regression tests pass, 0 failed. Chess game builds clean for iOS sim and reaches its frame loop without aborting. Runtime: chess UI still doesn't render — remaining issue is in the UIKit lifecycle / CAMetalLayer setup (legacy-app vs scene-API hybrid), not a compiler bug. See current/CHECKPOINT.md "Next step" for the diagnosis + options.
This commit is contained in:
@@ -129,8 +129,11 @@ pub const TypeInfo = union(enum) {
|
||||
pub const FunctionInfo = struct {
|
||||
params: []const TypeId,
|
||||
ret: TypeId,
|
||||
call_conv: CallConv = .default,
|
||||
};
|
||||
|
||||
pub const CallConv = enum { default, c };
|
||||
|
||||
pub const ClosureInfo = struct {
|
||||
params: []const TypeId,
|
||||
ret: TypeId,
|
||||
@@ -337,8 +340,12 @@ pub const TypeTable = struct {
|
||||
}
|
||||
|
||||
pub fn functionType(self: *TypeTable, params: []const TypeId, ret: TypeId) TypeId {
|
||||
return self.functionTypeCC(params, ret, .default);
|
||||
}
|
||||
|
||||
pub fn functionTypeCC(self: *TypeTable, params: []const TypeId, ret: TypeId, cc: TypeInfo.CallConv) TypeId {
|
||||
const owned_params = self.alloc.dupe(TypeId, params) catch unreachable;
|
||||
return self.intern(.{ .function = .{ .params = owned_params, .ret = ret } });
|
||||
return self.intern(.{ .function = .{ .params = owned_params, .ret = ret, .call_conv = cc } });
|
||||
}
|
||||
|
||||
pub fn closureType(self: *TypeTable, params: []const TypeId, ret: TypeId) TypeId {
|
||||
@@ -653,6 +660,8 @@ fn hashTypeInfo(h: *std.hash.Wyhash, info: TypeInfo) void {
|
||||
.function => |f| {
|
||||
for (f.params) |p| h.update(std.mem.asBytes(&p));
|
||||
h.update(std.mem.asBytes(&f.ret));
|
||||
const cc_byte: u8 = @intFromEnum(f.call_conv);
|
||||
h.update(&.{cc_byte});
|
||||
},
|
||||
.closure => |c| {
|
||||
for (c.params) |p| h.update(std.mem.asBytes(&p));
|
||||
@@ -692,6 +701,7 @@ fn typeInfoEql(a: TypeInfo, b: TypeInfo) bool {
|
||||
for (f.params, g.params) |fp, gp| {
|
||||
if (fp != gp) return false;
|
||||
}
|
||||
if (f.call_conv != g.call_conv) return false;
|
||||
return f.ret == g.ret;
|
||||
},
|
||||
.closure => |c| {
|
||||
|
||||
Reference in New Issue
Block a user