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