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 {