| author | |
| committer | |
| log | fa5d54e74ccc6f8648bc52dfe2e9ef3b20841a88 |
| tree | 757c97c421be9f4367f06849e2bdba816d86bfda |
| parent | ffece163a2a9251e12062b7909dddadeffa34ea1 |
| signature | Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM |
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 { |
| 2355 | 2355 | ||
| 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); |
| 2357 | 2357 | ||
| 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 | }; |
| 2371 | 2371 | ||
| 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 | } | ||
| 2377 | 2373 | ||
| 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); |
| 3113 | 3109 | ||
| 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 | } |
| 3123 | 3117 | ||
| 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)); | ||
| 192 | 193 | ||
| 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); |
| 194 | 195 | ||
| 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's | 201 | // ...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 | }; |
| 818 | 809 | ||
| 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 | } |
| 824 | 815 | ||
| 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( |
| 922 | 912 | ||
| 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 | } |
| 7016 | 7004 | ||
| 7017 | const ParamTypeIterator = struct { | 7005 | const 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, | ||
| 7027 | 7036 | ||
| 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 only | 7445 | .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 | }; |
| 7485 | pub fn iterateParamTypes( | 7499 | pub 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 | } |
| 7502 | 7529 |
test/c_abi/cfuncs.c+65| ... | @@ -15834,6 +15834,13 @@ struct BigStruct { | ... | @@ -15834,6 +15834,13 @@ struct BigStruct { |
| 15834 | 15834 | ||
| 15835 | void zig_big_struct(struct BigStruct); | 15835 | void zig_big_struct(struct BigStruct); |
| 15836 | 15836 | ||
| 15837 | struct Struct_u64_u64_u64_u64 zig_sret_gpr_straddle( | ||
| 15838 | struct Struct_u64_u64, struct Struct_u64_u64, struct Struct_u64_u64); | ||
| 15839 | uint64_t zig_sse_then_gpr(double, double, double, double, double, double, | ||
| 15840 | struct Struct_u64_u64); | ||
| 15841 | double zig_sse_budget_straddle(double, double, double, double, double, double, | ||
| 15842 | double, double, struct Struct_f64_f64); | ||
| 15843 | |||
| 15837 | union BigUnion { | 15844 | union 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 | } |
| 16279 | 16286 | ||
| 16287 | struct 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 | |||
| 16299 | uint64_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 | |||
| 16310 | double 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 | |||
| 16323 | void 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 | |||
| 16334 | void 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 | |||
| 16339 | void 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 | |||
| 16280 | struct BigStruct c_big_struct_both(struct BigStruct x) { | 16345 | struct 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 | } |
| 17287 | 17287 | ||
| 17288 | extern fn c_sret_gpr_straddle(Struct_u64_u64, Struct_u64_u64, Struct_u64_u64) Struct_u64_u64_u64_u64; | ||
| 17289 | extern fn c_test_sret_gpr_straddle() void; | ||
| 17290 | |||
| 17291 | test "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 | |||
| 17306 | export 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 | |||
| 17320 | extern fn c_sse_then_gpr(f64, f64, f64, f64, f64, f64, Struct_u64_u64) u64; | ||
| 17321 | extern fn c_test_sse_then_gpr() void; | ||
| 17322 | |||
| 17323 | test "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 | |||
| 17331 | export 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 | |||
| 17341 | extern fn c_sse_budget_straddle(f64, f64, f64, f64, f64, f64, f64, f64, Struct_f64_f64) f64; | ||
| 17342 | extern fn c_test_sse_budget_straddle() void; | ||
| 17343 | |||
| 17344 | test "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 | |||
| 17353 | export 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 | |||
| 17288 | export fn zig_ret_bool() bool { | 17375 | export fn zig_ret_bool() bool { |
| 17289 | return true; | 17376 | return true; |
| 17290 | } | 17377 | } |