diff options
| -rw-r--r-- | src/Air.zig | 6 | ||||
| -rw-r--r-- | src/Liveness.zig | 1 | ||||
| -rw-r--r-- | src/Sema.zig | 84 | ||||
| -rw-r--r-- | src/arch/aarch64/CodeGen.zig | 11 | ||||
| -rw-r--r-- | src/arch/arm/CodeGen.zig | 11 | ||||
| -rw-r--r-- | src/arch/riscv64/CodeGen.zig | 11 | ||||
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 2 | ||||
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 11 | ||||
| -rw-r--r-- | src/codegen/c.zig | 8 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 16 | ||||
| -rw-r--r-- | src/print_air.zig | 1 | ||||
| -rw-r--r-- | src/value.zig | 22 | ||||
| -rw-r--r-- | test/behavior/floatop.zig | 42 | ||||
| -rw-r--r-- | test/behavior/floatop_stage1.zig | 34 | ||||
| -rw-r--r-- | test/behavior/math.zig | 12 |
15 files changed, 217 insertions, 55 deletions
diff --git a/src/Air.zig b/src/Air.zig index 14f8f96d38..6888f51963 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -237,6 +237,10 @@ pub const Inst = struct { /// Uses the `ty_op` field. popcount, + /// Computes the square root of a floating point number. + /// Uses the `un_op` field. + sqrt, + /// `<`. Result type is always bool. /// Uses the `bin_op` field. cmp_lt, @@ -749,6 +753,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .max, => return air.typeOf(datas[inst].bin_op.lhs), + .sqrt => return air.typeOf(datas[inst].un_op), + .cmp_lt, .cmp_lte, .cmp_eq, diff --git a/src/Liveness.zig b/src/Liveness.zig index f07e438246..bed7de1507 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -338,6 +338,7 @@ fn analyzeInst( .ret_load, .tag_name, .error_name, + .sqrt, => { const operand = inst_datas[inst].un_op; return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none }); diff --git a/src/Sema.zig b/src/Sema.zig index 1dba136a48..b1772502bf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -745,19 +745,19 @@ fn analyzeBodyInner( .clz => try sema.zirClzCtz(block, inst, .clz, Value.clz), .ctz => try sema.zirClzCtz(block, inst, .ctz, Value.ctz), - .sqrt => try sema.zirUnaryMath(block, inst), - .sin => try sema.zirUnaryMath(block, inst), - .cos => try sema.zirUnaryMath(block, inst), - .exp => try sema.zirUnaryMath(block, inst), - .exp2 => try sema.zirUnaryMath(block, inst), - .log => try sema.zirUnaryMath(block, inst), - .log2 => try sema.zirUnaryMath(block, inst), - .log10 => try sema.zirUnaryMath(block, inst), - .fabs => try sema.zirUnaryMath(block, inst), - .floor => try sema.zirUnaryMath(block, inst), - .ceil => try sema.zirUnaryMath(block, inst), - .trunc => try sema.zirUnaryMath(block, inst), - .round => try sema.zirUnaryMath(block, inst), + .sqrt => try sema.zirUnaryMath(block, inst, .sqrt), + .sin => try sema.zirUnaryMath(block, inst, .sin), + .cos => try sema.zirUnaryMath(block, inst, .cos), + .exp => try sema.zirUnaryMath(block, inst, .exp), + .exp2 => try sema.zirUnaryMath(block, inst, .exp2), + .log => try sema.zirUnaryMath(block, inst, .log), + .log2 => try sema.zirUnaryMath(block, inst, .log2), + .log10 => try sema.zirUnaryMath(block, inst, .log10), + .fabs => try sema.zirUnaryMath(block, inst, .fabs), + .floor => try sema.zirUnaryMath(block, inst, .floor), + .ceil => try sema.zirUnaryMath(block, inst, .ceil), + .trunc => try sema.zirUnaryMath(block, inst, .trunc), + .round => try sema.zirUnaryMath(block, inst, .round), .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent), .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), @@ -11010,10 +11010,64 @@ fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A return block.addUnOp(.error_name, operand); } -fn zirUnaryMath(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirUnaryMath( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, + zir_tag: Zir.Inst.Tag, +) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); + const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - return sema.fail(block, src, "TODO: Sema.zirUnaryMath", .{}); + const operand = sema.resolveInst(inst_data.operand); + const operand_ty = sema.typeOf(operand); + const operand_zig_ty_tag = operand_ty.zigTypeTag(); + + const is_float = operand_zig_ty_tag == .Float or operand_zig_ty_tag == .ComptimeFloat; + if (!is_float) { + return sema.fail(block, src, "expected float type, found '{s}'", .{@tagName(operand_zig_ty_tag)}); + } + + switch (zir_tag) { + .sqrt => { + switch (operand_ty.tag()) { + .f128, + .comptime_float, + .c_longdouble, + => |t| return sema.fail(block, src, "TODO implement @sqrt for type '{s}'", .{@tagName(t)}), + else => {}, + } + + const maybe_operand_val = try sema.resolveMaybeUndefVal(block, src, operand); + if (maybe_operand_val) |val| { + if (val.isUndef()) + return sema.addConstUndef(operand_ty); + const result_val = try val.sqrt(operand_ty, sema.arena); + return sema.addConstant(operand_ty, result_val); + } + + try sema.requireRuntimeBlock(block, src); + return block.addUnOp(.sqrt, operand); + }, + + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .trunc, + .round, + => return sema.fail(block, src, "TODO: implement zirUnaryMath for ZIR tag '{s}'", .{@tagName(zir_tag)}), + + else => unreachable, + } } fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 6e8f88a2a7..d0413af02f 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -528,6 +528,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), + .sqrt => try self.airUnaryMath(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), @@ -1223,6 +1225,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { if (!self.liveness.operandDies(inst, op_index)) return false; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 1859ce874f..fb473ef412 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -520,6 +520,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), + .sqrt => try self.airUnaryMath(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), @@ -1377,6 +1379,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { if (!self.liveness.operandDies(inst, op_index)) return false; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 612ff78bd6..ce5dc39bf8 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -507,6 +507,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), + .sqrt => try self.airUnaryMath(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), @@ -1166,6 +1168,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { if (!self.liveness.operandDies(inst, op_index)) return false; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 8e0ffac76b..b1e7a0d3a1 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1681,6 +1681,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .unwrap_errunion_payload_ptr, .unwrap_errunion_err_ptr, + .sqrt, + .ptr_slice_len_ptr, .ptr_slice_ptr_ptr, .int_to_float, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index a60b8c78f0..10a6120556 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -599,6 +599,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), + .sqrt => try self.airUnaryMath(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), @@ -1578,6 +1580,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { if (!self.liveness.operandDies(inst, op_index)) return false; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 44b904f031..fc3848df21 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1446,6 +1446,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul_sat => try airSatOp(f, inst, "muls_"), .shl_sat => try airSatOp(f, inst, "shls_"), + .sqrt => try airSqrt(f, inst), + .add_with_overflow => try airAddWithOverflow(f, inst), .sub_with_overflow => try airSubWithOverflow(f, inst), .mul_with_overflow => try airMulWithOverflow(f, inst), @@ -3393,6 +3395,12 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { return CValue.none; } +fn airSqrt(f: *Function, inst: Air.Inst.Index) !CValue { + _ = f; + _ = inst; + return f.fail("TODO: C backend: implement sqrt", .{}); +} + fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { return switch (order) { .Unordered => "memory_order_relaxed", diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index abf371c85b..5a2c21eece 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2050,6 +2050,8 @@ pub const FuncGen = struct { .shr => try self.airShr(inst, false), .shr_exact => try self.airShr(inst, true), + .sqrt => try self.airSqrt(inst), + .cmp_eq => try self.airCmp(inst, .eq), .cmp_gt => try self.airCmp(inst, .gt), .cmp_gte => try self.airCmp(inst, .gte), @@ -4211,6 +4213,20 @@ pub const FuncGen = struct { } } + fn airSqrt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + const operand_ty = self.air.typeOf(un_op); + + const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const fn_val = self.getIntrinsic("llvm.sqrt", &.{operand_llvm_ty}); + const params = [_]*const llvm.Value{operand}; + + return self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, ""); + } + fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; diff --git a/src/print_air.zig b/src/print_air.zig index 6e1ed3f3d7..341e736b91 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -158,6 +158,7 @@ const Writer = struct { .ret_load, .tag_name, .error_name, + .sqrt, => try w.writeUnOp(s, inst), .breakpoint, diff --git a/src/value.zig b/src/value.zig index cc6827b0cc..4bb0a58aed 100644 --- a/src/value.zig +++ b/src/value.zig @@ -3265,6 +3265,28 @@ pub const Value = extern union { } } + pub fn sqrt(val: Value, float_type: Type, arena: Allocator) !Value { + switch (float_type.tag()) { + .f16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @sqrt(f)); + }, + .f32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @sqrt(f)); + }, + .f64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @sqrt(f)); + }, + + // TODO: implement @sqrt for these types + .f128, .comptime_float, .c_longdouble => unreachable, + + else => unreachable, + } + } + /// This type is not copyable since it may contain pointers to its inner data. pub const Payload = struct { tag: Tag, diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 20ef4ce68d..7807c690f6 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -72,3 +72,45 @@ test "negative f128 floatToInt at compile-time" { var b = @floatToInt(i64, a); try expect(@as(i64, -2) == b); } + +test "@sqrt" { + comptime try testSqrt(); + try testSqrt(); +} + +fn testSqrt() !void { + { + var a: f16 = 4; + try expect(@sqrt(a) == 2); + } + { + var a: f32 = 9; + try expect(@sqrt(a) == 3); + var b: f32 = 1.1; + try expect(math.approxEqAbs(f32, @sqrt(b), 1.0488088481701516, epsilon)); + } + { + var a: f64 = 25; + try expect(@sqrt(a) == 5); + } +} + +test "more @sqrt f16 tests" { + // TODO these are not all passing at comptime + try expect(@sqrt(@as(f16, 0.0)) == 0.0); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon)); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon)); + try expect(@sqrt(@as(f16, 4.0)) == 2.0); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon)); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon)); + try expect(@sqrt(@as(f16, 64.0)) == 8.0); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon)); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon)); + + // special cases + try expect(math.isPositiveInf(@sqrt(@as(f16, math.inf(f16))))); + try expect(@sqrt(@as(f16, 0.0)) == 0.0); + try expect(@sqrt(@as(f16, -0.0)) == -0.0); + try expect(math.isNan(@sqrt(@as(f16, -1.0)))); + try expect(math.isNan(@sqrt(@as(f16, math.nan(f16))))); +} diff --git a/test/behavior/floatop_stage1.zig b/test/behavior/floatop_stage1.zig index 303288a118..cd11f41b40 100644 --- a/test/behavior/floatop_stage1.zig +++ b/test/behavior/floatop_stage1.zig @@ -14,20 +14,6 @@ test "@sqrt" { } fn testSqrt() !void { - { - var a: f16 = 4; - try expect(@sqrt(a) == 2); - } - { - var a: f32 = 9; - try expect(@sqrt(a) == 3); - var b: f32 = 1.1; - try expect(math.approxEqAbs(f32, @sqrt(b), 1.0488088481701516, epsilon)); - } - { - var a: f64 = 25; - try expect(@sqrt(a) == 5); - } if (has_f80_rt) { var a: f80 = 25; try expect(@sqrt(a) == 5); @@ -51,26 +37,6 @@ fn testSqrt() !void { } } -test "more @sqrt f16 tests" { - // TODO these are not all passing at comptime - try expect(@sqrt(@as(f16, 0.0)) == 0.0); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon)); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon)); - try expect(@sqrt(@as(f16, 4.0)) == 2.0); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon)); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon)); - try expect(@sqrt(@as(f16, 64.0)) == 8.0); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon)); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon)); - - // special cases - try expect(math.isPositiveInf(@sqrt(@as(f16, math.inf(f16))))); - try expect(@sqrt(@as(f16, 0.0)) == 0.0); - try expect(@sqrt(@as(f16, -0.0)) == -0.0); - try expect(math.isNan(@sqrt(@as(f16, -1.0)))); - try expect(math.isNan(@sqrt(@as(f16, math.nan(f16))))); -} - test "@sin" { comptime try testSin(); try testSin(); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index a1243eb7c1..c23e8ebe3e 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -792,8 +792,6 @@ fn remdiv(comptime T: type) !void { } test "@sqrt" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - try testSqrt(f64, 12.0); comptime try testSqrt(f64, 12.0); try testSqrt(f32, 13.0); @@ -801,10 +799,12 @@ test "@sqrt" { try testSqrt(f16, 13.0); comptime try testSqrt(f16, 13.0); - const x = 14.0; - const y = x * x; - const z = @sqrt(y); - comptime try expect(z == x); + if (builtin.zig_backend == .stage1) { + const x = 14.0; + const y = x * x; + const z = @sqrt(y); + comptime try expect(z == x); + } } fn testSqrt(comptime T: type, x: T) !void { |
