aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt7
-rw-r--r--lib/compiler_rt.zig12
-rw-r--r--lib/compiler_rt/common.zig12
-rw-r--r--lib/compiler_rt/negXf2.zig42
-rw-r--r--lib/compiler_rt/negdf2.zig19
-rw-r--r--lib/compiler_rt/negsf2.zig19
-rw-r--r--lib/compiler_rt/negtf2.zig11
-rw-r--r--lib/compiler_rt/negxf2.zig11
-rw-r--r--lib/std/fmt.zig2
-rw-r--r--lib/std/math/copysign.zig1
-rw-r--r--lib/std/math/signbit.zig1
-rw-r--r--src/Air.zig6
-rw-r--r--src/Liveness.zig2
-rw-r--r--src/Sema.zig4
-rw-r--r--src/arch/aarch64/CodeGen.zig3
-rw-r--r--src/arch/arm/CodeGen.zig1
-rw-r--r--src/arch/riscv64/CodeGen.zig1
-rw-r--r--src/arch/sparc64/CodeGen.zig1
-rw-r--r--src/arch/wasm/CodeGen.zig1
-rw-r--r--src/arch/x86_64/CodeGen.zig1
-rw-r--r--src/codegen/c.zig16
-rw-r--r--src/codegen/llvm.zig20
-rw-r--r--src/codegen/llvm/bindings.zig3
-rw-r--r--src/print_air.zig1
-rw-r--r--src/value.zig50
-rw-r--r--test/behavior/floatop.zig103
26 files changed, 278 insertions, 72 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 29f521c789..b28748fba4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -608,7 +608,6 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/multf3.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/multi3.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulxf3.zig"
- "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXf2.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXi2.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/negv.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/os_version_check.zig"
@@ -623,11 +622,15 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/sincos.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/sqrt.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/stack_probe.zig"
- "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subdf3.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/subo.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/subsf3.zig"
+ "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subdf3.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/subtf3.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/subxf3.zig"
+ "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negsf2.zig"
+ "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negdf2.zig"
+ "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negtf2.zig"
+ "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negxf2.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/tan.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/trig.zig"
"${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunc.zig"
diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig
index 3216fcc357..105c5ed7ad 100644
--- a/lib/compiler_rt.zig
+++ b/lib/compiler_rt.zig
@@ -4,12 +4,13 @@ comptime {
_ = @import("compiler_rt/atomics.zig");
_ = @import("compiler_rt/addf3.zig");
- _ = @import("compiler_rt/adddf3.zig");
_ = @import("compiler_rt/addsf3.zig");
+ _ = @import("compiler_rt/adddf3.zig");
_ = @import("compiler_rt/addtf3.zig");
_ = @import("compiler_rt/addxf3.zig");
- _ = @import("compiler_rt/subdf3.zig");
+
_ = @import("compiler_rt/subsf3.zig");
+ _ = @import("compiler_rt/subdf3.zig");
_ = @import("compiler_rt/subtf3.zig");
_ = @import("compiler_rt/subxf3.zig");
@@ -19,6 +20,11 @@ comptime {
_ = @import("compiler_rt/multf3.zig");
_ = @import("compiler_rt/mulxf3.zig");
+ _ = @import("compiler_rt/negsf2.zig");
+ _ = @import("compiler_rt/negdf2.zig");
+ _ = @import("compiler_rt/negtf2.zig");
+ _ = @import("compiler_rt/negxf2.zig");
+
_ = @import("compiler_rt/comparef.zig");
_ = @import("compiler_rt/cmpsf2.zig");
_ = @import("compiler_rt/cmpdf2.zig");
@@ -172,8 +178,6 @@ comptime {
_ = @import("compiler_rt/mulo.zig");
_ = @import("compiler_rt/cmp.zig");
- _ = @import("compiler_rt/negXf2.zig");
-
_ = @import("compiler_rt/os_version_check.zig");
_ = @import("compiler_rt/emutls.zig");
_ = @import("compiler_rt/arm.zig");
diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig
index b6e4a5d311..538b237e5e 100644
--- a/lib/compiler_rt/common.zig
+++ b/lib/compiler_rt/common.zig
@@ -188,3 +188,15 @@ pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeIn
significand.* <<= @intCast(std.math.Log2Int(Z), shift);
return @as(i32, 1) - shift;
}
+
+pub inline fn fneg(a: anytype) @TypeOf(a) {
+ const F = @TypeOf(a);
+ const bits = @typeInfo(F).Float.bits;
+ const U = @Type(.{ .Int = .{
+ .signedness = .unsigned,
+ .bits = bits,
+ } });
+ const sign_bit_mask = @as(U, 1) << (bits - 1);
+ const negated = @bitCast(U, a) ^ sign_bit_mask;
+ return @bitCast(F, negated);
+}
diff --git a/lib/compiler_rt/negXf2.zig b/lib/compiler_rt/negXf2.zig
deleted file mode 100644
index bcff3660f4..0000000000
--- a/lib/compiler_rt/negXf2.zig
+++ /dev/null
@@ -1,42 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-const common = @import("common.zig");
-
-pub const panic = common.panic;
-
-comptime {
- if (common.want_aeabi) {
- @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage });
- @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage });
- } else {
- @export(__negsf2, .{ .name = "__negsf2", .linkage = common.linkage });
- @export(__negdf2, .{ .name = "__negdf2", .linkage = common.linkage });
- }
-}
-
-pub fn __negsf2(a: f32) callconv(.C) f32 {
- return negXf2(f32, a);
-}
-
-fn __aeabi_fneg(a: f32) callconv(.AAPCS) f32 {
- return negXf2(f32, a);
-}
-
-pub fn __negdf2(a: f64) callconv(.C) f64 {
- return negXf2(f64, a);
-}
-
-fn __aeabi_dneg(a: f64) callconv(.AAPCS) f64 {
- return negXf2(f64, a);
-}
-
-inline fn negXf2(comptime T: type, a: T) T {
- const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
-
- const significandBits = std.math.floatMantissaBits(T);
- const exponentBits = std.math.floatExponentBits(T);
-
- const signBit = (@as(Z, 1) << (significandBits + exponentBits));
-
- return @bitCast(T, @bitCast(Z, a) ^ signBit);
-}
diff --git a/lib/compiler_rt/negdf2.zig b/lib/compiler_rt/negdf2.zig
new file mode 100644
index 0000000000..c730ada7e0
--- /dev/null
+++ b/lib/compiler_rt/negdf2.zig
@@ -0,0 +1,19 @@
+const common = @import("./common.zig");
+
+pub const panic = common.panic;
+
+comptime {
+ if (common.want_aeabi) {
+ @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage });
+ } else {
+ @export(__negdf2, .{ .name = "__negdf2", .linkage = common.linkage });
+ }
+}
+
+fn __negdf2(a: f64) callconv(.C) f64 {
+ return common.fneg(a);
+}
+
+fn __aeabi_dneg(a: f64) callconv(.AAPCS) f64 {
+ return common.fneg(a);
+}
diff --git a/lib/compiler_rt/negsf2.zig b/lib/compiler_rt/negsf2.zig
new file mode 100644
index 0000000000..4cb32097ba
--- /dev/null
+++ b/lib/compiler_rt/negsf2.zig
@@ -0,0 +1,19 @@
+const common = @import("./common.zig");
+
+pub const panic = common.panic;
+
+comptime {
+ if (common.want_aeabi) {
+ @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage });
+ } else {
+ @export(__negsf2, .{ .name = "__negsf2", .linkage = common.linkage });
+ }
+}
+
+fn __negsf2(a: f32) callconv(.C) f32 {
+ return common.fneg(a);
+}
+
+fn __aeabi_fneg(a: f32) callconv(.AAPCS) f32 {
+ return common.fneg(a);
+}
diff --git a/lib/compiler_rt/negtf2.zig b/lib/compiler_rt/negtf2.zig
new file mode 100644
index 0000000000..c1c1e97802
--- /dev/null
+++ b/lib/compiler_rt/negtf2.zig
@@ -0,0 +1,11 @@
+const common = @import("./common.zig");
+
+pub const panic = common.panic;
+
+comptime {
+ @export(__negtf2, .{ .name = "__negtf2", .linkage = common.linkage });
+}
+
+fn __negtf2(a: f128) callconv(.C) f128 {
+ return common.fneg(a);
+}
diff --git a/lib/compiler_rt/negxf2.zig b/lib/compiler_rt/negxf2.zig
new file mode 100644
index 0000000000..4e8258453b
--- /dev/null
+++ b/lib/compiler_rt/negxf2.zig
@@ -0,0 +1,11 @@
+const common = @import("./common.zig");
+
+pub const panic = common.panic;
+
+comptime {
+ @export(__negxf2, .{ .name = "__negxf2", .linkage = common.linkage });
+}
+
+fn __negxf2(a: f80) callconv(.C) f80 {
+ return common.fneg(a);
+}
diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig
index b49a954800..fb826f4562 100644
--- a/lib/std/fmt.zig
+++ b/lib/std/fmt.zig
@@ -2225,7 +2225,6 @@ test "float.scientific.precision" {
}
test "float.special" {
- if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO
try expectFmt("f64: nan", "f64: {}", .{math.nan_f64});
// negative nan is not defined by IEE 754,
// and ARM thus normalizes it to positive nan
@@ -2237,7 +2236,6 @@ test "float.special" {
}
test "float.hexadecimal.special" {
- if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO
try expectFmt("f64: nan", "f64: {x}", .{math.nan_f64});
// negative nan is not defined by IEE 754,
// and ARM thus normalizes it to positive nan
diff --git a/lib/std/math/copysign.zig b/lib/std/math/copysign.zig
index 521724a998..b5fd6d4d9a 100644
--- a/lib/std/math/copysign.zig
+++ b/lib/std/math/copysign.zig
@@ -13,7 +13,6 @@ pub fn copysign(magnitude: anytype, sign: @TypeOf(magnitude)) @TypeOf(magnitude)
}
test "math.copysign" {
- if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO
inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
try expect(copysign(@as(T, 1.0), @as(T, 1.0)) == 1.0);
try expect(copysign(@as(T, 2.0), @as(T, -2.0)) == -2.0);
diff --git a/lib/std/math/signbit.zig b/lib/std/math/signbit.zig
index cb19212b5b..9aab487d37 100644
--- a/lib/std/math/signbit.zig
+++ b/lib/std/math/signbit.zig
@@ -10,7 +10,6 @@ pub fn signbit(x: anytype) bool {
}
test "math.signbit" {
- if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO
inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
try expect(!signbit(@as(T, 0.0)));
try expect(!signbit(@as(T, 1.0)));
diff --git a/src/Air.zig b/src/Air.zig
index 53421b6475..2b1c718140 100644
--- a/src/Air.zig
+++ b/src/Air.zig
@@ -288,6 +288,11 @@ pub const Inst = struct {
/// Rounds a floating pointer number to the nearest integer towards zero.
/// Uses the `un_op` field.
trunc_float,
+ /// Float negation. This affects the sign of zero, inf, and NaN, which is impossible
+ /// to do with sub. Integers are not allowed and must be represented with sub with
+ /// LHS of zero.
+ /// Uses the `un_op` field.
+ neg,
/// `<`. Result type is always bool.
/// Uses the `bin_op` field.
@@ -970,6 +975,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.ceil,
.round,
.trunc_float,
+ .neg,
=> return air.typeOf(datas[inst].un_op),
.cmp_lt,
diff --git a/src/Liveness.zig b/src/Liveness.zig
index ecb755ae0a..e0a60b50fa 100644
--- a/src/Liveness.zig
+++ b/src/Liveness.zig
@@ -287,6 +287,7 @@ pub fn categorizeOperand(
.ceil,
.round,
.trunc_float,
+ .neg,
.cmp_lt_errors_len,
=> {
const o = air_datas[inst].un_op;
@@ -834,6 +835,7 @@ fn analyzeInst(
.ceil,
.round,
.trunc_float,
+ .neg,
.cmp_lt_errors_len,
.set_err_return_trace,
=> {
diff --git a/src/Sema.zig b/src/Sema.zig
index fdf3810d4a..a071c97df3 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -10070,12 +10070,14 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
}
if (rhs_scalar_ty.isAnyFloat()) {
- // We handle comptime negation here to ensure negative zero is represented in the bits.
+ // We handle float negation here to ensure negative zero is represented in the bits.
if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {
if (rhs_val.isUndef()) return sema.addConstUndef(rhs_ty);
const target = sema.mod.getTarget();
return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, target));
}
+ try sema.requireRuntimeBlock(block, rhs_src);
+ return block.addUnOp(.neg, rhs);
}
const lhs = if (rhs_ty.zigTypeTag() == .Vector)
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig
index 47d7d1282f..64d49f2508 100644
--- a/src/arch/aarch64/CodeGen.zig
+++ b/src/arch/aarch64/CodeGen.zig
@@ -582,7 +582,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.floor,
.ceil,
.round,
- .trunc_float
+ .trunc_float,
+ .neg,
=> try self.airUnaryMath(inst),
.add_with_overflow => try self.airOverflow(inst),
diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig
index 9a93969209..b1b5c0fcb3 100644
--- a/src/arch/arm/CodeGen.zig
+++ b/src/arch/arm/CodeGen.zig
@@ -596,6 +596,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.ceil,
.round,
.trunc_float,
+ .neg,
=> try self.airUnaryMath(inst),
.add_with_overflow => try self.airOverflow(inst),
diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig
index 94d019781e..1d4108a77e 100644
--- a/src/arch/riscv64/CodeGen.zig
+++ b/src/arch/riscv64/CodeGen.zig
@@ -516,6 +516,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.ceil,
.round,
.trunc_float,
+ .neg,
=> try self.airUnaryMath(inst),
.add_with_overflow => try self.airAddWithOverflow(inst),
diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig
index a28cb0002c..75260156f8 100644
--- a/src/arch/sparc64/CodeGen.zig
+++ b/src/arch/sparc64/CodeGen.zig
@@ -529,6 +529,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.ceil,
.round,
.trunc_float,
+ .neg,
=> @panic("TODO try self.airUnaryMath(inst)"),
.add_with_overflow => try self.airAddSubWithOverflow(inst),
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index c614f7eb48..d2d21925b2 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -1607,6 +1607,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.log10,
.fabs,
.round,
+ .neg,
.cmpxchg_weak,
.cmpxchg_strong,
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index 5c30d495c7..8616c4ac5c 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -605,6 +605,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.ceil,
.round,
.trunc_float,
+ .neg,
=> try self.airUnaryMath(inst),
.add_with_overflow => try self.airAddSubShlWithOverflow(inst),
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 54c0dfb3c7..15eb917fda 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -1755,6 +1755,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.mul_sat => try airSatOp(f, inst, "muls_"),
.shl_sat => try airSatOp(f, inst, "shls_"),
+ .neg => try airNeg(f, inst),
+
.sqrt,
.sin,
.cos,
@@ -4098,6 +4100,20 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
return local;
}
+fn airNeg(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst)) return CValue.none;
+
+ const un_op = f.air.instructions.items(.data)[inst].un_op;
+ const writer = f.object.writer();
+ const inst_ty = f.air.typeOfIndex(inst);
+ const operand = try f.resolveInst(un_op);
+ const local = try f.allocLocal(inst_ty, .Const);
+ try writer.writeAll("-");
+ try f.writeCValue(writer, operand);
+ try writer.writeAll(";\n");
+ return local;
+}
+
fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
if (f.liveness.isUnused(inst)) return CValue.none;
const pl_op = f.air.instructions.items(.data)[inst].pl_op;
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index c62860c88d..529cd206ac 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -4022,6 +4022,7 @@ pub const FuncGen = struct {
.ceil => try self.airUnaryOp(inst, .ceil),
.round => try self.airUnaryOp(inst, .round),
.trunc_float => try self.airUnaryOp(inst, .trunc),
+ .neg => try self.airUnaryOp(inst, .neg),
.cmp_eq => try self.airCmp(inst, .eq),
.cmp_gt => try self.airCmp(inst, .gt),
@@ -6548,13 +6549,14 @@ pub const FuncGen = struct {
fabs,
floor,
fma,
+ fmax,
+ fmin,
+ fmod,
log,
log10,
log2,
- fmax,
- fmin,
mul,
- fmod,
+ neg,
round,
sin,
sqrt,
@@ -6587,6 +6589,7 @@ pub const FuncGen = struct {
var fn_name_buf: [64]u8 = undefined;
const strat: FloatOpStrat = if (intrinsics_allowed) switch (op) {
// Some operations are dedicated LLVM instructions, not available as intrinsics
+ .neg => return self.builder.buildFNeg(params[0], ""),
.add => return self.builder.buildFAdd(params[0], params[1], ""),
.sub => return self.builder.buildFSub(params[0], params[1], ""),
.mul => return self.builder.buildFMul(params[0], params[1], ""),
@@ -6598,6 +6601,17 @@ pub const FuncGen = struct {
} else b: {
const float_bits = scalar_ty.floatBits(target);
break :b switch (op) {
+ .neg => {
+ // In this case we can generate a softfloat negation by XORing the
+ // bits with a constant.
+ const int_llvm_ty = self.dg.context.intType(float_bits);
+ const one = int_llvm_ty.constInt(1, .False);
+ const shift_amt = int_llvm_ty.constInt(float_bits - 1, .False);
+ const sign_mask = one.constShl(shift_amt);
+ const bitcasted_operand = self.builder.buildBitCast(params[0], int_llvm_ty, "");
+ const result = self.builder.buildXor(bitcasted_operand, sign_mask, "");
+ return self.builder.buildBitCast(result, llvm_ty, "");
+ },
.add, .sub, .div, .mul => FloatOpStrat{
.libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{
@tagName(op), compilerRtFloatAbbrev(float_bits),
diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig
index 671014ba3b..8aa8e5c6f2 100644
--- a/src/codegen/llvm/bindings.zig
+++ b/src/codegen/llvm/bindings.zig
@@ -549,6 +549,9 @@ pub const Builder = opaque {
pub const buildFSub = LLVMBuildFSub;
extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+ pub const buildFNeg = LLVMBuildFNeg;
+ extern fn LLVMBuildFNeg(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value;
+
pub const buildSub = LLVMBuildSub;
extern fn LLVMBuildSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
diff --git a/src/print_air.zig b/src/print_air.zig
index d6db7ca75f..a58b27fe2f 100644
--- a/src/print_air.zig
+++ b/src/print_air.zig
@@ -168,6 +168,7 @@ const Writer = struct {
.ceil,
.round,
.trunc_float,
+ .neg,
.cmp_lt_errors_len,
.set_err_return_trace,
=> try w.writeUnOp(s, inst),
diff --git a/src/value.zig b/src/value.zig
index e5332a72e7..90cdf82834 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -2189,12 +2189,25 @@ pub const Value = extern union {
return ty.isTupleOrAnonStruct() and ty.structFieldCount() != 0;
},
.Float => {
- const a_nan = a.isNan();
- const b_nan = b.isNan();
- if (a_nan or b_nan) {
- return a_nan and b_nan;
+ switch (ty.floatBits(target)) {
+ 16 => return @bitCast(u16, a.toFloat(f16)) == @bitCast(u16, b.toFloat(f16)),
+ 32 => return @bitCast(u32, a.toFloat(f32)) == @bitCast(u32, b.toFloat(f32)),
+ 64 => return @bitCast(u64, a.toFloat(f64)) == @bitCast(u64, b.toFloat(f64)),
+ 80 => return @bitCast(u80, a.toFloat(f80)) == @bitCast(u80, b.toFloat(f80)),
+ 128 => return @bitCast(u128, a.toFloat(f128)) == @bitCast(u128, b.toFloat(f128)),
+ else => unreachable,
}
- return order(a, b, target).compare(.eq);
+ },
+ .ComptimeFloat => {
+ const a_float = a.toFloat(f128);
+ const b_float = b.toFloat(f128);
+
+ const a_nan = std.math.isNan(a_float);
+ const b_nan = std.math.isNan(b_float);
+ if (a_nan != b_nan) return false;
+ if (std.math.signbit(a_float) != std.math.signbit(b_float)) return false;
+ if (a_nan) return true;
+ return a_float == b_float;
},
.Optional => {
if (a.tag() != .opt_payload and b.tag() == .opt_payload) {
@@ -2231,18 +2244,25 @@ pub const Value = extern union {
var buf: ToTypeBuffer = undefined;
return val.toType(&buf).hashWithHasher(hasher, mod);
},
- .Float, .ComptimeFloat => {
- // Normalize the float here because this hash must match eql semantics.
- // These functions are used for hash maps so we want NaN to equal itself,
- // and -0.0 to equal +0.0.
+ .Float => {
+ // For hash/eql purposes, we treat floats as their IEEE integer representation.
+ switch (ty.floatBits(mod.getTarget())) {
+ 16 => std.hash.autoHash(hasher, @bitCast(u16, val.toFloat(f16))),
+ 32 => std.hash.autoHash(hasher, @bitCast(u32, val.toFloat(f32))),
+ 64 => std.hash.autoHash(hasher, @bitCast(u64, val.toFloat(f64))),
+ 80 => std.hash.autoHash(hasher, @bitCast(u80, val.toFloat(f80))),
+ 128 => std.hash.autoHash(hasher, @bitCast(u128, val.toFloat(f128))),
+ else => unreachable,
+ }
+ },
+ .ComptimeFloat => {
const float = val.toFloat(f128);
- if (std.math.isNan(float)) {
- std.hash.autoHash(hasher, std.math.nan_u128);
- } else if (float == 0.0) {
- var normalized_zero: f128 = 0.0;
- std.hash.autoHash(hasher, @bitCast(u128, normalized_zero));
- } else {
+ const is_nan = std.math.isNan(float);
+ std.hash.autoHash(hasher, is_nan);
+ if (!is_nan) {
std.hash.autoHash(hasher, @bitCast(u128, float));
+ } else {
+ std.hash.autoHash(hasher, std.math.signbit(float));
}
},
.Bool, .Int, .ComptimeInt, .Pointer => switch (val.tag()) {
diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig
index ac35834928..c057f7a842 100644
--- a/test/behavior/floatop.zig
+++ b/test/behavior/floatop.zig
@@ -574,6 +574,7 @@ test "negation f32" {
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_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
@@ -593,6 +594,8 @@ test "negation f64" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
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_wasm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
@@ -707,3 +710,103 @@ test "comptime_float zero divided by zero produces zero" {
try expect((0.0 / 0.0) == 0.0);
}
+
+test "nan negation f16" {
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+
+ const nan_comptime = comptime math.nan(f16);
+ const neg_nan_comptime = -nan_comptime;
+
+ var nan_runtime = math.nan(f16);
+ const neg_nan_runtime = -nan_runtime;
+
+ try expect(!math.signbit(nan_runtime));
+ try expect(math.signbit(neg_nan_runtime));
+
+ try expect(!math.signbit(nan_comptime));
+ try expect(math.signbit(neg_nan_comptime));
+}
+
+test "nan negation f32" {
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+
+ const nan_comptime = comptime math.nan(f32);
+ const neg_nan_comptime = -nan_comptime;
+
+ var nan_runtime = math.nan(f32);
+ const neg_nan_runtime = -nan_runtime;
+
+ try expect(!math.signbit(nan_runtime));
+ try expect(math.signbit(neg_nan_runtime));
+
+ try expect(!math.signbit(nan_comptime));
+ try expect(math.signbit(neg_nan_comptime));
+}
+
+test "nan negation f64" {
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+
+ const nan_comptime = comptime math.nan(f64);
+ const neg_nan_comptime = -nan_comptime;
+
+ var nan_runtime = math.nan(f64);
+ const neg_nan_runtime = -nan_runtime;
+
+ try expect(!math.signbit(nan_runtime));
+ try expect(math.signbit(neg_nan_runtime));
+
+ try expect(!math.signbit(nan_comptime));
+ try expect(math.signbit(neg_nan_comptime));
+}
+
+test "nan negation f128" {
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+
+ const nan_comptime = comptime math.nan(f128);
+ const neg_nan_comptime = -nan_comptime;
+
+ var nan_runtime = math.nan(f128);
+ const neg_nan_runtime = -nan_runtime;
+
+ try expect(!math.signbit(nan_runtime));
+ try expect(math.signbit(neg_nan_runtime));
+
+ try expect(!math.signbit(nan_comptime));
+ try expect(math.signbit(neg_nan_comptime));
+}
+
+test "nan negation f80" {
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+
+ const nan_comptime = comptime math.nan(f80);
+ const neg_nan_comptime = -nan_comptime;
+
+ var nan_runtime = math.nan(f80);
+ const neg_nan_runtime = -nan_runtime;
+
+ try expect(!math.signbit(nan_runtime));
+ try expect(math.signbit(neg_nan_runtime));
+
+ try expect(!math.signbit(nan_comptime));
+ try expect(math.signbit(neg_nan_comptime));
+}