authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-07 19:12:16-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-07 19:47:46-04:00
log24c67992e00b0043ab9409d58ffcecb2f689ee6f
tree5c5733861b3e54ff1b5ea4b099d3524188549fb7
parentb19fd485b185743907c71cedc0cc3a87ffcce27b

x86_64: hack around silent `f80` miscompilations

The x87 kind sucks.

2 files changed, 83 insertions(+), 45 deletions(-)

src/arch/x86_64/CodeGen.zig+80-42
...@@ -5263,21 +5263,29 @@ fn airRound(self: *Self, inst: Air.Inst.Index, mode: RoundMode) !void {...@@ -5263,21 +5263,29 @@ fn airRound(self: *Self, inst: Air.Inst.Index, mode: RoundMode) !void {
5263 const un_op = self.air.instructions.items(.data)[inst].un_op;5263 const un_op = self.air.instructions.items(.data)[inst].un_op;
5264 const ty = self.typeOf(un_op);5264 const ty = self.typeOf(un_op);
52655265
5266 const src_mcv = try self.resolveInst(un_op);5266 const result = result: {
5267 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv))5267 switch (try self.genRoundLibcall(ty, .{ .air_ref = un_op }, mode)) {
5268 src_mcv5268 .none => {},
5269 else5269 else => |dst_mcv| break :result dst_mcv,
5270 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);5270 }
5271 const dst_reg = dst_mcv.getReg().?;5271
5272 const dst_lock = self.register_manager.lockReg(dst_reg);5272 const src_mcv = try self.resolveInst(un_op);
5273 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);5273 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv))
5274 try self.genRound(ty, dst_reg, src_mcv, mode);5274 src_mcv
5275 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });5275 else
5276 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
5277 const dst_reg = dst_mcv.getReg().?;
5278 const dst_lock = self.register_manager.lockReg(dst_reg);
5279 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
5280 try self.genRound(ty, dst_reg, src_mcv, mode);
5281 break :result dst_mcv;
5282 };
5283 return self.finishAir(inst, result, .{ un_op, .none, .none });
5276}5284}
52775285
5278fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: RoundMode) !void {5286fn getRoundTag(self: *Self, ty: Type) ?Mir.Inst.FixedTag {
5279 const mod = self.bin_file.options.module.?;5287 const mod = self.bin_file.options.module.?;
5280 const mir_tag = @as(?Mir.Inst.FixedTag, if (self.hasFeature(.sse4_1)) switch (ty.zigTypeTag(mod)) {5288 return if (self.hasFeature(.sse4_1)) switch (ty.zigTypeTag(mod)) {
5281 .Float => switch (ty.floatBits(self.target.*)) {5289 .Float => switch (ty.floatBits(self.target.*)) {
5282 32 => if (self.hasFeature(.avx)) .{ .v_ss, .round } else .{ ._ss, .round },5290 32 => if (self.hasFeature(.avx)) .{ .v_ss, .round } else .{ ._ss, .round },
5283 64 => if (self.hasFeature(.avx)) .{ .v_sd, .round } else .{ ._sd, .round },5291 64 => if (self.hasFeature(.avx)) .{ .v_sd, .round } else .{ ._sd, .round },
...@@ -5304,26 +5312,38 @@ fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: Ro...@@ -5304,26 +5312,38 @@ fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: Ro
5304 else => null,5312 else => null,
5305 },5313 },
5306 else => unreachable,5314 else => unreachable,
5307 } else null) orelse {5315 } else null;
5308 if (ty.zigTypeTag(mod) != .Float)5316}
5309 return self.fail("TODO implement genRound for {}", .{ty.fmt(mod)});5317
53105318fn genRoundLibcall(self: *Self, ty: Type, src_mcv: MCValue, mode: RoundMode) !MCValue {
5311 var callee: ["__trunc?".len]u8 = undefined;5319 const mod = self.bin_file.options.module.?;
5312 const res = try self.genCall(.{ .lib = .{5320 if (self.getRoundTag(ty)) |_| return .none;
5313 .return_type = ty.toIntern(),5321
5314 .param_types = &.{ty.toIntern()},5322 if (ty.zigTypeTag(mod) != .Float)
5315 .callee = std.fmt.bufPrint(&callee, "{s}{s}{s}", .{5323 return self.fail("TODO implement genRound for {}", .{ty.fmt(mod)});
5316 floatLibcAbiPrefix(ty),5324
5317 switch (mode.mode) {5325 var callee: ["__trunc?".len]u8 = undefined;
5318 .down => "floor",5326 return try self.genCall(.{ .lib = .{
5319 .up => "ceil",5327 .return_type = ty.toIntern(),
5320 .zero => "trunc",5328 .param_types = &.{ty.toIntern()},
5321 else => unreachable,5329 .callee = std.fmt.bufPrint(&callee, "{s}{s}{s}", .{
5322 },5330 floatLibcAbiPrefix(ty),
5323 floatLibcAbiSuffix(ty),5331 switch (mode.mode) {
5324 }) catch unreachable,5332 .down => "floor",
5325 } }, &.{ty}, &.{src_mcv});5333 .up => "ceil",
5326 return self.genSetReg(dst_reg, ty, res);5334 .zero => "trunc",
5335 else => unreachable,
5336 },
5337 floatLibcAbiSuffix(ty),
5338 }) catch unreachable,
5339 } }, &.{ty}, &.{src_mcv});
5340}
5341
5342fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: RoundMode) !void {
5343 const mod = self.bin_file.options.module.?;
5344 const mir_tag = self.getRoundTag(ty) orelse {
5345 const result = try self.genRoundLibcall(ty, src_mcv, mode);
5346 return self.genSetReg(dst_reg, ty, result);
5327 };5347 };
5328 const abi_size: u32 = @intCast(ty.abiSize(mod));5348 const abi_size: u32 = @intCast(ty.abiSize(mod));
5329 const dst_alias = registerAlias(dst_reg, abi_size);5349 const dst_alias = registerAlias(dst_reg, abi_size);
...@@ -6760,11 +6780,17 @@ fn genBinOp(...@@ -6760,11 +6780,17 @@ fn genBinOp(
6760 else => unreachable,6780 else => unreachable,
6761 }) {6781 }) {
6762 var callee: ["__add?f3".len]u8 = undefined;6782 var callee: ["__add?f3".len]u8 = undefined;
6763 return self.genCall(.{ .lib = .{6783 const result = try self.genCall(.{ .lib = .{
6764 .return_type = lhs_ty.toIntern(),6784 .return_type = lhs_ty.toIntern(),
6765 .param_types = &.{ lhs_ty.toIntern(), rhs_ty.toIntern() },6785 .param_types = &.{ lhs_ty.toIntern(), rhs_ty.toIntern() },
6766 .callee = switch (air_tag) {6786 .callee = switch (air_tag) {
6767 .add, .sub, .mul, .div_float => std.fmt.bufPrint(&callee, "__{s}{c}f3", .{6787 .add,
6788 .sub,
6789 .mul,
6790 .div_float,
6791 .div_trunc,
6792 .div_floor,
6793 => std.fmt.bufPrint(&callee, "__{s}{c}f3", .{
6768 @tagName(air_tag)[0..3],6794 @tagName(air_tag)[0..3],
6769 floatCompilerRtAbiName(lhs_ty.floatBits(self.target.*)),6795 floatCompilerRtAbiName(lhs_ty.floatBits(self.target.*)),
6770 }),6796 }),
...@@ -6778,6 +6804,17 @@ fn genBinOp(...@@ -6778,6 +6804,17 @@ fn genBinOp(
6778 }),6804 }),
6779 } catch unreachable,6805 } catch unreachable,
6780 } }, &.{ lhs_ty, rhs_ty }, &.{ .{ .air_ref = lhs_air }, .{ .air_ref = rhs_air } });6806 } }, &.{ lhs_ty, rhs_ty }, &.{ .{ .air_ref = lhs_air }, .{ .air_ref = rhs_air } });
6807 return switch (air_tag) {
6808 .div_trunc, .div_floor => try self.genRoundLibcall(lhs_ty, result, .{
6809 .mode = switch (air_tag) {
6810 .div_trunc => .zero,
6811 .div_floor => .down,
6812 else => unreachable,
6813 },
6814 .precision = .inexact,
6815 }),
6816 else => result,
6817 };
6781 }6818 }
67826819
6783 if ((lhs_ty.scalarType(mod).isRuntimeFloat() and6820 if ((lhs_ty.scalarType(mod).isRuntimeFloat() and
...@@ -7666,16 +7703,14 @@ fn genBinOp(...@@ -7666,16 +7703,14 @@ fn genBinOp(
76667703
7667 switch (air_tag) {7704 switch (air_tag) {
7668 .add, .add_wrap, .sub, .sub_wrap, .mul, .mul_wrap, .div_float, .div_exact => {},7705 .add, .add_wrap, .sub, .sub_wrap, .mul, .mul_wrap, .div_float, .div_exact => {},
7669 .div_trunc, .div_floor => try self.genRound(7706 .div_trunc, .div_floor => try self.genRound(lhs_ty, dst_reg, .{ .register = dst_reg }, .{
7670 lhs_ty,7707 .mode = switch (air_tag) {
7671 dst_reg,
7672 .{ .register = dst_reg },
7673 .{ .mode = switch (air_tag) {
7674 .div_trunc => .zero,7708 .div_trunc => .zero,
7675 .div_floor => .down,7709 .div_floor => .down,
7676 else => unreachable,7710 else => unreachable,
7677 }, .precision = .inexact },7711 },
7678 ),7712 .precision = .inexact,
7713 }),
7679 .bit_and, .bit_or, .xor => {},7714 .bit_and, .bit_or, .xor => {},
7680 .max, .min => if (maybe_mask_reg) |mask_reg| if (self.hasFeature(.avx)) {7715 .max, .min => if (maybe_mask_reg) |mask_reg| if (self.hasFeature(.avx)) {
7681 const rhs_copy_reg = registerAlias(src_mcv.getReg().?, abi_size);7716 const rhs_copy_reg = registerAlias(src_mcv.getReg().?, abi_size);
...@@ -8673,7 +8708,10 @@ fn genCall(self: *Self, info: union(enum) {...@@ -8673,7 +8708,10 @@ fn genCall(self: *Self, info: union(enum) {
8673 try self.spillRegisters(&regs);8708 try self.spillRegisters(&regs);
8674 try arg_locks.appendSlice(&self.register_manager.lockRegs(2, regs));8709 try arg_locks.appendSlice(&self.register_manager.lockRegs(2, regs));
8675 },8710 },
8676 .load_frame => try self.genCopy(arg_ty, dst_arg, src_arg),8711 .load_frame => {
8712 try self.genCopy(arg_ty, dst_arg, src_arg);
8713 try self.freeValue(src_arg);
8714 },
8677 else => unreachable,8715 else => unreachable,
8678 };8716 };
86798717
test/behavior/floatop.zig+3-3
...@@ -1029,7 +1029,7 @@ test "@floor f80/f128/c_longdouble" {...@@ -1029,7 +1029,7 @@ test "@floor f80/f128/c_longdouble" {
1029 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1029 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1030 if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;1030 if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
1031 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1031 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1032 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;1032 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
10331033
1034 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {1034 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
1035 // https://github.com/ziglang/zig/issues/126021035 // https://github.com/ziglang/zig/issues/12602
...@@ -1111,7 +1111,7 @@ test "@ceil f80/f128/c_longdouble" {...@@ -1111,7 +1111,7 @@ test "@ceil f80/f128/c_longdouble" {
1111 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1111 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1112 if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;1112 if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
1113 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1113 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1114 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;1114 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
11151115
1116 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {1116 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
1117 // https://github.com/ziglang/zig/issues/126021117 // https://github.com/ziglang/zig/issues/12602
...@@ -1203,7 +1203,7 @@ test "@trunc f80/f128/c_longdouble" {...@@ -1203,7 +1203,7 @@ test "@trunc f80/f128/c_longdouble" {
1203 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1203 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1204 if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;1204 if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
1205 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1205 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1206 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;1206 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
12071207
1208 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {1208 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
1209 // https://github.com/ziglang/zig/issues/126021209 // https://github.com/ziglang/zig/issues/12602