Commit 6a29646a55

antlilja <liljaanton2001@gmail.com>
2023-07-17 00:16:49
Rename `@fabs` to `@abs` and accept integers
Replaces the @fabs builtin with a new @abs builtins which accepts floats, signed integers and vectors of said types.
1 parent 1606717
src/arch/aarch64/CodeGen.zig
@@ -713,7 +713,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .log,
             .log2,
             .log10,
-            .fabs,
             .floor,
             .ceil,
             .round,
@@ -788,6 +787,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .clz             => try self.airClz(inst),
             .ctz             => try self.airCtz(inst),
             .popcount        => try self.airPopcount(inst),
+            .abs             => try self.airAbs(inst),
             .byte_swap       => try self.airByteSwap(inst),
             .bit_reverse     => try self.airBitReverse(inst),
             .tag_name        => try self.airTagName(inst),
@@ -3550,6 +3550,12 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
+fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});
+    return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
 fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
src/arch/arm/CodeGen.zig
@@ -699,7 +699,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .log,
             .log2,
             .log10,
-            .fabs,
             .floor,
             .ceil,
             .round,
@@ -774,6 +773,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .clz             => try self.airClz(inst),
             .ctz             => try self.airCtz(inst),
             .popcount        => try self.airPopcount(inst),
+            .abs             => try self.airAbs(inst),
             .byte_swap       => try self.airByteSwap(inst),
             .bit_reverse     => try self.airBitReverse(inst),
             .tag_name        => try self.airTagName(inst),
@@ -2591,6 +2591,13 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
     // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
+fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+    _ = ty_op;
+    return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});
+    // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
 fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
     _ = ty_op;
src/arch/riscv64/CodeGen.zig
@@ -523,7 +523,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .log,
             .log2,
             .log10,
-            .fabs,
             .floor,
             .ceil,
             .round,
@@ -607,6 +606,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .clz             => try self.airClz(inst),
             .ctz             => try self.airCtz(inst),
             .popcount        => try self.airPopcount(inst),
+            .abs             => try self.airAbs(inst),
             .byte_swap       => try self.airByteSwap(inst),
             .bit_reverse     => try self.airBitReverse(inst),
             .tag_name        => try self.airTagName(inst),
@@ -1447,6 +1447,12 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
+fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch});
+    return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
 fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch});
src/arch/sparc64/CodeGen.zig
@@ -543,7 +543,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .log,
             .log2,
             .log10,
-            .fabs,
+            .abs,
             .floor,
             .ceil,
             .round,
src/arch/wasm/CodeGen.zig
@@ -1866,13 +1866,14 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
         .log => func.airUnaryFloatOp(inst, .log),
         .log2 => func.airUnaryFloatOp(inst, .log2),
         .log10 => func.airUnaryFloatOp(inst, .log10),
-        .fabs => func.airUnaryFloatOp(inst, .fabs),
         .floor => func.airUnaryFloatOp(inst, .floor),
         .ceil => func.airUnaryFloatOp(inst, .ceil),
         .round => func.airUnaryFloatOp(inst, .round),
         .trunc_float => func.airUnaryFloatOp(inst, .trunc),
         .neg => func.airUnaryFloatOp(inst, .neg),
 
+        .abs => func.airAbs(inst),
+
         .add_with_overflow => func.airAddSubWithOverflow(inst, .add),
         .sub_with_overflow => func.airAddSubWithOverflow(inst, .sub),
         .shl_with_overflow => func.airShlWithOverflow(inst),
@@ -2786,6 +2787,82 @@ const FloatOp = enum {
     }
 };
 
+fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
+    const mod = func.bin_file.base.options.module.?;
+    const ty_op = func.air.instructions.items(.data)[inst].ty_op;
+    const operand = try func.resolveInst(ty_op.operand);
+    const ty = func.typeOf(ty_op.operand);
+    const scalar_ty = ty.scalarType(mod);
+
+    switch (scalar_ty.zigTypeTag(mod)) {
+        .Int => if (ty.zigTypeTag(mod) == .Vector) {
+            return func.fail("TODO implement airAbs for {}", .{ty.fmt(mod)});
+        } else {
+            const int_bits = ty.intInfo(mod).bits;
+            const wasm_bits = toWasmBits(int_bits) orelse {
+                return func.fail("TODO: airAbs for signed integers larger than '{d}' bits", .{int_bits});
+            };
+
+            const op = try operand.toLocal(func, ty);
+
+            try func.emitWValue(op);
+            switch (wasm_bits) {
+                32 => {
+                    if (wasm_bits != int_bits) {
+                        try func.addImm32(wasm_bits - int_bits);
+                        try func.addTag(.i32_shl);
+                    }
+                    try func.addImm32(31);
+                    try func.addTag(.i32_shr_s);
+
+                    const tmp = try func.allocLocal(ty);
+                    try func.addLabel(.local_tee, tmp.local.value);
+
+                    try func.emitWValue(op);
+                    try func.addTag(.i32_xor);
+                    try func.emitWValue(tmp);
+                    try func.addTag(.i32_sub);
+
+                    if (int_bits != wasm_bits) {
+                        try func.emitWValue(WValue{ .imm32 = (@as(u32, 1) << @intCast(int_bits)) - 1 });
+                        try func.addTag(.i32_and);
+                    }
+                },
+                64 => {
+                    if (wasm_bits != int_bits) {
+                        try func.addImm64(wasm_bits - int_bits);
+                        try func.addTag(.i64_shl);
+                    }
+                    try func.addImm64(63);
+                    try func.addTag(.i64_shr_s);
+
+                    const tmp = try func.allocLocal(ty);
+                    try func.addLabel(.local_tee, tmp.local.value);
+
+                    try func.emitWValue(op);
+                    try func.addTag(.i64_xor);
+                    try func.emitWValue(tmp);
+                    try func.addTag(.i64_sub);
+
+                    if (int_bits != wasm_bits) {
+                        try func.emitWValue(WValue{ .imm64 = (@as(u64, 1) << @intCast(int_bits)) - 1 });
+                        try func.addTag(.i64_and);
+                    }
+                },
+                else => return func.fail("TODO: Implement airAbs for {}", .{ty.fmt(mod)}),
+            }
+
+            const result = try (WValue{ .stack = {} }).toLocal(func, ty);
+            func.finishAir(inst, result, &.{ty_op.operand});
+        },
+        .Float => {
+            const result = try (try func.floatOp(.fabs, ty, &.{operand})).toLocal(func, ty);
+            func.finishAir(inst, result, &.{ty_op.operand});
+        },
+        else => unreachable,
+    }
+}
+
 fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError!void {
     const un_op = func.air.instructions.items(.data)[inst].un_op;
     const operand = try func.resolveInst(un_op);
src/arch/x86_64/CodeGen.zig
@@ -1809,11 +1809,13 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .round,
             => try self.airUnaryMath(inst),
 
-            .floor => try self.airRound(inst, 0b1_0_01),
-            .ceil => try self.airRound(inst, 0b1_0_10),
+            .floor       => try self.airRound(inst, 0b1_0_01),
+            .ceil        => try self.airRound(inst, 0b1_0_10),
             .trunc_float => try self.airRound(inst, 0b1_0_11),
-            .sqrt => try self.airSqrt(inst),
-            .neg, .fabs => try self.airFloatSign(inst),
+            .sqrt        => try self.airSqrt(inst),
+            .neg         => try self.airFloatSign(inst),
+
+            .abs => try self.airAbs(inst),
 
             .add_with_overflow => try self.airAddSubWithOverflow(inst),
             .sub_with_overflow => try self.airAddSubWithOverflow(inst),
@@ -4885,28 +4887,26 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
 }
 
-fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void {
+fn floatSign(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty: Type) !void {
     const mod = self.bin_file.options.module.?;
     const tag = self.air.instructions.items(.tag)[inst];
-    const un_op = self.air.instructions.items(.data)[inst].un_op;
-    const ty = self.typeOf(un_op);
     const abi_size: u32 = switch (ty.abiSize(mod)) {
         1...16 => 16,
         17...32 => 32,
-        else => return self.fail("TODO implement airFloatSign for {}", .{
+        else => return self.fail("TODO implement floatSign for {}", .{
             ty.fmt(mod),
         }),
     };
     const scalar_bits = ty.scalarType(mod).floatBits(self.target.*);
-    if (scalar_bits == 80) return self.fail("TODO implement airFloatSign for {}", .{
+    if (scalar_bits == 80) return self.fail("TODO implement floatSign for {}", .{
         ty.fmt(mod),
     });
 
-    const src_mcv = try self.resolveInst(un_op);
+    const src_mcv = try self.resolveInst(operand);
     const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
     defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
 
-    const dst_mcv: MCValue = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv))
+    const dst_mcv: MCValue = if (src_mcv.isRegister() and self.reuseOperand(inst, operand, 0, src_mcv))
         src_mcv
     else if (self.hasFeature(.avx))
         .{ .register = try self.register_manager.allocReg(inst, sse) }
@@ -4923,7 +4923,7 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void {
 
     const sign_val = switch (tag) {
         .neg => try vec_ty.minInt(mod, vec_ty),
-        .fabs => try vec_ty.maxInt(mod, vec_ty),
+        .abs => try vec_ty.maxInt(mod, vec_ty),
         else => unreachable,
     };
 
@@ -4939,24 +4939,24 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void {
         switch (scalar_bits) {
             16, 128 => if (abi_size <= 16 or self.hasFeature(.avx2)) switch (tag) {
                 .neg => .{ .vp_, .xor },
-                .fabs => .{ .vp_, .@"and" },
+                .abs => .{ .vp_, .@"and" },
                 else => unreachable,
             } else switch (tag) {
                 .neg => .{ .v_ps, .xor },
-                .fabs => .{ .v_ps, .@"and" },
+                .abs => .{ .v_ps, .@"and" },
                 else => unreachable,
             },
             32 => switch (tag) {
                 .neg => .{ .v_ps, .xor },
-                .fabs => .{ .v_ps, .@"and" },
+                .abs => .{ .v_ps, .@"and" },
                 else => unreachable,
             },
             64 => switch (tag) {
                 .neg => .{ .v_pd, .xor },
-                .fabs => .{ .v_pd, .@"and" },
+                .abs => .{ .v_pd, .@"and" },
                 else => unreachable,
             },
-            80 => return self.fail("TODO implement airFloatSign for {}", .{
+            80 => return self.fail("TODO implement floatSign for {}", .{
                 ty.fmt(self.bin_file.options.module.?),
             }),
             else => unreachable,
@@ -4971,20 +4971,20 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void {
         switch (scalar_bits) {
             16, 128 => switch (tag) {
                 .neg => .{ .p_, .xor },
-                .fabs => .{ .p_, .@"and" },
+                .abs => .{ .p_, .@"and" },
                 else => unreachable,
             },
             32 => switch (tag) {
                 .neg => .{ ._ps, .xor },
-                .fabs => .{ ._ps, .@"and" },
+                .abs => .{ ._ps, .@"and" },
                 else => unreachable,
             },
             64 => switch (tag) {
                 .neg => .{ ._pd, .xor },
-                .fabs => .{ ._pd, .@"and" },
+                .abs => .{ ._pd, .@"and" },
                 else => unreachable,
             },
-            80 => return self.fail("TODO implement airFloatSign for {}", .{
+            80 => return self.fail("TODO implement floatSign for {}", .{
                 ty.fmt(self.bin_file.options.module.?),
             }),
             else => unreachable,
@@ -4992,7 +4992,14 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void {
         registerAlias(dst_reg, abi_size),
         sign_mem,
     );
-    return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
+    return self.finishAir(inst, dst_mcv, .{ operand, .none, .none });
+}
+
+fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void {
+    const un_op = self.air.instructions.items(.data)[inst].un_op;
+    const ty = self.typeOf(un_op);
+
+    return self.floatSign(inst, un_op, ty);
 }
 
 fn airRound(self: *Self, inst: Air.Inst.Index, mode: u4) !void {
@@ -5082,6 +5089,52 @@ fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: u4
     }
 }
 
+fn airAbs(self: *Self, inst: Air.Inst.Index) !void {
+    const mod = self.bin_file.options.module.?;
+    const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+    const ty = self.typeOf(ty_op.operand);
+    const scalar_ty = ty.scalarType(mod);
+
+    switch (scalar_ty.zigTypeTag(mod)) {
+        .Int => if (ty.zigTypeTag(mod) == .Vector) {
+            return self.fail("TODO implement airAbs for {}", .{ty.fmt(mod)});
+        } else {
+            if (ty.abiSize(mod) > 8) {
+                return self.fail("TODO implement abs for integer abi sizes larger than 8", .{});
+            }
+            const src_mcv = try self.resolveInst(ty_op.operand);
+            const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
+
+            try self.genUnOpMir(.{ ._, .neg }, ty, dst_mcv);
+
+            const cmov_abi_size = @max(@as(u32, @intCast(ty.abiSize(mod))), 2);
+            switch (src_mcv) {
+                .register => |val_reg| try self.asmCmovccRegisterRegister(
+                    registerAlias(dst_mcv.register, cmov_abi_size),
+                    registerAlias(val_reg, cmov_abi_size),
+                    .l,
+                ),
+                .memory, .indirect, .load_frame => try self.asmCmovccRegisterMemory(
+                    registerAlias(dst_mcv.register, cmov_abi_size),
+                    src_mcv.mem(Memory.PtrSize.fromSize(cmov_abi_size)),
+                    .l,
+                ),
+                else => {
+                    const val_reg = try self.copyToTmpRegister(ty, src_mcv);
+                    try self.asmCmovccRegisterRegister(
+                        registerAlias(dst_mcv.register, cmov_abi_size),
+                        registerAlias(val_reg, cmov_abi_size),
+                        .l,
+                    );
+                },
+            }
+            return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
+        },
+        .Float => return self.floatSign(inst, ty_op.operand, ty),
+        else => unreachable,
+    }
+}
+
 fn airSqrt(self: *Self, inst: Air.Inst.Index) !void {
     const mod = self.bin_file.options.module.?;
     const un_op = self.air.instructions.items(.data)[inst].un_op;
src/arch/x86_64/encoder.zig
@@ -105,7 +105,7 @@ pub const Instruction = struct {
                         try writer.print("{s} ptr [rip", .{@tagName(rip.ptr_size)});
                         if (rip.disp != 0) try writer.print(" {c} 0x{x}", .{
                             @as(u8, if (rip.disp < 0) '-' else '+'),
-                            std.math.absCast(rip.disp),
+                            @abs(rip.disp),
                         });
                         try writer.writeByte(']');
                     },
@@ -140,7 +140,7 @@ pub const Instruction = struct {
                                 try writer.print(" {c} ", .{@as(u8, if (sib.disp < 0) '-' else '+')})
                             else if (sib.disp < 0)
                                 try writer.writeByte('-');
-                            try writer.print("0x{x}", .{std.math.absCast(sib.disp)});
+                            try writer.print("0x{x}", .{@abs(sib.disp)});
                             any = true;
                         }
 
src/codegen/c.zig
@@ -2912,6 +2912,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
             },
             .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none),
             .mod       => try airBinBuiltinCall(f, inst, "mod", .none),
+            .abs       => try airAbs(f, inst),
 
             .add_wrap => try airBinBuiltinCall(f, inst, "addw", .bits),
             .sub_wrap => try airBinBuiltinCall(f, inst, "subw", .bits),
@@ -2931,7 +2932,6 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
             .log         => try airUnFloatOp(f, inst, "log"),
             .log2        => try airUnFloatOp(f, inst, "log2"),
             .log10       => try airUnFloatOp(f, inst, "log10"),
-            .fabs        => try airUnFloatOp(f, inst, "fabs"),
             .floor       => try airUnFloatOp(f, inst, "floor"),
             .ceil        => try airUnFloatOp(f, inst, "ceil"),
             .round       => try airUnFloatOp(f, inst, "round"),
@@ -7076,23 +7076,35 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {
     return local;
 }
 
-fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue {
+fn airAbs(f: *Function, inst: Air.Inst.Index) !CValue {
     const mod = f.object.dg.module;
-    const un_op = f.air.instructions.items(.data)[inst].un_op;
+    const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+    const operand = try f.resolveInst(ty_op.operand);
+    const ty = f.typeOf(ty_op.operand);
+    const scalar_ty = ty.scalarType(mod);
 
-    const operand = try f.resolveInst(un_op);
-    try reap(f, inst, &.{un_op});
+    switch (scalar_ty.zigTypeTag(mod)) {
+        .Int => if (ty.zigTypeTag(mod) == .Vector) {
+            return f.fail("TODO implement airAbs for '{}'", .{ty.fmt(mod)});
+        } else {
+            return airUnBuiltinCall(f, inst, "abs", .none);
+        },
+        .Float => return unFloatOp(f, inst, operand, ty, "fabs"),
+        else => unreachable,
+    }
+}
 
-    const inst_ty = f.typeOfIndex(inst);
-    const inst_scalar_ty = inst_ty.scalarType(mod);
+fn unFloatOp(f: *Function, inst: Air.Inst.Index, operand: CValue, ty: Type, operation: []const u8) !CValue {
+    const mod = f.object.dg.module;
+    const scalar_ty = ty.scalarType(mod);
 
     const writer = f.object.writer();
-    const local = try f.allocLocal(inst, inst_ty);
-    const v = try Vectorize.start(f, inst, writer, inst_ty);
+    const local = try f.allocLocal(inst, ty);
+    const v = try Vectorize.start(f, inst, writer, ty);
     try f.writeCValue(writer, local, .Other);
     try v.elem(f, writer);
     try writer.writeAll(" = zig_libc_name_");
-    try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty);
+    try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
     try writer.writeByte('(');
     try writer.writeAll(operation);
     try writer.writeAll(")(");
@@ -7104,6 +7116,16 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal
     return local;
 }
 
+fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue {
+    const un_op = f.air.instructions.items(.data)[inst].un_op;
+
+    const operand = try f.resolveInst(un_op);
+    try reap(f, inst, &.{un_op});
+
+    const inst_ty = f.typeOfIndex(inst);
+    return unFloatOp(f, inst, operand, inst_ty, operation);
+}
+
 fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue {
     const mod = f.object.dg.module;
     const bin_op = f.air.instructions.items(.data)[inst].bin_op;
src/codegen/llvm.zig
@@ -4729,6 +4729,7 @@ pub const FuncGen = struct {
                 .div_exact => try self.airDivExact(inst, .normal),
                 .rem       => try self.airRem(inst, .normal),
                 .mod       => try self.airMod(inst, .normal),
+                .abs       => try self.airAbs(inst),
                 .ptr_add   => try self.airPtrAdd(inst),
                 .ptr_sub   => try self.airPtrSub(inst),
                 .shl       => try self.airShl(inst),
@@ -4766,7 +4767,6 @@ pub const FuncGen = struct {
                 .log          => try self.airUnaryOp(inst, .log),
                 .log2         => try self.airUnaryOp(inst, .log2),
                 .log10        => try self.airUnaryOp(inst, .log10),
-                .fabs         => try self.airUnaryOp(inst, .fabs),
                 .floor        => try self.airUnaryOp(inst, .floor),
                 .ceil         => try self.airUnaryOp(inst, .ceil),
                 .round        => try self.airUnaryOp(inst, .round),
@@ -8237,6 +8237,28 @@ pub const FuncGen = struct {
         else if (is_signed_int) .ashr else .lshr, lhs, casted_rhs, "");
     }
 
+    fn airAbs(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
+        const o = self.dg.object;
+        const mod = o.module;
+        const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+        const operand = try self.resolveInst(ty_op.operand);
+        const operand_ty = self.typeOf(ty_op.operand);
+        const scalar_ty = operand_ty.scalarType(mod);
+
+        switch (scalar_ty.zigTypeTag(mod)) {
+            .Int => return self.wip.callIntrinsic(
+                .normal,
+                .none,
+                .abs,
+                &.{try o.lowerType(operand_ty)},
+                &.{ operand, try o.builder.intValue(.i1, 0) },
+                "",
+            ),
+            .Float => return self.buildFloatOp(.fabs, .normal, operand_ty, 1, .{operand}),
+            else => unreachable,
+        }
+    }
+
     fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
         const o = self.dg.object;
         const mod = o.module;
src/Liveness/Verify.zig
@@ -110,6 +110,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
             .addrspace_cast,
             .c_va_arg,
             .c_va_copy,
+            .abs,
             => {
                 const ty_op = data[inst].ty_op;
                 try self.verifyInstOperands(inst, .{ ty_op.operand, .none, .none });
@@ -136,7 +137,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
             .log,
             .log2,
             .log10,
-            .fabs,
             .floor,
             .ceil,
             .round,
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,
src/AstGen.zig
@@ -2601,7 +2601,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
             .log,
             .log2,
             .log10,
-            .fabs,
+            .abs,
             .floor,
             .ceil,
             .trunc,
@@ -8385,7 +8385,7 @@ fn builtinCall(
         .log                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .log),
         .log2                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .log2),
         .log10                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .log10),
-        .fabs                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .fabs),
+        .abs                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .abs),
         .floor                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .floor),
         .ceil                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .ceil),
         .trunc                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .trunc),
src/AstRlAnnotate.zig
@@ -929,7 +929,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
         .log,
         .log2,
         .log10,
-        .fabs,
+        .abs,
         .floor,
         .ceil,
         .trunc,
src/Autodoc.zig
@@ -1669,7 +1669,7 @@ fn walkInstruction(
         .log,
         .log2,
         .log10,
-        .fabs,
+        .abs,
         .floor,
         .ceil,
         .trunc,
src/BuiltinFn.zig
@@ -102,7 +102,7 @@ pub const Tag = enum {
     log,
     log2,
     log10,
-    fabs,
+    abs,
     floor,
     ceil,
     trunc,
@@ -874,9 +874,9 @@ pub const list = list: {
             },
         },
         .{
-            "@fabs",
+            "@abs",
             .{
-                .tag = .fabs,
+                .tag = .abs,
                 .param_count = 1,
             },
         },
src/Liveness.zig
@@ -384,6 +384,7 @@ pub fn categorizeOperand(
         .addrspace_cast,
         .c_va_arg,
         .c_va_copy,
+        .abs,
         => {
             const o = air_datas[inst].ty_op;
             if (o.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none);
@@ -420,7 +421,6 @@ pub fn categorizeOperand(
         .log,
         .log2,
         .log10,
-        .fabs,
         .floor,
         .ceil,
         .round,
@@ -1027,6 +1027,7 @@ fn analyzeInst(
         .addrspace_cast,
         .c_va_arg,
         .c_va_copy,
+        .abs,
         => {
             const o = inst_datas[inst].ty_op;
             return analyzeOperands(a, pass, data, inst, .{ o.operand, .none, .none });
@@ -1054,7 +1055,6 @@ fn analyzeInst(
         .log,
         .log2,
         .log10,
-        .fabs,
         .floor,
         .ceil,
         .round,
src/print_air.zig
@@ -188,7 +188,7 @@ const Writer = struct {
             .log,
             .log2,
             .log10,
-            .fabs,
+            .abs,
             .floor,
             .ceil,
             .round,
src/print_zir.zig
@@ -262,7 +262,7 @@ const Writer = struct {
             .log,
             .log2,
             .log10,
-            .fabs,
+            .abs,
             .floor,
             .ceil,
             .trunc,
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();
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 };
src/value.zig
@@ -1989,7 +1989,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;
     }
 
@@ -3706,36 +3706,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 {
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,