diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 66da85bfc5..868bf4d337 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -10315,15 +10315,18 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError .off = -dst_reg_off.off, } }, }), - .register_pair => |dst_regs| switch (src_mcv) { - .register_pair => |src_regs| for (dst_regs, src_regs) |dst_reg, src_reg| - try self.genSetReg(dst_reg, Type.usize, .{ .register = src_reg }), - else => for (dst_regs, 0..) |dst_reg, dst_reg_i| try self.genSetReg( + .register_pair => |dst_regs| for (dst_regs, 0..) |dst_reg, dst_reg_i| switch (src_mcv) { + .register_pair => |src_regs| try self.genSetReg( + dst_reg, + Type.usize, + .{ .register = src_regs[dst_reg_i] }, + ), + else => try self.genSetReg( dst_reg, Type.usize, src_mcv.address().offset(@intCast(dst_reg_i * 8)).deref(), ), - .air_ref => |src_ref| try self.genCopy(ty, dst_mcv, try self.resolveInst(src_ref)), + .air_ref => |src_ref| return self.genCopy(ty, dst_mcv, try self.resolveInst(src_ref)), }, .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv), .memory, .load_direct, .load_got, .load_tlv => { @@ -12536,7 +12539,9 @@ fn resolveCallingConventionValues( result.stack_align = .@"8"; }, .C, .SysV, .Win64 => { - var param_reg_i: usize = 0; + var ret_int_reg_i: usize = 0; + var ret_sse_reg_i: usize = 0; + var param_int_reg_i: usize = 0; var param_sse_reg_i: usize = 0; result.stack_align = .@"16"; @@ -12556,97 +12561,134 @@ fn resolveCallingConventionValues( // TODO: is this even possible for C calling convention? result.return_value = InstTracking.init(.none); } else { + var ret_tracking: [2]InstTracking = undefined; + var ret_tracking_i: usize = 0; + const classes = switch (resolved_cc) { .SysV => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none), .Win64 => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)}, else => unreachable, }; - for ( - classes, - abi.getCAbiIntReturnRegs(resolved_cc)[0..classes.len], - 0.., - ) |class, ret_reg, ret_reg_i| { - result.return_value = switch (class) { - .integer => switch (ret_reg_i) { - 0 => InstTracking.init(.{ .register = registerAlias( - ret_reg, - @intCast(@min(ret_ty.abiSize(mod), 8)), - ) }), - 1 => InstTracking.init(.{ .register_pair = .{ - result.return_value.short.register, - registerAlias(ret_reg, @intCast(ret_ty.abiSize(mod) - 8)), - } }), - else => return self.fail("TODO handle multiple classes per type", .{}), - }, - .float, .sse => switch (ret_reg_i) { - 0 => InstTracking.init(.{ .register = .xmm0 }), - else => return self.fail("TODO handle multiple classes per type", .{}), - }, - .sseup => continue, - .memory => switch (ret_reg_i) { - 0 => ret: { - const ret_indirect_reg = - abi.getCAbiIntParamRegs(resolved_cc)[param_reg_i]; - param_reg_i += 1; - break :ret .{ - .short = .{ .indirect = .{ .reg = ret_reg } }, - .long = .{ .indirect = .{ .reg = ret_indirect_reg } }, - }; - }, - else => return self.fail("TODO handle multiple classes per type", .{}), - }, - else => return self.fail("TODO handle calling convention class {s}", .{ - @tagName(class), - }), - }; - } + for (classes) |class| switch (class) { + .integer => { + const ret_int_reg = registerAlias( + abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i], + @intCast(@min(ret_ty.abiSize(mod), 8)), + ); + ret_int_reg_i += 1; + + ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_int_reg }); + ret_tracking_i += 1; + }, + .sse, .float, .float_combine, .win_i128 => { + const ret_sse_reg = registerAlias( + abi.getCAbiSseReturnRegs(resolved_cc)[ret_sse_reg_i], + @intCast(ret_ty.abiSize(mod)), + ); + ret_sse_reg_i += 1; + + ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_sse_reg }); + ret_tracking_i += 1; + }, + .sseup => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .sse), + .x87 => { + ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = .st0 }); + ret_tracking_i += 1; + }, + .x87up => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .x87), + .complex_x87 => { + ret_tracking[ret_tracking_i] = + InstTracking.init(.{ .register_pair = .{ .st0, .st1 } }); + ret_tracking_i += 1; + }, + .memory => { + const ret_int_reg = abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i].to64(); + ret_int_reg_i += 1; + const ret_indirect_reg = abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i]; + param_int_reg_i += 1; + + ret_tracking[ret_tracking_i] = .{ + .short = .{ .indirect = .{ .reg = ret_int_reg } }, + .long = .{ .indirect = .{ .reg = ret_indirect_reg } }, + }; + ret_tracking_i += 1; + }, + .none => unreachable, + }; + result.return_value = switch (ret_tracking_i) { + else => unreachable, + 1 => ret_tracking[0], + 2 => InstTracking.init(.{ .register_pair = .{ + ret_tracking[0].short.register, ret_tracking[1].short.register, + } }), + }; } // Input params for (param_types, result.args) |ty, *arg| { assert(ty.hasRuntimeBitsIgnoreComptime(mod)); + switch (resolved_cc) { + .SysV => {}, + .Win64 => { + param_int_reg_i = @max(param_int_reg_i, param_sse_reg_i); + param_sse_reg_i = param_int_reg_i; + }, + else => unreachable, + } + + var arg_mcv: [2]MCValue = undefined; + var arg_mcv_i: usize = 0; const classes = switch (self.target.os.tag) { .windows => &[1]abi.Class{abi.classifyWindows(ty, mod)}, else => mem.sliceTo(&abi.classifySystemV(ty, mod, .arg), .none), }; - for (classes, 0..) |class, class_i| { - switch (class) { - .integer => if (param_reg_i < abi.getCAbiIntParamRegs(resolved_cc).len) { - const param_reg = abi.getCAbiIntParamRegs(resolved_cc)[param_reg_i]; - param_reg_i += 1; + for (classes) |class| switch (class) { + .integer => { + const param_int_regs = abi.getCAbiIntParamRegs(resolved_cc); + if (param_int_reg_i >= param_int_regs.len) break; - arg.* = switch (class_i) { - 0 => .{ .register = param_reg }, - 1 => .{ .register_pair = .{ arg.register, param_reg } }, - else => return self.fail("TODO handle multiple classes per type", .{}), - }; - } else break, - .float, .sse => switch (self.target.os.tag) { - .windows => if (param_reg_i < 4) { - if (class_i > 0) - return self.fail("TODO handle multiple classes per type", .{}); - arg.* = .{ - .register = @enumFromInt(@intFromEnum(Register.xmm0) + param_reg_i), - }; - param_reg_i += 1; - } else break, - else => if (param_sse_reg_i < 8) { - if (class_i > 0) - return self.fail("TODO handle multiple classes per type", .{}); - arg.* = .{ .register = @enumFromInt( - @intFromEnum(Register.xmm0) + param_sse_reg_i, - ) }; - param_sse_reg_i += 1; - } else break, - }, - .sseup => {}, - .memory => break, - else => return self.fail("TODO handle calling convention class {s}", .{ - @tagName(class), - }), - } - } else continue; + const param_int_reg = registerAlias( + abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i], + @intCast(@min(ty.abiSize(mod), 8)), + ); + param_int_reg_i += 1; + + arg_mcv[arg_mcv_i] = .{ .register = param_int_reg }; + arg_mcv_i += 1; + }, + .sse, .float, .float_combine => { + const param_sse_regs = abi.getCAbiSseParamRegs(resolved_cc); + if (param_sse_reg_i >= param_sse_regs.len) break; + + const param_sse_reg = registerAlias( + abi.getCAbiSseParamRegs(resolved_cc)[param_sse_reg_i], + @intCast(ty.abiSize(mod)), + ); + param_sse_reg_i += 1; + + arg_mcv[arg_mcv_i] = .{ .register = param_sse_reg }; + arg_mcv_i += 1; + }, + .sseup => assert(arg_mcv[arg_mcv_i - 1].register.class() == .sse), + .x87, .x87up, .complex_x87, .memory => break, + .none => unreachable, + .win_i128 => { + const param_int_reg = + abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i].to64(); + param_int_reg_i += 1; + + arg_mcv[arg_mcv_i] = .{ .indirect = .{ .reg = param_int_reg } }; + arg_mcv_i += 1; + }, + } else { + arg.* = switch (arg_mcv_i) { + else => unreachable, + 1 => arg_mcv[0], + 2 => .{ .register_pair = .{ arg_mcv[0].register, arg_mcv[1].register } }, + }; + continue; + } const param_size: u31 = @intCast(ty.abiSize(mod)); const param_align: u31 = @intCast(ty.abiAlignment(mod).toByteUnitsOptional().?); @@ -12746,7 +12788,10 @@ fn registerAlias(reg: Register, size_bytes: u32) Register { reg else unreachable, - .x87 => unreachable, + .x87 => if (size_bytes == 16) + reg + else + unreachable, .mmx => if (size_bytes <= 8) reg else diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 306e15e5ff..682c2644e1 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -447,7 +447,9 @@ pub const SysV = struct { pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 } ++ sse_avx_regs; pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; + pub const c_abi_sse_param_regs = sse_avx_regs[0..8].*; pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; + pub const c_abi_sse_return_regs = sse_avx_regs[0..2].*; }; pub const Win64 = struct { @@ -460,7 +462,9 @@ pub const Win64 = struct { pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 } ++ sse_avx_regs; pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 }; + pub const c_abi_sse_param_regs = sse_avx_regs[0..4].*; pub const c_abi_int_return_regs = [_]Register{.rax}; + pub const c_abi_sse_return_regs = sse_avx_regs[0..1].*; }; pub fn resolveCallingConvention( @@ -500,6 +504,14 @@ pub fn getCAbiIntParamRegs(cc: std.builtin.CallingConvention) []const Register { }; } +pub fn getCAbiSseParamRegs(cc: std.builtin.CallingConvention) []const Register { + return switch (cc) { + .SysV => &SysV.c_abi_sse_param_regs, + .Win64 => &Win64.c_abi_sse_param_regs, + else => unreachable, + }; +} + pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register { return switch (cc) { .SysV => &SysV.c_abi_int_return_regs, @@ -508,6 +520,14 @@ pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register }; } +pub fn getCAbiSseReturnRegs(cc: std.builtin.CallingConvention) []const Register { + return switch (cc) { + .SysV => &SysV.c_abi_sse_return_regs, + .Win64 => &Win64.c_abi_sse_return_regs, + else => unreachable, + }; +} + const gp_regs = [_]Register{ .rax, .rcx, .rdx, .rbx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15, }; diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index feb0c0fe1c..d01700ed89 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -375,11 +375,11 @@ test "comptime @bitCast packed struct to int and back" { test "comptime bitcast with fields following f80" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; const FloatT = extern struct { f: f80, x: u128 align(16) }; const x: FloatT = .{ .f = 0.5, .x = 123 }; diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 7dc5e53595..f9624880d9 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -1379,7 +1379,7 @@ test "comptime fixed-width float zero divided by zero produces NaN" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; inline for (.{ f16, f32, f64, f80, f128 }) |F| { try expect(math.isNan(@as(F, 0) / @as(F, 0))); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index b122a31ba4..8b2447ba37 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1538,9 +1538,9 @@ test "NaN comparison" { test "NaN comparison f80" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; try testNanEqNan(f80); try comptime testNanEqNan(f80);