diff --git a/doc/langref.html.in b/doc/langref.html.in index bd3641b8e2..3a5ca57b53 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -9421,14 +9421,17 @@ fn doTheTest() !void { Supports {#link|Floats#} and {#link|Vectors#} of floats.

{#header_close#} - {#header_open|@fabs#} -
{#syntax#}@fabs(value: anytype) @TypeOf(value){#endsyntax#}
+ {#header_open|@abs#} +
{#syntax#}@abs(value: anytype) anytype{#endsyntax#}

- Returns the absolute value of a floating point number. Uses a dedicated hardware instruction + Returns the absolute value of an integer or a floating point number. Uses a dedicated hardware instruction when available. + + The return type is always an unsigned integer of the same bit width as the operand if the operand is an integer. + Unsigned integer operands are supported. The builtin cannot overflow for signed integer operands.

- Supports {#link|Floats#} and {#link|Vectors#} of floats. + Supports {#link|Floats#}, {#link|Integers#} and {#link|Vectors#} of floats or integers.

{#header_close#} {#header_open|@floor#} diff --git a/lib/compiler_rt/divc3.zig b/lib/compiler_rt/divc3.zig index c4241c1483..92d2b39f66 100644 --- a/lib/compiler_rt/divc3.zig +++ b/lib/compiler_rt/divc3.zig @@ -3,7 +3,6 @@ const isNan = std.math.isNan; const isInf = std.math.isInf; const scalbn = std.math.scalbn; const ilogb = std.math.ilogb; -const fabs = std.math.fabs; const maxInt = std.math.maxInt; const minInt = std.math.minInt; const isFinite = std.math.isFinite; @@ -16,7 +15,7 @@ pub inline fn divc3(comptime T: type, a: T, b: T, c_in: T, d_in: T) Complex(T) { var d = d_in; // logbw used to prevent under/over-flow - const logbw = ilogb(@max(fabs(c), fabs(d))); + const logbw = ilogb(@max(@abs(c), @abs(d))); const logbw_finite = logbw != maxInt(i32) and logbw != minInt(i32); const ilogbw = if (logbw_finite) b: { c = scalbn(c, -logbw); diff --git a/lib/compiler_rt/divxf3_test.zig b/lib/compiler_rt/divxf3_test.zig index 98118602fd..0aec97b54d 100644 --- a/lib/compiler_rt/divxf3_test.zig +++ b/lib/compiler_rt/divxf3_test.zig @@ -30,9 +30,9 @@ fn test__divxf3(a: f80, b: f80) !void { const x_minus_eps: f80 = @bitCast((@as(u80, @bitCast(x)) - 1) | integerBit); // Make sure result is more accurate than the adjacent floats - const err_x = @fabs(@mulAdd(f80, x, b, -a)); - const err_x_plus_eps = @fabs(@mulAdd(f80, x_plus_eps, b, -a)); - const err_x_minus_eps = @fabs(@mulAdd(f80, x_minus_eps, b, -a)); + const err_x = @abs(@mulAdd(f80, x, b, -a)); + const err_x_plus_eps = @abs(@mulAdd(f80, x_plus_eps, b, -a)); + const err_x_minus_eps = @abs(@mulAdd(f80, x_minus_eps, b, -a)); try testing.expect(err_x_minus_eps > err_x); try testing.expect(err_x_plus_eps > err_x); diff --git a/lib/compiler_rt/float_from_int.zig b/lib/compiler_rt/float_from_int.zig index cb3fa67987..5ef511a4bf 100644 --- a/lib/compiler_rt/float_from_int.zig +++ b/lib/compiler_rt/float_from_int.zig @@ -18,7 +18,7 @@ pub fn floatFromInt(comptime T: type, x: anytype) T { const max_exp = exp_bias; // Sign - var abs_val = math.absCast(x); + var abs_val = if (@TypeOf(x) == comptime_int or @typeInfo(@TypeOf(x)).Int.signedness == .signed) @abs(x) else x; const sign_bit = if (x < 0) @as(uT, 1) << (float_bits - 1) else 0; var result: uT = sign_bit; diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index 35e92ee04e..95386f45f0 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -539,7 +539,7 @@ fn replace_variables( .int => |i| { const buf = try std.fmt.allocPrint(allocator, "{s}{}{s}", .{ beginline, i, endline }); const isNegative = i < 0; - const digits = (if (0 < i) std.math.log10(std.math.absCast(i)) else 0) + 1; + const digits = (if (0 < i) std.math.log10(@abs(i)) else 0) + 1; last_index = start_index + @intFromBool(isNegative) + digits + 1; allocator.free(content_buf); diff --git a/lib/std/dwarf/expressions.zig b/lib/std/dwarf/expressions.zig index a57c9add90..f89edc08a1 100644 --- a/lib/std/dwarf/expressions.zig +++ b/lib/std/dwarf/expressions.zig @@ -520,7 +520,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type { if (self.stack.items.len == 0) return error.InvalidExpression; const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral()); self.stack.items[self.stack.items.len - 1] = .{ - .generic = std.math.absCast(value), + .generic = @abs(value), }; }, OP.@"and" => { diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 2f041aeb95..8a72047652 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1413,7 +1413,7 @@ pub fn formatInt( const min_int_bits = comptime @max(value_info.bits, 8); const MinInt = std.meta.Int(.unsigned, min_int_bits); - const abs_value = math.absCast(int_value); + const abs_value = @abs(int_value); // The worst case in terms of space needed is base 2, plus 1 for the sign var buf: [1 + @max(@as(comptime_int, value_info.bits), 1)]u8 = undefined; diff --git a/lib/std/io/fixed_buffer_stream.zig b/lib/std/io/fixed_buffer_stream.zig index 27b978744c..f62ac415a4 100644 --- a/lib/std/io/fixed_buffer_stream.zig +++ b/lib/std/io/fixed_buffer_stream.zig @@ -81,7 +81,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type { pub fn seekBy(self: *Self, amt: i64) SeekError!void { if (amt < 0) { - const abs_amt = std.math.absCast(amt); + const abs_amt = @abs(amt); const abs_amt_usize = std.math.cast(usize, abs_amt) orelse std.math.maxInt(usize); if (abs_amt_usize > self.pos) { self.pos = 0; diff --git a/lib/std/math.zig b/lib/std/math.zig index 7e835059a3..f5ee7019cd 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -130,7 +130,7 @@ pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool { if (isNan(x) or isNan(y)) return false; - return @fabs(x - y) <= tolerance; + return @abs(x - y) <= tolerance; } /// Performs an approximate comparison of two floating point values `x` and `y`. @@ -158,7 +158,7 @@ pub fn approxEqRel(comptime T: type, x: T, y: T, tolerance: T) bool { if (isNan(x) or isNan(y)) return false; - return @fabs(x - y) <= @max(@fabs(x), @fabs(y)) * tolerance; + return @abs(x - y) <= @max(@abs(x), @abs(y)) * tolerance; } test "approxEqAbs and approxEqRel" { @@ -466,7 +466,7 @@ pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T { /// Shifts left. Overflowed bits are truncated. /// A negative shift amount results in a right shift. pub fn shl(comptime T: type, a: T, shift_amt: anytype) T { - const abs_shift_amt = absCast(shift_amt); + const abs_shift_amt = @abs(shift_amt); const casted_shift_amt = blk: { if (@typeInfo(T) == .Vector) { @@ -510,7 +510,7 @@ test "shl" { /// Shifts right. Overflowed bits are truncated. /// A negative shift amount results in a left shift. pub fn shr(comptime T: type, a: T, shift_amt: anytype) T { - const abs_shift_amt = absCast(shift_amt); + const abs_shift_amt = @abs(shift_amt); const casted_shift_amt = blk: { if (@typeInfo(T) == .Vector) { @@ -740,52 +740,6 @@ fn testOverflow() !void { try testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000); } -/// Returns the absolute value of x, where x is a value of a signed integer type. -/// Does not convert and returns a value of a signed integer type. -/// Use `absCast` if you want to convert the result and get an unsigned type. -/// Use `@fabs` if you need the absolute value of a floating point value. -pub fn absInt(x: anytype) !@TypeOf(x) { - const T = @TypeOf(x); - return switch (@typeInfo(T)) { - .Int => |info| { - comptime assert(info.signedness == .signed); // must pass a signed integer to absInt - if (x == minInt(T)) { - return error.Overflow; - } else { - @setRuntimeSafety(false); - return if (x < 0) -x else x; - } - }, - .Vector => |vinfo| blk: { - switch (@typeInfo(vinfo.child)) { - .Int => |info| { - comptime assert(info.signedness == .signed); // must pass a signed integer to absInt - if (@reduce(.Or, x == @as(T, @splat(minInt(vinfo.child))))) { - return error.Overflow; - } - const zero: T = @splat(0); - break :blk @select(vinfo.child, x > zero, x, -x); - }, - else => @compileError("Expected vector of ints, found " ++ @typeName(T)), - } - }, - else => @compileError("Expected an int or vector, found " ++ @typeName(T)), - }; -} - -test "absInt" { - try testAbsInt(); - try comptime testAbsInt(); -} -fn testAbsInt() !void { - try testing.expect((absInt(@as(i32, -10)) catch unreachable) == 10); - try testing.expect((absInt(@as(i32, 10)) catch unreachable) == 10); - try testing.expectEqual(@Vector(3, i32){ 10, 10, 0 }, (absInt(@Vector(3, i32){ -10, 10, 0 }) catch unreachable)); - - try testing.expectError(error.Overflow, absInt(@as(i32, minInt(i32)))); - try testing.expectError(error.Overflow, absInt(@Vector(3, i32){ 10, -10, minInt(i32) })); -} - /// Divide numerator by denominator, rounding toward zero. Returns an /// error on overflow or when denominator is zero. pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T { @@ -968,50 +922,6 @@ fn testRem() !void { try testing.expectError(error.DivisionByZero, rem(f32, 10, 0)); } -/// Returns the absolute value of a floating point number. -/// Uses a dedicated hardware instruction when available. -/// This is the same as calling the builtin @fabs -pub inline fn fabs(value: anytype) @TypeOf(value) { - return @fabs(value); -} - -/// Returns the absolute value of the integer parameter. -/// Converts result type to unsigned if needed and returns a value of an unsigned integer type. -/// Use `absInt` if you want to keep your integer type signed. -pub fn absCast(x: anytype) switch (@typeInfo(@TypeOf(x))) { - .ComptimeInt => comptime_int, - .Int => |int_info| std.meta.Int(.unsigned, int_info.bits), - else => @compileError("absCast only accepts integers"), -} { - switch (@typeInfo(@TypeOf(x))) { - .ComptimeInt => { - if (x < 0) { - return -x; - } else { - return x; - } - }, - .Int => |int_info| { - if (int_info.signedness == .unsigned) return x; - const Uint = std.meta.Int(.unsigned, int_info.bits); - if (x < 0) { - return ~@as(Uint, @bitCast(x +% -1)); - } else { - return @as(Uint, @intCast(x)); - } - }, - else => unreachable, - } -} - -test "absCast" { - try testing.expectEqual(@as(u1, 1), absCast(@as(i1, -1))); - try testing.expectEqual(@as(u32, 999), absCast(@as(i32, -999))); - try testing.expectEqual(@as(u32, 999), absCast(@as(i32, 999))); - try testing.expectEqual(@as(u32, -minInt(i32)), absCast(@as(i32, minInt(i32)))); - try testing.expectEqual(999, absCast(-999)); -} - /// Returns the negation of the integer parameter. /// Result is a signed integer. pub fn negateCast(x: anytype) !std.meta.Int(.signed, @bitSizeOf(@TypeOf(x))) { diff --git a/lib/std/math/asin.zig b/lib/std/math/asin.zig index ac1d01ff55..dedd4d7e72 100644 --- a/lib/std/math/asin.zig +++ b/lib/std/math/asin.zig @@ -60,7 +60,7 @@ fn asin32(x: f32) f32 { } // 1 > |x| >= 0.5 - const z = (1 - @fabs(x)) * 0.5; + const z = (1 - @abs(x)) * 0.5; const s = @sqrt(z); const fx = pio2 - 2 * (s + s * r32(z)); @@ -119,7 +119,7 @@ fn asin64(x: f64) f64 { } // 1 > |x| >= 0.5 - const z = (1 - @fabs(x)) * 0.5; + const z = (1 - @abs(x)) * 0.5; const s = @sqrt(z); const r = r64(z); var fx: f64 = undefined; diff --git a/lib/std/math/atan.zig b/lib/std/math/atan.zig index 75be6ea746..2b57ceb074 100644 --- a/lib/std/math/atan.zig +++ b/lib/std/math/atan.zig @@ -73,7 +73,7 @@ fn atan32(x_: f32) f32 { } id = null; } else { - x = @fabs(x); + x = @abs(x); // |x| < 1.1875 if (ix < 0x3F980000) { // 7/16 <= |x| < 11/16 @@ -171,7 +171,7 @@ fn atan64(x_: f64) f64 { } id = null; } else { - x = @fabs(x); + x = @abs(x); // |x| < 1.1875 if (ix < 0x3FF30000) { // 7/16 <= |x| < 11/16 diff --git a/lib/std/math/atan2.zig b/lib/std/math/atan2.zig index 026c76b5b2..b3ed7b7bca 100644 --- a/lib/std/math/atan2.zig +++ b/lib/std/math/atan2.zig @@ -108,7 +108,7 @@ fn atan2_32(y: f32, x: f32) f32 { if ((m & 2) != 0 and iy + (26 << 23) < ix) { break :z 0.0; } else { - break :z math.atan(@fabs(y / x)); + break :z math.atan(@abs(y / x)); } }; @@ -198,7 +198,7 @@ fn atan2_64(y: f64, x: f64) f64 { if ((m & 2) != 0 and iy +% (64 << 20) < ix) { break :z 0.0; } else { - break :z math.atan(@fabs(y / x)); + break :z math.atan(@abs(y / x)); } }; diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index f6cd79a756..3e6d52e9f7 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -29,7 +29,7 @@ pub fn calcLimbLen(scalar: anytype) usize { return 1; } - const w_value = std.math.absCast(scalar); + const w_value = @abs(scalar); return @as(usize, @intCast(@divFloor(@as(Limb, @intCast(math.log2(w_value))), limb_bits) + 1)); } @@ -240,7 +240,7 @@ pub const Mutable = struct { switch (@typeInfo(T)) { .Int => |info| { - var w_value = std.math.absCast(value); + var w_value = @abs(value); if (info.bits <= limb_bits) { self.limbs[0] = w_value; @@ -255,7 +255,7 @@ pub const Mutable = struct { } }, .ComptimeInt => { - comptime var w_value = std.math.absCast(value); + comptime var w_value = @abs(value); if (w_value <= maxInt(Limb)) { self.limbs[0] = w_value; diff --git a/lib/std/math/complex/cosh.zig b/lib/std/math/complex/cosh.zig index a6650feab4..b83562601d 100644 --- a/lib/std/math/complex/cosh.zig +++ b/lib/std/math/complex/cosh.zig @@ -44,12 +44,12 @@ fn cosh32(z: Complex(f32)) Complex(f32) { // |x|>= 9, so cosh(x) ~= exp(|x|) if (ix < 0x42b17218) { // x < 88.7: exp(|x|) won't overflow - const h = @exp(@fabs(x)) * 0.5; + const h = @exp(@abs(x)) * 0.5; return Complex(f32).init(math.copysign(h, x) * @cos(y), h * @sin(y)); } // x < 192.7: scale to avoid overflow else if (ix < 0x4340b1e7) { - const v = Complex(f32).init(@fabs(x), y); + const v = Complex(f32).init(@abs(x), y); const r = ldexp_cexp(v, -1); return Complex(f32).init(r.re, r.im * math.copysign(@as(f32, 1.0), x)); } @@ -112,12 +112,12 @@ fn cosh64(z: Complex(f64)) Complex(f64) { // |x|>= 22, so cosh(x) ~= exp(|x|) if (ix < 0x40862e42) { // x < 710: exp(|x|) won't overflow - const h = @exp(@fabs(x)) * 0.5; + const h = @exp(@abs(x)) * 0.5; return Complex(f64).init(h * @cos(y), math.copysign(h, x) * @sin(y)); } // x < 1455: scale to avoid overflow else if (ix < 0x4096bbaa) { - const v = Complex(f64).init(@fabs(x), y); + const v = Complex(f64).init(@abs(x), y); const r = ldexp_cexp(v, -1); return Complex(f64).init(r.re, r.im * math.copysign(@as(f64, 1.0), x)); } diff --git a/lib/std/math/complex/sinh.zig b/lib/std/math/complex/sinh.zig index 24d55ab9ab..c5aad570f8 100644 --- a/lib/std/math/complex/sinh.zig +++ b/lib/std/math/complex/sinh.zig @@ -44,12 +44,12 @@ fn sinh32(z: Complex(f32)) Complex(f32) { // |x|>= 9, so cosh(x) ~= exp(|x|) if (ix < 0x42b17218) { // x < 88.7: exp(|x|) won't overflow - const h = @exp(@fabs(x)) * 0.5; + const h = @exp(@abs(x)) * 0.5; return Complex(f32).init(math.copysign(h, x) * @cos(y), h * @sin(y)); } // x < 192.7: scale to avoid overflow else if (ix < 0x4340b1e7) { - const v = Complex(f32).init(@fabs(x), y); + const v = Complex(f32).init(@abs(x), y); const r = ldexp_cexp(v, -1); return Complex(f32).init(r.re * math.copysign(@as(f32, 1.0), x), r.im); } @@ -111,12 +111,12 @@ fn sinh64(z: Complex(f64)) Complex(f64) { // |x|>= 22, so cosh(x) ~= exp(|x|) if (ix < 0x40862e42) { // x < 710: exp(|x|) won't overflow - const h = @exp(@fabs(x)) * 0.5; + const h = @exp(@abs(x)) * 0.5; return Complex(f64).init(math.copysign(h, x) * @cos(y), h * @sin(y)); } // x < 1455: scale to avoid overflow else if (ix < 0x4096bbaa) { - const v = Complex(f64).init(@fabs(x), y); + const v = Complex(f64).init(@abs(x), y); const r = ldexp_cexp(v, -1); return Complex(f64).init(r.re * math.copysign(@as(f64, 1.0), x), r.im); } diff --git a/lib/std/math/complex/sqrt.zig b/lib/std/math/complex/sqrt.zig index fe2e8e6531..5ecbd564f7 100644 --- a/lib/std/math/complex/sqrt.zig +++ b/lib/std/math/complex/sqrt.zig @@ -43,7 +43,7 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { // sqrt(-inf + i nan) = nan +- inf i // sqrt(-inf + iy) = 0 + inf i if (math.signbit(x)) { - return Complex(f32).init(@fabs(x - y), math.copysign(x, y)); + return Complex(f32).init(@abs(x - y), math.copysign(x, y)); } else { return Complex(f32).init(x, math.copysign(y - y, y)); } @@ -64,7 +64,7 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { } else { const t = @sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); return Complex(f32).init( - @as(f32, @floatCast(@fabs(y) / (2.0 * t))), + @as(f32, @floatCast(@abs(y) / (2.0 * t))), @as(f32, @floatCast(math.copysign(t, y))), ); } @@ -94,7 +94,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { // sqrt(-inf + i nan) = nan +- inf i // sqrt(-inf + iy) = 0 + inf i if (math.signbit(x)) { - return Complex(f64).init(@fabs(x - y), math.copysign(x, y)); + return Complex(f64).init(@abs(x - y), math.copysign(x, y)); } else { return Complex(f64).init(x, math.copysign(y - y, y)); } @@ -104,7 +104,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { // scale to avoid overflow var scale = false; - if (@fabs(x) >= threshold or @fabs(y) >= threshold) { + if (@abs(x) >= threshold or @abs(y) >= threshold) { x *= 0.25; y *= 0.25; scale = true; @@ -116,7 +116,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { result = Complex(f64).init(t, y / (2.0 * t)); } else { const t = @sqrt((-x + math.hypot(f64, x, y)) * 0.5); - result = Complex(f64).init(@fabs(y) / (2.0 * t), math.copysign(t, y)); + result = Complex(f64).init(@abs(y) / (2.0 * t), math.copysign(t, y)); } if (scale) { diff --git a/lib/std/math/complex/tanh.zig b/lib/std/math/complex/tanh.zig index 076fcd195f..65074f28e1 100644 --- a/lib/std/math/complex/tanh.zig +++ b/lib/std/math/complex/tanh.zig @@ -44,7 +44,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) { // x >= 11 if (ix >= 0x41300000) { - const exp_mx = @exp(-@fabs(x)); + const exp_mx = @exp(-@abs(x)); return Complex(f32).init(math.copysign(@as(f32, 1.0), x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx); } @@ -87,7 +87,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) { // x >= 22 if (ix >= 0x40360000) { - const exp_mx = @exp(-@fabs(x)); + const exp_mx = @exp(-@abs(x)); return Complex(f64).init(math.copysign(@as(f64, 1.0), x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx); } diff --git a/lib/std/math/pow.zig b/lib/std/math/pow.zig index 927a4d68f4..b095ff1df7 100644 --- a/lib/std/math/pow.zig +++ b/lib/std/math/pow.zig @@ -82,7 +82,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { } // pow(x, +inf) = +0 for |x| < 1 // pow(x, -inf) = +0 for |x| > 1 - else if ((@fabs(x) < 1) == math.isPositiveInf(y)) { + else if ((@abs(x) < 1) == math.isPositiveInf(y)) { return 0; } // pow(x, -inf) = +inf for |x| < 1 @@ -115,7 +115,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { return 1 / @sqrt(x); } - const r1 = math.modf(@fabs(y)); + const r1 = math.modf(@abs(y)); var yi = r1.ipart; var yf = r1.fpart; diff --git a/lib/std/meta.zig b/lib/std/meta.zig index c8ef19c017..d65d53f8f6 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -1104,6 +1104,6 @@ pub fn isError(error_union: anytype) bool { } test "isError" { - try std.testing.expect(isError(math.absInt(@as(i8, -128)))); - try std.testing.expect(!isError(math.absInt(@as(i8, -127)))); + try std.testing.expect(isError(math.divTrunc(u8, 5, 0))); + try std.testing.expect(!isError(math.divTrunc(u8, 5, 5))); } diff --git a/lib/std/rand/ziggurat.zig b/lib/std/rand/ziggurat.zig index 09d695b88d..64417c1f3f 100644 --- a/lib/std/rand/ziggurat.zig +++ b/lib/std/rand/ziggurat.zig @@ -33,7 +33,7 @@ pub fn next_f64(random: Random, comptime tables: ZigTable) f64 { }; const x = u * tables.x[i]; - const test_x = if (tables.is_symmetric) @fabs(x) else x; + const test_x = if (tables.is_symmetric) @abs(x) else x; // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i]) if (test_x < tables.x[i + 1]) { diff --git a/lib/std/zig/c_builtins.zig b/lib/std/zig/c_builtins.zig index 33336543fa..e2b4c4302b 100644 --- a/lib/std/zig/c_builtins.zig +++ b/lib/std/zig/c_builtins.zig @@ -88,13 +88,19 @@ pub inline fn __builtin_log10f(val: f32) f32 { // Standard C Library bug: The absolute value of the most negative integer remains negative. pub inline fn __builtin_abs(val: c_int) c_int { - return std.math.absInt(val) catch std.math.minInt(c_int); + return if (val == std.math.minInt(c_int)) val else @intCast(@abs(val)); +} +pub inline fn __builtin_labs(val: c_long) c_long { + return if (val == std.math.minInt(c_long)) val else @intCast(@abs(val)); +} +pub inline fn __builtin_llabs(val: c_longlong) c_longlong { + return if (val == std.math.minInt(c_longlong)) val else @intCast(@abs(val)); } pub inline fn __builtin_fabs(val: f64) f64 { - return @fabs(val); + return @abs(val); } pub inline fn __builtin_fabsf(val: f32) f32 { - return @fabs(val); + return @abs(val); } pub inline fn __builtin_floor(val: f64) f64 { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 45a89a0c3b..709d9f7706 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -1503,6 +1503,8 @@ fn renderBuiltinCall( try ais.writer().writeAll("@ptrFromInt"); } else if (mem.eql(u8, slice, "@ptrToInt")) { try ais.writer().writeAll("@intFromPtr"); + } else if (mem.eql(u8, slice, "@fabs")) { + try ais.writer().writeAll("@abs"); } else { try renderToken(ais, tree, builtin_token, .none); // @name } diff --git a/lib/zig.h b/lib/zig.h index 5ac70d2a24..233c2961cb 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -946,6 +946,24 @@ typedef unsigned long zig_Builtin64; typedef unsigned long long zig_Builtin64; #endif +#define zig_builtin8_rev(name, val) __builtin_##name(val) + +#define zig_builtin16_rev(name, val) __builtin_##name(val) + +#if INT_MIN <= INT32_MIN +#define zig_builtin32_rev(name, val) __builtin_##name(val) +#elif LONG_MIN <= INT32_MIN +#define zig_builtin32_rev(name, val) __builtin_l##name(val) +#endif + +#if INT_MIN <= INT64_MIN +#define zig_builtin64_rev(name, val) __builtin_##name(val) +#elif LONG_MIN <= INT64_MIN +#define zig_builtin64_rev(name, val) __builtin_l##name(val) +#elif LLONG_MIN <= INT64_MIN +#define zig_builtin64_rev(name, val) __builtin_ll##name(val) +#endif + static inline uint8_t zig_byte_swap_u8(uint8_t val, uint8_t bits) { return zig_wrap_u8(val >> (8 - bits), bits); } @@ -1141,6 +1159,24 @@ zig_builtin_clz(16) zig_builtin_clz(32) zig_builtin_clz(64) +#if zig_has_builtin(abs) || defined(zig_gnuc) +#define zig_builtin_abs(w) \ + static inline int##w##_t zig_abs_i##w(int##w##_t val) { \ + return zig_builtin##w##_rev(abs, val); \ + } +#else +#define zig_builtin_abs(w) \ + static inline int##w##_t zig_abs_i##w(int##w##_t val) { \ + if (val == INT##w##_MIN) return val; \ + int##w##_t tmp = val >> (w - 1); \ + return (val ^ tmp) - tmp; \ + } +#endif +zig_builtin_abs(8) +zig_builtin_abs(16) +zig_builtin_abs(32) +zig_builtin_abs(64) + /* ======================== 128-bit Integer Support ========================= */ #if !defined(zig_has_int128) @@ -1466,6 +1502,11 @@ static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { return zig_wrap_i128(zig_bitCast_i128(zig_mul_u128(zig_bitCast_u128(lhs), zig_bitCast_u128(rhs))), bits); } +static inline zig_u128 zig_abs_i128(zig_i128 val) { + zig_i128 tmp = zig_shr_i128(val, 127); + return zig_bitCast_u128(zig_sub_i128(zig_xor_i128(val, tmp), tmp)); +} + #if zig_has_int128 static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { diff --git a/src/Air.zig b/src/Air.zig index 2126b473a8..8bcc4dbf92 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -356,9 +356,10 @@ pub const Inst = struct { /// Base 10 logarithm of a floating point number. /// Uses the `un_op` field. log10, - /// Aboslute value of a floating point number. - /// Uses the `un_op` field. - fabs, + /// Aboslute value of an integer, floating point number or vector. + /// Result type is always unsigned if the operand is an integer. + /// Uses the `ty_op` field. + abs, /// Floor: rounds a floating pointer number down to the nearest integer. /// Uses the `un_op` field. floor, @@ -1279,7 +1280,6 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) .log, .log2, .log10, - .fabs, .floor, .ceil, .round, @@ -1384,6 +1384,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) .addrspace_cast, .c_va_arg, .c_va_copy, + .abs, => return air.getRefType(datas[inst].ty_op.ty), .loop, @@ -1697,7 +1698,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { .log, .log2, .log10, - .fabs, + .abs, .floor, .ceil, .round, diff --git a/src/AstGen.zig b/src/AstGen.zig index e29457bb46..12e33bd803 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2601,7 +2601,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .log, .log2, .log10, - .fabs, + .abs, .floor, .ceil, .trunc, @@ -8385,7 +8385,7 @@ fn builtinCall( .log => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log), .log2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log2), .log10 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log10), - .fabs => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .fabs), + .abs => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .abs), .floor => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .floor), .ceil => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .ceil), .trunc => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .trunc), diff --git a/src/AstRlAnnotate.zig b/src/AstRlAnnotate.zig index 4e30aff268..f9d6804328 100644 --- a/src/AstRlAnnotate.zig +++ b/src/AstRlAnnotate.zig @@ -929,7 +929,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. .log, .log2, .log10, - .fabs, + .abs, .floor, .ceil, .trunc, diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 55fe164767..96f4d59d10 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -1669,7 +1669,7 @@ fn walkInstruction( .log, .log2, .log10, - .fabs, + .abs, .floor, .ceil, .trunc, diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig index f526d28e19..0056854e77 100644 --- a/src/BuiltinFn.zig +++ b/src/BuiltinFn.zig @@ -102,7 +102,7 @@ pub const Tag = enum { log, log2, log10, - fabs, + abs, floor, ceil, trunc, @@ -874,9 +874,9 @@ pub const list = list: { }, }, .{ - "@fabs", + "@abs", .{ - .tag = .fabs, + .tag = .abs, .param_count = 1, }, }, diff --git a/src/Liveness.zig b/src/Liveness.zig index 2d3b1ee139..36c45ccbfc 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -384,6 +384,7 @@ pub fn categorizeOperand( .addrspace_cast, .c_va_arg, .c_va_copy, + .abs, => { const o = air_datas[inst].ty_op; if (o.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); @@ -420,7 +421,6 @@ pub fn categorizeOperand( .log, .log2, .log10, - .fabs, .floor, .ceil, .round, @@ -1027,6 +1027,7 @@ fn analyzeInst( .addrspace_cast, .c_va_arg, .c_va_copy, + .abs, => { const o = inst_datas[inst].ty_op; return analyzeOperands(a, pass, data, inst, .{ o.operand, .none, .none }); @@ -1054,7 +1055,6 @@ fn analyzeInst( .log, .log2, .log10, - .fabs, .floor, .ceil, .round, diff --git a/src/Liveness/Verify.zig b/src/Liveness/Verify.zig index 0db21f6a88..ec1b621f53 100644 --- a/src/Liveness/Verify.zig +++ b/src/Liveness/Verify.zig @@ -110,6 +110,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { .addrspace_cast, .c_va_arg, .c_va_copy, + .abs, => { const ty_op = data[inst].ty_op; try self.verifyInstOperands(inst, .{ ty_op.operand, .none, .none }); @@ -136,7 +137,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { .log, .log2, .log10, - .fabs, .floor, .ceil, .round, diff --git a/src/Sema.zig b/src/Sema.zig index 9b7fedfbf6..963f7f5489 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1156,6 +1156,7 @@ fn analyzeBodyInner( .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount), + .abs => try sema.zirAbs(block, inst), .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt), .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin), @@ -1166,7 +1167,6 @@ fn analyzeBodyInner( .log => try sema.zirUnaryMath(block, inst, .log, Value.log), .log2 => try sema.zirUnaryMath(block, inst, .log2, Value.log2), .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10), - .fabs => try sema.zirUnaryMath(block, inst, .fabs, Value.fabs), .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor), .ceil => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil), .round => try sema.zirUnaryMath(block, inst, .round, Value.round), @@ -20178,6 +20178,69 @@ fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A return block.addUnOp(.error_name, operand); } +fn zirAbs( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, +) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const operand = try sema.resolveInst(inst_data.operand); + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const operand_ty = sema.typeOf(operand); + const scalar_ty = operand_ty.scalarType(mod); + + const result_ty = switch (scalar_ty.zigTypeTag(mod)) { + .ComptimeFloat, .Float, .ComptimeInt => operand_ty, + .Int => if (scalar_ty.isSignedInt(mod)) try operand_ty.toUnsigned(mod) else return operand, + else => return sema.fail( + block, + operand_src, + "expected integer, float, or vector of either integers or floats, found '{}'", + .{operand_ty.fmt(mod)}, + ), + }; + + return (try sema.maybeConstantUnaryMath(operand, result_ty, Value.abs)) orelse { + try sema.requireRuntimeBlock(block, operand_src, null); + return block.addTyOp(.abs, result_ty, operand); + }; +} + +fn maybeConstantUnaryMath( + sema: *Sema, + operand: Air.Inst.Ref, + result_ty: Type, + comptime eval: fn (Value, Type, Allocator, *Module) Allocator.Error!Value, +) CompileError!?Air.Inst.Ref { + const mod = sema.mod; + switch (result_ty.zigTypeTag(mod)) { + .Vector => if (try sema.resolveMaybeUndefVal(operand)) |val| { + const scalar_ty = result_ty.scalarType(mod); + const vec_len = result_ty.vectorLen(mod); + if (val.isUndef(mod)) + return try mod.undefRef(result_ty); + + const elems = try sema.arena.alloc(InternPool.Index, vec_len); + for (elems, 0..) |*elem, i| { + const elem_val = try val.elemValue(sema.mod, i); + elem.* = try (try eval(elem_val, scalar_ty, sema.arena, sema.mod)).intern(scalar_ty, mod); + } + return Air.internedToRef((try mod.intern(.{ .aggregate = .{ + .ty = result_ty.toIntern(), + .storage = .{ .elems = elems }, + } }))); + }, + else => if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { + if (operand_val.isUndef(mod)) + return try mod.undefRef(result_ty); + const result_val = try eval(operand_val, result_ty, sema.arena, sema.mod); + return Air.internedToRef(result_val.toIntern()); + }, + } + return null; +} + fn zirUnaryMath( sema: *Sema, block: *Block, @@ -20193,58 +20256,22 @@ fn zirUnaryMath( const operand = try sema.resolveInst(inst_data.operand); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_ty = sema.typeOf(operand); + const scalar_ty = operand_ty.scalarType(mod); - switch (operand_ty.zigTypeTag(mod)) { + switch (scalar_ty.zigTypeTag(mod)) { .ComptimeFloat, .Float => {}, - .Vector => { - const scalar_ty = operand_ty.scalarType(mod); - switch (scalar_ty.zigTypeTag(mod)) { - .ComptimeFloat, .Float => {}, - else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(sema.mod)}), - } - }, - else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(sema.mod)}), + else => return sema.fail( + block, + operand_src, + "expected vector of floats or float type, found '{}'", + .{operand_ty.fmt(sema.mod)}, + ), } - switch (operand_ty.zigTypeTag(mod)) { - .Vector => { - const scalar_ty = operand_ty.scalarType(mod); - const vec_len = operand_ty.vectorLen(mod); - const result_ty = try mod.vectorType(.{ - .len = vec_len, - .child = scalar_ty.toIntern(), - }); - if (try sema.resolveMaybeUndefVal(operand)) |val| { - if (val.isUndef(mod)) - return mod.undefRef(result_ty); - - const elems = try sema.arena.alloc(InternPool.Index, vec_len); - for (elems, 0..) |*elem, i| { - const elem_val = try val.elemValue(sema.mod, i); - elem.* = try (try eval(elem_val, scalar_ty, sema.arena, sema.mod)).intern(scalar_ty, mod); - } - return Air.internedToRef((try mod.intern(.{ .aggregate = .{ - .ty = result_ty.toIntern(), - .storage = .{ .elems = elems }, - } }))); - } - - try sema.requireRuntimeBlock(block, operand_src, null); - return block.addUnOp(air_tag, operand); - }, - .ComptimeFloat, .Float => { - if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { - if (operand_val.isUndef(mod)) - return mod.undefRef(operand_ty); - const result_val = try eval(operand_val, operand_ty, sema.arena, sema.mod); - return Air.internedToRef(result_val.toIntern()); - } - - try sema.requireRuntimeBlock(block, operand_src, null); - return block.addUnOp(air_tag, operand); - }, - else => unreachable, - } + return (try sema.maybeConstantUnaryMath(operand, operand_ty, eval)) orelse { + try sema.requireRuntimeBlock(block, operand_src, null); + return block.addUnOp(air_tag, operand); + }; } fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -37503,7 +37530,7 @@ fn float128IntPartToBigInt( float: f128, ) !std.math.big.int.Managed { const is_negative = std.math.signbit(float); - const floored = @floor(@fabs(float)); + const floored = @floor(@abs(float)); var rational = try std.math.big.Rational.init(arena); defer rational.q.deinit(); diff --git a/src/Zir.zig b/src/Zir.zig index 47d1053292..62c48ecbb6 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -846,8 +846,8 @@ pub const Inst = struct { log2, /// Implement builtin `@log10`. Uses `un_node`. log10, - /// Implement builtin `@fabs`. Uses `un_node`. - fabs, + /// Implement builtin `@abs`. Uses `un_node`. + abs, /// Implement builtin `@floor`. Uses `un_node`. floor, /// Implement builtin `@ceil`. Uses `un_node`. @@ -1198,7 +1198,7 @@ pub const Inst = struct { .log, .log2, .log10, - .fabs, + .abs, .floor, .ceil, .trunc, @@ -1493,7 +1493,7 @@ pub const Inst = struct { .log, .log2, .log10, - .fabs, + .abs, .floor, .ceil, .trunc, @@ -1756,7 +1756,7 @@ pub const Inst = struct { .log = .un_node, .log2 = .un_node, .log10 = .un_node, - .fabs = .un_node, + .abs = .un_node, .floor = .un_node, .ceil = .un_node, .trunc = .un_node, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index b7f7b96da7..cdd683390b 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -713,7 +713,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .log, .log2, .log10, - .fabs, .floor, .ceil, .round, @@ -788,6 +787,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .clz => try self.airClz(inst), .ctz => try self.airCtz(inst), .popcount => try self.airPopcount(inst), + .abs => try self.airAbs(inst), .byte_swap => try self.airByteSwap(inst), .bit_reverse => try self.airBitReverse(inst), .tag_name => try self.airTagName(inst), @@ -3550,6 +3550,12 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airAbs(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index c967dd7b63..5afb944474 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -699,7 +699,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .log, .log2, .log10, - .fabs, .floor, .ceil, .round, @@ -774,6 +773,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .clz => try self.airClz(inst), .ctz => try self.airCtz(inst), .popcount => try self.airPopcount(inst), + .abs => try self.airAbs(inst), .byte_swap => try self.airByteSwap(inst), .bit_reverse => try self.airBitReverse(inst), .tag_name => try self.airTagName(inst), @@ -2591,6 +2591,13 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airAbs(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + _ = ty_op; + return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch}); + // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; _ = ty_op; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 1a60e64cf6..6c2748e8f3 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -523,7 +523,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .log, .log2, .log10, - .fabs, .floor, .ceil, .round, @@ -607,6 +606,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .clz => try self.airClz(inst), .ctz => try self.airCtz(inst), .popcount => try self.airPopcount(inst), + .abs => try self.airAbs(inst), .byte_swap => try self.airByteSwap(inst), .bit_reverse => try self.airBitReverse(inst), .tag_name => try self.airTagName(inst), @@ -1447,6 +1447,12 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airAbs(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index e527d093a5..be02614327 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -543,7 +543,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .log, .log2, .log10, - .fabs, + .abs, .floor, .ceil, .round, diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 76a27ec718..850e08a6bc 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1866,13 +1866,14 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .log => func.airUnaryFloatOp(inst, .log), .log2 => func.airUnaryFloatOp(inst, .log2), .log10 => func.airUnaryFloatOp(inst, .log10), - .fabs => func.airUnaryFloatOp(inst, .fabs), .floor => func.airUnaryFloatOp(inst, .floor), .ceil => func.airUnaryFloatOp(inst, .ceil), .round => func.airUnaryFloatOp(inst, .round), .trunc_float => func.airUnaryFloatOp(inst, .trunc), .neg => func.airUnaryFloatOp(inst, .neg), + .abs => func.airAbs(inst), + .add_with_overflow => func.airAddSubWithOverflow(inst, .add), .sub_with_overflow => func.airAddSubWithOverflow(inst, .sub), .shl_with_overflow => func.airShlWithOverflow(inst), @@ -2786,6 +2787,82 @@ const FloatOp = enum { } }; +fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { + const mod = func.bin_file.base.options.module.?; + const ty_op = func.air.instructions.items(.data)[inst].ty_op; + const operand = try func.resolveInst(ty_op.operand); + const ty = func.typeOf(ty_op.operand); + const scalar_ty = ty.scalarType(mod); + + switch (scalar_ty.zigTypeTag(mod)) { + .Int => if (ty.zigTypeTag(mod) == .Vector) { + return func.fail("TODO implement airAbs for {}", .{ty.fmt(mod)}); + } else { + const int_bits = ty.intInfo(mod).bits; + const wasm_bits = toWasmBits(int_bits) orelse { + return func.fail("TODO: airAbs for signed integers larger than '{d}' bits", .{int_bits}); + }; + + const op = try operand.toLocal(func, ty); + + try func.emitWValue(op); + switch (wasm_bits) { + 32 => { + if (wasm_bits != int_bits) { + try func.addImm32(wasm_bits - int_bits); + try func.addTag(.i32_shl); + } + try func.addImm32(31); + try func.addTag(.i32_shr_s); + + const tmp = try func.allocLocal(ty); + try func.addLabel(.local_tee, tmp.local.value); + + try func.emitWValue(op); + try func.addTag(.i32_xor); + try func.emitWValue(tmp); + try func.addTag(.i32_sub); + + if (int_bits != wasm_bits) { + try func.emitWValue(WValue{ .imm32 = (@as(u32, 1) << @intCast(int_bits)) - 1 }); + try func.addTag(.i32_and); + } + }, + 64 => { + if (wasm_bits != int_bits) { + try func.addImm64(wasm_bits - int_bits); + try func.addTag(.i64_shl); + } + try func.addImm64(63); + try func.addTag(.i64_shr_s); + + const tmp = try func.allocLocal(ty); + try func.addLabel(.local_tee, tmp.local.value); + + try func.emitWValue(op); + try func.addTag(.i64_xor); + try func.emitWValue(tmp); + try func.addTag(.i64_sub); + + if (int_bits != wasm_bits) { + try func.emitWValue(WValue{ .imm64 = (@as(u64, 1) << @intCast(int_bits)) - 1 }); + try func.addTag(.i64_and); + } + }, + else => return func.fail("TODO: Implement airAbs for {}", .{ty.fmt(mod)}), + } + + const result = try (WValue{ .stack = {} }).toLocal(func, ty); + func.finishAir(inst, result, &.{ty_op.operand}); + }, + .Float => { + const result = try (try func.floatOp(.fabs, ty, &.{operand})).toLocal(func, ty); + func.finishAir(inst, result, &.{ty_op.operand}); + }, + else => unreachable, + } +} + fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void { const un_op = func.air.instructions.items(.data)[inst].un_op; const operand = try func.resolveInst(un_op); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8f588256b6..8092d94f79 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1809,11 +1809,13 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .round, => try self.airUnaryMath(inst), - .floor => try self.airRound(inst, 0b1_0_01), - .ceil => try self.airRound(inst, 0b1_0_10), + .floor => try self.airRound(inst, 0b1_0_01), + .ceil => try self.airRound(inst, 0b1_0_10), .trunc_float => try self.airRound(inst, 0b1_0_11), - .sqrt => try self.airSqrt(inst), - .neg, .fabs => try self.airFloatSign(inst), + .sqrt => try self.airSqrt(inst), + .neg => try self.airFloatSign(inst), + + .abs => try self.airAbs(inst), .add_with_overflow => try self.airAddSubWithOverflow(inst), .sub_with_overflow => try self.airAddSubWithOverflow(inst), @@ -4885,28 +4887,26 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); } -fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void { +fn floatSign(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty: Type) !void { const mod = self.bin_file.options.module.?; const tag = self.air.instructions.items(.tag)[inst]; - const un_op = self.air.instructions.items(.data)[inst].un_op; - const ty = self.typeOf(un_op); const abi_size: u32 = switch (ty.abiSize(mod)) { 1...16 => 16, 17...32 => 32, - else => return self.fail("TODO implement airFloatSign for {}", .{ + else => return self.fail("TODO implement floatSign for {}", .{ ty.fmt(mod), }), }; const scalar_bits = ty.scalarType(mod).floatBits(self.target.*); - if (scalar_bits == 80) return self.fail("TODO implement airFloatSign for {}", .{ + if (scalar_bits == 80) return self.fail("TODO implement floatSign for {}", .{ ty.fmt(mod), }); - const src_mcv = try self.resolveInst(un_op); + const src_mcv = try self.resolveInst(operand); const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; defer if (src_lock) |lock| self.register_manager.unlockReg(lock); - const dst_mcv: MCValue = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv)) + const dst_mcv: MCValue = if (src_mcv.isRegister() and self.reuseOperand(inst, operand, 0, src_mcv)) src_mcv else if (self.hasFeature(.avx)) .{ .register = try self.register_manager.allocReg(inst, sse) } @@ -4923,7 +4923,7 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void { const sign_val = switch (tag) { .neg => try vec_ty.minInt(mod, vec_ty), - .fabs => try vec_ty.maxInt(mod, vec_ty), + .abs => try vec_ty.maxInt(mod, vec_ty), else => unreachable, }; @@ -4939,24 +4939,24 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void { switch (scalar_bits) { 16, 128 => if (abi_size <= 16 or self.hasFeature(.avx2)) switch (tag) { .neg => .{ .vp_, .xor }, - .fabs => .{ .vp_, .@"and" }, + .abs => .{ .vp_, .@"and" }, else => unreachable, } else switch (tag) { .neg => .{ .v_ps, .xor }, - .fabs => .{ .v_ps, .@"and" }, + .abs => .{ .v_ps, .@"and" }, else => unreachable, }, 32 => switch (tag) { .neg => .{ .v_ps, .xor }, - .fabs => .{ .v_ps, .@"and" }, + .abs => .{ .v_ps, .@"and" }, else => unreachable, }, 64 => switch (tag) { .neg => .{ .v_pd, .xor }, - .fabs => .{ .v_pd, .@"and" }, + .abs => .{ .v_pd, .@"and" }, else => unreachable, }, - 80 => return self.fail("TODO implement airFloatSign for {}", .{ + 80 => return self.fail("TODO implement floatSign for {}", .{ ty.fmt(self.bin_file.options.module.?), }), else => unreachable, @@ -4971,20 +4971,20 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void { switch (scalar_bits) { 16, 128 => switch (tag) { .neg => .{ .p_, .xor }, - .fabs => .{ .p_, .@"and" }, + .abs => .{ .p_, .@"and" }, else => unreachable, }, 32 => switch (tag) { .neg => .{ ._ps, .xor }, - .fabs => .{ ._ps, .@"and" }, + .abs => .{ ._ps, .@"and" }, else => unreachable, }, 64 => switch (tag) { .neg => .{ ._pd, .xor }, - .fabs => .{ ._pd, .@"and" }, + .abs => .{ ._pd, .@"and" }, else => unreachable, }, - 80 => return self.fail("TODO implement airFloatSign for {}", .{ + 80 => return self.fail("TODO implement floatSign for {}", .{ ty.fmt(self.bin_file.options.module.?), }), else => unreachable, @@ -4992,7 +4992,14 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void { registerAlias(dst_reg, abi_size), sign_mem, ); - return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none }); + return self.finishAir(inst, dst_mcv, .{ operand, .none, .none }); +} + +fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const ty = self.typeOf(un_op); + + return self.floatSign(inst, un_op, ty); } fn airRound(self: *Self, inst: Air.Inst.Index, mode: u4) !void { @@ -5082,6 +5089,52 @@ fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: u4 } } +fn airAbs(self: *Self, inst: Air.Inst.Index) !void { + const mod = self.bin_file.options.module.?; + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const ty = self.typeOf(ty_op.operand); + const scalar_ty = ty.scalarType(mod); + + switch (scalar_ty.zigTypeTag(mod)) { + .Int => if (ty.zigTypeTag(mod) == .Vector) { + return self.fail("TODO implement airAbs for {}", .{ty.fmt(mod)}); + } else { + if (ty.abiSize(mod) > 8) { + return self.fail("TODO implement abs for integer abi sizes larger than 8", .{}); + } + const src_mcv = try self.resolveInst(ty_op.operand); + const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, src_mcv); + + try self.genUnOpMir(.{ ._, .neg }, ty, dst_mcv); + + const cmov_abi_size = @max(@as(u32, @intCast(ty.abiSize(mod))), 2); + switch (src_mcv) { + .register => |val_reg| try self.asmCmovccRegisterRegister( + registerAlias(dst_mcv.register, cmov_abi_size), + registerAlias(val_reg, cmov_abi_size), + .l, + ), + .memory, .indirect, .load_frame => try self.asmCmovccRegisterMemory( + registerAlias(dst_mcv.register, cmov_abi_size), + src_mcv.mem(Memory.PtrSize.fromSize(cmov_abi_size)), + .l, + ), + else => { + const val_reg = try self.copyToTmpRegister(ty, src_mcv); + try self.asmCmovccRegisterRegister( + registerAlias(dst_mcv.register, cmov_abi_size), + registerAlias(val_reg, cmov_abi_size), + .l, + ); + }, + } + return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); + }, + .Float => return self.floatSign(inst, ty_op.operand, ty), + else => unreachable, + } +} + fn airSqrt(self: *Self, inst: Air.Inst.Index) !void { const mod = self.bin_file.options.module.?; const un_op = self.air.instructions.items(.data)[inst].un_op; diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index bc4c59dc86..0923a43a77 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -105,7 +105,7 @@ pub const Instruction = struct { try writer.print("{s} ptr [rip", .{@tagName(rip.ptr_size)}); if (rip.disp != 0) try writer.print(" {c} 0x{x}", .{ @as(u8, if (rip.disp < 0) '-' else '+'), - std.math.absCast(rip.disp), + @abs(rip.disp), }); try writer.writeByte(']'); }, @@ -140,7 +140,7 @@ pub const Instruction = struct { try writer.print(" {c} ", .{@as(u8, if (sib.disp < 0) '-' else '+')}) else if (sib.disp < 0) try writer.writeByte('-'); - try writer.print("0x{x}", .{std.math.absCast(sib.disp)}); + try writer.print("0x{x}", .{@abs(sib.disp)}); any = true; } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 90474a9e28..a442d4bcbe 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2912,6 +2912,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, }, .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none), .mod => try airBinBuiltinCall(f, inst, "mod", .none), + .abs => try airAbs(f, inst), .add_wrap => try airBinBuiltinCall(f, inst, "addw", .bits), .sub_wrap => try airBinBuiltinCall(f, inst, "subw", .bits), @@ -2931,7 +2932,6 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .log => try airUnFloatOp(f, inst, "log"), .log2 => try airUnFloatOp(f, inst, "log2"), .log10 => try airUnFloatOp(f, inst, "log10"), - .fabs => try airUnFloatOp(f, inst, "fabs"), .floor => try airUnFloatOp(f, inst, "floor"), .ceil => try airUnFloatOp(f, inst, "ceil"), .round => try airUnFloatOp(f, inst, "round"), @@ -7076,23 +7076,35 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { return local; } -fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue { +fn airAbs(f: *Function, inst: Air.Inst.Index) !CValue { const mod = f.object.dg.module; - const un_op = f.air.instructions.items(.data)[inst].un_op; + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + const operand = try f.resolveInst(ty_op.operand); + const ty = f.typeOf(ty_op.operand); + const scalar_ty = ty.scalarType(mod); - const operand = try f.resolveInst(un_op); - try reap(f, inst, &.{un_op}); + switch (scalar_ty.zigTypeTag(mod)) { + .Int => if (ty.zigTypeTag(mod) == .Vector) { + return f.fail("TODO implement airAbs for '{}'", .{ty.fmt(mod)}); + } else { + return airUnBuiltinCall(f, inst, "abs", .none); + }, + .Float => return unFloatOp(f, inst, operand, ty, "fabs"), + else => unreachable, + } +} - const inst_ty = f.typeOfIndex(inst); - const inst_scalar_ty = inst_ty.scalarType(mod); +fn unFloatOp(f: *Function, inst: Air.Inst.Index, operand: CValue, ty: Type, operation: []const u8) !CValue { + const mod = f.object.dg.module; + const scalar_ty = ty.scalarType(mod); const writer = f.object.writer(); - const local = try f.allocLocal(inst, inst_ty); - const v = try Vectorize.start(f, inst, writer, inst_ty); + const local = try f.allocLocal(inst, ty); + const v = try Vectorize.start(f, inst, writer, ty); try f.writeCValue(writer, local, .Other); try v.elem(f, writer); try writer.writeAll(" = zig_libc_name_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); try writer.writeByte('('); try writer.writeAll(operation); try writer.writeAll(")("); @@ -7104,6 +7116,16 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal return local; } +fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue { + const un_op = f.air.instructions.items(.data)[inst].un_op; + + const operand = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); + + const inst_ty = f.typeOfIndex(inst); + return unFloatOp(f, inst, operand, inst_ty, operation); +} + fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue { const mod = f.object.dg.module; const bin_op = f.air.instructions.items(.data)[inst].bin_op; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index bfbcac1e73..4e6f7733fe 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4729,6 +4729,7 @@ pub const FuncGen = struct { .div_exact => try self.airDivExact(inst, .normal), .rem => try self.airRem(inst, .normal), .mod => try self.airMod(inst, .normal), + .abs => try self.airAbs(inst), .ptr_add => try self.airPtrAdd(inst), .ptr_sub => try self.airPtrSub(inst), .shl => try self.airShl(inst), @@ -4766,7 +4767,6 @@ pub const FuncGen = struct { .log => try self.airUnaryOp(inst, .log), .log2 => try self.airUnaryOp(inst, .log2), .log10 => try self.airUnaryOp(inst, .log10), - .fabs => try self.airUnaryOp(inst, .fabs), .floor => try self.airUnaryOp(inst, .floor), .ceil => try self.airUnaryOp(inst, .ceil), .round => try self.airUnaryOp(inst, .round), @@ -8237,6 +8237,28 @@ pub const FuncGen = struct { else if (is_signed_int) .ashr else .lshr, lhs, casted_rhs, ""); } + fn airAbs(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { + const o = self.dg.object; + const mod = o.module; + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const operand = try self.resolveInst(ty_op.operand); + const operand_ty = self.typeOf(ty_op.operand); + const scalar_ty = operand_ty.scalarType(mod); + + switch (scalar_ty.zigTypeTag(mod)) { + .Int => return self.wip.callIntrinsic( + .normal, + .none, + .abs, + &.{try o.lowerType(operand_ty)}, + &.{ operand, try o.builder.intValue(.i1, 0) }, + "", + ), + .Float => return self.buildFloatOp(.fabs, .normal, operand_ty, 1, .{operand}), + else => unreachable, + } + } + fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const o = self.dg.object; const mod = o.module; diff --git a/src/print_air.zig b/src/print_air.zig index d16aa1e0ae..3a3c18c9f3 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -188,7 +188,7 @@ const Writer = struct { .log, .log2, .log10, - .fabs, + .abs, .floor, .ceil, .round, diff --git a/src/print_zir.zig b/src/print_zir.zig index bef5f2c815..5ced6cafe7 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -262,7 +262,7 @@ const Writer = struct { .log, .log2, .log10, - .fabs, + .abs, .floor, .ceil, .trunc, diff --git a/src/type.zig b/src/type.zig index 6345f1ef6a..79be8b4c5b 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3197,6 +3197,17 @@ pub const Type = struct { }; } + pub fn toUnsigned(ty: Type, mod: *Module) !Type { + return switch (ty.zigTypeTag(mod)) { + .Int => mod.intType(.unsigned, ty.intInfo(mod).bits), + .Vector => try mod.vectorType(.{ + .len = ty.vectorLen(mod), + .child = (try ty.childType(mod).toUnsigned(mod)).toIntern(), + }), + else => unreachable, + }; + } + pub const @"u1": Type = .{ .ip_index = .u1_type }; pub const @"u8": Type = .{ .ip_index = .u8_type }; pub const @"u16": Type = .{ .ip_index = .u16_type }; diff --git a/src/value.zig b/src/value.zig index e7029369e7..48a2f0fca2 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1993,7 +1993,7 @@ pub const Value = struct { return 1; } - const w_value = @fabs(scalar); + const w_value = @abs(scalar); return @divFloor(@as(std.math.big.Limb, @intFromFloat(std.math.log2(w_value))), @typeInfo(std.math.big.Limb).Int.bits) + 1; } @@ -3710,36 +3710,55 @@ pub const Value = struct { } })).toValue(); } - pub fn fabs(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value { - if (float_type.zigTypeTag(mod) == .Vector) { - const result_data = try arena.alloc(InternPool.Index, float_type.vectorLen(mod)); - const scalar_ty = float_type.scalarType(mod); + pub fn abs(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value { + if (ty.zigTypeTag(mod) == .Vector) { + const result_data = try arena.alloc(InternPool.Index, ty.vectorLen(mod)); + const scalar_ty = ty.scalarType(mod); for (result_data, 0..) |*scalar, i| { const elem_val = try val.elemValue(mod, i); - scalar.* = try (try fabsScalar(elem_val, scalar_ty, mod)).intern(scalar_ty, mod); + scalar.* = try (try absScalar(elem_val, scalar_ty, mod, arena)).intern(scalar_ty, mod); } return (try mod.intern(.{ .aggregate = .{ - .ty = float_type.toIntern(), + .ty = ty.toIntern(), .storage = .{ .elems = result_data }, } })).toValue(); } - return fabsScalar(val, float_type, mod); + return absScalar(val, ty, mod, arena); } - pub fn fabsScalar(val: Value, float_type: Type, mod: *Module) Allocator.Error!Value { - const target = mod.getTarget(); - const storage: InternPool.Key.Float.Storage = switch (float_type.floatBits(target)) { - 16 => .{ .f16 = @fabs(val.toFloat(f16, mod)) }, - 32 => .{ .f32 = @fabs(val.toFloat(f32, mod)) }, - 64 => .{ .f64 = @fabs(val.toFloat(f64, mod)) }, - 80 => .{ .f80 = @fabs(val.toFloat(f80, mod)) }, - 128 => .{ .f128 = @fabs(val.toFloat(f128, mod)) }, + pub fn absScalar(val: Value, ty: Type, mod: *Module, arena: Allocator) Allocator.Error!Value { + switch (ty.zigTypeTag(mod)) { + .Int => { + var buffer: Value.BigIntSpace = undefined; + var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena); + operand_bigint.abs(); + + return mod.intValue_big(try ty.toUnsigned(mod), operand_bigint.toConst()); + }, + .ComptimeInt => { + var buffer: Value.BigIntSpace = undefined; + var operand_bigint = try val.toBigInt(&buffer, mod).toManaged(arena); + operand_bigint.abs(); + + return mod.intValue_big(ty, operand_bigint.toConst()); + }, + .ComptimeFloat, .Float => { + const target = mod.getTarget(); + const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(target)) { + 16 => .{ .f16 = @abs(val.toFloat(f16, mod)) }, + 32 => .{ .f32 = @abs(val.toFloat(f32, mod)) }, + 64 => .{ .f64 = @abs(val.toFloat(f64, mod)) }, + 80 => .{ .f80 = @abs(val.toFloat(f80, mod)) }, + 128 => .{ .f128 = @abs(val.toFloat(f128, mod)) }, + else => unreachable, + }; + return (try mod.intern(.{ .float = .{ + .ty = ty.toIntern(), + .storage = storage, + } })).toValue(); + }, else => unreachable, - }; - return (try mod.intern(.{ .float = .{ - .ty = float_type.toIntern(), - .storage = storage, - } })).toValue(); + } } pub fn floor(val: Value, float_type: Type, arena: Allocator, mod: *Module) !Value { diff --git a/stage1/zig.h b/stage1/zig.h index 5ac70d2a24..233c2961cb 100644 --- a/stage1/zig.h +++ b/stage1/zig.h @@ -946,6 +946,24 @@ typedef unsigned long zig_Builtin64; typedef unsigned long long zig_Builtin64; #endif +#define zig_builtin8_rev(name, val) __builtin_##name(val) + +#define zig_builtin16_rev(name, val) __builtin_##name(val) + +#if INT_MIN <= INT32_MIN +#define zig_builtin32_rev(name, val) __builtin_##name(val) +#elif LONG_MIN <= INT32_MIN +#define zig_builtin32_rev(name, val) __builtin_l##name(val) +#endif + +#if INT_MIN <= INT64_MIN +#define zig_builtin64_rev(name, val) __builtin_##name(val) +#elif LONG_MIN <= INT64_MIN +#define zig_builtin64_rev(name, val) __builtin_l##name(val) +#elif LLONG_MIN <= INT64_MIN +#define zig_builtin64_rev(name, val) __builtin_ll##name(val) +#endif + static inline uint8_t zig_byte_swap_u8(uint8_t val, uint8_t bits) { return zig_wrap_u8(val >> (8 - bits), bits); } @@ -1141,6 +1159,24 @@ zig_builtin_clz(16) zig_builtin_clz(32) zig_builtin_clz(64) +#if zig_has_builtin(abs) || defined(zig_gnuc) +#define zig_builtin_abs(w) \ + static inline int##w##_t zig_abs_i##w(int##w##_t val) { \ + return zig_builtin##w##_rev(abs, val); \ + } +#else +#define zig_builtin_abs(w) \ + static inline int##w##_t zig_abs_i##w(int##w##_t val) { \ + if (val == INT##w##_MIN) return val; \ + int##w##_t tmp = val >> (w - 1); \ + return (val ^ tmp) - tmp; \ + } +#endif +zig_builtin_abs(8) +zig_builtin_abs(16) +zig_builtin_abs(32) +zig_builtin_abs(64) + /* ======================== 128-bit Integer Support ========================= */ #if !defined(zig_has_int128) @@ -1466,6 +1502,11 @@ static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { return zig_wrap_i128(zig_bitCast_i128(zig_mul_u128(zig_bitCast_u128(lhs), zig_bitCast_u128(rhs))), bits); } +static inline zig_u128 zig_abs_i128(zig_i128 val) { + zig_i128 tmp = zig_shr_i128(val, 127); + return zig_bitCast_u128(zig_sub_i128(zig_xor_i128(val, tmp), tmp)); +} + #if zig_has_int128 static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm index 1db7712fe3..2f6d9abc4e 100644 Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ diff --git a/test/behavior.zig b/test/behavior.zig index ba65fc4fad..7523c60ae8 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -237,6 +237,7 @@ test { _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); + _ = @import("behavior/abs.zig"); if (builtin.cpu.arch == .wasm32) { _ = @import("behavior/wasm.zig"); diff --git a/test/behavior/abs.zig b/test/behavior/abs.zig new file mode 100644 index 0000000000..eefea306c4 --- /dev/null +++ b/test/behavior/abs.zig @@ -0,0 +1,371 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const expect = std.testing.expect; + +test "@abs integers" { + 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_riscv64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + + try comptime testAbsIntegers(); + try testAbsIntegers(); +} + +fn testAbsIntegers() !void { + { + var x: i32 = -1000; + try expect(@abs(x) == 1000); + } + { + var x: i32 = 0; + try expect(@abs(x) == 0); + } + { + var x: i32 = 1000; + try expect(@abs(x) == 1000); + } + { + var x: i64 = std.math.minInt(i64); + try expect(@abs(x) == @as(u64, -std.math.minInt(i64))); + } + { + var x: i5 = -1; + try expect(@abs(x) == 1); + } + { + var x: i5 = -5; + try expect(@abs(x) == 5); + } + comptime { + try expect(@abs(@as(i2, -2)) == 2); + } +} + +test "@abs unsigned integers" { + 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_riscv64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + + try comptime testAbsUnsignedIntegers(); + try testAbsUnsignedIntegers(); +} + +fn testAbsUnsignedIntegers() !void { + { + var x: u32 = 1000; + try expect(@abs(x) == 1000); + } + { + var x: u32 = 0; + try expect(@abs(x) == 0); + } + { + var x: u32 = 1000; + try expect(@abs(x) == 1000); + } + { + var x: u5 = 1; + try expect(@abs(x) == 1); + } + { + var x: u5 = 5; + try expect(@abs(x) == 5); + } + comptime { + try expect(@abs(@as(u2, 2)) == 2); + } +} + +test "@abs floats" { + 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_riscv64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + + try comptime testAbsFloats(f16); + if (builtin.zig_backend != .stage2_x86_64) try testAbsFloats(f16); + try comptime testAbsFloats(f32); + try testAbsFloats(f32); + try comptime testAbsFloats(f64); + try testAbsFloats(f64); + try comptime testAbsFloats(f80); + if (builtin.zig_backend != .stage2_x86_64 and builtin.zig_backend != .stage2_wasm) try testAbsFloats(f80); + try comptime testAbsFloats(f128); + if (builtin.zig_backend != .stage2_x86_64 and builtin.zig_backend != .stage2_wasm) try testAbsFloats(f128); +} + +fn testAbsFloats(comptime T: type) !void { + { + var x: T = -2.62; + try expect(@abs(x) == 2.62); + } + { + var x: T = 2.62; + try expect(@abs(x) == 2.62); + } + { + var x: T = 0.0; + try expect(@abs(x) == 0.0); + } + { + var x: T = -std.math.pi; + try expect(@abs(x) == std.math.pi); + } + + { + var x: T = -std.math.inf(T); + try expect(@abs(x) == std.math.inf(T)); + } + { + var x: T = std.math.inf(T); + try expect(@abs(x) == std.math.inf(T)); + } + comptime { + try expect(@abs(@as(T, -std.math.e)) == std.math.e); + } +} + +test "@abs int vectors" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + 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_riscv64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + + try comptime testAbsIntVectors(1); + try testAbsIntVectors(1); + try comptime testAbsIntVectors(2); + try testAbsIntVectors(2); + try comptime testAbsIntVectors(3); + try testAbsIntVectors(3); + try comptime testAbsIntVectors(4); + try testAbsIntVectors(4); + try comptime testAbsIntVectors(8); + try testAbsIntVectors(8); + try comptime testAbsIntVectors(16); + try testAbsIntVectors(16); + try comptime testAbsIntVectors(17); + try testAbsIntVectors(17); +} + +fn testAbsIntVectors(comptime len: comptime_int) !void { + const I32 = @Vector(len, i32); + const U32 = @Vector(len, u32); + const I64 = @Vector(len, i64); + const U64 = @Vector(len, u64); + { + var x: I32 = @splat(-10); + var y: U32 = @splat(10); + try expect(std.mem.eql(u32, &@as([len]u32, y), &@as([len]u32, @abs(x)))); + } + { + var x: I32 = @splat(10); + var y: U32 = @splat(10); + try expect(std.mem.eql(u32, &@as([len]u32, y), &@as([len]u32, @abs(x)))); + } + { + var x: I32 = @splat(0); + var y: U32 = @splat(0); + try expect(std.mem.eql(u32, &@as([len]u32, y), &@as([len]u32, @abs(x)))); + } + { + var x: I64 = @splat(-10); + var y: U64 = @splat(10); + try expect(std.mem.eql(u64, &@as([len]u64, y), &@as([len]u64, @abs(x)))); + } + { + var x: I64 = @splat(std.math.minInt(i64)); + var y: U64 = @splat(-std.math.minInt(i64)); + try expect(std.mem.eql(u64, &@as([len]u64, y), &@as([len]u64, @abs(x)))); + } + { + var x = std.simd.repeat(len, @Vector(4, i32){ -2, 5, std.math.minInt(i32), -7 }); + var y = std.simd.repeat(len, @Vector(4, u32){ 2, 5, -std.math.minInt(i32), 7 }); + try expect(std.mem.eql(u32, &@as([len]u32, y), &@as([len]u32, @abs(x)))); + } +} + +test "@abs unsigned int vectors" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + 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_riscv64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + + try comptime testAbsUnsignedIntVectors(1); + try testAbsUnsignedIntVectors(1); + try comptime testAbsUnsignedIntVectors(2); + try testAbsUnsignedIntVectors(2); + try comptime testAbsUnsignedIntVectors(3); + try testAbsUnsignedIntVectors(3); + try comptime testAbsUnsignedIntVectors(4); + try testAbsUnsignedIntVectors(4); + try comptime testAbsUnsignedIntVectors(8); + try testAbsUnsignedIntVectors(8); + try comptime testAbsUnsignedIntVectors(16); + try testAbsUnsignedIntVectors(16); + try comptime testAbsUnsignedIntVectors(17); + try testAbsUnsignedIntVectors(17); +} + +fn testAbsUnsignedIntVectors(comptime len: comptime_int) !void { + const U32 = @Vector(len, u32); + const U64 = @Vector(len, u64); + { + var x: U32 = @splat(10); + var y: U32 = @splat(10); + try expect(std.mem.eql(u32, &@as([len]u32, y), &@as([len]u32, @abs(x)))); + } + { + var x: U32 = @splat(10); + var y: U32 = @splat(10); + try expect(std.mem.eql(u32, &@as([len]u32, y), &@as([len]u32, @abs(x)))); + } + { + var x: U32 = @splat(0); + var y: U32 = @splat(0); + try expect(std.mem.eql(u32, &@as([len]u32, y), &@as([len]u32, @abs(x)))); + } + { + var x: U64 = @splat(10); + var y: U64 = @splat(10); + try expect(std.mem.eql(u64, &@as([len]u64, y), &@as([len]u64, @abs(x)))); + } + { + var x = std.simd.repeat(len, @Vector(3, u32){ 2, 5, 7 }); + var y = std.simd.repeat(len, @Vector(3, u32){ 2, 5, 7 }); + try expect(std.mem.eql(u32, &@as([len]u32, y), &@as([len]u32, @abs(x)))); + } +} + +test "@abs float vectors" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + 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_riscv64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + + // https://github.com/ziglang/zig/issues/12827 + if (builtin.zig_backend == .stage2_llvm and + builtin.os.tag == .macos and + builtin.target.cpu.arch == .x86_64) return error.SkipZigTest; + + @setEvalBranchQuota(2000); + try comptime testAbsFloatVectors(f16, 1); + try testAbsFloatVectors(f16, 1); + try comptime testAbsFloatVectors(f16, 2); + try testAbsFloatVectors(f16, 2); + try comptime testAbsFloatVectors(f16, 3); + try testAbsFloatVectors(f16, 3); + try comptime testAbsFloatVectors(f16, 4); + try testAbsFloatVectors(f16, 4); + try comptime testAbsFloatVectors(f16, 8); + try testAbsFloatVectors(f16, 8); + try comptime testAbsFloatVectors(f16, 16); + try testAbsFloatVectors(f16, 16); + try comptime testAbsFloatVectors(f16, 17); + + try testAbsFloatVectors(f32, 17); + try comptime testAbsFloatVectors(f32, 1); + try testAbsFloatVectors(f32, 1); + try comptime testAbsFloatVectors(f32, 2); + try testAbsFloatVectors(f32, 2); + try comptime testAbsFloatVectors(f32, 3); + try testAbsFloatVectors(f32, 3); + try comptime testAbsFloatVectors(f32, 4); + try testAbsFloatVectors(f32, 4); + try comptime testAbsFloatVectors(f32, 8); + try testAbsFloatVectors(f32, 8); + try comptime testAbsFloatVectors(f32, 16); + try testAbsFloatVectors(f32, 16); + try comptime testAbsFloatVectors(f32, 17); + try testAbsFloatVectors(f32, 17); + + try comptime testAbsFloatVectors(f64, 1); + try testAbsFloatVectors(f64, 1); + try comptime testAbsFloatVectors(f64, 2); + try testAbsFloatVectors(f64, 2); + try comptime testAbsFloatVectors(f64, 3); + try testAbsFloatVectors(f64, 3); + try comptime testAbsFloatVectors(f64, 4); + try testAbsFloatVectors(f64, 4); + try comptime testAbsFloatVectors(f64, 8); + try testAbsFloatVectors(f64, 8); + try comptime testAbsFloatVectors(f64, 16); + try testAbsFloatVectors(f64, 16); + try comptime testAbsFloatVectors(f64, 17); + try testAbsFloatVectors(f64, 17); + + try comptime testAbsFloatVectors(f80, 1); + try testAbsFloatVectors(f80, 1); + try comptime testAbsFloatVectors(f80, 2); + try testAbsFloatVectors(f80, 2); + try comptime testAbsFloatVectors(f80, 3); + try testAbsFloatVectors(f80, 3); + try comptime testAbsFloatVectors(f80, 4); + try testAbsFloatVectors(f80, 4); + try comptime testAbsFloatVectors(f80, 8); + try testAbsFloatVectors(f80, 8); + try comptime testAbsFloatVectors(f80, 16); + try testAbsFloatVectors(f80, 16); + try comptime testAbsFloatVectors(f80, 17); + try testAbsFloatVectors(f80, 17); + + try comptime testAbsFloatVectors(f128, 1); + try testAbsFloatVectors(f128, 1); + try comptime testAbsFloatVectors(f128, 2); + try testAbsFloatVectors(f128, 2); + try comptime testAbsFloatVectors(f128, 3); + try testAbsFloatVectors(f128, 3); + try comptime testAbsFloatVectors(f128, 4); + try testAbsFloatVectors(f128, 4); + try comptime testAbsFloatVectors(f128, 8); + try testAbsFloatVectors(f128, 8); + try comptime testAbsFloatVectors(f128, 16); + try testAbsFloatVectors(f128, 16); + try comptime testAbsFloatVectors(f128, 17); + try testAbsFloatVectors(f128, 17); +} + +fn testAbsFloatVectors(comptime T: type, comptime len: comptime_int) !void { + const V = @Vector(len, T); + { + var x: V = @splat(-7.5); + var y: V = @splat(7.5); + try expect(std.mem.eql(T, &@as([len]T, y), &@as([len]T, @abs(x)))); + } + { + var x: V = @splat(7.5); + var y: V = @splat(7.5); + try expect(std.mem.eql(T, &@as([len]T, y), &@as([len]T, @abs(x)))); + } + { + var x: V = @splat(0.0); + var y: V = @splat(0.0); + try expect(std.mem.eql(T, &@as([len]T, y), &@as([len]T, @abs(x)))); + } + { + var x: V = @splat(-std.math.pi); + var y: V = @splat(std.math.pi); + try expect(std.mem.eql(T, &@as([len]T, y), &@as([len]T, @abs(x)))); + } + { + var x: V = @splat(std.math.pi); + var y: V = @splat(std.math.pi); + try expect(std.mem.eql(T, &@as([len]T, y), &@as([len]T, @abs(x)))); + } +} diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 33460b827e..7ac965d5b4 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -523,7 +523,7 @@ fn testLog10WithVectors() !void { try expect(@log10(@as(f32, 0.4)) == result[3]); } -test "@fabs" { +test "@abs" { 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_sparc64) return error.SkipZigTest; // TODO @@ -533,24 +533,24 @@ test "@fabs" { } fn testFabs() !void { - try expect(@fabs(@as(f16, -2.5)) == 2.5); - try expect(@fabs(@as(f16, 2.5)) == 2.5); - try expect(@fabs(@as(f32, -2.5)) == 2.5); - try expect(@fabs(@as(f32, 2.5)) == 2.5); - try expect(@fabs(@as(f64, -2.5)) == 2.5); - try expect(@fabs(@as(f64, 2.5)) == 2.5); + try expect(@abs(@as(f16, -2.5)) == 2.5); + try expect(@abs(@as(f16, 2.5)) == 2.5); + try expect(@abs(@as(f32, -2.5)) == 2.5); + try expect(@abs(@as(f32, 2.5)) == 2.5); + try expect(@abs(@as(f64, -2.5)) == 2.5); + try expect(@abs(@as(f64, 2.5)) == 2.5); // TODO test f128, and c_longdouble // https://github.com/ziglang/zig/issues/4026 // { // var a: f80 = -2.5; // var b: f80 = 2.5; - // try expect(@fabs(a) == 2.5); - // try expect(@fabs(b) == 2.5); + // try expect(@abs(a) == 2.5); + // try expect(@abs(b) == 2.5); // } } -test "@fabs with vectors" { +test "@abs with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -562,14 +562,15 @@ test "@fabs with vectors" { fn testFabsWithVectors() !void { var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; - var result = @fabs(v); - try expect(math.approxEqAbs(f32, @fabs(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @fabs(@as(f32, -2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @fabs(@as(f32, 0.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @fabs(@as(f32, -0.4)), result[3], epsilon)); + var result = @abs(v); + try expect(math.approxEqAbs(f32, @abs(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @abs(@as(f32, -2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @abs(@as(f32, 0.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @abs(@as(f32, -0.4)), result[3], epsilon)); } -test "another, possibly redundant, @fabs test" { +test "another, possibly redundant, @abs test" { + if (builtin.zig_backend == .stage2_wasm) 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_x86_64) return error.SkipZigTest; // TODO @@ -587,11 +588,12 @@ test "another, possibly redundant, @fabs test" { const x = 14.0; const y = -x; - const z = @fabs(y); + const z = @abs(y); try comptime std.testing.expectEqual(x, z); } -test "@fabs f80" { +test "@abs f80" { + if (builtin.zig_backend == .stage2_wasm) 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_x86_64) return error.SkipZigTest; // TODO @@ -604,11 +606,12 @@ test "@fabs f80" { fn testFabsLegacy(comptime T: type, x: T) !void { const y = -x; - const z = @fabs(y); + const z = @abs(y); try expect(x == z); } -test "a third @fabs test, surely there should not be three fabs tests" { +test "a third @abs test, surely there should not be three fabs tests" { + if (builtin.zig_backend == .stage2_wasm) 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_x86_64) return error.SkipZigTest; // TODO @@ -617,23 +620,23 @@ test "a third @fabs test, surely there should not be three fabs tests" { inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { // normals - try expect(@fabs(@as(T, 1.0)) == 1.0); - try expect(@fabs(@as(T, -1.0)) == 1.0); - try expect(@fabs(math.floatMin(T)) == math.floatMin(T)); - try expect(@fabs(-math.floatMin(T)) == math.floatMin(T)); - try expect(@fabs(math.floatMax(T)) == math.floatMax(T)); - try expect(@fabs(-math.floatMax(T)) == math.floatMax(T)); + try expect(@abs(@as(T, 1.0)) == 1.0); + try expect(@abs(@as(T, -1.0)) == 1.0); + try expect(@abs(math.floatMin(T)) == math.floatMin(T)); + try expect(@abs(-math.floatMin(T)) == math.floatMin(T)); + try expect(@abs(math.floatMax(T)) == math.floatMax(T)); + try expect(@abs(-math.floatMax(T)) == math.floatMax(T)); // subnormals - try expect(@fabs(@as(T, 0.0)) == 0.0); - try expect(@fabs(@as(T, -0.0)) == 0.0); - try expect(@fabs(math.floatTrueMin(T)) == math.floatTrueMin(T)); - try expect(@fabs(-math.floatTrueMin(T)) == math.floatTrueMin(T)); + try expect(@abs(@as(T, 0.0)) == 0.0); + try expect(@abs(@as(T, -0.0)) == 0.0); + try expect(@abs(math.floatTrueMin(T)) == math.floatTrueMin(T)); + try expect(@abs(-math.floatTrueMin(T)) == math.floatTrueMin(T)); // non-finite numbers - try expect(math.isPositiveInf(@fabs(math.inf(T)))); - try expect(math.isPositiveInf(@fabs(-math.inf(T)))); - try expect(math.isNan(@fabs(math.nan(T)))); + try expect(math.isPositiveInf(@abs(math.inf(T)))); + try expect(math.isPositiveInf(@abs(-math.inf(T)))); + try expect(math.isNan(@abs(math.nan(T)))); } } diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 9e4ec783e7..18c39e7b8e 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1391,7 +1391,7 @@ fn frem(comptime T: type) !void { } fn fremOne(comptime T: type, a: T, b: T, c: T, epsilon: T) !void { - try expect(@fabs(@rem(a, b) - c) < epsilon); + try expect(@abs(@rem(a, b) - c) < epsilon); } test "float modulo division using @mod" { @@ -1434,7 +1434,7 @@ fn fmod(comptime T: type) !void { } fn fmodOne(comptime T: type, a: T, b: T, c: T, epsilon: T) !void { - try expect(@fabs(@mod(@as(T, a), @as(T, b)) - @as(T, c)) < epsilon); + try expect(@abs(@mod(@as(T, a), @as(T, b)) - @as(T, c)) < epsilon); } test "@round" { @@ -1627,7 +1627,7 @@ fn testAbsFloat() !void { try testAbsFloatOne(10.05, 10.05); } fn testAbsFloatOne(in: f32, out: f32) !void { - try expect(@fabs(@as(f32, in)) == @as(f32, out)); + try expect(@abs(@as(f32, in)) == @as(f32, out)); } test "mod lazy values" {