authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-07 18:52:57-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-07 18:52:57-05:00
log9bbfbacae0707cf0712e4d6b227b17d097708c7e
treed61c60e53f167ecb3c5b24235f5fa30f01fd8c23
parentdd49ed1c642d917af40bf4a0e03d013f40b3903b
parenta028488384c599aa997ba04bbd5ed98f2172630c
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10801 from schmee/stage2-sqrt

stage2: implement @sqrt for f{16,32,64}

16 files changed, 196 insertions(+), 57 deletions(-)

src/Air.zig+6
......@@ -237,6 +237,10 @@ pub const Inst = struct {
237237 /// Uses the `ty_op` field.
238238 popcount,
239239
240 /// Computes the square root of a floating point number.
241 /// Uses the `un_op` field.
242 sqrt,
243
240244 /// `<`. Result type is always bool.
241245 /// Uses the `bin_op` field.
242246 cmp_lt,
......@@ -749,6 +753,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
749753 .max,
750754 => return air.typeOf(datas[inst].bin_op.lhs),
751755
756 .sqrt => return air.typeOf(datas[inst].un_op),
757
752758 .cmp_lt,
753759 .cmp_lte,
754760 .cmp_eq,
src/Liveness.zig+1
......@@ -338,6 +338,7 @@ fn analyzeInst(
338338 .ret_load,
339339 .tag_name,
340340 .error_name,
341 .sqrt,
341342 => {
342343 const operand = inst_datas[inst].un_op;
343344 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
src/Sema.zig+37-16
......@@ -745,19 +745,19 @@ fn analyzeBodyInner(
745745 .clz => try sema.zirClzCtz(block, inst, .clz, Value.clz),
746746 .ctz => try sema.zirClzCtz(block, inst, .ctz, Value.ctz),
747747
748 .sqrt => try sema.zirUnaryMath(block, inst),
749 .sin => try sema.zirUnaryMath(block, inst),
750 .cos => try sema.zirUnaryMath(block, inst),
751 .exp => try sema.zirUnaryMath(block, inst),
752 .exp2 => try sema.zirUnaryMath(block, inst),
753 .log => try sema.zirUnaryMath(block, inst),
754 .log2 => try sema.zirUnaryMath(block, inst),
755 .log10 => try sema.zirUnaryMath(block, inst),
756 .fabs => try sema.zirUnaryMath(block, inst),
757 .floor => try sema.zirUnaryMath(block, inst),
758 .ceil => try sema.zirUnaryMath(block, inst),
759 .trunc => try sema.zirUnaryMath(block, inst),
760 .round => try sema.zirUnaryMath(block, inst),
748 .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt),
749 .sin => @panic("TODO"),
750 .cos => @panic("TODO"),
751 .exp => @panic("TODO"),
752 .exp2 => @panic("TODO"),
753 .log => @panic("TODO"),
754 .log2 => @panic("TODO"),
755 .log10 => @panic("TODO"),
756 .fabs => @panic("TODO"),
757 .floor => @panic("TODO"),
758 .ceil => @panic("TODO"),
759 .trunc => @panic("TODO"),
760 .round => @panic("TODO"),
761761
762762 .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent),
763763 .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon),
......@@ -11010,10 +11010,31 @@ fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1101011010 return block.addUnOp(.error_name, operand);
1101111011}
1101211012
11013fn zirUnaryMath(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11013fn zirUnaryMath(
11014 sema: *Sema,
11015 block: *Block,
11016 inst: Zir.Inst.Index,
11017 air_tag: Air.Inst.Tag,
11018 eval: fn (Value, Type, Allocator, std.Target) Allocator.Error!Value,
11019) CompileError!Air.Inst.Ref {
11020 const tracy = trace(@src());
11021 defer tracy.end();
11022
1101411023 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
11015 const src = inst_data.src();
11016 return sema.fail(block, src, "TODO: Sema.zirUnaryMath", .{});
11024 const operand = sema.resolveInst(inst_data.operand);
11025 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
11026 const operand_ty = sema.typeOf(operand);
11027 try sema.checkFloatType(block, operand_src, operand_ty);
11028
11029 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| {
11030 if (operand_val.isUndef()) return sema.addConstUndef(operand_ty);
11031 const target = sema.mod.getTarget();
11032 const result_val = try eval(operand_val, operand_ty, sema.arena, target);
11033 return sema.addConstant(operand_ty, result_val);
11034 }
11035
11036 try sema.requireRuntimeBlock(block, operand_src);
11037 return block.addUnOp(air_tag, operand);
1101711038}
1101811039
1101911040fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
src/arch/aarch64/CodeGen.zig+11
......@@ -528,6 +528,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
528528 .max => try self.airMax(inst),
529529 .slice => try self.airSlice(inst),
530530
531 .sqrt => try self.airUnaryMath(inst),
532
531533 .add_with_overflow => try self.airAddWithOverflow(inst),
532534 .sub_with_overflow => try self.airSubWithOverflow(inst),
533535 .mul_with_overflow => try self.airMulWithOverflow(inst),
......@@ -1223,6 +1225,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
12231225 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
12241226}
12251227
1228fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
1229 const un_op = self.air.instructions.items(.data)[inst].un_op;
1230 const result: MCValue = if (self.liveness.isUnused(inst))
1231 .dead
1232 else
1233 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
1234 return self.finishAir(inst, result, .{ un_op, .none, .none });
1235}
1236
12261237fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
12271238 if (!self.liveness.operandDies(inst, op_index))
12281239 return false;
src/arch/arm/CodeGen.zig+11
......@@ -520,6 +520,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
520520 .max => try self.airMax(inst),
521521 .slice => try self.airSlice(inst),
522522
523 .sqrt => try self.airUnaryMath(inst),
524
523525 .add_with_overflow => try self.airAddWithOverflow(inst),
524526 .sub_with_overflow => try self.airSubWithOverflow(inst),
525527 .mul_with_overflow => try self.airMulWithOverflow(inst),
......@@ -1377,6 +1379,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
13771379 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
13781380}
13791381
1382fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
1383 const un_op = self.air.instructions.items(.data)[inst].un_op;
1384 const result: MCValue = if (self.liveness.isUnused(inst))
1385 .dead
1386 else
1387 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
1388 return self.finishAir(inst, result, .{ un_op, .none, .none });
1389}
1390
13801391fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
13811392 if (!self.liveness.operandDies(inst, op_index))
13821393 return false;
src/arch/riscv64/CodeGen.zig+11
......@@ -507,6 +507,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
507507 .max => try self.airMax(inst),
508508 .slice => try self.airSlice(inst),
509509
510 .sqrt => try self.airUnaryMath(inst),
511
510512 .add_with_overflow => try self.airAddWithOverflow(inst),
511513 .sub_with_overflow => try self.airSubWithOverflow(inst),
512514 .mul_with_overflow => try self.airMulWithOverflow(inst),
......@@ -1166,6 +1168,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
11661168 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11671169}
11681170
1171fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
1172 const un_op = self.air.instructions.items(.data)[inst].un_op;
1173 const result: MCValue = if (self.liveness.isUnused(inst))
1174 .dead
1175 else
1176 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
1177 return self.finishAir(inst, result, .{ un_op, .none, .none });
1178}
1179
11691180fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
11701181 if (!self.liveness.operandDies(inst, op_index))
11711182 return false;
src/arch/wasm/CodeGen.zig+2
......@@ -1681,6 +1681,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
16811681 .unwrap_errunion_payload_ptr,
16821682 .unwrap_errunion_err_ptr,
16831683
1684 .sqrt,
1685
16841686 .ptr_slice_len_ptr,
16851687 .ptr_slice_ptr_ptr,
16861688 .int_to_float,
src/arch/x86_64/CodeGen.zig+11
......@@ -599,6 +599,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
599599 .max => try self.airMax(inst),
600600 .slice => try self.airSlice(inst),
601601
602 .sqrt => try self.airUnaryMath(inst),
603
602604 .add_with_overflow => try self.airAddWithOverflow(inst),
603605 .sub_with_overflow => try self.airSubWithOverflow(inst),
604606 .mul_with_overflow => try self.airMulWithOverflow(inst),
......@@ -1578,6 +1580,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
15781580 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
15791581}
15801582
1583fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
1584 const un_op = self.air.instructions.items(.data)[inst].un_op;
1585 const result: MCValue = if (self.liveness.isUnused(inst))
1586 .dead
1587 else
1588 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
1589 return self.finishAir(inst, result, .{ un_op, .none, .none });
1590}
1591
15811592fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
15821593 if (!self.liveness.operandDies(inst, op_index))
15831594 return false;
src/codegen/c.zig+8
......@@ -1446,6 +1446,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
14461446 .mul_sat => try airSatOp(f, inst, "muls_"),
14471447 .shl_sat => try airSatOp(f, inst, "shls_"),
14481448
1449 .sqrt => try airSqrt(f, inst),
1450
14491451 .add_with_overflow => try airAddWithOverflow(f, inst),
14501452 .sub_with_overflow => try airSubWithOverflow(f, inst),
14511453 .mul_with_overflow => try airMulWithOverflow(f, inst),
......@@ -3393,6 +3395,12 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
33933395 return CValue.none;
33943396}
33953397
3398fn airSqrt(f: *Function, inst: Air.Inst.Index) !CValue {
3399 _ = f;
3400 _ = inst;
3401 return f.fail("TODO: C backend: implement sqrt", .{});
3402}
3403
33963404fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
33973405 return switch (order) {
33983406 .Unordered => "memory_order_relaxed",
src/codegen/llvm.zig+16
......@@ -2050,6 +2050,8 @@ pub const FuncGen = struct {
20502050 .shr => try self.airShr(inst, false),
20512051 .shr_exact => try self.airShr(inst, true),
20522052
2053 .sqrt => try self.airSqrt(inst),
2054
20532055 .cmp_eq => try self.airCmp(inst, .eq),
20542056 .cmp_gt => try self.airCmp(inst, .gt),
20552057 .cmp_gte => try self.airCmp(inst, .gte),
......@@ -4211,6 +4213,20 @@ pub const FuncGen = struct {
42114213 }
42124214 }
42134215
4216 fn airSqrt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
4217 if (self.liveness.isUnused(inst)) return null;
4218
4219 const un_op = self.air.instructions.items(.data)[inst].un_op;
4220 const operand = try self.resolveInst(un_op);
4221 const operand_ty = self.air.typeOf(un_op);
4222
4223 const operand_llvm_ty = try self.dg.llvmType(operand_ty);
4224 const fn_val = self.getIntrinsic("llvm.sqrt", &.{operand_llvm_ty});
4225 const params = [_]*const llvm.Value{operand};
4226
4227 return self.builder.buildCall(fn_val, &params, params.len, .C, .Auto, "");
4228 }
4229
42144230 fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value {
42154231 if (self.liveness.isUnused(inst)) return null;
42164232
src/print_air.zig+1
......@@ -158,6 +158,7 @@ const Writer = struct {
158158 .ret_load,
159159 .tag_name,
160160 .error_name,
161 .sqrt,
161162 => try w.writeUnOp(s, inst),
162163
163164 .breakpoint,
src/stage1/codegen.cpp+1-1
......@@ -6996,7 +6996,7 @@ static LLVMValueRef ir_render_soft_f80_float_op(CodeGen *g, Stage1Air *executabl
69966996 const char *func_name;
69976997 switch (instruction->fn_id) {
69986998 case BuiltinFnIdSqrt:
6999 func_name = "__sqrt";
6999 func_name = "__sqrtx";
70007000 break;
70017001 case BuiltinFnIdSin:
70027002 func_name = "__sinx";
src/value.zig+32
......@@ -3265,6 +3265,38 @@ pub const Value = extern union {
32653265 }
32663266 }
32673267
3268 pub fn sqrt(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
3269 switch (float_type.floatBits(target)) {
3270 16 => {
3271 const f = val.toFloat(f16);
3272 return Value.Tag.float_16.create(arena, @sqrt(f));
3273 },
3274 32 => {
3275 const f = val.toFloat(f32);
3276 return Value.Tag.float_32.create(arena, @sqrt(f));
3277 },
3278 64 => {
3279 const f = val.toFloat(f64);
3280 return Value.Tag.float_64.create(arena, @sqrt(f));
3281 },
3282 80 => {
3283 if (true) {
3284 @panic("TODO implement compiler_rt __sqrtx");
3285 }
3286 const f = val.toFloat(f80);
3287 return Value.Tag.float_80.create(arena, @sqrt(f));
3288 },
3289 128 => {
3290 if (true) {
3291 @panic("TODO implement compiler_rt sqrtq");
3292 }
3293 const f = val.toFloat(f128);
3294 return Value.Tag.float_128.create(arena, @sqrt(f));
3295 },
3296 else => unreachable,
3297 }
3298 }
3299
32683300 /// This type is not copyable since it may contain pointers to its inner data.
32693301 pub const Payload = struct {
32703302 tag: Tag,
test/behavior/floatop.zig+42
......@@ -72,3 +72,45 @@ test "negative f128 floatToInt at compile-time" {
7272 var b = @floatToInt(i64, a);
7373 try expect(@as(i64, -2) == b);
7474}
75
76test "@sqrt" {
77 comptime try testSqrt();
78 try testSqrt();
79}
80
81fn testSqrt() !void {
82 {
83 var a: f16 = 4;
84 try expect(@sqrt(a) == 2);
85 }
86 {
87 var a: f32 = 9;
88 try expect(@sqrt(a) == 3);
89 var b: f32 = 1.1;
90 try expect(math.approxEqAbs(f32, @sqrt(b), 1.0488088481701516, epsilon));
91 }
92 {
93 var a: f64 = 25;
94 try expect(@sqrt(a) == 5);
95 }
96}
97
98test "more @sqrt f16 tests" {
99 // TODO these are not all passing at comptime
100 try expect(@sqrt(@as(f16, 0.0)) == 0.0);
101 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon));
102 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon));
103 try expect(@sqrt(@as(f16, 4.0)) == 2.0);
104 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon));
105 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon));
106 try expect(@sqrt(@as(f16, 64.0)) == 8.0);
107 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon));
108 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon));
109
110 // special cases
111 try expect(math.isPositiveInf(@sqrt(@as(f16, math.inf(f16)))));
112 try expect(@sqrt(@as(f16, 0.0)) == 0.0);
113 try expect(@sqrt(@as(f16, -0.0)) == -0.0);
114 try expect(math.isNan(@sqrt(@as(f16, -1.0))));
115 try expect(math.isNan(@sqrt(@as(f16, math.nan(f16)))));
116}
test/behavior/floatop_stage1.zig-34
......@@ -14,20 +14,6 @@ test "@sqrt" {
1414}
1515
1616fn testSqrt() !void {
17 {
18 var a: f16 = 4;
19 try expect(@sqrt(a) == 2);
20 }
21 {
22 var a: f32 = 9;
23 try expect(@sqrt(a) == 3);
24 var b: f32 = 1.1;
25 try expect(math.approxEqAbs(f32, @sqrt(b), 1.0488088481701516, epsilon));
26 }
27 {
28 var a: f64 = 25;
29 try expect(@sqrt(a) == 5);
30 }
3117 if (has_f80_rt) {
3218 var a: f80 = 25;
3319 try expect(@sqrt(a) == 5);
......@@ -51,26 +37,6 @@ fn testSqrt() !void {
5137 }
5238}
5339
54test "more @sqrt f16 tests" {
55 // TODO these are not all passing at comptime
56 try expect(@sqrt(@as(f16, 0.0)) == 0.0);
57 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon));
58 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon));
59 try expect(@sqrt(@as(f16, 4.0)) == 2.0);
60 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon));
61 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon));
62 try expect(@sqrt(@as(f16, 64.0)) == 8.0);
63 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon));
64 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon));
65
66 // special cases
67 try expect(math.isPositiveInf(@sqrt(@as(f16, math.inf(f16)))));
68 try expect(@sqrt(@as(f16, 0.0)) == 0.0);
69 try expect(@sqrt(@as(f16, -0.0)) == -0.0);
70 try expect(math.isNan(@sqrt(@as(f16, -1.0))));
71 try expect(math.isNan(@sqrt(@as(f16, math.nan(f16)))));
72}
73
7440test "@sin" {
7541 comptime try testSin();
7642 try testSin();
test/behavior/math.zig+6-6
......@@ -792,8 +792,6 @@ fn remdiv(comptime T: type) !void {
792792}
793793
794794test "@sqrt" {
795 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
796
797795 try testSqrt(f64, 12.0);
798796 comptime try testSqrt(f64, 12.0);
799797 try testSqrt(f32, 13.0);
......@@ -801,10 +799,12 @@ test "@sqrt" {
801799 try testSqrt(f16, 13.0);
802800 comptime try testSqrt(f16, 13.0);
803801
804 const x = 14.0;
805 const y = x * x;
806 const z = @sqrt(y);
807 comptime try expect(z == x);
802 if (builtin.zig_backend == .stage1) {
803 const x = 14.0;
804 const y = x * x;
805 const z = @sqrt(y);
806 comptime try expect(z == x);
807 }
808808}
809809
810810fn testSqrt(comptime T: type, x: T) !void {