Commit bcf4a13913

antlilja <liljaanton2001@gmail.com>
2023-07-17 00:21:45
Remove `@fabs`, fabs and absCast/Int from std lib
Replaces occurences of @fabs absCast and absInt with new @abs builtin. Also removes the std.math.fabs alias from math.zig.
1 parent 1c02e58
lib/compiler_rt/divc3.zig
@@ -3,7 +3,6 @@ const isNan = std.math.isNan;
 const isInf = std.math.isInf;
 const scalbn = std.math.scalbn;
 const ilogb = std.math.ilogb;
-const fabs = std.math.fabs;
 const maxInt = std.math.maxInt;
 const minInt = std.math.minInt;
 const isFinite = std.math.isFinite;
@@ -16,7 +15,7 @@ pub inline fn divc3(comptime T: type, a: T, b: T, c_in: T, d_in: T) Complex(T) {
     var d = d_in;
 
     // logbw used to prevent under/over-flow
-    const logbw = ilogb(@max(fabs(c), fabs(d)));
+    const logbw = ilogb(@max(@abs(c), @abs(d)));
     const logbw_finite = logbw != maxInt(i32) and logbw != minInt(i32);
     const ilogbw = if (logbw_finite) b: {
         c = scalbn(c, -logbw);
lib/compiler_rt/divxf3_test.zig
@@ -30,9 +30,9 @@ fn test__divxf3(a: f80, b: f80) !void {
     const x_minus_eps: f80 = @bitCast((@as(u80, @bitCast(x)) - 1) | integerBit);
 
     // Make sure result is more accurate than the adjacent floats
-    const err_x = @fabs(@mulAdd(f80, x, b, -a));
-    const err_x_plus_eps = @fabs(@mulAdd(f80, x_plus_eps, b, -a));
-    const err_x_minus_eps = @fabs(@mulAdd(f80, x_minus_eps, b, -a));
+    const err_x = @abs(@mulAdd(f80, x, b, -a));
+    const err_x_plus_eps = @abs(@mulAdd(f80, x_plus_eps, b, -a));
+    const err_x_minus_eps = @abs(@mulAdd(f80, x_minus_eps, b, -a));
 
     try testing.expect(err_x_minus_eps > err_x);
     try testing.expect(err_x_plus_eps > err_x);
lib/compiler_rt/float_from_int.zig
@@ -18,7 +18,7 @@ pub fn floatFromInt(comptime T: type, x: anytype) T {
     const max_exp = exp_bias;
 
     // Sign
-    var abs_val = math.absCast(x);
+    var abs_val = if (@TypeOf(x) == comptime_int or @typeInfo(@TypeOf(x)).Int.signedness == .signed) @abs(x) else x;
     const sign_bit = if (x < 0) @as(uT, 1) << (float_bits - 1) else 0;
     var result: uT = sign_bit;
 
lib/std/Build/Step/ConfigHeader.zig
@@ -539,7 +539,7 @@ fn replace_variables(
                 .int => |i| {
                     const buf = try std.fmt.allocPrint(allocator, "{s}{}{s}", .{ beginline, i, endline });
                     const isNegative = i < 0;
-                    const digits = (if (0 < i) std.math.log10(std.math.absCast(i)) else 0) + 1;
+                    const digits = (if (0 < i) std.math.log10(@abs(i)) else 0) + 1;
                     last_index = start_index + @intFromBool(isNegative) + digits + 1;
 
                     allocator.free(content_buf);
lib/std/dwarf/expressions.zig
@@ -520,7 +520,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                     if (self.stack.items.len == 0) return error.InvalidExpression;
                     const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
                     self.stack.items[self.stack.items.len - 1] = .{
-                        .generic = std.math.absCast(value),
+                        .generic = @abs(value),
                     };
                 },
                 OP.@"and" => {
lib/std/io/fixed_buffer_stream.zig
@@ -81,7 +81,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
 
         pub fn seekBy(self: *Self, amt: i64) SeekError!void {
             if (amt < 0) {
-                const abs_amt = std.math.absCast(amt);
+                const abs_amt = @abs(amt);
                 const abs_amt_usize = std.math.cast(usize, abs_amt) orelse std.math.maxInt(usize);
                 if (abs_amt_usize > self.pos) {
                     self.pos = 0;
lib/std/math/big/int.zig
@@ -29,7 +29,7 @@ pub fn calcLimbLen(scalar: anytype) usize {
         return 1;
     }
 
-    const w_value = std.math.absCast(scalar);
+    const w_value = @abs(scalar);
     return @as(usize, @intCast(@divFloor(@as(Limb, @intCast(math.log2(w_value))), limb_bits) + 1));
 }
 
@@ -240,7 +240,7 @@ pub const Mutable = struct {
 
         switch (@typeInfo(T)) {
             .Int => |info| {
-                var w_value = std.math.absCast(value);
+                var w_value = @abs(value);
 
                 if (info.bits <= limb_bits) {
                     self.limbs[0] = w_value;
@@ -255,7 +255,7 @@ pub const Mutable = struct {
                 }
             },
             .ComptimeInt => {
-                comptime var w_value = std.math.absCast(value);
+                comptime var w_value = @abs(value);
 
                 if (w_value <= maxInt(Limb)) {
                     self.limbs[0] = w_value;
lib/std/math/complex/cosh.zig
@@ -44,12 +44,12 @@ fn cosh32(z: Complex(f32)) Complex(f32) {
         // |x|>= 9, so cosh(x) ~= exp(|x|)
         if (ix < 0x42b17218) {
             // x < 88.7: exp(|x|) won't overflow
-            const h = @exp(@fabs(x)) * 0.5;
+            const h = @exp(@abs(x)) * 0.5;
             return Complex(f32).init(math.copysign(h, x) * @cos(y), h * @sin(y));
         }
         // x < 192.7: scale to avoid overflow
         else if (ix < 0x4340b1e7) {
-            const v = Complex(f32).init(@fabs(x), y);
+            const v = Complex(f32).init(@abs(x), y);
             const r = ldexp_cexp(v, -1);
             return Complex(f32).init(r.re, r.im * math.copysign(@as(f32, 1.0), x));
         }
@@ -112,12 +112,12 @@ fn cosh64(z: Complex(f64)) Complex(f64) {
         // |x|>= 22, so cosh(x) ~= exp(|x|)
         if (ix < 0x40862e42) {
             // x < 710: exp(|x|) won't overflow
-            const h = @exp(@fabs(x)) * 0.5;
+            const h = @exp(@abs(x)) * 0.5;
             return Complex(f64).init(h * @cos(y), math.copysign(h, x) * @sin(y));
         }
         // x < 1455: scale to avoid overflow
         else if (ix < 0x4096bbaa) {
-            const v = Complex(f64).init(@fabs(x), y);
+            const v = Complex(f64).init(@abs(x), y);
             const r = ldexp_cexp(v, -1);
             return Complex(f64).init(r.re, r.im * math.copysign(@as(f64, 1.0), x));
         }
lib/std/math/complex/sinh.zig
@@ -44,12 +44,12 @@ fn sinh32(z: Complex(f32)) Complex(f32) {
         // |x|>= 9, so cosh(x) ~= exp(|x|)
         if (ix < 0x42b17218) {
             // x < 88.7: exp(|x|) won't overflow
-            const h = @exp(@fabs(x)) * 0.5;
+            const h = @exp(@abs(x)) * 0.5;
             return Complex(f32).init(math.copysign(h, x) * @cos(y), h * @sin(y));
         }
         // x < 192.7: scale to avoid overflow
         else if (ix < 0x4340b1e7) {
-            const v = Complex(f32).init(@fabs(x), y);
+            const v = Complex(f32).init(@abs(x), y);
             const r = ldexp_cexp(v, -1);
             return Complex(f32).init(r.re * math.copysign(@as(f32, 1.0), x), r.im);
         }
@@ -111,12 +111,12 @@ fn sinh64(z: Complex(f64)) Complex(f64) {
         // |x|>= 22, so cosh(x) ~= exp(|x|)
         if (ix < 0x40862e42) {
             // x < 710: exp(|x|) won't overflow
-            const h = @exp(@fabs(x)) * 0.5;
+            const h = @exp(@abs(x)) * 0.5;
             return Complex(f64).init(math.copysign(h, x) * @cos(y), h * @sin(y));
         }
         // x < 1455: scale to avoid overflow
         else if (ix < 0x4096bbaa) {
-            const v = Complex(f64).init(@fabs(x), y);
+            const v = Complex(f64).init(@abs(x), y);
             const r = ldexp_cexp(v, -1);
             return Complex(f64).init(r.re * math.copysign(@as(f64, 1.0), x), r.im);
         }
lib/std/math/complex/sqrt.zig
@@ -43,7 +43,7 @@ fn sqrt32(z: Complex(f32)) Complex(f32) {
         // sqrt(-inf + i nan)   = nan +- inf i
         // sqrt(-inf + iy)      = 0 + inf i
         if (math.signbit(x)) {
-            return Complex(f32).init(@fabs(x - y), math.copysign(x, y));
+            return Complex(f32).init(@abs(x - y), math.copysign(x, y));
         } else {
             return Complex(f32).init(x, math.copysign(y - y, y));
         }
@@ -64,7 +64,7 @@ fn sqrt32(z: Complex(f32)) Complex(f32) {
     } else {
         const t = @sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5);
         return Complex(f32).init(
-            @as(f32, @floatCast(@fabs(y) / (2.0 * t))),
+            @as(f32, @floatCast(@abs(y) / (2.0 * t))),
             @as(f32, @floatCast(math.copysign(t, y))),
         );
     }
@@ -94,7 +94,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {
         // sqrt(-inf + i nan)   = nan +- inf i
         // sqrt(-inf + iy)      = 0 + inf i
         if (math.signbit(x)) {
-            return Complex(f64).init(@fabs(x - y), math.copysign(x, y));
+            return Complex(f64).init(@abs(x - y), math.copysign(x, y));
         } else {
             return Complex(f64).init(x, math.copysign(y - y, y));
         }
@@ -104,7 +104,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {
 
     // scale to avoid overflow
     var scale = false;
-    if (@fabs(x) >= threshold or @fabs(y) >= threshold) {
+    if (@abs(x) >= threshold or @abs(y) >= threshold) {
         x *= 0.25;
         y *= 0.25;
         scale = true;
@@ -116,7 +116,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {
         result = Complex(f64).init(t, y / (2.0 * t));
     } else {
         const t = @sqrt((-x + math.hypot(f64, x, y)) * 0.5);
-        result = Complex(f64).init(@fabs(y) / (2.0 * t), math.copysign(t, y));
+        result = Complex(f64).init(@abs(y) / (2.0 * t), math.copysign(t, y));
     }
 
     if (scale) {
lib/std/math/complex/tanh.zig
@@ -44,7 +44,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) {
 
     // x >= 11
     if (ix >= 0x41300000) {
-        const exp_mx = @exp(-@fabs(x));
+        const exp_mx = @exp(-@abs(x));
         return Complex(f32).init(math.copysign(@as(f32, 1.0), x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx);
     }
 
@@ -87,7 +87,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) {
 
     // x >= 22
     if (ix >= 0x40360000) {
-        const exp_mx = @exp(-@fabs(x));
+        const exp_mx = @exp(-@abs(x));
         return Complex(f64).init(math.copysign(@as(f64, 1.0), x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx);
     }
 
lib/std/math/asin.zig
@@ -60,7 +60,7 @@ fn asin32(x: f32) f32 {
     }
 
     // 1 > |x| >= 0.5
-    const z = (1 - @fabs(x)) * 0.5;
+    const z = (1 - @abs(x)) * 0.5;
     const s = @sqrt(z);
     const fx = pio2 - 2 * (s + s * r32(z));
 
@@ -119,7 +119,7 @@ fn asin64(x: f64) f64 {
     }
 
     // 1 > |x| >= 0.5
-    const z = (1 - @fabs(x)) * 0.5;
+    const z = (1 - @abs(x)) * 0.5;
     const s = @sqrt(z);
     const r = r64(z);
     var fx: f64 = undefined;
lib/std/math/atan.zig
@@ -73,7 +73,7 @@ fn atan32(x_: f32) f32 {
         }
         id = null;
     } else {
-        x = @fabs(x);
+        x = @abs(x);
         // |x| < 1.1875
         if (ix < 0x3F980000) {
             // 7/16 <= |x| < 11/16
@@ -171,7 +171,7 @@ fn atan64(x_: f64) f64 {
         }
         id = null;
     } else {
-        x = @fabs(x);
+        x = @abs(x);
         // |x| < 1.1875
         if (ix < 0x3FF30000) {
             // 7/16 <= |x| < 11/16
lib/std/math/atan2.zig
@@ -108,7 +108,7 @@ fn atan2_32(y: f32, x: f32) f32 {
         if ((m & 2) != 0 and iy + (26 << 23) < ix) {
             break :z 0.0;
         } else {
-            break :z math.atan(@fabs(y / x));
+            break :z math.atan(@abs(y / x));
         }
     };
 
@@ -198,7 +198,7 @@ fn atan2_64(y: f64, x: f64) f64 {
         if ((m & 2) != 0 and iy +% (64 << 20) < ix) {
             break :z 0.0;
         } else {
-            break :z math.atan(@fabs(y / x));
+            break :z math.atan(@abs(y / x));
         }
     };
 
lib/std/math/pow.zig
@@ -82,7 +82,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
         }
         // pow(x, +inf) = +0    for |x| < 1
         // pow(x, -inf) = +0    for |x| > 1
-        else if ((@fabs(x) < 1) == math.isPositiveInf(y)) {
+        else if ((@abs(x) < 1) == math.isPositiveInf(y)) {
             return 0;
         }
         // pow(x, -inf) = +inf  for |x| < 1
@@ -115,7 +115,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
         return 1 / @sqrt(x);
     }
 
-    const r1 = math.modf(@fabs(y));
+    const r1 = math.modf(@abs(y));
     var yi = r1.ipart;
     var yf = r1.fpart;
 
lib/std/rand/ziggurat.zig
@@ -33,7 +33,7 @@ pub fn next_f64(random: Random, comptime tables: ZigTable) f64 {
         };
 
         const x = u * tables.x[i];
-        const test_x = if (tables.is_symmetric) @fabs(x) else x;
+        const test_x = if (tables.is_symmetric) @abs(x) else x;
 
         // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i])
         if (test_x < tables.x[i + 1]) {
lib/std/zig/c_builtins.zig
@@ -88,13 +88,19 @@ pub inline fn __builtin_log10f(val: f32) f32 {
 
 // Standard C Library bug: The absolute value of the most negative integer remains negative.
 pub inline fn __builtin_abs(val: c_int) c_int {
-    return std.math.absInt(val) catch std.math.minInt(c_int);
+    return if (val == std.math.minInt(c_int)) val else @intCast(@abs(val));
+}
+pub inline fn __builtin_labs(val: c_long) c_long {
+    return if (val == std.math.minInt(c_long)) val else @intCast(@abs(val));
+}
+pub inline fn __builtin_llabs(val: c_longlong) c_longlong {
+    return if (val == std.math.minInt(c_longlong)) val else @intCast(@abs(val));
 }
 pub inline fn __builtin_fabs(val: f64) f64 {
-    return @fabs(val);
+    return @abs(val);
 }
 pub inline fn __builtin_fabsf(val: f32) f32 {
-    return @fabs(val);
+    return @abs(val);
 }
 
 pub inline fn __builtin_floor(val: f64) f64 {
lib/std/fmt.zig
@@ -1413,7 +1413,7 @@ pub fn formatInt(
     const min_int_bits = comptime @max(value_info.bits, 8);
     const MinInt = std.meta.Int(.unsigned, min_int_bits);
 
-    const abs_value = math.absCast(int_value);
+    const abs_value = @abs(int_value);
     // The worst case in terms of space needed is base 2, plus 1 for the sign
     var buf: [1 + @max(@as(comptime_int, value_info.bits), 1)]u8 = undefined;
 
lib/std/math.zig
@@ -130,7 +130,7 @@ pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool {
     if (isNan(x) or isNan(y))
         return false;
 
-    return @fabs(x - y) <= tolerance;
+    return @abs(x - y) <= tolerance;
 }
 
 /// Performs an approximate comparison of two floating point values `x` and `y`.
@@ -158,7 +158,7 @@ pub fn approxEqRel(comptime T: type, x: T, y: T, tolerance: T) bool {
     if (isNan(x) or isNan(y))
         return false;
 
-    return @fabs(x - y) <= @max(@fabs(x), @fabs(y)) * tolerance;
+    return @abs(x - y) <= @max(@abs(x), @abs(y)) * tolerance;
 }
 
 test "approxEqAbs and approxEqRel" {
@@ -466,7 +466,7 @@ pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
 /// Shifts left. Overflowed bits are truncated.
 /// A negative shift amount results in a right shift.
 pub fn shl(comptime T: type, a: T, shift_amt: anytype) T {
-    const abs_shift_amt = absCast(shift_amt);
+    const abs_shift_amt = @abs(shift_amt);
 
     const casted_shift_amt = blk: {
         if (@typeInfo(T) == .Vector) {
@@ -510,7 +510,7 @@ test "shl" {
 /// Shifts right. Overflowed bits are truncated.
 /// A negative shift amount results in a left shift.
 pub fn shr(comptime T: type, a: T, shift_amt: anytype) T {
-    const abs_shift_amt = absCast(shift_amt);
+    const abs_shift_amt = @abs(shift_amt);
 
     const casted_shift_amt = blk: {
         if (@typeInfo(T) == .Vector) {
@@ -740,52 +740,6 @@ fn testOverflow() !void {
     try testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000);
 }
 
-/// Returns the absolute value of x, where x is a value of a signed integer type.
-/// Does not convert and returns a value of a signed integer type.
-/// Use `absCast` if you want to convert the result and get an unsigned type.
-/// Use `@fabs` if you need the absolute value of a floating point value.
-pub fn absInt(x: anytype) !@TypeOf(x) {
-    const T = @TypeOf(x);
-    return switch (@typeInfo(T)) {
-        .Int => |info| {
-            comptime assert(info.signedness == .signed); // must pass a signed integer to absInt
-            if (x == minInt(T)) {
-                return error.Overflow;
-            } else {
-                @setRuntimeSafety(false);
-                return if (x < 0) -x else x;
-            }
-        },
-        .Vector => |vinfo| blk: {
-            switch (@typeInfo(vinfo.child)) {
-                .Int => |info| {
-                    comptime assert(info.signedness == .signed); // must pass a signed integer to absInt
-                    if (@reduce(.Or, x == @as(T, @splat(minInt(vinfo.child))))) {
-                        return error.Overflow;
-                    }
-                    const zero: T = @splat(0);
-                    break :blk @select(vinfo.child, x > zero, x, -x);
-                },
-                else => @compileError("Expected vector of ints, found " ++ @typeName(T)),
-            }
-        },
-        else => @compileError("Expected an int or vector, found " ++ @typeName(T)),
-    };
-}
-
-test "absInt" {
-    try testAbsInt();
-    try comptime testAbsInt();
-}
-fn testAbsInt() !void {
-    try testing.expect((absInt(@as(i32, -10)) catch unreachable) == 10);
-    try testing.expect((absInt(@as(i32, 10)) catch unreachable) == 10);
-    try testing.expectEqual(@Vector(3, i32){ 10, 10, 0 }, (absInt(@Vector(3, i32){ -10, 10, 0 }) catch unreachable));
-
-    try testing.expectError(error.Overflow, absInt(@as(i32, minInt(i32))));
-    try testing.expectError(error.Overflow, absInt(@Vector(3, i32){ 10, -10, minInt(i32) }));
-}
-
 /// Divide numerator by denominator, rounding toward zero. Returns an
 /// error on overflow or when denominator is zero.
 pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {
@@ -968,50 +922,6 @@ fn testRem() !void {
     try testing.expectError(error.DivisionByZero, rem(f32, 10, 0));
 }
 
-/// Returns the absolute value of a floating point number.
-/// Uses a dedicated hardware instruction when available.
-/// This is the same as calling the builtin @fabs
-pub inline fn fabs(value: anytype) @TypeOf(value) {
-    return @fabs(value);
-}
-
-/// Returns the absolute value of the integer parameter.
-/// Converts result type to unsigned if needed and returns a value of an unsigned integer type.
-/// Use `absInt` if you want to keep your integer type signed.
-pub fn absCast(x: anytype) switch (@typeInfo(@TypeOf(x))) {
-    .ComptimeInt => comptime_int,
-    .Int => |int_info| std.meta.Int(.unsigned, int_info.bits),
-    else => @compileError("absCast only accepts integers"),
-} {
-    switch (@typeInfo(@TypeOf(x))) {
-        .ComptimeInt => {
-            if (x < 0) {
-                return -x;
-            } else {
-                return x;
-            }
-        },
-        .Int => |int_info| {
-            if (int_info.signedness == .unsigned) return x;
-            const Uint = std.meta.Int(.unsigned, int_info.bits);
-            if (x < 0) {
-                return ~@as(Uint, @bitCast(x +% -1));
-            } else {
-                return @as(Uint, @intCast(x));
-            }
-        },
-        else => unreachable,
-    }
-}
-
-test "absCast" {
-    try testing.expectEqual(@as(u1, 1), absCast(@as(i1, -1)));
-    try testing.expectEqual(@as(u32, 999), absCast(@as(i32, -999)));
-    try testing.expectEqual(@as(u32, 999), absCast(@as(i32, 999)));
-    try testing.expectEqual(@as(u32, -minInt(i32)), absCast(@as(i32, minInt(i32))));
-    try testing.expectEqual(999, absCast(-999));
-}
-
 /// Returns the negation of the integer parameter.
 /// Result is a signed integer.
 pub fn negateCast(x: anytype) !std.meta.Int(.signed, @bitSizeOf(@TypeOf(x))) {
lib/std/meta.zig
@@ -1104,6 +1104,6 @@ pub fn isError(error_union: anytype) bool {
 }
 
 test "isError" {
-    try std.testing.expect(isError(math.absInt(@as(i8, -128))));
-    try std.testing.expect(!isError(math.absInt(@as(i8, -127))));
+    try std.testing.expect(isError(math.divTrunc(u8, 5, 0)));
+    try std.testing.expect(!isError(math.divTrunc(u8, 5, 5)));
 }
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) {