| author | |
| committer | |
| log | 77a334451f1329110d6c1bd07b46813cef10e97c |
| tree | 6708ceee35b9c7fc1c358696cd18a7b05d87bbf7 |
| parent | be18459c81f831e455750e62f37dae6aeef62f1d |
| parent | b3b96b5e288ddf3694b4a0d203c684b9b8f6b49f |
| signature |
stage2: lower float negation explicitly + modify hash/eql logic for floats26 files changed, 278 insertions(+), 72 deletions(-)
CMakeLists.txt+5-2| ... | ... | @@ -608,7 +608,6 @@ set(ZIG_STAGE2_SOURCES |
| 608 | 608 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multf3.zig" |
| 609 | 609 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multi3.zig" |
| 610 | 610 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulxf3.zig" |
| 611 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXf2.zig" | |
| 612 | 611 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXi2.zig" |
| 613 | 612 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negv.zig" |
| 614 | 613 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/os_version_check.zig" |
| ... | ... | @@ -623,11 +622,15 @@ set(ZIG_STAGE2_SOURCES |
| 623 | 622 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sincos.zig" |
| 624 | 623 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sqrt.zig" |
| 625 | 624 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/stack_probe.zig" |
| 626 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subdf3.zig" | |
| 627 | 625 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subo.zig" |
| 628 | 626 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subsf3.zig" |
| 627 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subdf3.zig" | |
| 629 | 628 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subtf3.zig" |
| 630 | 629 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subxf3.zig" |
| 630 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negsf2.zig" | |
| 631 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negdf2.zig" | |
| 632 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negtf2.zig" | |
| 633 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negxf2.zig" | |
| 631 | 634 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/tan.zig" |
| 632 | 635 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trig.zig" |
| 633 | 636 | "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunc.zig" |
lib/compiler_rt.zig+8-4| ... | ... | @@ -4,12 +4,13 @@ comptime { |
| 4 | 4 | _ = @import("compiler_rt/atomics.zig"); |
| 5 | 5 | |
| 6 | 6 | _ = @import("compiler_rt/addf3.zig"); |
| 7 | _ = @import("compiler_rt/adddf3.zig"); | |
| 8 | 7 | _ = @import("compiler_rt/addsf3.zig"); |
| 8 | _ = @import("compiler_rt/adddf3.zig"); | |
| 9 | 9 | _ = @import("compiler_rt/addtf3.zig"); |
| 10 | 10 | _ = @import("compiler_rt/addxf3.zig"); |
| 11 | _ = @import("compiler_rt/subdf3.zig"); | |
| 11 | ||
| 12 | 12 | _ = @import("compiler_rt/subsf3.zig"); |
| 13 | _ = @import("compiler_rt/subdf3.zig"); | |
| 13 | 14 | _ = @import("compiler_rt/subtf3.zig"); |
| 14 | 15 | _ = @import("compiler_rt/subxf3.zig"); |
| 15 | 16 | |
| ... | ... | @@ -19,6 +20,11 @@ comptime { |
| 19 | 20 | _ = @import("compiler_rt/multf3.zig"); |
| 20 | 21 | _ = @import("compiler_rt/mulxf3.zig"); |
| 21 | 22 | |
| 23 | _ = @import("compiler_rt/negsf2.zig"); | |
| 24 | _ = @import("compiler_rt/negdf2.zig"); | |
| 25 | _ = @import("compiler_rt/negtf2.zig"); | |
| 26 | _ = @import("compiler_rt/negxf2.zig"); | |
| 27 | ||
| 22 | 28 | _ = @import("compiler_rt/comparef.zig"); |
| 23 | 29 | _ = @import("compiler_rt/cmpsf2.zig"); |
| 24 | 30 | _ = @import("compiler_rt/cmpdf2.zig"); |
| ... | ... | @@ -172,8 +178,6 @@ comptime { |
| 172 | 178 | _ = @import("compiler_rt/mulo.zig"); |
| 173 | 179 | _ = @import("compiler_rt/cmp.zig"); |
| 174 | 180 | |
| 175 | _ = @import("compiler_rt/negXf2.zig"); | |
| 176 | ||
| 177 | 181 | _ = @import("compiler_rt/os_version_check.zig"); |
| 178 | 182 | _ = @import("compiler_rt/emutls.zig"); |
| 179 | 183 | _ = @import("compiler_rt/arm.zig"); |
lib/compiler_rt/common.zig+12| ... | ... | @@ -188,3 +188,15 @@ pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeIn |
| 188 | 188 | significand.* <<= @intCast(std.math.Log2Int(Z), shift); |
| 189 | 189 | return @as(i32, 1) - shift; |
| 190 | 190 | } |
| 191 | ||
| 192 | pub inline fn fneg(a: anytype) @TypeOf(a) { | |
| 193 | const F = @TypeOf(a); | |
| 194 | const bits = @typeInfo(F).Float.bits; | |
| 195 | const U = @Type(.{ .Int = .{ | |
| 196 | .signedness = .unsigned, | |
| 197 | .bits = bits, | |
| 198 | } }); | |
| 199 | const sign_bit_mask = @as(U, 1) << (bits - 1); | |
| 200 | const negated = @bitCast(U, a) ^ sign_bit_mask; | |
| 201 | return @bitCast(F, negated); | |
| 202 | } |
lib/compiler_rt/negXf2.zig deleted-42| ... | ... | @@ -1,42 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const common = @import("common.zig"); | |
| 4 | ||
| 5 | pub const panic = common.panic; | |
| 6 | ||
| 7 | comptime { | |
| 8 | if (common.want_aeabi) { | |
| 9 | @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage }); | |
| 10 | @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage }); | |
| 11 | } else { | |
| 12 | @export(__negsf2, .{ .name = "__negsf2", .linkage = common.linkage }); | |
| 13 | @export(__negdf2, .{ .name = "__negdf2", .linkage = common.linkage }); | |
| 14 | } | |
| 15 | } | |
| 16 | ||
| 17 | pub fn __negsf2(a: f32) callconv(.C) f32 { | |
| 18 | return negXf2(f32, a); | |
| 19 | } | |
| 20 | ||
| 21 | fn __aeabi_fneg(a: f32) callconv(.AAPCS) f32 { | |
| 22 | return negXf2(f32, a); | |
| 23 | } | |
| 24 | ||
| 25 | pub fn __negdf2(a: f64) callconv(.C) f64 { | |
| 26 | return negXf2(f64, a); | |
| 27 | } | |
| 28 | ||
| 29 | fn __aeabi_dneg(a: f64) callconv(.AAPCS) f64 { | |
| 30 | return negXf2(f64, a); | |
| 31 | } | |
| 32 | ||
| 33 | inline fn negXf2(comptime T: type, a: T) T { | |
| 34 | const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); | |
| 35 | ||
| 36 | const significandBits = std.math.floatMantissaBits(T); | |
| 37 | const exponentBits = std.math.floatExponentBits(T); | |
| 38 | ||
| 39 | const signBit = (@as(Z, 1) << (significandBits + exponentBits)); | |
| 40 | ||
| 41 | return @bitCast(T, @bitCast(Z, a) ^ signBit); | |
| 42 | } |
lib/compiler_rt/negdf2.zig created+19| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | ||
| 3 | pub const panic = common.panic; | |
| 4 | ||
| 5 | comptime { | |
| 6 | if (common.want_aeabi) { | |
| 7 | @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage }); | |
| 8 | } else { | |
| 9 | @export(__negdf2, .{ .name = "__negdf2", .linkage = common.linkage }); | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn __negdf2(a: f64) callconv(.C) f64 { | |
| 14 | return common.fneg(a); | |
| 15 | } | |
| 16 | ||
| 17 | fn __aeabi_dneg(a: f64) callconv(.AAPCS) f64 { | |
| 18 | return common.fneg(a); | |
| 19 | } |
lib/compiler_rt/negsf2.zig created+19| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | ||
| 3 | pub const panic = common.panic; | |
| 4 | ||
| 5 | comptime { | |
| 6 | if (common.want_aeabi) { | |
| 7 | @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage }); | |
| 8 | } else { | |
| 9 | @export(__negsf2, .{ .name = "__negsf2", .linkage = common.linkage }); | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn __negsf2(a: f32) callconv(.C) f32 { | |
| 14 | return common.fneg(a); | |
| 15 | } | |
| 16 | ||
| 17 | fn __aeabi_fneg(a: f32) callconv(.AAPCS) f32 { | |
| 18 | return common.fneg(a); | |
| 19 | } |
lib/compiler_rt/negtf2.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | ||
| 3 | pub const panic = common.panic; | |
| 4 | ||
| 5 | comptime { | |
| 6 | @export(__negtf2, .{ .name = "__negtf2", .linkage = common.linkage }); | |
| 7 | } | |
| 8 | ||
| 9 | fn __negtf2(a: f128) callconv(.C) f128 { | |
| 10 | return common.fneg(a); | |
| 11 | } |
lib/compiler_rt/negxf2.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const common = @import("./common.zig"); | |
| 2 | ||
| 3 | pub const panic = common.panic; | |
| 4 | ||
| 5 | comptime { | |
| 6 | @export(__negxf2, .{ .name = "__negxf2", .linkage = common.linkage }); | |
| 7 | } | |
| 8 | ||
| 9 | fn __negxf2(a: f80) callconv(.C) f80 { | |
| 10 | return common.fneg(a); | |
| 11 | } |
lib/std/fmt.zig-2| ... | ... | @@ -2225,7 +2225,6 @@ test "float.scientific.precision" { |
| 2225 | 2225 | } |
| 2226 | 2226 | |
| 2227 | 2227 | test "float.special" { |
| 2228 | if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 2229 | 2228 | try expectFmt("f64: nan", "f64: {}", .{math.nan_f64}); |
| 2230 | 2229 | // negative nan is not defined by IEE 754, |
| 2231 | 2230 | // and ARM thus normalizes it to positive nan |
| ... | ... | @@ -2237,7 +2236,6 @@ test "float.special" { |
| 2237 | 2236 | } |
| 2238 | 2237 | |
| 2239 | 2238 | test "float.hexadecimal.special" { |
| 2240 | if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 2241 | 2239 | try expectFmt("f64: nan", "f64: {x}", .{math.nan_f64}); |
| 2242 | 2240 | // negative nan is not defined by IEE 754, |
| 2243 | 2241 | // and ARM thus normalizes it to positive nan |
lib/std/math/copysign.zig-1| ... | ... | @@ -13,7 +13,6 @@ pub fn copysign(magnitude: anytype, sign: @TypeOf(magnitude)) @TypeOf(magnitude) |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | 15 | test "math.copysign" { |
| 16 | if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 17 | 16 | inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { |
| 18 | 17 | try expect(copysign(@as(T, 1.0), @as(T, 1.0)) == 1.0); |
| 19 | 18 | try expect(copysign(@as(T, 2.0), @as(T, -2.0)) == -2.0); |
lib/std/math/signbit.zig-1| ... | ... | @@ -10,7 +10,6 @@ pub fn signbit(x: anytype) bool { |
| 10 | 10 | } |
| 11 | 11 | |
| 12 | 12 | test "math.signbit" { |
| 13 | if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 14 | 13 | inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { |
| 15 | 14 | try expect(!signbit(@as(T, 0.0))); |
| 16 | 15 | try expect(!signbit(@as(T, 1.0))); |
src/Air.zig+6| ... | ... | @@ -288,6 +288,11 @@ pub const Inst = struct { |
| 288 | 288 | /// Rounds a floating pointer number to the nearest integer towards zero. |
| 289 | 289 | /// Uses the `un_op` field. |
| 290 | 290 | trunc_float, |
| 291 | /// Float negation. This affects the sign of zero, inf, and NaN, which is impossible | |
| 292 | /// to do with sub. Integers are not allowed and must be represented with sub with | |
| 293 | /// LHS of zero. | |
| 294 | /// Uses the `un_op` field. | |
| 295 | neg, | |
| 291 | 296 | |
| 292 | 297 | /// `<`. Result type is always bool. |
| 293 | 298 | /// Uses the `bin_op` field. |
| ... | ... | @@ -970,6 +975,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 970 | 975 | .ceil, |
| 971 | 976 | .round, |
| 972 | 977 | .trunc_float, |
| 978 | .neg, | |
| 973 | 979 | => return air.typeOf(datas[inst].un_op), |
| 974 | 980 | |
| 975 | 981 | .cmp_lt, |
src/Liveness.zig+2| ... | ... | @@ -287,6 +287,7 @@ pub fn categorizeOperand( |
| 287 | 287 | .ceil, |
| 288 | 288 | .round, |
| 289 | 289 | .trunc_float, |
| 290 | .neg, | |
| 290 | 291 | .cmp_lt_errors_len, |
| 291 | 292 | => { |
| 292 | 293 | const o = air_datas[inst].un_op; |
| ... | ... | @@ -834,6 +835,7 @@ fn analyzeInst( |
| 834 | 835 | .ceil, |
| 835 | 836 | .round, |
| 836 | 837 | .trunc_float, |
| 838 | .neg, | |
| 837 | 839 | .cmp_lt_errors_len, |
| 838 | 840 | .set_err_return_trace, |
| 839 | 841 | => { |
src/Sema.zig+3-1| ... | ... | @@ -10070,12 +10070,14 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 10070 | 10070 | } |
| 10071 | 10071 | |
| 10072 | 10072 | if (rhs_scalar_ty.isAnyFloat()) { |
| 10073 | // We handle comptime negation here to ensure negative zero is represented in the bits. | |
| 10073 | // We handle float negation here to ensure negative zero is represented in the bits. | |
| 10074 | 10074 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { |
| 10075 | 10075 | if (rhs_val.isUndef()) return sema.addConstUndef(rhs_ty); |
| 10076 | 10076 | const target = sema.mod.getTarget(); |
| 10077 | 10077 | return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, target)); |
| 10078 | 10078 | } |
| 10079 | try sema.requireRuntimeBlock(block, rhs_src); | |
| 10080 | return block.addUnOp(.neg, rhs); | |
| 10079 | 10081 | } |
| 10080 | 10082 | |
| 10081 | 10083 | const lhs = if (rhs_ty.zigTypeTag() == .Vector) |
src/arch/aarch64/CodeGen.zig+2-1| ... | ... | @@ -582,7 +582,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 582 | 582 | .floor, |
| 583 | 583 | .ceil, |
| 584 | 584 | .round, |
| 585 | .trunc_float | |
| 585 | .trunc_float, | |
| 586 | .neg, | |
| 586 | 587 | => try self.airUnaryMath(inst), |
| 587 | 588 | |
| 588 | 589 | .add_with_overflow => try self.airOverflow(inst), |
src/arch/arm/CodeGen.zig+1| ... | ... | @@ -596,6 +596,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 596 | 596 | .ceil, |
| 597 | 597 | .round, |
| 598 | 598 | .trunc_float, |
| 599 | .neg, | |
| 599 | 600 | => try self.airUnaryMath(inst), |
| 600 | 601 | |
| 601 | 602 | .add_with_overflow => try self.airOverflow(inst), |
src/arch/riscv64/CodeGen.zig+1| ... | ... | @@ -516,6 +516,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 516 | 516 | .ceil, |
| 517 | 517 | .round, |
| 518 | 518 | .trunc_float, |
| 519 | .neg, | |
| 519 | 520 | => try self.airUnaryMath(inst), |
| 520 | 521 | |
| 521 | 522 | .add_with_overflow => try self.airAddWithOverflow(inst), |
src/arch/sparc64/CodeGen.zig+1| ... | ... | @@ -529,6 +529,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 529 | 529 | .ceil, |
| 530 | 530 | .round, |
| 531 | 531 | .trunc_float, |
| 532 | .neg, | |
| 532 | 533 | => @panic("TODO try self.airUnaryMath(inst)"), |
| 533 | 534 | |
| 534 | 535 | .add_with_overflow => try self.airAddSubWithOverflow(inst), |
src/arch/wasm/CodeGen.zig+1| ... | ... | @@ -1607,6 +1607,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1607 | 1607 | .log10, |
| 1608 | 1608 | .fabs, |
| 1609 | 1609 | .round, |
| 1610 | .neg, | |
| 1610 | 1611 | |
| 1611 | 1612 | .cmpxchg_weak, |
| 1612 | 1613 | .cmpxchg_strong, |
src/arch/x86_64/CodeGen.zig+1| ... | ... | @@ -605,6 +605,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 605 | 605 | .ceil, |
| 606 | 606 | .round, |
| 607 | 607 | .trunc_float, |
| 608 | .neg, | |
| 608 | 609 | => try self.airUnaryMath(inst), |
| 609 | 610 | |
| 610 | 611 | .add_with_overflow => try self.airAddSubShlWithOverflow(inst), |
src/codegen/c.zig+16| ... | ... | @@ -1755,6 +1755,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1755 | 1755 | .mul_sat => try airSatOp(f, inst, "muls_"), |
| 1756 | 1756 | .shl_sat => try airSatOp(f, inst, "shls_"), |
| 1757 | 1757 | |
| 1758 | .neg => try airNeg(f, inst), | |
| 1759 | ||
| 1758 | 1760 | .sqrt, |
| 1759 | 1761 | .sin, |
| 1760 | 1762 | .cos, |
| ... | ... | @@ -4098,6 +4100,20 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4098 | 4100 | return local; |
| 4099 | 4101 | } |
| 4100 | 4102 | |
| 4103 | fn airNeg(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 4104 | if (f.liveness.isUnused(inst)) return CValue.none; | |
| 4105 | ||
| 4106 | const un_op = f.air.instructions.items(.data)[inst].un_op; | |
| 4107 | const writer = f.object.writer(); | |
| 4108 | const inst_ty = f.air.typeOfIndex(inst); | |
| 4109 | const operand = try f.resolveInst(un_op); | |
| 4110 | const local = try f.allocLocal(inst_ty, .Const); | |
| 4111 | try writer.writeAll("-"); | |
| 4112 | try f.writeCValue(writer, operand); | |
| 4113 | try writer.writeAll(";\n"); | |
| 4114 | return local; | |
| 4115 | } | |
| 4116 | ||
| 4101 | 4117 | fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4102 | 4118 | if (f.liveness.isUnused(inst)) return CValue.none; |
| 4103 | 4119 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; |
src/codegen/llvm.zig+17-3| ... | ... | @@ -4022,6 +4022,7 @@ pub const FuncGen = struct { |
| 4022 | 4022 | .ceil => try self.airUnaryOp(inst, .ceil), |
| 4023 | 4023 | .round => try self.airUnaryOp(inst, .round), |
| 4024 | 4024 | .trunc_float => try self.airUnaryOp(inst, .trunc), |
| 4025 | .neg => try self.airUnaryOp(inst, .neg), | |
| 4025 | 4026 | |
| 4026 | 4027 | .cmp_eq => try self.airCmp(inst, .eq), |
| 4027 | 4028 | .cmp_gt => try self.airCmp(inst, .gt), |
| ... | ... | @@ -6548,13 +6549,14 @@ pub const FuncGen = struct { |
| 6548 | 6549 | fabs, |
| 6549 | 6550 | floor, |
| 6550 | 6551 | fma, |
| 6552 | fmax, | |
| 6553 | fmin, | |
| 6554 | fmod, | |
| 6551 | 6555 | log, |
| 6552 | 6556 | log10, |
| 6553 | 6557 | log2, |
| 6554 | fmax, | |
| 6555 | fmin, | |
| 6556 | 6558 | mul, |
| 6557 | fmod, | |
| 6559 | neg, | |
| 6558 | 6560 | round, |
| 6559 | 6561 | sin, |
| 6560 | 6562 | sqrt, |
| ... | ... | @@ -6587,6 +6589,7 @@ pub const FuncGen = struct { |
| 6587 | 6589 | var fn_name_buf: [64]u8 = undefined; |
| 6588 | 6590 | const strat: FloatOpStrat = if (intrinsics_allowed) switch (op) { |
| 6589 | 6591 | // Some operations are dedicated LLVM instructions, not available as intrinsics |
| 6592 | .neg => return self.builder.buildFNeg(params[0], ""), | |
| 6590 | 6593 | .add => return self.builder.buildFAdd(params[0], params[1], ""), |
| 6591 | 6594 | .sub => return self.builder.buildFSub(params[0], params[1], ""), |
| 6592 | 6595 | .mul => return self.builder.buildFMul(params[0], params[1], ""), |
| ... | ... | @@ -6598,6 +6601,17 @@ pub const FuncGen = struct { |
| 6598 | 6601 | } else b: { |
| 6599 | 6602 | const float_bits = scalar_ty.floatBits(target); |
| 6600 | 6603 | break :b switch (op) { |
| 6604 | .neg => { | |
| 6605 | // In this case we can generate a softfloat negation by XORing the | |
| 6606 | // bits with a constant. | |
| 6607 | const int_llvm_ty = self.dg.context.intType(float_bits); | |
| 6608 | const one = int_llvm_ty.constInt(1, .False); | |
| 6609 | const shift_amt = int_llvm_ty.constInt(float_bits - 1, .False); | |
| 6610 | const sign_mask = one.constShl(shift_amt); | |
| 6611 | const bitcasted_operand = self.builder.buildBitCast(params[0], int_llvm_ty, ""); | |
| 6612 | const result = self.builder.buildXor(bitcasted_operand, sign_mask, ""); | |
| 6613 | return self.builder.buildBitCast(result, llvm_ty, ""); | |
| 6614 | }, | |
| 6601 | 6615 | .add, .sub, .div, .mul => FloatOpStrat{ |
| 6602 | 6616 | .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{ |
| 6603 | 6617 | @tagName(op), compilerRtFloatAbbrev(float_bits), |
src/codegen/llvm/bindings.zig+3| ... | ... | @@ -549,6 +549,9 @@ pub const Builder = opaque { |
| 549 | 549 | pub const buildFSub = LLVMBuildFSub; |
| 550 | 550 | extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 551 | 551 | |
| 552 | pub const buildFNeg = LLVMBuildFNeg; | |
| 553 | extern fn LLVMBuildFNeg(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value; | |
| 554 | ||
| 552 | 555 | pub const buildSub = LLVMBuildSub; |
| 553 | 556 | extern fn LLVMBuildSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; |
| 554 | 557 |
src/print_air.zig+1| ... | ... | @@ -168,6 +168,7 @@ const Writer = struct { |
| 168 | 168 | .ceil, |
| 169 | 169 | .round, |
| 170 | 170 | .trunc_float, |
| 171 | .neg, | |
| 171 | 172 | .cmp_lt_errors_len, |
| 172 | 173 | .set_err_return_trace, |
| 173 | 174 | => try w.writeUnOp(s, inst), |
src/value.zig+35-15| ... | ... | @@ -2189,12 +2189,25 @@ pub const Value = extern union { |
| 2189 | 2189 | return ty.isTupleOrAnonStruct() and ty.structFieldCount() != 0; |
| 2190 | 2190 | }, |
| 2191 | 2191 | .Float => { |
| 2192 | const a_nan = a.isNan(); | |
| 2193 | const b_nan = b.isNan(); | |
| 2194 | if (a_nan or b_nan) { | |
| 2195 | return a_nan and b_nan; | |
| 2192 | switch (ty.floatBits(target)) { | |
| 2193 | 16 => return @bitCast(u16, a.toFloat(f16)) == @bitCast(u16, b.toFloat(f16)), | |
| 2194 | 32 => return @bitCast(u32, a.toFloat(f32)) == @bitCast(u32, b.toFloat(f32)), | |
| 2195 | 64 => return @bitCast(u64, a.toFloat(f64)) == @bitCast(u64, b.toFloat(f64)), | |
| 2196 | 80 => return @bitCast(u80, a.toFloat(f80)) == @bitCast(u80, b.toFloat(f80)), | |
| 2197 | 128 => return @bitCast(u128, a.toFloat(f128)) == @bitCast(u128, b.toFloat(f128)), | |
| 2198 | else => unreachable, | |
| 2196 | 2199 | } |
| 2197 | return order(a, b, target).compare(.eq); | |
| 2200 | }, | |
| 2201 | .ComptimeFloat => { | |
| 2202 | const a_float = a.toFloat(f128); | |
| 2203 | const b_float = b.toFloat(f128); | |
| 2204 | ||
| 2205 | const a_nan = std.math.isNan(a_float); | |
| 2206 | const b_nan = std.math.isNan(b_float); | |
| 2207 | if (a_nan != b_nan) return false; | |
| 2208 | if (std.math.signbit(a_float) != std.math.signbit(b_float)) return false; | |
| 2209 | if (a_nan) return true; | |
| 2210 | return a_float == b_float; | |
| 2198 | 2211 | }, |
| 2199 | 2212 | .Optional => { |
| 2200 | 2213 | if (a.tag() != .opt_payload and b.tag() == .opt_payload) { |
| ... | ... | @@ -2231,18 +2244,25 @@ pub const Value = extern union { |
| 2231 | 2244 | var buf: ToTypeBuffer = undefined; |
| 2232 | 2245 | return val.toType(&buf).hashWithHasher(hasher, mod); |
| 2233 | 2246 | }, |
| 2234 | .Float, .ComptimeFloat => { | |
| 2235 | // Normalize the float here because this hash must match eql semantics. | |
| 2236 | // These functions are used for hash maps so we want NaN to equal itself, | |
| 2237 | // and -0.0 to equal +0.0. | |
| 2247 | .Float => { | |
| 2248 | // For hash/eql purposes, we treat floats as their IEEE integer representation. | |
| 2249 | switch (ty.floatBits(mod.getTarget())) { | |
| 2250 | 16 => std.hash.autoHash(hasher, @bitCast(u16, val.toFloat(f16))), | |
| 2251 | 32 => std.hash.autoHash(hasher, @bitCast(u32, val.toFloat(f32))), | |
| 2252 | 64 => std.hash.autoHash(hasher, @bitCast(u64, val.toFloat(f64))), | |
| 2253 | 80 => std.hash.autoHash(hasher, @bitCast(u80, val.toFloat(f80))), | |
| 2254 | 128 => std.hash.autoHash(hasher, @bitCast(u128, val.toFloat(f128))), | |
| 2255 | else => unreachable, | |
| 2256 | } | |
| 2257 | }, | |
| 2258 | .ComptimeFloat => { | |
| 2238 | 2259 | const float = val.toFloat(f128); |
| 2239 | if (std.math.isNan(float)) { | |
| 2240 | std.hash.autoHash(hasher, std.math.nan_u128); | |
| 2241 | } else if (float == 0.0) { | |
| 2242 | var normalized_zero: f128 = 0.0; | |
| 2243 | std.hash.autoHash(hasher, @bitCast(u128, normalized_zero)); | |
| 2244 | } else { | |
| 2260 | const is_nan = std.math.isNan(float); | |
| 2261 | std.hash.autoHash(hasher, is_nan); | |
| 2262 | if (!is_nan) { | |
| 2245 | 2263 | std.hash.autoHash(hasher, @bitCast(u128, float)); |
| 2264 | } else { | |
| 2265 | std.hash.autoHash(hasher, std.math.signbit(float)); | |
| 2246 | 2266 | } |
| 2247 | 2267 | }, |
| 2248 | 2268 | .Bool, .Int, .ComptimeInt, .Pointer => switch (val.tag()) { |
test/behavior/floatop.zig+103| ... | ... | @@ -574,6 +574,7 @@ test "negation f32" { |
| 574 | 574 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 575 | 575 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 576 | 576 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 577 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 577 | 578 | |
| 578 | 579 | const S = struct { |
| 579 | 580 | fn doTheTest() !void { |
| ... | ... | @@ -593,6 +594,8 @@ test "negation f64" { |
| 593 | 594 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 594 | 595 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 595 | 596 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 597 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 598 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 596 | 599 | |
| 597 | 600 | const S = struct { |
| 598 | 601 | fn doTheTest() !void { |
| ... | ... | @@ -707,3 +710,103 @@ test "comptime_float zero divided by zero produces zero" { |
| 707 | 710 | |
| 708 | 711 | try expect((0.0 / 0.0) == 0.0); |
| 709 | 712 | } |
| 713 | ||
| 714 | test "nan negation f16" { | |
| 715 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 716 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 717 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 718 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 719 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 720 | ||
| 721 | const nan_comptime = comptime math.nan(f16); | |
| 722 | const neg_nan_comptime = -nan_comptime; | |
| 723 | ||
| 724 | var nan_runtime = math.nan(f16); | |
| 725 | const neg_nan_runtime = -nan_runtime; | |
| 726 | ||
| 727 | try expect(!math.signbit(nan_runtime)); | |
| 728 | try expect(math.signbit(neg_nan_runtime)); | |
| 729 | ||
| 730 | try expect(!math.signbit(nan_comptime)); | |
| 731 | try expect(math.signbit(neg_nan_comptime)); | |
| 732 | } | |
| 733 | ||
| 734 | test "nan negation f32" { | |
| 735 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 736 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 737 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 738 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 739 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 740 | ||
| 741 | const nan_comptime = comptime math.nan(f32); | |
| 742 | const neg_nan_comptime = -nan_comptime; | |
| 743 | ||
| 744 | var nan_runtime = math.nan(f32); | |
| 745 | const neg_nan_runtime = -nan_runtime; | |
| 746 | ||
| 747 | try expect(!math.signbit(nan_runtime)); | |
| 748 | try expect(math.signbit(neg_nan_runtime)); | |
| 749 | ||
| 750 | try expect(!math.signbit(nan_comptime)); | |
| 751 | try expect(math.signbit(neg_nan_comptime)); | |
| 752 | } | |
| 753 | ||
| 754 | test "nan negation f64" { | |
| 755 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 756 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 757 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 758 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 759 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 760 | ||
| 761 | const nan_comptime = comptime math.nan(f64); | |
| 762 | const neg_nan_comptime = -nan_comptime; | |
| 763 | ||
| 764 | var nan_runtime = math.nan(f64); | |
| 765 | const neg_nan_runtime = -nan_runtime; | |
| 766 | ||
| 767 | try expect(!math.signbit(nan_runtime)); | |
| 768 | try expect(math.signbit(neg_nan_runtime)); | |
| 769 | ||
| 770 | try expect(!math.signbit(nan_comptime)); | |
| 771 | try expect(math.signbit(neg_nan_comptime)); | |
| 772 | } | |
| 773 | ||
| 774 | test "nan negation f128" { | |
| 775 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 776 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 777 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 778 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 779 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 780 | ||
| 781 | const nan_comptime = comptime math.nan(f128); | |
| 782 | const neg_nan_comptime = -nan_comptime; | |
| 783 | ||
| 784 | var nan_runtime = math.nan(f128); | |
| 785 | const neg_nan_runtime = -nan_runtime; | |
| 786 | ||
| 787 | try expect(!math.signbit(nan_runtime)); | |
| 788 | try expect(math.signbit(neg_nan_runtime)); | |
| 789 | ||
| 790 | try expect(!math.signbit(nan_comptime)); | |
| 791 | try expect(math.signbit(neg_nan_comptime)); | |
| 792 | } | |
| 793 | ||
| 794 | test "nan negation f80" { | |
| 795 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 796 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 797 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 798 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 799 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 800 | ||
| 801 | const nan_comptime = comptime math.nan(f80); | |
| 802 | const neg_nan_comptime = -nan_comptime; | |
| 803 | ||
| 804 | var nan_runtime = math.nan(f80); | |
| 805 | const neg_nan_runtime = -nan_runtime; | |
| 806 | ||
| 807 | try expect(!math.signbit(nan_runtime)); | |
| 808 | try expect(math.signbit(neg_nan_runtime)); | |
| 809 | ||
| 810 | try expect(!math.signbit(nan_comptime)); | |
| 811 | try expect(math.signbit(neg_nan_comptime)); | |
| 812 | } |