authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-09-30 22:59:39-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-01 15:09:52-04:00
logda335f0ee4dfe5a736919acc5ab20777838b42c7
treeebf15e21461cd6138c20c264c4194b4591bf7470
parentfbe5bf469e17ffd57b34762fd0206587f5758a52

x86_64: implement float `@sqrt` builtin


2 files changed, 93 insertions(+), 88 deletions(-)

src/arch/x86_64/CodeGen.zig+15-3
...@@ -5359,9 +5359,21 @@ fn airSqrt(self: *Self, inst: Air.Inst.Index) !void {...@@ -5359,9 +5359,21 @@ fn airSqrt(self: *Self, inst: Air.Inst.Index) !void {
5359 else => unreachable,5359 else => unreachable,
5360 },5360 },
5361 else => unreachable,5361 else => unreachable,
5362 }) orelse return self.fail("TODO implement airSqrt for {}", .{5362 }) orelse {
5363 ty.fmt(mod),5363 if (ty.zigTypeTag(mod) != .Float) return self.fail("TODO implement airSqrt for {}", .{
5364 });5364 ty.fmt(mod),
5365 });
5366
5367 var callee: ["__sqrt?".len]u8 = undefined;
5368 break :result try self.genCall(.{ .lib = .{
5369 .return_type = ty.toIntern(),
5370 .param_types = &.{ty.toIntern()},
5371 .callee = std.fmt.bufPrint(&callee, "{s}sqrt{s}", .{
5372 floatLibcAbiPrefix(ty),
5373 floatLibcAbiSuffix(ty),
5374 }) catch unreachable,
5375 } }, &.{ty}, &.{src_mcv});
5376 };
5365 switch (mir_tag[0]) {5377 switch (mir_tag[0]) {
5366 .v_ss, .v_sd => if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory(5378 .v_ss, .v_sd => if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory(
5367 mir_tag,5379 mir_tag,
test/behavior/floatop.zig+78-85
...@@ -2,12 +2,6 @@ const std = @import("std");...@@ -2,12 +2,6 @@ const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const expect = std.testing.expect;3const expect = std.testing.expect;
4const math = std.math;4const math = std.math;
5const has_f80_rt = switch (builtin.cpu.arch) {
6 .x86_64, .x86 => true,
7 else => false,
8};
9const no_x86_64_hardware_f16_support = builtin.zig_backend == .stage2_x86_64 and
10 !std.Target.x86.featureSetHas(builtin.cpu.features, .f16c);
115
12const epsilon_16 = 0.002;6const epsilon_16 = 0.002;
13const epsilon = 0.000001;7const epsilon = 0.000001;
...@@ -247,42 +241,93 @@ test "negative f128 intFromFloat at compile-time" {...@@ -247,42 +241,93 @@ test "negative f128 intFromFloat at compile-time" {
247 try expect(@as(i64, -2) == b);241 try expect(@as(i64, -2) == b);
248}242}
249243
250test "@sqrt" {244test "@sqrt f16" {
251 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO245 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
252 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO246 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
253 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO247 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
254 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;248 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
249 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
255250
256 try testSqrt();251 try testSqrt(f16);
257 try comptime testSqrt();252 try comptime testSqrt(f16);
258}253}
259254
260fn testSqrt() !void {255test "@sqrt f32/f64" {
261 try expect(@sqrt(@as(f16, 4)) == 2);256 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
262 try expect(@sqrt(@as(f32, 9)) == 3);257 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
263 try expect(@sqrt(@as(f64, 25)) == 5);258 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
264 try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 1.1)), 1.0488088481701516, epsilon));259 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
265 try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 2.0)), 1.4142135623730950, epsilon));260 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
261
262 try testSqrt(f32);
263 try comptime testSqrt(f32);
264 try testSqrt(f64);
265 try comptime testSqrt(f64);
266}
266267
267 if (false) {268test "@sqrt f80/f128/c_longdouble" {
268 if (has_f80_rt) {269 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
269 // TODO https://github.com/ziglang/zig/issues/10875270 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
270 if (builtin.os.tag != .freebsd) {271 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
271 var a: f80 = 25;272 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
272 try expect(@sqrt(a) == 5);273 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
273 }274
274 }275 if (builtin.os.tag == .freebsd) {
275 {276 // TODO https://github.com/ziglang/zig/issues/10875
276 const a: comptime_float = 25.0;277 return error.SkipZigTest;
277 try expect(@sqrt(a) == 5.0);
278 }
279 // TODO test f128, and c_longdouble
280 // https://github.com/ziglang/zig/issues/4026
281 //{
282 // var a: f128 = 49;
283 //try expect(@sqrt(a) == 7);
284 //}
285 }278 }
279
280 try testSqrt(f80);
281 try comptime testSqrt(f80);
282 try testSqrt(f128);
283 try comptime testSqrt(f128);
284 try testSqrt(c_longdouble);
285 try comptime testSqrt(c_longdouble);
286}
287
288fn testSqrt(comptime T: type) !void {
289 const eps = epsForType(T);
290 var four: T = 4.0;
291 try expect(@sqrt(four) == 2.0);
292 var nine: T = 9.0;
293 try expect(@sqrt(nine) == 3.0);
294 var twenty_five: T = 25.0;
295 try expect(@sqrt(twenty_five) == 5.0);
296 var sixty_four: T = 64.0;
297 try expect(@sqrt(sixty_four) == 8.0);
298 var one_point_one: T = 1.1;
299
300 try expect(math.approxEqAbs(T, @sqrt(one_point_one), 1.0488088481701516, eps));
301 var two: T = 2.0;
302 try expect(math.approxEqAbs(T, @sqrt(two), 1.4142135623730950, eps));
303 var three_point_six: T = 3.6;
304 try expect(math.approxEqAbs(T, @sqrt(three_point_six), 1.8973665961010276, eps));
305 var sixty_four_point_one: T = 64.1;
306 try expect(math.approxEqAbs(T, @sqrt(sixty_four_point_one), 8.00624756049923802, eps));
307 var twelve: T = 12.0;
308 try expect(math.approxEqAbs(T, @sqrt(twelve), 3.46410161513775459, eps));
309 var thirteen: T = 13.0;
310 try expect(math.approxEqAbs(T, @sqrt(thirteen), 3.60555127546398929, eps));
311 var fourteen: T = 14.0;
312 try expect(math.approxEqAbs(T, @sqrt(fourteen), 3.74165738677394139, eps));
313 var a: T = 7.539840;
314 try expect(math.approxEqAbs(T, @sqrt(a), 2.74587690911300684, eps));
315 var b: T = 19.230934;
316 try expect(math.approxEqAbs(T, @sqrt(b), 4.38530888307767894, eps));
317 var c: T = 8942.230469;
318 try expect(math.approxEqAbs(T, @sqrt(c), 94.5633674791671111, eps));
319
320 // special cases
321 var inf: T = math.inf(T);
322 try expect(math.isPositiveInf(@sqrt(inf)));
323 var zero: T = 0.0;
324 try expect(@sqrt(zero) == 0.0);
325 var neg_zero: T = -0.0;
326 try expect(@sqrt(neg_zero) == 0.0);
327 var neg_one: T = -1.0;
328 try expect(math.isNan(@sqrt(neg_one)));
329 var nan: T = math.nan(T);
330 try expect(math.isNan(@sqrt(nan)));
286}331}
287332
288test "@sqrt with vectors" {333test "@sqrt with vectors" {
...@@ -304,58 +349,6 @@ fn testSqrtWithVectors() !void {...@@ -304,58 +349,6 @@ fn testSqrtWithVectors() !void {
304 try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 4.4)), result[3], epsilon));349 try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 4.4)), result[3], epsilon));
305}350}
306351
307test "more @sqrt f16 tests" {
308 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
309 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
310 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
311 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
312 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
313
314 // TODO these are not all passing at comptime
315 try expect(@sqrt(@as(f16, 0.0)) == 0.0);
316 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon));
317 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon));
318 try expect(@sqrt(@as(f16, 4.0)) == 2.0);
319 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon));
320 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon));
321 try expect(@sqrt(@as(f16, 64.0)) == 8.0);
322 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon));
323 try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon));
324
325 // special cases
326 try expect(math.isPositiveInf(@sqrt(@as(f16, math.inf(f16)))));
327 try expect(@sqrt(@as(f16, 0.0)) == 0.0);
328 try expect(@sqrt(@as(f16, -0.0)) == -0.0);
329 try expect(math.isNan(@sqrt(@as(f16, -1.0))));
330 try expect(math.isNan(@sqrt(@as(f16, math.nan(f16)))));
331}
332
333test "another, possibly redundant @sqrt test" {
334 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
335 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
336 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
337 if (no_x86_64_hardware_f16_support) return error.SkipZigTest;
338
339 try testSqrtLegacy(f64, 12.0);
340 try comptime testSqrtLegacy(f64, 12.0);
341 try testSqrtLegacy(f32, 13.0);
342 try comptime testSqrtLegacy(f32, 13.0);
343 try testSqrtLegacy(f16, 13.0);
344 try comptime testSqrtLegacy(f16, 13.0);
345
346 // TODO: make this pass
347 if (false) {
348 const x = 14.0;
349 const y = x * x;
350 const z = @sqrt(y);
351 try comptime expect(z == x);
352 }
353}
354
355fn testSqrtLegacy(comptime T: type, x: T) !void {
356 try expect(@sqrt(x * x) == x);
357}
358
359test "@sin f16" {352test "@sin f16" {
360 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO353 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
361 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO354 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO