Merge pull request #17248 from antlilja/abs

Replace @fabs builtin with new @abs builtin
This commit is contained in:
Andrew Kelley 2023-09-27 17:25:19 -07:00 committed by GitHub
commit 937138cb90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 937 additions and 309 deletions

View File

@ -9421,14 +9421,17 @@ fn doTheTest() !void {
Supports {#link|Floats#} and {#link|Vectors#} of floats.
</p>
{#header_close#}
{#header_open|@fabs#}
<pre>{#syntax#}@fabs(value: anytype) @TypeOf(value){#endsyntax#}</pre>
{#header_open|@abs#}
<pre>{#syntax#}@abs(value: anytype) anytype{#endsyntax#}</pre>
<p>
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.
</p>
<p>
Supports {#link|Floats#} and {#link|Vectors#} of floats.
Supports {#link|Floats#}, {#link|Integers#} and {#link|Vectors#} of floats or integers.
</p>
{#header_close#}
{#header_open|@floor#}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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" => {

View File

@ -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;

View File

@ -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;

View File

@ -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))) {

View File

@ -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;

View File

@ -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

View File

@ -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));
}
};

View File

@ -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;

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);
}

View File

@ -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;

View File

@ -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)));
}

View File

@ -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]) {

View File

@ -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 {

View File

@ -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
}

View File

@ -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) {

View File

@ -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,

View File

@ -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),

View File

@ -929,7 +929,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
.log,
.log2,
.log10,
.fabs,
.abs,
.floor,
.ceil,
.trunc,

View File

@ -1669,7 +1669,7 @@ fn walkInstruction(
.log,
.log2,
.log10,
.fabs,
.abs,
.floor,
.ceil,
.trunc,

View File

@ -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,
},
},

View File

@ -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,

View File

@ -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,

View File

@ -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();

View File

@ -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,

View File

@ -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});

View File

@ -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;

View File

@ -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});

View File

@ -543,7 +543,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.log,
.log2,
.log10,
.fabs,
.abs,
.floor,
.ceil,
.round,

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -188,7 +188,7 @@ const Writer = struct {
.log,
.log2,
.log10,
.fabs,
.abs,
.floor,
.ceil,
.round,

View File

@ -262,7 +262,7 @@ const Writer = struct {
.log,
.log2,
.log10,
.fabs,
.abs,
.floor,
.ceil,
.trunc,

View File

@ -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 };

View File

@ -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 {

View File

@ -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) {

Binary file not shown.

View File

@ -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");

371
test/behavior/abs.zig Normal file
View File

@ -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))));
}
}

View File

@ -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))));
}
}

View File

@ -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" {