authorgravatar for PecoraInPannaCotta@gmail.comGiuseppe Cesarano <PecoraInPannaCotta@gmail.com> 2026-10-12 09:35:39+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-12 09:35:39+02:00
log4ed10da2cf9d73c045b110d58c53a840ed9ae18d
treecf37a9745fc036586fe8218f7f37bed1d32bc033
parenteee0e6d70e8b4987a608fc1ea9451fcaf9dd6d16

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
......@@ -2350,25 +2350,21 @@ pub const Object = struct {
23502350
23512351 if (fn_info.return_type == .noreturn_type) try attributes.addFnAttr(.noreturn, &o.builder);
23522352
2353 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types);
2354 if (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)) == .sret) {
2353 const ret_strat = try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type));
2354 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types, ret_strat);
2355 if (ret_strat == .sret) {
23552356 try o.addSRetFnAttributes(
23562357 attributes,
23572358 try o.lowerType(.fromInterned(fn_info.return_type), .in_memory),
23582359 Type.fromInterned(fn_info.return_type).abiAlignment(zcu).toLlvm(),
23592360 .declaration,
23602361 );
2361 it.llvm_index += 1;
23622362 } else if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) {
23632363 .signed => try attributes.addRetAttr(.signext, &o.builder),
23642364 .unsigned => try attributes.addRetAttr(.zeroext, &o.builder),
23652365 };
23662366
2367 const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing;
2368 if (err_return_tracing) {
2369 try attributes.addParamAttr(it.llvm_index, .nonnull, &o.builder);
2370 it.llvm_index += 1;
2371 }
2367 if (it.errorReturnTraceIndex()) |i| try attributes.addParamAttr(i, .nonnull, &o.builder);
23722368
23732369 var remaining_inreg_int = cc_info.inreg_int_params;
23742370 var remaining_inreg_float = cc_info.inreg_float_params;
......@@ -3106,17 +3102,14 @@ pub const Object = struct {
31063102 var llvm_params: std.ArrayList(Builder.Type) = .empty;
31073103 defer llvm_params.deinit(o.gpa);
31083104
3109 if (ret_strat == .sret) {
3110 try llvm_params.append(o.gpa, .ptr);
3111 }
3112
3113 if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) {
3105 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types, ret_strat);
3106 if (it.sretIndex() != null) try llvm_params.append(o.gpa, .ptr);
3107 if (it.errorReturnTraceIndex() != null) {
31143108 // First parameter is a pointer to `std.lang.StackTrace`.
31153109 const llvm_ptr_ty = try o.builder.ptrType(toLlvmAddressSpace(.generic, target));
31163110 try llvm_params.append(o.gpa, llvm_ptr_ty);
31173111 }
31183112
3119 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types);
31203113 while (try it.next()) |lowering| switch (lowering) {
31213114 .no_bits => continue,
31223115 .byval => {
src/codegen/llvm/FuncGen.zig+113-86
......@@ -189,24 +189,15 @@ pub fn genMainBody(fg: *FuncGen) TodoError!void {
189189 const fn_ty: Type = .fromInterned(ip.getNav(fg.nav_index).resolved.?.type);
190190 const fn_info = zcu.typeToFunc(fn_ty).?;
191191 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
195196 // Populate `fg.ret_ptr`...
196 fg.ret_ptr = switch (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type))) {
197 .sret => rp: {
198 defer it.llvm_index += 1;
199 break :rp fg.wip.arg(it.llvm_index);
200 },
201 else => .none,
202 };
197 fg.ret_ptr = if (it.sretIndex()) |i| fg.wip.arg(i) else .none;
203198 // ...and `fg.err_ret_trace`...
204 if (fn_info.cc == .auto and comp.config.any_error_tracing) {
205 fg.err_ret_trace = fg.wip.arg(it.llvm_index);
206 it.llvm_index += 1;
207 } else {
208 fg.err_ret_trace = .none;
209 }
199 fg.err_ret_trace = if (it.errorReturnTraceIndex()) |i| fg.wip.arg(i) else .none;
200
210201 // ...and as for `fg.args`, we'll put all of the arguments into this ArrayList, and once that's
211202 // done we'll use its buffer as `fg.args`.
212203 var args: std.ArrayList(Builder.Value) = .empty;
......@@ -816,13 +807,12 @@ fn buildCall(
816807 },
817808 };
818809
819 const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing;
820 if (err_return_tracing) {
810 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types, ret_strat);
811 if (it.errorReturnTraceIndex() != null) {
821812 assert(fg.err_ret_trace != .none);
822813 try llvm_args.append(fg.gpa, fg.err_ret_trace);
823814 }
824815
825 var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types);
826816 while (try it.nextCall(arg_types)) |lowering| {
827817 const arg_ty: Type = .fromInterned(arg_types[it.zig_index - 1]);
828818 const arg_val = arg_values[it.zig_index - 1];
......@@ -922,9 +912,7 @@ fn buildCall(
922912
923913 {
924914 // Add argument attributes.
925 it = iterateParamTypes(o, fn_info.cc, fn_info.param_types);
926 it.llvm_index += @intFromBool(ret_strat == .sret);
927 it.llvm_index += @intFromBool(err_return_tracing);
915 it = iterateParamTypes(o, fn_info.cc, fn_info.param_types, ret_strat);
928916 var remaining_inreg_int = cc_info.inreg_int_params;
929917 var remaining_inreg_float = cc_info.inreg_float_params;
930918 while (try it.next()) |lowering| switch (lowering) {
......@@ -6997,6 +6985,24 @@ fn typeOfIndex(fg: *FuncGen, inst: Air.Inst.Index) Type {
69976985}
69986986
69996987const ParamTypeIterator = struct {
6988 const Regs = struct {
6989 gpr: u8 = 0,
6990 fpr: u8 = 0,
6991
6992 pub fn tryTake(self: *Regs, taking: Regs) bool {
6993 if (self.gpr < taking.gpr or self.fpr < taking.fpr) return false;
6994
6995 self.gpr -= taking.gpr;
6996 self.fpr -= taking.fpr;
6997
6998 return true;
6999 }
7000
7001 pub fn total(self: Regs) u8 {
7002 return self.gpr + self.fpr;
7003 }
7004 };
7005
70007006 object: *Object,
70017007 cc: std.lang.CallingConvention,
70027008 param_types: []const InternPool.Index,
......@@ -7006,6 +7012,9 @@ const ParamTypeIterator = struct {
70067012 types_buffer: [8]Builder.Type,
70077013 offsets_buffer: [9]u64,
70087014 byval_attr: ?Object.Byval,
7015 free_regs: Regs,
7016 has_sret: bool,
7017 has_err_ret_trace: bool,
70097018
70107019 const Lowering = union(enum) {
70117020 no_bits,
......@@ -7375,110 +7384,128 @@ const ParamTypeIterator = struct {
73757384 const o = it.object;
73767385 const zcu = o.zcu;
73777386 const target = zcu.getTarget();
7387
7388 var used_regs: ParamTypeIterator.Regs = .{ .gpr = 0, .fpr = 0 };
73787389 const classes = x86_64_abi.classifySystemV(ty, zcu, target, .arg);
7379 var types_len: u32 = 0;
7380 const classes_len = for (classes, 0..) |class, class_index| switch (class) {
7390 const classes_len = std.mem.findScalar(x86_64_abi.Class, classes[0..], .none) orelse classes.len;
7391 const early_lowering: ?Lowering = for (classes[0..classes_len], 0..) |class, index| switch (class) {
73817392 .integer => {
7382 it.types_buffer[types_len] = try o.builder.intType(@min(8 * ty.abiSize(zcu) - 64 * class_index, 64));
7383 it.offsets_buffer[types_len] = 8 * class_index;
7384 types_len += 1;
7393 it.types_buffer[used_regs.total()] = try o.builder.intType(@min(8 * ty.abiSize(zcu) - 64 * index, 64));
7394 it.offsets_buffer[used_regs.total()] = 8 * index;
7395 used_regs.gpr += 1;
73857396 },
73867397 .sse => {
7387 it.types_buffer[types_len] = .double;
7388 it.offsets_buffer[types_len] = 8 * class_index;
7389 types_len += 1;
7398 it.types_buffer[used_regs.total()] = .double;
7399 it.offsets_buffer[used_regs.total()] = 8 * index;
7400 used_regs.fpr += 1;
73907401 },
73917402 .sseup => {
7392 if (it.types_buffer[types_len - 1] == .double) {
7393 if (ty.isVector(zcu)) {
7394 it.zig_index += 1;
7395 it.llvm_index += 1;
7396 return .byval;
7397 }
7398 it.types_buffer[types_len - 1] = .fp128;
7403 if (it.types_buffer[used_regs.total() - 1] == .double) {
7404 if (ty.isVector(zcu)) break .byval;
7405 it.types_buffer[used_regs.total() - 1] = .fp128;
73997406 } else {
7400 it.types_buffer[types_len] = .double;
7401 it.offsets_buffer[types_len] = 8 * class_index;
7402 types_len += 1;
7407 it.types_buffer[used_regs.total()] = .double;
7408 it.offsets_buffer[used_regs.total()] = 8 * index;
7409 used_regs.fpr += 1;
74037410 }
74047411 },
74057412 .float => {
7406 it.types_buffer[types_len] = .float;
7407 it.offsets_buffer[types_len] = 8 * class_index;
7408 types_len += 1;
7413 it.types_buffer[used_regs.total()] = .float;
7414 it.offsets_buffer[used_regs.total()] = 8 * index;
7415 used_regs.fpr += 1;
74097416 },
74107417 .float_combine => {
7411 it.types_buffer[types_len] = try it.object.builder.vectorType(.normal, 2, .float);
7412 it.offsets_buffer[types_len] = 8 * class_index;
7413 types_len += 1;
7418 it.types_buffer[used_regs.total()] = try it.object.builder.vectorType(.normal, 2, .float);
7419 it.offsets_buffer[used_regs.total()] = 8 * index;
7420 used_regs.fpr += 1;
74147421 },
7415 .x87 => {
7416 it.zig_index += 1;
7417 it.llvm_index += 1;
7418 it.byval_attr = .{};
7419 return .byref;
7420 },
7421 .x87up => unreachable,
7422 .none => break class_index,
7423 .memory => {
7424 it.zig_index += 1;
7425 it.llvm_index += 1;
7426 it.byval_attr = .{};
7427 return .byref;
7422 .x87, .memory => break .byref,
7423 .integer_per_element => {
7424 used_regs.gpr += @intCast(@min(ty.vectorLen(zcu), it.free_regs.gpr));
7425 break .byval;
74287426 },
7429 .win_i128 => unreachable, // windows only
7427 .none,
7428 .x87up,
7429 // Only produced by `classifyWindows`; see `next_x86_64_win`.
7430 .win_i128,
74307431 .bool_vector_mask,
7431 .integer_per_element,
74327432 .sse_per_element,
7433 // Only produced for `ctx == .ret`; this classifies with `.arg`.
74337434 .sse_sse_x87_per_qword,
74347435 .sse_per_xword,
74357436 .sse_per_yword,
74367437 .sse_per_zword,
7437 => {
7438 it.zig_index += 1;
7439 it.llvm_index += 1;
7440 return .byval;
7438 => unreachable,
7439 } else null;
7440
7441 const fits = it.free_regs.tryTake(used_regs);
7442
7443 const len = used_regs.total();
7444 it.offsets_buffer[len] = 8 * classes_len;
7445 it.types_len = len;
7446
7447 const lowering: Lowering = early_lowering orelse switch (len) {
7448 0 => unreachable,
7449 1 => single: {
7450 if (isByRef(ty, zcu)) break :single .multiple_llvm_types;
7451
7452 const reg_ty = it.types_buffer[0];
7453 const llvm_ty = try o.lowerType(ty, .as_value);
7454 break :single if (reg_ty == llvm_ty or (reg_ty == .i64 and llvm_ty.isPointer(&o.builder)))
7455 .byval
7456 else
7457 .multiple_llvm_types;
74417458 },
7442 } else classes.len;
7443 if (types_len > 1) {
7444 if (it.llvm_index + classes_len > 6) {
7445 it.zig_index += 1;
7446 it.llvm_index += 1;
7447 it.byval_attr = .{};
7448 return .byref;
7449 }
7450 } else if (!isByRef(ty, zcu)) {
7451 const llvm_ty = try o.lowerType(ty, .as_value);
7452 if (it.types_buffer[0] == llvm_ty or
7453 (it.types_buffer[0] == .i64 and llvm_ty.isPointer(&o.builder)))
7454 {
7455 it.zig_index += 1;
7456 it.llvm_index += 1;
7457 return .byval;
7458 }
7459 }
7460 it.offsets_buffer[types_len] = 8 * classes_len;
7461 it.types_len = types_len;
7462 it.llvm_index += types_len;
7459 else => if (fits) .multiple_llvm_types else .byref,
7460 };
7461
74637462 it.zig_index += 1;
7464 return .multiple_llvm_types;
7463 it.llvm_index += if (lowering == .multiple_llvm_types) len else 1;
7464
7465 if (lowering == .byref) it.byval_attr = .{};
7466
7467 return lowering;
7468 }
7469
7470 pub fn sretIndex(it: ParamTypeIterator) ?u32 {
7471 return if (it.has_sret) 0 else null;
7472 }
7473
7474 pub fn errorReturnTraceIndex(it: ParamTypeIterator) ?u32 {
7475 return if (it.has_err_ret_trace)
7476 if (it.sretIndex() != null) 1 else 0
7477 else
7478 null;
74657479 }
74667480};
74677481pub fn iterateParamTypes(
74687482 object: *Object,
74697483 cc: std.lang.CallingConvention,
74707484 param_types: []const InternPool.Index,
7485 ret_strat: FnReturnStrat,
74717486) ParamTypeIterator {
7487 const has_sret = ret_strat == .sret;
7488 const has_err_ret_trace = cc == .auto and object.zcu.comp.config.any_error_tracing;
7489
74727490 return .{
74737491 .object = object,
74747492 .cc = cc,
74757493 .param_types = param_types,
74767494 .zig_index = 0,
7477 .llvm_index = 0,
7495 .llvm_index = @as(u32, @intFromBool(has_sret)) + @intFromBool(has_err_ret_trace),
74787496 .types_len = undefined,
74797497 .types_buffer = undefined,
74807498 .offsets_buffer = undefined,
74817499 .byval_attr = null,
7500 .free_regs = switch (cc) {
7501 .x86_64_sysv, .x86_64_x32 => .{
7502 .gpr = 6 - @as(u8, @intFromBool(has_sret)),
7503 .fpr = 8,
7504 },
7505 else => undefined,
7506 },
7507 .has_sret = has_sret,
7508 .has_err_ret_trace = has_err_ret_trace,
74827509 };
74837510}
74847511
test/c_abi/cfuncs.c+65
......@@ -15835,6 +15835,13 @@ struct BigStruct {
1583515835
1583615836void zig_big_struct(struct BigStruct);
1583715837
15838struct Struct_u64_u64_u64_u64 zig_sret_gpr_straddle(
15839 struct Struct_u64_u64, struct Struct_u64_u64, struct Struct_u64_u64);
15840uint64_t zig_sse_then_gpr(double, double, double, double, double, double,
15841 struct Struct_u64_u64);
15842double zig_sse_budget_straddle(double, double, double, double, double, double,
15843 double, double, struct Struct_f64_f64);
15844
1583815845union BigUnion {
1583915846 struct BigStruct a;
1584015847};
......@@ -16278,6 +16285,64 @@ struct SplitStructMixed c_ret_split_struct_mixed(void) {
1627816285 return s;
1627916286}
1628016287
16288struct Struct_u64_u64_u64_u64 c_sret_gpr_straddle(
16289 struct Struct_u64_u64 p, struct Struct_u64_u64 q, struct Struct_u64_u64 r) {
16290 assert_or_panic(p.a == 1);
16291 assert_or_panic(p.b == 2);
16292 assert_or_panic(q.a == 3);
16293 assert_or_panic(q.b == 4);
16294 assert_or_panic(r.a == 5);
16295 assert_or_panic(r.b == 6);
16296 struct Struct_u64_u64_u64_u64 res = {p.a, q.b, r.a, r.b};
16297 return res;
16298}
16299
16300uint64_t c_sse_then_gpr(double a, double b, double c, double d, double e, double f,
16301 struct Struct_u64_u64 s) {
16302 assert_or_panic(a == 1.0);
16303 assert_or_panic(b == 2.0);
16304 assert_or_panic(c == 3.0);
16305 assert_or_panic(d == 4.0);
16306 assert_or_panic(e == 5.0);
16307 assert_or_panic(f == 6.0);
16308 return s.a + s.b;
16309}
16310
16311double c_sse_budget_straddle(double a, double b, double c, double d, double e, double f,
16312 double g, double h, struct Struct_f64_f64 s) {
16313 assert_or_panic(a == 1.0);
16314 assert_or_panic(b == 2.0);
16315 assert_or_panic(c == 3.0);
16316 assert_or_panic(d == 4.0);
16317 assert_or_panic(e == 5.0);
16318 assert_or_panic(f == 6.0);
16319 assert_or_panic(g == 7.0);
16320 assert_or_panic(h == 8.0);
16321 return s.a + s.b;
16322}
16323
16324void c_test_sret_gpr_straddle(void) {
16325 struct Struct_u64_u64 p = {10, 20};
16326 struct Struct_u64_u64 q = {30, 40};
16327 struct Struct_u64_u64 r = {50, 60};
16328 struct Struct_u64_u64_u64_u64 res = zig_sret_gpr_straddle(p, q, r);
16329 assert_or_panic(res.a == 10);
16330 assert_or_panic(res.b == 40);
16331 assert_or_panic(res.c == 50);
16332 assert_or_panic(res.d == 60);
16333}
16334
16335void c_test_sse_then_gpr(void) {
16336 struct Struct_u64_u64 s = {100, 23};
16337 assert_or_panic(zig_sse_then_gpr(10.0, 20.0, 30.0, 40.0, 50.0, 60.0, s) == 123);
16338}
16339
16340void c_test_sse_budget_straddle(void) {
16341 struct Struct_f64_f64 s = {100.0, 23.0};
16342 assert_or_panic(zig_sse_budget_straddle(10.0, 20.0, 30.0, 40.0, 50.0, 60.0,
16343 70.0, 80.0, s) == 123.0);
16344}
16345
1628116346struct BigStruct c_big_struct_both(struct BigStruct x) {
1628216347 assert_or_panic(x.a == 1);
1628316348 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 {
1728517285 return s;
1728617286}
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
1728817375export fn zig_ret_bool() bool {
1728917376 return true;
1729017377}