authorgravatar for PecoraInPannaCotta@gmail.comGiuseppe Cesarano <PecoraInPannaCotta@gmail.com> 2026-10-12 09:35:39+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-14 21:06:53+02:00
logfa5d54e74ccc6f8648bc52dfe2e9ef3b20841a88
tree757c97c421be9f4367f06849e2bdba816d86bfda
parentffece163a2a9251e12062b7909dddadeffa34ea1
signature Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

Fixed x86_64-SysV ABI when registers are exhausted (#36615)

Fixes the original bug and another class (?) of bugs that I found while reading the file. In some places where `iterateParamTypes` was called and when the `sret` return strategy was used, the `llvm.index` wasn't incremented. This index was also used to determine whether the lowering for composed types should be `.byref` or `.multiple_llvm_types`. The missing increments caused the mismatch in the original bug report. The second issue that this PR addresses is the use of `llvm_index` to select the lowering for such composed types. Basically, a function with 7 or more floats would cause the `llvm_index` to grow to 7. The following check would then trigger: `if (it.llvm_index + classes_len > 6) {...}`, forcing the lowering to `.byref`. The problem is that if the 8th argument would have fitted in the GPRs (or even the FPU registers), this lowering would be incorrect. Closes #36581 Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36615 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

4 files changed, 272 insertions(+), 100 deletions(-)

src/codegen/llvm.zig+7-14
...@@ -2355,25 +2355,21 @@ pub const Object = struct {...@@ -2355,25 +2355,21 @@ pub const Object = struct {
23552355
2356 if (fn_info.return_type == .noreturn_type) try attributes.addFnAttr(.noreturn, &o.builder);2356 if (fn_info.return_type == .noreturn_type) try attributes.addFnAttr(.noreturn, &o.builder);
23572357
2358 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types);2358 const ret_strat = try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type));
2359 if (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)) == .sret) {2359 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types, ret_strat);
2360 if (ret_strat == .sret) {
2360 try o.addSRetFnAttributes(2361 try o.addSRetFnAttributes(
2361 attributes,2362 attributes,
2362 try o.lowerType(.fromInterned(fn_info.return_type), .in_memory),2363 try o.lowerType(.fromInterned(fn_info.return_type), .in_memory),
2363 Type.fromInterned(fn_info.return_type).abiAlignment(zcu).toLlvm(),2364 Type.fromInterned(fn_info.return_type).abiAlignment(zcu).toLlvm(),
2364 .declaration,2365 .declaration,
2365 );2366 );
2366 it.llvm_index += 1;
2367 } else if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) {2367 } else if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) {
2368 .signed => try attributes.addRetAttr(.signext, &o.builder),2368 .signed => try attributes.addRetAttr(.signext, &o.builder),
2369 .unsigned => try attributes.addRetAttr(.zeroext, &o.builder),2369 .unsigned => try attributes.addRetAttr(.zeroext, &o.builder),
2370 };2370 };
23712371
2372 const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing;2372 if (it.errorReturnTraceIndex()) |i| try attributes.addParamAttr(i, .nonnull, &o.builder);
2373 if (err_return_tracing) {
2374 try attributes.addParamAttr(it.llvm_index, .nonnull, &o.builder);
2375 it.llvm_index += 1;
2376 }
23772373
2378 var remaining_inreg_int = cc_info.inreg_int_params;2374 var remaining_inreg_int = cc_info.inreg_int_params;
2379 var remaining_inreg_float = cc_info.inreg_float_params;2375 var remaining_inreg_float = cc_info.inreg_float_params;
...@@ -3111,17 +3107,14 @@ pub const Object = struct {...@@ -3111,17 +3107,14 @@ pub const Object = struct {
3111 var llvm_params: std.ArrayList(Builder.Type) = .empty;3107 var llvm_params: std.ArrayList(Builder.Type) = .empty;
3112 defer llvm_params.deinit(o.gpa);3108 defer llvm_params.deinit(o.gpa);
31133109
3114 if (ret_strat == .sret) {3110 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types, ret_strat);
3115 try llvm_params.append(o.gpa, .ptr);3111 if (it.sretIndex() != null) try llvm_params.append(o.gpa, .ptr);
3116 }3112 if (it.errorReturnTraceIndex() != null) {
3117
3118 if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) {
3119 // First parameter is a pointer to `std.lang.StackTrace`.3113 // First parameter is a pointer to `std.lang.StackTrace`.
3120 const llvm_ptr_ty = try o.builder.ptrType(toLlvmAddressSpace(.generic, target));3114 const llvm_ptr_ty = try o.builder.ptrType(toLlvmAddressSpace(.generic, target));
3121 try llvm_params.append(o.gpa, llvm_ptr_ty);3115 try llvm_params.append(o.gpa, llvm_ptr_ty);
3122 }3116 }
31233117
3124 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types);
3125 while (try it.next()) |lowering| switch (lowering) {3118 while (try it.next()) |lowering| switch (lowering) {
3126 .no_bits => continue,3119 .no_bits => continue,
3127 .byval => {3120 .byval => {
src/codegen/llvm/FuncGen.zig+113-86
...@@ -189,24 +189,15 @@ pub fn genMainBody(fg: *FuncGen) TodoError!void {...@@ -189,24 +189,15 @@ pub fn genMainBody(fg: *FuncGen) TodoError!void {
189 const fn_ty: Type = .fromInterned(ip.getNav(fg.nav_index).resolved.?.type);189 const fn_ty: Type = .fromInterned(ip.getNav(fg.nav_index).resolved.?.type);
190 const fn_info = zcu.typeToFunc(fn_ty).?;190 const fn_info = zcu.typeToFunc(fn_ty).?;
191 const param_types = fn_info.param_types.get(ip);191 const param_types = fn_info.param_types.get(ip);
192 const ret_strat = try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type));
192193
193 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types.get(ip));194 var it = iterateParamTypes(o, fn_info.cc, param_types, ret_strat);
194195
195 // Populate `fg.ret_ptr`...196 // Populate `fg.ret_ptr`...
196 fg.ret_ptr = switch (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type))) {197 fg.ret_ptr = if (it.sretIndex()) |i| fg.wip.arg(i) else .none;
197 .sret => rp: {
198 defer it.llvm_index += 1;
199 break :rp fg.wip.arg(it.llvm_index);
200 },
201 else => .none,
202 };
203 // ...and `fg.err_ret_trace`...198 // ...and `fg.err_ret_trace`...
204 if (fn_info.cc == .auto and comp.config.any_error_tracing) {199 fg.err_ret_trace = if (it.errorReturnTraceIndex()) |i| fg.wip.arg(i) else .none;
205 fg.err_ret_trace = fg.wip.arg(it.llvm_index);200
206 it.llvm_index += 1;
207 } else {
208 fg.err_ret_trace = .none;
209 }
210 // ...and as for `fg.args`, we'll put all of the arguments into this ArrayList, and once that's201 // ...and as for `fg.args`, we'll put all of the arguments into this ArrayList, and once that's
211 // done we'll use its buffer as `fg.args`.202 // done we'll use its buffer as `fg.args`.
212 var args: std.ArrayList(Builder.Value) = .empty;203 var args: std.ArrayList(Builder.Value) = .empty;
...@@ -816,13 +807,12 @@ fn buildCall(...@@ -816,13 +807,12 @@ fn buildCall(
816 },807 },
817 };808 };
818809
819 const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing;810 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types, ret_strat);
820 if (err_return_tracing) {811 if (it.errorReturnTraceIndex() != null) {
821 assert(fg.err_ret_trace != .none);812 assert(fg.err_ret_trace != .none);
822 try llvm_args.append(fg.gpa, fg.err_ret_trace);813 try llvm_args.append(fg.gpa, fg.err_ret_trace);
823 }814 }
824815
825 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types);
826 while (try it.nextCall(arg_types)) |lowering| {816 while (try it.nextCall(arg_types)) |lowering| {
827 const arg_ty: Type = .fromInterned(arg_types[it.zig_index - 1]);817 const arg_ty: Type = .fromInterned(arg_types[it.zig_index - 1]);
828 const arg_val = arg_values[it.zig_index - 1];818 const arg_val = arg_values[it.zig_index - 1];
...@@ -922,9 +912,7 @@ fn buildCall(...@@ -922,9 +912,7 @@ fn buildCall(
922912
923 {913 {
924 // Add argument attributes.914 // Add argument attributes.
925 it = iterateParamTypes(o, fn_info.cc, fn_info.param_types);915 it = iterateParamTypes(o, fn_info.cc, fn_info.param_types, ret_strat);
926 it.llvm_index += @intFromBool(ret_strat == .sret);
927 it.llvm_index += @intFromBool(err_return_tracing);
928 var remaining_inreg_int = cc_info.inreg_int_params;916 var remaining_inreg_int = cc_info.inreg_int_params;
929 var remaining_inreg_float = cc_info.inreg_float_params;917 var remaining_inreg_float = cc_info.inreg_float_params;
930 while (try it.next()) |lowering| switch (lowering) {918 while (try it.next()) |lowering| switch (lowering) {
...@@ -7015,6 +7003,24 @@ fn typeOfIndex(fg: *FuncGen, inst: Air.Inst.Index) Type {...@@ -7015,6 +7003,24 @@ fn typeOfIndex(fg: *FuncGen, inst: Air.Inst.Index) Type {
7015}7003}
70167004
7017const ParamTypeIterator = struct {7005const ParamTypeIterator = struct {
7006 const Regs = struct {
7007 gpr: u8 = 0,
7008 fpr: u8 = 0,
7009
7010 pub fn tryTake(self: *Regs, taking: Regs) bool {
7011 if (self.gpr < taking.gpr or self.fpr < taking.fpr) return false;
7012
7013 self.gpr -= taking.gpr;
7014 self.fpr -= taking.fpr;
7015
7016 return true;
7017 }
7018
7019 pub fn total(self: Regs) u8 {
7020 return self.gpr + self.fpr;
7021 }
7022 };
7023
7018 object: *Object,7024 object: *Object,
7019 cc: std.lang.CallingConvention,7025 cc: std.lang.CallingConvention,
7020 param_types: []const InternPool.Index,7026 param_types: []const InternPool.Index,
...@@ -7024,6 +7030,9 @@ const ParamTypeIterator = struct {...@@ -7024,6 +7030,9 @@ const ParamTypeIterator = struct {
7024 types_buffer: [8]Builder.Type,7030 types_buffer: [8]Builder.Type,
7025 offsets_buffer: [9]u64,7031 offsets_buffer: [9]u64,
7026 byval_attr: ?Object.Byval,7032 byval_attr: ?Object.Byval,
7033 free_regs: Regs,
7034 has_sret: bool,
7035 has_err_ret_trace: bool,
70277036
7028 const Lowering = union(enum) {7037 const Lowering = union(enum) {
7029 no_bits,7038 no_bits,
...@@ -7393,110 +7402,128 @@ const ParamTypeIterator = struct {...@@ -7393,110 +7402,128 @@ const ParamTypeIterator = struct {
7393 const o = it.object;7402 const o = it.object;
7394 const zcu = o.zcu;7403 const zcu = o.zcu;
7395 const target = zcu.getTarget();7404 const target = zcu.getTarget();
7405
7406 var used_regs: ParamTypeIterator.Regs = .{ .gpr = 0, .fpr = 0 };
7396 const classes = x86_64_abi.classifySystemV(ty, zcu, target, .arg);7407 const classes = x86_64_abi.classifySystemV(ty, zcu, target, .arg);
7397 var types_len: u32 = 0;7408 const classes_len = std.mem.findScalar(x86_64_abi.Class, classes[0..], .none) orelse classes.len;
7398 const classes_len = for (classes, 0..) |class, class_index| switch (class) {7409 const early_lowering: ?Lowering = for (classes[0..classes_len], 0..) |class, index| switch (class) {
7399 .integer => {7410 .integer => {
7400 it.types_buffer[types_len] = try o.builder.intType(@min(8 * ty.abiSize(zcu) - 64 * class_index, 64));7411 it.types_buffer[used_regs.total()] = try o.builder.intType(@min(8 * ty.abiSize(zcu) - 64 * index, 64));
7401 it.offsets_buffer[types_len] = 8 * class_index;7412 it.offsets_buffer[used_regs.total()] = 8 * index;
7402 types_len += 1;7413 used_regs.gpr += 1;
7403 },7414 },
7404 .sse => {7415 .sse => {
7405 it.types_buffer[types_len] = .double;7416 it.types_buffer[used_regs.total()] = .double;
7406 it.offsets_buffer[types_len] = 8 * class_index;7417 it.offsets_buffer[used_regs.total()] = 8 * index;
7407 types_len += 1;7418 used_regs.fpr += 1;
7408 },7419 },
7409 .sseup => {7420 .sseup => {
7410 if (it.types_buffer[types_len - 1] == .double) {7421 if (it.types_buffer[used_regs.total() - 1] == .double) {
7411 if (ty.isVector(zcu)) {7422 if (ty.isVector(zcu)) break .byval;
7412 it.zig_index += 1;7423 it.types_buffer[used_regs.total() - 1] = .fp128;
7413 it.llvm_index += 1;
7414 return .byval;
7415 }
7416 it.types_buffer[types_len - 1] = .fp128;
7417 } else {7424 } else {
7418 it.types_buffer[types_len] = .double;7425 it.types_buffer[used_regs.total()] = .double;
7419 it.offsets_buffer[types_len] = 8 * class_index;7426 it.offsets_buffer[used_regs.total()] = 8 * index;
7420 types_len += 1;7427 used_regs.fpr += 1;
7421 }7428 }
7422 },7429 },
7423 .float => {7430 .float => {
7424 it.types_buffer[types_len] = .float;7431 it.types_buffer[used_regs.total()] = .float;
7425 it.offsets_buffer[types_len] = 8 * class_index;7432 it.offsets_buffer[used_regs.total()] = 8 * index;
7426 types_len += 1;7433 used_regs.fpr += 1;
7427 },7434 },
7428 .float_combine => {7435 .float_combine => {
7429 it.types_buffer[types_len] = try it.object.builder.vectorType(.normal, 2, .float);7436 it.types_buffer[used_regs.total()] = try it.object.builder.vectorType(.normal, 2, .float);
7430 it.offsets_buffer[types_len] = 8 * class_index;7437 it.offsets_buffer[used_regs.total()] = 8 * index;
7431 types_len += 1;7438 used_regs.fpr += 1;
7432 },7439 },
7433 .x87 => {7440 .x87, .memory => break .byref,
7434 it.zig_index += 1;7441 .integer_per_element => {
7435 it.llvm_index += 1;7442 used_regs.gpr += @intCast(@min(ty.vectorLen(zcu), it.free_regs.gpr));
7436 it.byval_attr = .{};7443 break .byval;
7437 return .byref;
7438 },
7439 .x87up => unreachable,
7440 .none => break class_index,
7441 .memory => {
7442 it.zig_index += 1;
7443 it.llvm_index += 1;
7444 it.byval_attr = .{};
7445 return .byref;
7446 },7444 },
7447 .win_i128 => unreachable, // windows only7445 .none,
7446 .x87up,
7447 // Only produced by `classifyWindows`; see `next_x86_64_win`.
7448 .win_i128,
7448 .bool_vector_mask,7449 .bool_vector_mask,
7449 .integer_per_element,
7450 .sse_per_element,7450 .sse_per_element,
7451 // Only produced for `ctx == .ret`; this classifies with `.arg`.
7451 .sse_sse_x87_per_qword,7452 .sse_sse_x87_per_qword,
7452 .sse_per_xword,7453 .sse_per_xword,
7453 .sse_per_yword,7454 .sse_per_yword,
7454 .sse_per_zword,7455 .sse_per_zword,
7455 => {7456 => unreachable,
7456 it.zig_index += 1;7457 } else null;
7457 it.llvm_index += 1;7458
7458 return .byval;7459 const fits = it.free_regs.tryTake(used_regs);
7460
7461 const len = used_regs.total();
7462 it.offsets_buffer[len] = 8 * classes_len;
7463 it.types_len = len;
7464
7465 const lowering: Lowering = early_lowering orelse switch (len) {
7466 0 => unreachable,
7467 1 => single: {
7468 if (isByRef(ty, zcu)) break :single .multiple_llvm_types;
7469
7470 const reg_ty = it.types_buffer[0];
7471 const llvm_ty = try o.lowerType(ty, .as_value);
7472 break :single if (reg_ty == llvm_ty or (reg_ty == .i64 and llvm_ty.isPointer(&o.builder)))
7473 .byval
7474 else
7475 .multiple_llvm_types;
7459 },7476 },
7460 } else classes.len;7477 else => if (fits) .multiple_llvm_types else .byref,
7461 if (types_len > 1) {7478 };
7462 if (it.llvm_index + classes_len > 6) {7479
7463 it.zig_index += 1;
7464 it.llvm_index += 1;
7465 it.byval_attr = .{};
7466 return .byref;
7467 }
7468 } else if (!isByRef(ty, zcu)) {
7469 const llvm_ty = try o.lowerType(ty, .as_value);
7470 if (it.types_buffer[0] == llvm_ty or
7471 (it.types_buffer[0] == .i64 and llvm_ty.isPointer(&o.builder)))
7472 {
7473 it.zig_index += 1;
7474 it.llvm_index += 1;
7475 return .byval;
7476 }
7477 }
7478 it.offsets_buffer[types_len] = 8 * classes_len;
7479 it.types_len = types_len;
7480 it.llvm_index += types_len;
7481 it.zig_index += 1;7480 it.zig_index += 1;
7482 return .multiple_llvm_types;7481 it.llvm_index += if (lowering == .multiple_llvm_types) len else 1;
7482
7483 if (lowering == .byref) it.byval_attr = .{};
7484
7485 return lowering;
7486 }
7487
7488 pub fn sretIndex(it: ParamTypeIterator) ?u32 {
7489 return if (it.has_sret) 0 else null;
7490 }
7491
7492 pub fn errorReturnTraceIndex(it: ParamTypeIterator) ?u32 {
7493 return if (it.has_err_ret_trace)
7494 if (it.sretIndex() != null) 1 else 0
7495 else
7496 null;
7483 }7497 }
7484};7498};
7485pub fn iterateParamTypes(7499pub fn iterateParamTypes(
7486 object: *Object,7500 object: *Object,
7487 cc: std.lang.CallingConvention,7501 cc: std.lang.CallingConvention,
7488 param_types: []const InternPool.Index,7502 param_types: []const InternPool.Index,
7503 ret_strat: FnReturnStrat,
7489) ParamTypeIterator {7504) ParamTypeIterator {
7505 const has_sret = ret_strat == .sret;
7506 const has_err_ret_trace = cc == .auto and object.zcu.comp.config.any_error_tracing;
7507
7490 return .{7508 return .{
7491 .object = object,7509 .object = object,
7492 .cc = cc,7510 .cc = cc,
7493 .param_types = param_types,7511 .param_types = param_types,
7494 .zig_index = 0,7512 .zig_index = 0,
7495 .llvm_index = 0,7513 .llvm_index = @as(u32, @intFromBool(has_sret)) + @intFromBool(has_err_ret_trace),
7496 .types_len = undefined,7514 .types_len = undefined,
7497 .types_buffer = undefined,7515 .types_buffer = undefined,
7498 .offsets_buffer = undefined,7516 .offsets_buffer = undefined,
7499 .byval_attr = null,7517 .byval_attr = null,
7518 .free_regs = switch (cc) {
7519 .x86_64_sysv, .x86_64_x32 => .{
7520 .gpr = 6 - @as(u8, @intFromBool(has_sret)),
7521 .fpr = 8,
7522 },
7523 else => undefined,
7524 },
7525 .has_sret = has_sret,
7526 .has_err_ret_trace = has_err_ret_trace,
7500 };7527 };
7501}7528}
75027529
test/c_abi/cfuncs.c+65
...@@ -15834,6 +15834,13 @@ struct BigStruct {...@@ -15834,6 +15834,13 @@ struct BigStruct {
1583415834
15835void zig_big_struct(struct BigStruct);15835void zig_big_struct(struct BigStruct);
1583615836
15837struct Struct_u64_u64_u64_u64 zig_sret_gpr_straddle(
15838 struct Struct_u64_u64, struct Struct_u64_u64, struct Struct_u64_u64);
15839uint64_t zig_sse_then_gpr(double, double, double, double, double, double,
15840 struct Struct_u64_u64);
15841double zig_sse_budget_straddle(double, double, double, double, double, double,
15842 double, double, struct Struct_f64_f64);
15843
15837union BigUnion {15844union BigUnion {
15838 struct BigStruct a;15845 struct BigStruct a;
15839};15846};
...@@ -16277,6 +16284,64 @@ struct SplitStructMixed c_ret_split_struct_mixed(void) {...@@ -16277,6 +16284,64 @@ struct SplitStructMixed c_ret_split_struct_mixed(void) {
16277 return s;16284 return s;
16278}16285}
1627916286
16287struct Struct_u64_u64_u64_u64 c_sret_gpr_straddle(
16288 struct Struct_u64_u64 p, struct Struct_u64_u64 q, struct Struct_u64_u64 r) {
16289 assert_or_panic(p.a == 1);
16290 assert_or_panic(p.b == 2);
16291 assert_or_panic(q.a == 3);
16292 assert_or_panic(q.b == 4);
16293 assert_or_panic(r.a == 5);
16294 assert_or_panic(r.b == 6);
16295 struct Struct_u64_u64_u64_u64 res = {p.a, q.b, r.a, r.b};
16296 return res;
16297}
16298
16299uint64_t c_sse_then_gpr(double a, double b, double c, double d, double e, double f,
16300 struct Struct_u64_u64 s) {
16301 assert_or_panic(a == 1.0);
16302 assert_or_panic(b == 2.0);
16303 assert_or_panic(c == 3.0);
16304 assert_or_panic(d == 4.0);
16305 assert_or_panic(e == 5.0);
16306 assert_or_panic(f == 6.0);
16307 return s.a + s.b;
16308}
16309
16310double c_sse_budget_straddle(double a, double b, double c, double d, double e, double f,
16311 double g, double h, struct Struct_f64_f64 s) {
16312 assert_or_panic(a == 1.0);
16313 assert_or_panic(b == 2.0);
16314 assert_or_panic(c == 3.0);
16315 assert_or_panic(d == 4.0);
16316 assert_or_panic(e == 5.0);
16317 assert_or_panic(f == 6.0);
16318 assert_or_panic(g == 7.0);
16319 assert_or_panic(h == 8.0);
16320 return s.a + s.b;
16321}
16322
16323void c_test_sret_gpr_straddle(void) {
16324 struct Struct_u64_u64 p = {10, 20};
16325 struct Struct_u64_u64 q = {30, 40};
16326 struct Struct_u64_u64 r = {50, 60};
16327 struct Struct_u64_u64_u64_u64 res = zig_sret_gpr_straddle(p, q, r);
16328 assert_or_panic(res.a == 10);
16329 assert_or_panic(res.b == 40);
16330 assert_or_panic(res.c == 50);
16331 assert_or_panic(res.d == 60);
16332}
16333
16334void c_test_sse_then_gpr(void) {
16335 struct Struct_u64_u64 s = {100, 23};
16336 assert_or_panic(zig_sse_then_gpr(10.0, 20.0, 30.0, 40.0, 50.0, 60.0, s) == 123);
16337}
16338
16339void c_test_sse_budget_straddle(void) {
16340 struct Struct_f64_f64 s = {100.0, 23.0};
16341 assert_or_panic(zig_sse_budget_straddle(10.0, 20.0, 30.0, 40.0, 50.0, 60.0,
16342 70.0, 80.0, s) == 123.0);
16343}
16344
16280struct BigStruct c_big_struct_both(struct BigStruct x) {16345struct BigStruct c_big_struct_both(struct BigStruct x) {
16281 assert_or_panic(x.a == 1);16346 assert_or_panic(x.a == 1);
16282 assert_or_panic(x.b == 2);16347 assert_or_panic(x.b == 2);
test/c_abi/main.zig+87
...@@ -17285,6 +17285,93 @@ export fn zig_big_struct_both(x: BigStruct) BigStruct {...@@ -17285,6 +17285,93 @@ export fn zig_big_struct_both(x: BigStruct) BigStruct {
17285 return s;17285 return s;
17286}17286}
1728717287
17288extern fn c_sret_gpr_straddle(Struct_u64_u64, Struct_u64_u64, Struct_u64_u64) Struct_u64_u64_u64_u64;
17289extern fn c_test_sret_gpr_straddle() void;
17290
17291test "sret with integer parameters straddling the register budget" {
17292 if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
17293 if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
17294
17295 const p: Struct_u64_u64 = .{ .a = 1, .b = 2 };
17296 const q: Struct_u64_u64 = .{ .a = 3, .b = 4 };
17297 const r: Struct_u64_u64 = .{ .a = 5, .b = 6 };
17298 const res = c_sret_gpr_straddle(p, q, r);
17299 try expect(res.a == 1);
17300 try expect(res.b == 4);
17301 try expect(res.c == 5);
17302 try expect(res.d == 6);
17303 c_test_sret_gpr_straddle();
17304}
17305
17306export fn zig_sret_gpr_straddle(
17307 p: Struct_u64_u64,
17308 q: Struct_u64_u64,
17309 r: Struct_u64_u64,
17310) Struct_u64_u64_u64_u64 {
17311 expect(p.a == 10) catch @panic("test failure: zig_sret_gpr_straddle p.a");
17312 expect(p.b == 20) catch @panic("test failure: zig_sret_gpr_straddle p.b");
17313 expect(q.a == 30) catch @panic("test failure: zig_sret_gpr_straddle q.a");
17314 expect(q.b == 40) catch @panic("test failure: zig_sret_gpr_straddle q.b");
17315 expect(r.a == 50) catch @panic("test failure: zig_sret_gpr_straddle r.a");
17316 expect(r.b == 60) catch @panic("test failure: zig_sret_gpr_straddle r.b");
17317 return .{ .a = p.a, .b = q.b, .c = r.a, .d = r.b };
17318}
17319
17320extern fn c_sse_then_gpr(f64, f64, f64, f64, f64, f64, Struct_u64_u64) u64;
17321extern fn c_test_sse_then_gpr() void;
17322
17323test "integer aggregate after the float parameter registers" {
17324 if (builtin.cpu.arch.isPowerPC32()) return error.SkipZigTest;
17325
17326 const s: Struct_u64_u64 = .{ .a = 100, .b = 23 };
17327 try expect(c_sse_then_gpr(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, s) == 123);
17328 c_test_sse_then_gpr();
17329}
17330
17331export fn zig_sse_then_gpr(a: f64, b: f64, c: f64, d: f64, e: f64, f: f64, s: Struct_u64_u64) u64 {
17332 expect(a == 10.0) catch @panic("test failure: zig_sse_then_gpr a");
17333 expect(b == 20.0) catch @panic("test failure: zig_sse_then_gpr b");
17334 expect(c == 30.0) catch @panic("test failure: zig_sse_then_gpr c");
17335 expect(d == 40.0) catch @panic("test failure: zig_sse_then_gpr d");
17336 expect(e == 50.0) catch @panic("test failure: zig_sse_then_gpr e");
17337 expect(f == 60.0) catch @panic("test failure: zig_sse_then_gpr f");
17338 return s.a +% s.b;
17339}
17340
17341extern fn c_sse_budget_straddle(f64, f64, f64, f64, f64, f64, f64, f64, Struct_f64_f64) f64;
17342extern fn c_test_sse_budget_straddle() void;
17343
17344test "float aggregate straddling the float register budget" {
17345 if (builtin.cpu.arch.isPowerPC()) return error.SkipZigTest;
17346 if (builtin.cpu.arch.isRiscv32()) return error.SkipZigTest;
17347
17348 const s: Struct_f64_f64 = .{ .a = 100.0, .b = 23.0 };
17349 try expect(c_sse_budget_straddle(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, s) == 123.0);
17350 c_test_sse_budget_straddle();
17351}
17352
17353export fn zig_sse_budget_straddle(
17354 a: f64,
17355 b: f64,
17356 c: f64,
17357 d: f64,
17358 e: f64,
17359 f: f64,
17360 g: f64,
17361 h: f64,
17362 s: Struct_f64_f64,
17363) f64 {
17364 expect(a == 10.0) catch @panic("test failure: zig_sse_budget_straddle a");
17365 expect(b == 20.0) catch @panic("test failure: zig_sse_budget_straddle b");
17366 expect(c == 30.0) catch @panic("test failure: zig_sse_budget_straddle c");
17367 expect(d == 40.0) catch @panic("test failure: zig_sse_budget_straddle d");
17368 expect(e == 50.0) catch @panic("test failure: zig_sse_budget_straddle e");
17369 expect(f == 60.0) catch @panic("test failure: zig_sse_budget_straddle f");
17370 expect(g == 70.0) catch @panic("test failure: zig_sse_budget_straddle g");
17371 expect(h == 80.0) catch @panic("test failure: zig_sse_budget_straddle h");
17372 return s.a + s.b;
17373}
17374
17288export fn zig_ret_bool() bool {17375export fn zig_ret_bool() bool {
17289 return true;17376 return true;
17290}17377}