mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
Merge pull request #17248 from antlilja/abs
Replace @fabs builtin with new @abs builtin
This commit is contained in:
commit
937138cb90
@ -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#}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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" => {
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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))) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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)));
|
||||
}
|
||||
|
||||
@ -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]) {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
41
lib/zig.h
41
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) {
|
||||
|
||||
11
src/Air.zig
11
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,
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -929,7 +929,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
|
||||
.log,
|
||||
.log2,
|
||||
.log10,
|
||||
.fabs,
|
||||
.abs,
|
||||
.floor,
|
||||
.ceil,
|
||||
.trunc,
|
||||
|
||||
@ -1669,7 +1669,7 @@ fn walkInstruction(
|
||||
.log,
|
||||
.log2,
|
||||
.log10,
|
||||
.fabs,
|
||||
.abs,
|
||||
.floor,
|
||||
.ceil,
|
||||
.trunc,
|
||||
|
||||
@ -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,
|
||||
},
|
||||
},
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
127
src/Sema.zig
127
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();
|
||||
|
||||
10
src/Zir.zig
10
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,
|
||||
|
||||
@ -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});
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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});
|
||||
|
||||
@ -543,7 +543,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.log,
|
||||
.log2,
|
||||
.log10,
|
||||
.fabs,
|
||||
.abs,
|
||||
.floor,
|
||||
.ceil,
|
||||
.round,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -188,7 +188,7 @@ const Writer = struct {
|
||||
.log,
|
||||
.log2,
|
||||
.log10,
|
||||
.fabs,
|
||||
.abs,
|
||||
.floor,
|
||||
.ceil,
|
||||
.round,
|
||||
|
||||
@ -262,7 +262,7 @@ const Writer = struct {
|
||||
.log,
|
||||
.log2,
|
||||
.log10,
|
||||
.fabs,
|
||||
.abs,
|
||||
.floor,
|
||||
.ceil,
|
||||
.trunc,
|
||||
|
||||
11
src/type.zig
11
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 };
|
||||
|
||||
@ -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 {
|
||||
|
||||
41
stage1/zig.h
41
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) {
|
||||
|
||||
BIN
stage1/zig1.wasm
BIN
stage1/zig1.wasm
Binary file not shown.
@ -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
371
test/behavior/abs.zig
Normal 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))));
|
||||
}
|
||||
}
|
||||
@ -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))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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" {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user