authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-03 00:37:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-03 01:24:26-07:00
log67ba4c5679cf84a81dc92980ef033ef69f442ed4
tree5afd94d62f0bbd2569b5a29ea14fc9a24c9898ed
parentb33f3b23c98d7bb03b116fb084b35ce6b9fc1257

stage2: add all functions to freestanding libc

Looks like all these functions are at least compiling successfully. I haven't tried to run their test suites yet. The one exception is `clone` which is crashing the compiler due to the inline assembly. Still, this is progress!

3 files changed, 1106 insertions(+), 1106 deletions(-)

lib/std/special/c.zig+1103-10
......@@ -7,20 +7,56 @@
77const std = @import("std");
88const builtin = @import("builtin");
99const math = std.math;
10const isNan = std.math.isNan;
11const maxInt = std.math.maxInt;
1012const native_os = builtin.os.tag;
13const native_arch = builtin.cpu.arch;
14const native_abi = builtin.abi;
1115const long_double_is_f128 = builtin.target.longDoubleIsF128();
1216
17const is_wasm = switch (native_arch) {
18 .wasm32, .wasm64 => true,
19 else => false,
20};
21const is_msvc = switch (native_abi) {
22 .msvc => true,
23 else => false,
24};
25const is_freestanding = switch (native_os) {
26 .freestanding => true,
27 else => false,
28};
29
1330comptime {
14 // When the self-hosted compiler is further along, all the logic from c_stage1.zig will
15 // be migrated to this file and then c_stage1.zig will be deleted. Until then we have a
16 // simpler implementation of c.zig that only uses features already implemented in self-hosted.
17 if (builtin.zig_backend == .stage1) {
18 _ = @import("c_stage1.zig");
31 if (is_freestanding and is_wasm and builtin.link_libc) {
32 @export(wasm_start, .{ .name = "_start", .linkage = .Strong });
33 }
34
35 if (builtin.zig_backend == .stage1) { // TODO remove this condition
36 if (native_os == .linux) {
37 @export(clone, .{ .name = "clone" });
38 }
1939 }
2040
2141 @export(memset, .{ .name = "memset", .linkage = .Strong });
2242 @export(__memset, .{ .name = "__memset", .linkage = .Strong });
2343 @export(memcpy, .{ .name = "memcpy", .linkage = .Strong });
44 @export(memmove, .{ .name = "memmove", .linkage = .Strong });
45 @export(memcmp, .{ .name = "memcmp", .linkage = .Strong });
46 @export(bcmp, .{ .name = "bcmp", .linkage = .Strong });
47
48 if (builtin.link_libc) {
49 @export(strcmp, .{ .name = "strcmp", .linkage = .Strong });
50 @export(strncmp, .{ .name = "strncmp", .linkage = .Strong });
51 @export(strerror, .{ .name = "strerror", .linkage = .Strong });
52 @export(strlen, .{ .name = "strlen", .linkage = .Strong });
53 @export(strcpy, .{ .name = "strcpy", .linkage = .Strong });
54 @export(strncpy, .{ .name = "strncpy", .linkage = .Strong });
55 @export(strcat, .{ .name = "strcat", .linkage = .Strong });
56 @export(strncat, .{ .name = "strncat", .linkage = .Strong });
57 } else if (is_msvc) {
58 @export(_fltused, .{ .name = "_fltused", .linkage = .Strong });
59 }
2460
2561 @export(trunc, .{ .name = "trunc", .linkage = .Strong });
2662 @export(truncf, .{ .name = "truncf", .linkage = .Strong });
......@@ -50,6 +86,28 @@ comptime {
5086 @export(ceil, .{ .name = "ceil", .linkage = .Strong });
5187 @export(ceilf, .{ .name = "ceilf", .linkage = .Strong });
5288 @export(ceill, .{ .name = "ceill", .linkage = .Strong });
89
90 @export(fmod, .{ .name = "fmod", .linkage = .Strong });
91 @export(fmodf, .{ .name = "fmodf", .linkage = .Strong });
92
93 @export(fma, .{ .name = "fma", .linkage = .Strong });
94 @export(fmaf, .{ .name = "fmaf", .linkage = .Strong });
95 @export(fmal, .{ .name = "fmal", .linkage = .Strong });
96
97 @export(sincos, .{ .name = "sincos", .linkage = .Strong });
98 @export(sincosf, .{ .name = "sincosf", .linkage = .Strong });
99
100 @export(fabs, .{ .name = "fabs", .linkage = .Strong });
101 @export(fabsf, .{ .name = "fabsf", .linkage = .Strong });
102
103 @export(round, .{ .name = "round", .linkage = .Strong });
104 @export(roundf, .{ .name = "roundf", .linkage = .Strong });
105
106 @export(fmin, .{ .name = "fmin", .linkage = .Strong });
107 @export(fminf, .{ .name = "fminf", .linkage = .Strong });
108
109 @export(fmax, .{ .name = "fmax", .linkage = .Strong });
110 @export(fmaxf, .{ .name = "fmaxf", .linkage = .Strong });
53111}
54112
55113// Avoid dragging in the runtime safety mechanisms into this .o file,
......@@ -57,11 +115,6 @@ comptime {
57115pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn {
58116 @setCold(true);
59117 _ = error_return_trace;
60 if (builtin.zig_backend != .stage1) {
61 while (true) {
62 @breakpoint();
63 }
64 }
65118 if (builtin.is_test) {
66119 std.debug.panic("{s}", .{msg});
67120 }
......@@ -71,6 +124,11 @@ pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) nore
71124 while (true) {}
72125}
73126
127extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
128fn wasm_start() callconv(.C) void {
129 _ = main(0, undefined);
130}
131
74132fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.C) ?[*]u8 {
75133 @setRuntimeSafety(false);
76134
......@@ -113,6 +171,193 @@ fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, len: usize) callconv(
113171 return dest;
114172}
115173
174fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 {
175 @setRuntimeSafety(false);
176
177 if (@ptrToInt(dest) < @ptrToInt(src)) {
178 var index: usize = 0;
179 while (index != n) : (index += 1) {
180 dest.?[index] = src.?[index];
181 }
182 } else {
183 var index = n;
184 while (index != 0) {
185 index -= 1;
186 dest.?[index] = src.?[index];
187 }
188 }
189
190 return dest;
191}
192
193fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) c_int {
194 @setRuntimeSafety(false);
195
196 var index: usize = 0;
197 while (index != n) : (index += 1) {
198 const compare_val = @bitCast(i8, vl.?[index] -% vr.?[index]);
199 if (compare_val != 0) {
200 return compare_val;
201 }
202 }
203
204 return 0;
205}
206
207test "memcmp" {
208 const base_arr = &[_]u8{ 1, 1, 1 };
209 const arr1 = &[_]u8{ 1, 1, 1 };
210 const arr2 = &[_]u8{ 1, 0, 1 };
211 const arr3 = &[_]u8{ 1, 2, 1 };
212
213 try std.testing.expect(memcmp(base_arr[0..], arr1[0..], base_arr.len) == 0);
214 try std.testing.expect(memcmp(base_arr[0..], arr2[0..], base_arr.len) > 0);
215 try std.testing.expect(memcmp(base_arr[0..], arr3[0..], base_arr.len) < 0);
216}
217
218fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) callconv(.C) c_int {
219 @setRuntimeSafety(false);
220
221 var index: usize = 0;
222 while (index != n) : (index += 1) {
223 if (vl[index] != vr[index]) {
224 return 1;
225 }
226 }
227
228 return 0;
229}
230
231test "bcmp" {
232 const base_arr = &[_]u8{ 1, 1, 1 };
233 const arr1 = &[_]u8{ 1, 1, 1 };
234 const arr2 = &[_]u8{ 1, 0, 1 };
235 const arr3 = &[_]u8{ 1, 2, 1 };
236
237 try std.testing.expect(bcmp(base_arr[0..], arr1[0..], base_arr.len) == 0);
238 try std.testing.expect(bcmp(base_arr[0..], arr2[0..], base_arr.len) != 0);
239 try std.testing.expect(bcmp(base_arr[0..], arr3[0..], base_arr.len) != 0);
240}
241
242var _fltused: c_int = 1;
243
244fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 {
245 var i: usize = 0;
246 while (src[i] != 0) : (i += 1) {
247 dest[i] = src[i];
248 }
249 dest[i] = 0;
250
251 return dest;
252}
253
254test "strcpy" {
255 var s1: [9:0]u8 = undefined;
256
257 s1[0] = 0;
258 _ = strcpy(&s1, "foobarbaz");
259 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
260}
261
262fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.C) [*:0]u8 {
263 var i: usize = 0;
264 while (i < n and src[i] != 0) : (i += 1) {
265 dest[i] = src[i];
266 }
267 while (i < n) : (i += 1) {
268 dest[i] = 0;
269 }
270
271 return dest;
272}
273
274test "strncpy" {
275 var s1: [9:0]u8 = undefined;
276
277 s1[0] = 0;
278 _ = strncpy(&s1, "foobarbaz", @sizeOf(@TypeOf(s1)));
279 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
280}
281
282fn strcat(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 {
283 var dest_end: usize = 0;
284 while (dest[dest_end] != 0) : (dest_end += 1) {}
285
286 var i: usize = 0;
287 while (src[i] != 0) : (i += 1) {
288 dest[dest_end + i] = src[i];
289 }
290 dest[dest_end + i] = 0;
291
292 return dest;
293}
294
295test "strcat" {
296 var s1: [9:0]u8 = undefined;
297
298 s1[0] = 0;
299 _ = strcat(&s1, "foo");
300 _ = strcat(&s1, "bar");
301 _ = strcat(&s1, "baz");
302 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
303}
304
305fn strncat(dest: [*:0]u8, src: [*:0]const u8, avail: usize) callconv(.C) [*:0]u8 {
306 var dest_end: usize = 0;
307 while (dest[dest_end] != 0) : (dest_end += 1) {}
308
309 var i: usize = 0;
310 while (i < avail and src[i] != 0) : (i += 1) {
311 dest[dest_end + i] = src[i];
312 }
313 dest[dest_end + i] = 0;
314
315 return dest;
316}
317
318test "strncat" {
319 var s1: [9:0]u8 = undefined;
320
321 s1[0] = 0;
322 _ = strncat(&s1, "foo1111", 3);
323 _ = strncat(&s1, "bar1111", 3);
324 _ = strncat(&s1, "baz1111", 3);
325 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
326}
327
328fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int {
329 return std.cstr.cmp(s1, s2);
330}
331
332fn strlen(s: [*:0]const u8) callconv(.C) usize {
333 return std.mem.len(s);
334}
335
336fn strncmp(_l: [*:0]const u8, _r: [*:0]const u8, _n: usize) callconv(.C) c_int {
337 if (_n == 0) return 0;
338 var l = _l;
339 var r = _r;
340 var n = _n - 1;
341 while (l[0] != 0 and r[0] != 0 and n != 0 and l[0] == r[0]) {
342 l += 1;
343 r += 1;
344 n -= 1;
345 }
346 return @as(c_int, l[0]) - @as(c_int, r[0]);
347}
348
349fn strerror(errnum: c_int) callconv(.C) [*:0]const u8 {
350 _ = errnum;
351 return "TODO strerror implementation";
352}
353
354test "strncmp" {
355 try std.testing.expect(strncmp("a", "b", 1) == -1);
356 try std.testing.expect(strncmp("a", "c", 1) == -2);
357 try std.testing.expect(strncmp("b", "a", 1) == 1);
358 try std.testing.expect(strncmp("\xff", "\x02", 1) == 253);
359}
360
116361fn trunc(a: f64) callconv(.C) f64 {
117362 return math.trunc(a);
118363}
......@@ -198,3 +443,851 @@ fn ceill(x: c_longdouble) callconv(.C) c_longdouble {
198443 }
199444 return math.ceil(x);
200445}
446
447fn fmodf(x: f32, y: f32) callconv(.C) f32 {
448 return generic_fmod(f32, x, y);
449}
450fn fmod(x: f64, y: f64) callconv(.C) f64 {
451 return generic_fmod(f64, x, y);
452}
453
454fn generic_fmod(comptime T: type, x: T, y: T) T {
455 @setRuntimeSafety(false);
456
457 const bits = @typeInfo(T).Float.bits;
458 const uint = std.meta.Int(.unsigned, bits);
459 const log2uint = math.Log2Int(uint);
460 const digits = if (T == f32) 23 else 52;
461 const exp_bits = if (T == f32) 9 else 12;
462 const bits_minus_1 = bits - 1;
463 const mask = if (T == f32) 0xff else 0x7ff;
464 var ux = @bitCast(uint, x);
465 var uy = @bitCast(uint, y);
466 var ex = @intCast(i32, (ux >> digits) & mask);
467 var ey = @intCast(i32, (uy >> digits) & mask);
468 const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1);
469 var i: uint = undefined;
470
471 if (uy << 1 == 0 or isNan(@bitCast(T, uy)) or ex == mask)
472 return (x * y) / (x * y);
473
474 if (ux << 1 <= uy << 1) {
475 if (ux << 1 == uy << 1)
476 return 0 * x;
477 return x;
478 }
479
480 // normalize x and y
481 if (ex == 0) {
482 i = ux << exp_bits;
483 while (i >> bits_minus_1 == 0) : ({
484 ex -= 1;
485 i <<= 1;
486 }) {}
487 ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1));
488 } else {
489 ux &= maxInt(uint) >> exp_bits;
490 ux |= 1 << digits;
491 }
492 if (ey == 0) {
493 i = uy << exp_bits;
494 while (i >> bits_minus_1 == 0) : ({
495 ey -= 1;
496 i <<= 1;
497 }) {}
498 uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1));
499 } else {
500 uy &= maxInt(uint) >> exp_bits;
501 uy |= 1 << digits;
502 }
503
504 // x mod y
505 while (ex > ey) : (ex -= 1) {
506 i = ux -% uy;
507 if (i >> bits_minus_1 == 0) {
508 if (i == 0)
509 return 0 * x;
510 ux = i;
511 }
512 ux <<= 1;
513 }
514 i = ux -% uy;
515 if (i >> bits_minus_1 == 0) {
516 if (i == 0)
517 return 0 * x;
518 ux = i;
519 }
520 while (ux >> digits == 0) : ({
521 ux <<= 1;
522 ex -= 1;
523 }) {}
524
525 // scale result up
526 if (ex > 0) {
527 ux -%= 1 << digits;
528 ux |= @as(uint, @bitCast(u32, ex)) << digits;
529 } else {
530 ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1));
531 }
532 if (T == f32) {
533 ux |= sx;
534 } else {
535 ux |= @intCast(uint, sx) << bits_minus_1;
536 }
537 return @bitCast(T, ux);
538}
539
540test "fmod, fmodf" {
541 inline for ([_]type{ f32, f64 }) |T| {
542 const nan_val = math.nan(T);
543 const inf_val = math.inf(T);
544
545 try std.testing.expect(isNan(generic_fmod(T, nan_val, 1.0)));
546 try std.testing.expect(isNan(generic_fmod(T, 1.0, nan_val)));
547 try std.testing.expect(isNan(generic_fmod(T, inf_val, 1.0)));
548 try std.testing.expect(isNan(generic_fmod(T, 0.0, 0.0)));
549 try std.testing.expect(isNan(generic_fmod(T, 1.0, 0.0)));
550
551 try std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0));
552 try std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0));
553
554 try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0));
555 try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0));
556 try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0));
557 try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0));
558 }
559}
560
561fn fmaf(a: f32, b: f32, c: f32) callconv(.C) f32 {
562 return math.fma(f32, a, b, c);
563}
564
565fn fma(a: f64, b: f64, c: f64) callconv(.C) f64 {
566 return math.fma(f64, a, b, c);
567}
568fn fmal(a: c_longdouble, b: c_longdouble, c: c_longdouble) callconv(.C) c_longdouble {
569 if (!long_double_is_f128) {
570 @panic("TODO implement this");
571 }
572 return math.fma(c_longdouble, a, b, c);
573}
574
575fn sincos(a: f64, r_sin: *f64, r_cos: *f64) callconv(.C) void {
576 r_sin.* = math.sin(a);
577 r_cos.* = math.cos(a);
578}
579
580fn sincosf(a: f32, r_sin: *f32, r_cos: *f32) callconv(.C) void {
581 r_sin.* = math.sin(a);
582 r_cos.* = math.cos(a);
583}
584
585fn fabs(a: f64) callconv(.C) f64 {
586 return math.fabs(a);
587}
588
589fn fabsf(a: f32) callconv(.C) f32 {
590 return math.fabs(a);
591}
592
593fn round(a: f64) callconv(.C) f64 {
594 return math.round(a);
595}
596
597fn roundf(a: f32) callconv(.C) f32 {
598 return math.round(a);
599}
600
601fn fminf(x: f32, y: f32) callconv(.C) f32 {
602 return generic_fmin(f32, x, y);
603}
604
605fn fmin(x: f64, y: f64) callconv(.C) f64 {
606 return generic_fmin(f64, x, y);
607}
608
609fn generic_fmin(comptime T: type, x: T, y: T) T {
610 if (isNan(x))
611 return y;
612 if (isNan(y))
613 return x;
614 return if (x < y) x else y;
615}
616
617test "fmin, fminf" {
618 inline for ([_]type{ f32, f64 }) |T| {
619 const nan_val = math.nan(T);
620
621 try std.testing.expect(isNan(generic_fmin(T, nan_val, nan_val)));
622 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, nan_val, 1.0));
623 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, nan_val));
624
625 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, 10.0));
626 try std.testing.expectEqual(@as(T, -1.0), generic_fmin(T, 1.0, -1.0));
627 }
628}
629
630fn fmaxf(x: f32, y: f32) callconv(.C) f32 {
631 return generic_fmax(f32, x, y);
632}
633
634fn fmax(x: f64, y: f64) callconv(.C) f64 {
635 return generic_fmax(f64, x, y);
636}
637
638fn generic_fmax(comptime T: type, x: T, y: T) T {
639 if (isNan(x))
640 return y;
641 if (isNan(y))
642 return x;
643 return if (x < y) y else x;
644}
645
646test "fmax, fmaxf" {
647 inline for ([_]type{ f32, f64 }) |T| {
648 const nan_val = math.nan(T);
649
650 try std.testing.expect(isNan(generic_fmax(T, nan_val, nan_val)));
651 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, nan_val, 1.0));
652 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, nan_val));
653
654 try std.testing.expectEqual(@as(T, 10.0), generic_fmax(T, 1.0, 10.0));
655 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, -1.0));
656 }
657}
658
659// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
660// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
661// potentially some edge cases remaining that are not handled in the same way.
662fn sqrt(x: f64) callconv(.C) f64 {
663 const tiny: f64 = 1.0e-300;
664 const sign: u32 = 0x80000000;
665 const u = @bitCast(u64, x);
666
667 var ix0 = @intCast(u32, u >> 32);
668 var ix1 = @intCast(u32, u & 0xFFFFFFFF);
669
670 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
671 if (ix0 & 0x7FF00000 == 0x7FF00000) {
672 return x * x + x;
673 }
674
675 // sqrt(+-0) = +-0
676 if (x == 0.0) {
677 return x;
678 }
679 // sqrt(-ve) = snan
680 if (ix0 & sign != 0) {
681 return math.snan(f64);
682 }
683
684 // normalize x
685 var m = @intCast(i32, ix0 >> 20);
686 if (m == 0) {
687 // subnormal
688 while (ix0 == 0) {
689 m -= 21;
690 ix0 |= ix1 >> 11;
691 ix1 <<= 21;
692 }
693
694 // subnormal
695 var i: u32 = 0;
696 while (ix0 & 0x00100000 == 0) : (i += 1) {
697 ix0 <<= 1;
698 }
699 m -= @intCast(i32, i) - 1;
700 ix0 |= ix1 >> @intCast(u5, 32 - i);
701 ix1 <<= @intCast(u5, i);
702 }
703
704 // unbias exponent
705 m -= 1023;
706 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
707 if (m & 1 != 0) {
708 ix0 += ix0 + (ix1 >> 31);
709 ix1 = ix1 +% ix1;
710 }
711 m >>= 1;
712
713 // sqrt(x) bit by bit
714 ix0 += ix0 + (ix1 >> 31);
715 ix1 = ix1 +% ix1;
716
717 var q: u32 = 0;
718 var q1: u32 = 0;
719 var s0: u32 = 0;
720 var s1: u32 = 0;
721 var r: u32 = 0x00200000;
722 var t: u32 = undefined;
723 var t1: u32 = undefined;
724
725 while (r != 0) {
726 t = s0 +% r;
727 if (t <= ix0) {
728 s0 = t + r;
729 ix0 -= t;
730 q += r;
731 }
732 ix0 = ix0 +% ix0 +% (ix1 >> 31);
733 ix1 = ix1 +% ix1;
734 r >>= 1;
735 }
736
737 r = sign;
738 while (r != 0) {
739 t1 = s1 +% r;
740 t = s0;
741 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
742 s1 = t1 +% r;
743 if (t1 & sign == sign and s1 & sign == 0) {
744 s0 += 1;
745 }
746 ix0 -= t;
747 if (ix1 < t1) {
748 ix0 -= 1;
749 }
750 ix1 = ix1 -% t1;
751 q1 += r;
752 }
753 ix0 = ix0 +% ix0 +% (ix1 >> 31);
754 ix1 = ix1 +% ix1;
755 r >>= 1;
756 }
757
758 // rounding direction
759 if (ix0 | ix1 != 0) {
760 var z = 1.0 - tiny; // raise inexact
761 if (z >= 1.0) {
762 z = 1.0 + tiny;
763 if (q1 == 0xFFFFFFFF) {
764 q1 = 0;
765 q += 1;
766 } else if (z > 1.0) {
767 if (q1 == 0xFFFFFFFE) {
768 q += 1;
769 }
770 q1 += 2;
771 } else {
772 q1 += q1 & 1;
773 }
774 }
775 }
776
777 ix0 = (q >> 1) + 0x3FE00000;
778 ix1 = q1 >> 1;
779 if (q & 1 != 0) {
780 ix1 |= 0x80000000;
781 }
782
783 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
784 // behaviour at least.
785 var iix0 = @intCast(i32, ix0);
786 iix0 = iix0 +% (m << 20);
787
788 const uz = (@intCast(u64, iix0) << 32) | ix1;
789 return @bitCast(f64, uz);
790}
791
792test "sqrt" {
793 const V = [_]f64{
794 0.0,
795 4.089288054930154,
796 7.538757127071935,
797 8.97780793672623,
798 5.304443821913729,
799 5.682408965311888,
800 0.5846878579110049,
801 3.650338664297043,
802 0.3178091951800732,
803 7.1505232436382835,
804 3.6589165881946464,
805 };
806
807 // Note that @sqrt will either generate the sqrt opcode (if supported by the
808 // target ISA) or a call to `sqrtf` otherwise.
809 for (V) |val|
810 try std.testing.expectEqual(@sqrt(val), sqrt(val));
811}
812
813test "sqrt special" {
814 try std.testing.expect(std.math.isPositiveInf(sqrt(std.math.inf(f64))));
815 try std.testing.expect(sqrt(0.0) == 0.0);
816 try std.testing.expect(sqrt(-0.0) == -0.0);
817 try std.testing.expect(isNan(sqrt(-1.0)));
818 try std.testing.expect(isNan(sqrt(std.math.nan(f64))));
819}
820
821fn sqrtf(x: f32) callconv(.C) f32 {
822 const tiny: f32 = 1.0e-30;
823 const sign: i32 = @bitCast(i32, @as(u32, 0x80000000));
824 var ix: i32 = @bitCast(i32, x);
825
826 if ((ix & 0x7F800000) == 0x7F800000) {
827 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
828 }
829
830 // zero
831 if (ix <= 0) {
832 if (ix & ~sign == 0) {
833 return x; // sqrt (+-0) = +-0
834 }
835 if (ix < 0) {
836 return math.snan(f32);
837 }
838 }
839
840 // normalize
841 var m = ix >> 23;
842 if (m == 0) {
843 // subnormal
844 var i: i32 = 0;
845 while (ix & 0x00800000 == 0) : (i += 1) {
846 ix <<= 1;
847 }
848 m -= i - 1;
849 }
850
851 m -= 127; // unbias exponent
852 ix = (ix & 0x007FFFFF) | 0x00800000;
853
854 if (m & 1 != 0) { // odd m, double x to even
855 ix += ix;
856 }
857
858 m >>= 1; // m = [m / 2]
859
860 // sqrt(x) bit by bit
861 ix += ix;
862 var q: i32 = 0; // q = sqrt(x)
863 var s: i32 = 0;
864 var r: i32 = 0x01000000; // r = moving bit right -> left
865
866 while (r != 0) {
867 const t = s + r;
868 if (t <= ix) {
869 s = t + r;
870 ix -= t;
871 q += r;
872 }
873 ix += ix;
874 r >>= 1;
875 }
876
877 // floating add to find rounding direction
878 if (ix != 0) {
879 var z = 1.0 - tiny; // inexact
880 if (z >= 1.0) {
881 z = 1.0 + tiny;
882 if (z > 1.0) {
883 q += 2;
884 } else {
885 if (q & 1 != 0) {
886 q += 1;
887 }
888 }
889 }
890 }
891
892 ix = (q >> 1) + 0x3f000000;
893 ix += m << 23;
894 return @bitCast(f32, ix);
895}
896
897test "sqrtf" {
898 const V = [_]f32{
899 0.0,
900 4.089288054930154,
901 7.538757127071935,
902 8.97780793672623,
903 5.304443821913729,
904 5.682408965311888,
905 0.5846878579110049,
906 3.650338664297043,
907 0.3178091951800732,
908 7.1505232436382835,
909 3.6589165881946464,
910 };
911
912 // Note that @sqrt will either generate the sqrt opcode (if supported by the
913 // target ISA) or a call to `sqrtf` otherwise.
914 for (V) |val|
915 try std.testing.expectEqual(@sqrt(val), sqrtf(val));
916}
917
918test "sqrtf special" {
919 try std.testing.expect(std.math.isPositiveInf(sqrtf(std.math.inf(f32))));
920 try std.testing.expect(sqrtf(0.0) == 0.0);
921 try std.testing.expect(sqrtf(-0.0) == -0.0);
922 try std.testing.expect(isNan(sqrtf(-1.0)));
923 try std.testing.expect(isNan(sqrtf(std.math.nan(f32))));
924}
925
926// TODO we should be able to put this directly in std/linux/x86_64.zig but
927// it causes a segfault in release mode. this is a workaround of calling it
928// across .o file boundaries. fix comptime @ptrCast of nakedcc functions.
929fn clone() callconv(.Naked) void {
930 switch (native_arch) {
931 .i386 => {
932 // __clone(func, stack, flags, arg, ptid, tls, ctid)
933 // +8, +12, +16, +20, +24, +28, +32
934 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
935 // eax, ebx, ecx, edx, esi, edi
936 asm volatile (
937 \\ push %%ebp
938 \\ mov %%esp,%%ebp
939 \\ push %%ebx
940 \\ push %%esi
941 \\ push %%edi
942 \\ // Setup the arguments
943 \\ mov 16(%%ebp),%%ebx
944 \\ mov 12(%%ebp),%%ecx
945 \\ and $-16,%%ecx
946 \\ sub $20,%%ecx
947 \\ mov 20(%%ebp),%%eax
948 \\ mov %%eax,4(%%ecx)
949 \\ mov 8(%%ebp),%%eax
950 \\ mov %%eax,0(%%ecx)
951 \\ mov 24(%%ebp),%%edx
952 \\ mov 28(%%ebp),%%esi
953 \\ mov 32(%%ebp),%%edi
954 \\ mov $120,%%eax
955 \\ int $128
956 \\ test %%eax,%%eax
957 \\ jnz 1f
958 \\ pop %%eax
959 \\ xor %%ebp,%%ebp
960 \\ call *%%eax
961 \\ mov %%eax,%%ebx
962 \\ xor %%eax,%%eax
963 \\ inc %%eax
964 \\ int $128
965 \\ hlt
966 \\1:
967 \\ pop %%edi
968 \\ pop %%esi
969 \\ pop %%ebx
970 \\ pop %%ebp
971 \\ ret
972 );
973 },
974 .x86_64 => {
975 asm volatile (
976 \\ xor %%eax,%%eax
977 \\ mov $56,%%al // SYS_clone
978 \\ mov %%rdi,%%r11
979 \\ mov %%rdx,%%rdi
980 \\ mov %%r8,%%rdx
981 \\ mov %%r9,%%r8
982 \\ mov 8(%%rsp),%%r10
983 \\ mov %%r11,%%r9
984 \\ and $-16,%%rsi
985 \\ sub $8,%%rsi
986 \\ mov %%rcx,(%%rsi)
987 \\ syscall
988 \\ test %%eax,%%eax
989 \\ jnz 1f
990 \\ xor %%ebp,%%ebp
991 \\ pop %%rdi
992 \\ call *%%r9
993 \\ mov %%eax,%%edi
994 \\ xor %%eax,%%eax
995 \\ mov $60,%%al // SYS_exit
996 \\ syscall
997 \\ hlt
998 \\1: ret
999 \\
1000 );
1001 },
1002 .aarch64 => {
1003 // __clone(func, stack, flags, arg, ptid, tls, ctid)
1004 // x0, x1, w2, x3, x4, x5, x6
1005
1006 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
1007 // x8, x0, x1, x2, x3, x4
1008 asm volatile (
1009 \\ // align stack and save func,arg
1010 \\ and x1,x1,#-16
1011 \\ stp x0,x3,[x1,#-16]!
1012 \\
1013 \\ // syscall
1014 \\ uxtw x0,w2
1015 \\ mov x2,x4
1016 \\ mov x3,x5
1017 \\ mov x4,x6
1018 \\ mov x8,#220 // SYS_clone
1019 \\ svc #0
1020 \\
1021 \\ cbz x0,1f
1022 \\ // parent
1023 \\ ret
1024 \\ // child
1025 \\1: ldp x1,x0,[sp],#16
1026 \\ blr x1
1027 \\ mov x8,#93 // SYS_exit
1028 \\ svc #0
1029 );
1030 },
1031 .arm, .thumb => {
1032 // __clone(func, stack, flags, arg, ptid, tls, ctid)
1033 // r0, r1, r2, r3, +0, +4, +8
1034
1035 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
1036 // r7 r0, r1, r2, r3, r4
1037 asm volatile (
1038 \\ stmfd sp!,{r4,r5,r6,r7}
1039 \\ mov r7,#120
1040 \\ mov r6,r3
1041 \\ mov r5,r0
1042 \\ mov r0,r2
1043 \\ and r1,r1,#-16
1044 \\ ldr r2,[sp,#16]
1045 \\ ldr r3,[sp,#20]
1046 \\ ldr r4,[sp,#24]
1047 \\ svc 0
1048 \\ tst r0,r0
1049 \\ beq 1f
1050 \\ ldmfd sp!,{r4,r5,r6,r7}
1051 \\ bx lr
1052 \\
1053 \\1: mov r0,r6
1054 \\ bl 3f
1055 \\2: mov r7,#1
1056 \\ svc 0
1057 \\ b 2b
1058 \\3: bx r5
1059 );
1060 },
1061 .riscv64 => {
1062 // __clone(func, stack, flags, arg, ptid, tls, ctid)
1063 // a0, a1, a2, a3, a4, a5, a6
1064
1065 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
1066 // a7 a0, a1, a2, a3, a4
1067 asm volatile (
1068 \\ # Save func and arg to stack
1069 \\ addi a1, a1, -16
1070 \\ sd a0, 0(a1)
1071 \\ sd a3, 8(a1)
1072 \\
1073 \\ # Call SYS_clone
1074 \\ mv a0, a2
1075 \\ mv a2, a4
1076 \\ mv a3, a5
1077 \\ mv a4, a6
1078 \\ li a7, 220 # SYS_clone
1079 \\ ecall
1080 \\
1081 \\ beqz a0, 1f
1082 \\ # Parent
1083 \\ ret
1084 \\
1085 \\ # Child
1086 \\1: ld a1, 0(sp)
1087 \\ ld a0, 8(sp)
1088 \\ jalr a1
1089 \\
1090 \\ # Exit
1091 \\ li a7, 93 # SYS_exit
1092 \\ ecall
1093 );
1094 },
1095 .mips, .mipsel => {
1096 // __clone(func, stack, flags, arg, ptid, tls, ctid)
1097 // 3, 4, 5, 6, 7, 8, 9
1098
1099 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
1100 // 2 4, 5, 6, 7, 8
1101 asm volatile (
1102 \\ # Save function pointer and argument pointer on new thread stack
1103 \\ and $5, $5, -8
1104 \\ subu $5, $5, 16
1105 \\ sw $4, 0($5)
1106 \\ sw $7, 4($5)
1107 \\ # Shuffle (fn,sp,fl,arg,ptid,tls,ctid) to (fl,sp,ptid,tls,ctid)
1108 \\ move $4, $6
1109 \\ lw $6, 16($sp)
1110 \\ lw $7, 20($sp)
1111 \\ lw $9, 24($sp)
1112 \\ subu $sp, $sp, 16
1113 \\ sw $9, 16($sp)
1114 \\ li $2, 4120
1115 \\ syscall
1116 \\ beq $7, $0, 1f
1117 \\ nop
1118 \\ addu $sp, $sp, 16
1119 \\ jr $ra
1120 \\ subu $2, $0, $2
1121 \\1:
1122 \\ beq $2, $0, 1f
1123 \\ nop
1124 \\ addu $sp, $sp, 16
1125 \\ jr $ra
1126 \\ nop
1127 \\1:
1128 \\ lw $25, 0($sp)
1129 \\ lw $4, 4($sp)
1130 \\ jalr $25
1131 \\ nop
1132 \\ move $4, $2
1133 \\ li $2, 4001
1134 \\ syscall
1135 );
1136 },
1137 .powerpc => {
1138 // __clone(func, stack, flags, arg, ptid, tls, ctid)
1139 // 3, 4, 5, 6, 7, 8, 9
1140
1141 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
1142 // 0 3, 4, 5, 6, 7
1143 asm volatile (
1144 \\# store non-volatile regs r30, r31 on stack in order to put our
1145 \\# start func and its arg there
1146 \\stwu 30, -16(1)
1147 \\stw 31, 4(1)
1148 \\
1149 \\# save r3 (func) into r30, and r6(arg) into r31
1150 \\mr 30, 3
1151 \\mr 31, 6
1152 \\
1153 \\# create initial stack frame for new thread
1154 \\clrrwi 4, 4, 4
1155 \\li 0, 0
1156 \\stwu 0, -16(4)
1157 \\
1158 \\#move c into first arg
1159 \\mr 3, 5
1160 \\#mr 4, 4
1161 \\mr 5, 7
1162 \\mr 6, 8
1163 \\mr 7, 9
1164 \\
1165 \\# move syscall number into r0
1166 \\li 0, 120
1167 \\
1168 \\sc
1169 \\
1170 \\# check for syscall error
1171 \\bns+ 1f # jump to label 1 if no summary overflow.
1172 \\#else
1173 \\neg 3, 3 #negate the result (errno)
1174 \\1:
1175 \\# compare sc result with 0
1176 \\cmpwi cr7, 3, 0
1177 \\
1178 \\# if not 0, jump to end
1179 \\bne cr7, 2f
1180 \\
1181 \\#else: we're the child
1182 \\#call funcptr: move arg (d) into r3
1183 \\mr 3, 31
1184 \\#move r30 (funcptr) into CTR reg
1185 \\mtctr 30
1186 \\# call CTR reg
1187 \\bctrl
1188 \\# mov SYS_exit into r0 (the exit param is already in r3)
1189 \\li 0, 1
1190 \\sc
1191 \\
1192 \\2:
1193 \\
1194 \\# restore stack
1195 \\lwz 30, 0(1)
1196 \\lwz 31, 4(1)
1197 \\addi 1, 1, 16
1198 \\
1199 \\blr
1200 );
1201 },
1202 .powerpc64, .powerpc64le => {
1203 // __clone(func, stack, flags, arg, ptid, tls, ctid)
1204 // 3, 4, 5, 6, 7, 8, 9
1205
1206 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
1207 // 0 3, 4, 5, 6, 7
1208 asm volatile (
1209 \\ # create initial stack frame for new thread
1210 \\ clrrdi 4, 4, 4
1211 \\ li 0, 0
1212 \\ stdu 0,-32(4)
1213 \\
1214 \\ # save fn and arg to child stack
1215 \\ std 3, 8(4)
1216 \\ std 6, 16(4)
1217 \\
1218 \\ # shuffle args into correct registers and call SYS_clone
1219 \\ mr 3, 5
1220 \\ #mr 4, 4
1221 \\ mr 5, 7
1222 \\ mr 6, 8
1223 \\ mr 7, 9
1224 \\ li 0, 120 # SYS_clone = 120
1225 \\ sc
1226 \\
1227 \\ # if error, negate return (errno)
1228 \\ bns+ 1f
1229 \\ neg 3, 3
1230 \\
1231 \\1:
1232 \\ # if we're the parent, return
1233 \\ cmpwi cr7, 3, 0
1234 \\ bnelr cr7
1235 \\
1236 \\ # we're the child. call fn(arg)
1237 \\ ld 3, 16(1)
1238 \\ ld 12, 8(1)
1239 \\ mtctr 12
1240 \\ bctrl
1241 \\
1242 \\ # call SYS_exit. exit code is already in r3 from fn return value
1243 \\ li 0, 1 # SYS_exit = 1
1244 \\ sc
1245 );
1246 },
1247 .sparcv9 => {
1248 // __clone(func, stack, flags, arg, ptid, tls, ctid)
1249 // i0, i1, i2, i3, i4, i5, sp
1250 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
1251 // g1 o0, o1, o2, o3, o4
1252 asm volatile (
1253 \\ save %%sp, -192, %%sp
1254 \\ # Save the func pointer and the arg pointer
1255 \\ mov %%i0, %%g2
1256 \\ mov %%i3, %%g3
1257 \\ # Shuffle the arguments
1258 \\ mov 217, %%g1
1259 \\ mov %%i2, %%o0
1260 \\ # Add some extra space for the initial frame
1261 \\ sub %%i1, 176 + 2047, %%o1
1262 \\ mov %%i4, %%o2
1263 \\ mov %%i5, %%o3
1264 \\ ldx [%%fp + 0x8af], %%o4
1265 \\ t 0x6d
1266 \\ bcs,pn %%xcc, 2f
1267 \\ nop
1268 \\ # The child pid is returned in o0 while o1 tells if this
1269 \\ # process is # the child (=1) or the parent (=0).
1270 \\ brnz %%o1, 1f
1271 \\ nop
1272 \\ # Parent process, return the child pid
1273 \\ mov %%o0, %%i0
1274 \\ ret
1275 \\ restore
1276 \\1:
1277 \\ # Child process, call func(arg)
1278 \\ mov %%g0, %%fp
1279 \\ call %%g2
1280 \\ mov %%g3, %%o0
1281 \\ # Exit
1282 \\ mov 1, %%g1
1283 \\ t 0x6d
1284 \\2:
1285 \\ # The syscall failed
1286 \\ sub %%g0, %%o0, %%i0
1287 \\ ret
1288 \\ restore
1289 );
1290 },
1291 else => @compileError("Implement clone() for this arch."),
1292 }
1293}
lib/std/special/c_stage1.zig deleted-1086
......@@ -1,1086 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const maxInt = std.math.maxInt;
4const isNan = std.math.isNan;
5const native_arch = builtin.cpu.arch;
6const native_abi = builtin.abi;
7const native_os = builtin.os.tag;
8const long_double_is_f128 = builtin.target.longDoubleIsF128();
9
10const is_wasm = switch (native_arch) {
11 .wasm32, .wasm64 => true,
12 else => false,
13};
14const is_msvc = switch (native_abi) {
15 .msvc => true,
16 else => false,
17};
18const is_freestanding = switch (native_os) {
19 .freestanding => true,
20 else => false,
21};
22comptime {
23 if (is_freestanding and is_wasm and builtin.link_libc) {
24 @export(wasm_start, .{ .name = "_start", .linkage = .Strong });
25 }
26 if (builtin.link_libc) {
27 @export(strcmp, .{ .name = "strcmp", .linkage = .Strong });
28 @export(strncmp, .{ .name = "strncmp", .linkage = .Strong });
29 @export(strerror, .{ .name = "strerror", .linkage = .Strong });
30 @export(strlen, .{ .name = "strlen", .linkage = .Strong });
31 @export(strcpy, .{ .name = "strcpy", .linkage = .Strong });
32 @export(strncpy, .{ .name = "strncpy", .linkage = .Strong });
33 @export(strcat, .{ .name = "strcat", .linkage = .Strong });
34 @export(strncat, .{ .name = "strncat", .linkage = .Strong });
35 } else if (is_msvc) {
36 @export(_fltused, .{ .name = "_fltused", .linkage = .Strong });
37 }
38}
39
40var _fltused: c_int = 1;
41
42extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
43fn wasm_start() callconv(.C) void {
44 _ = main(0, undefined);
45}
46
47fn strcpy(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 {
48 var i: usize = 0;
49 while (src[i] != 0) : (i += 1) {
50 dest[i] = src[i];
51 }
52 dest[i] = 0;
53
54 return dest;
55}
56
57test "strcpy" {
58 var s1: [9:0]u8 = undefined;
59
60 s1[0] = 0;
61 _ = strcpy(&s1, "foobarbaz");
62 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
63}
64
65fn strncpy(dest: [*:0]u8, src: [*:0]const u8, n: usize) callconv(.C) [*:0]u8 {
66 var i: usize = 0;
67 while (i < n and src[i] != 0) : (i += 1) {
68 dest[i] = src[i];
69 }
70 while (i < n) : (i += 1) {
71 dest[i] = 0;
72 }
73
74 return dest;
75}
76
77test "strncpy" {
78 var s1: [9:0]u8 = undefined;
79
80 s1[0] = 0;
81 _ = strncpy(&s1, "foobarbaz", @sizeOf(@TypeOf(s1)));
82 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
83}
84
85fn strcat(dest: [*:0]u8, src: [*:0]const u8) callconv(.C) [*:0]u8 {
86 var dest_end: usize = 0;
87 while (dest[dest_end] != 0) : (dest_end += 1) {}
88
89 var i: usize = 0;
90 while (src[i] != 0) : (i += 1) {
91 dest[dest_end + i] = src[i];
92 }
93 dest[dest_end + i] = 0;
94
95 return dest;
96}
97
98test "strcat" {
99 var s1: [9:0]u8 = undefined;
100
101 s1[0] = 0;
102 _ = strcat(&s1, "foo");
103 _ = strcat(&s1, "bar");
104 _ = strcat(&s1, "baz");
105 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
106}
107
108fn strncat(dest: [*:0]u8, src: [*:0]const u8, avail: usize) callconv(.C) [*:0]u8 {
109 var dest_end: usize = 0;
110 while (dest[dest_end] != 0) : (dest_end += 1) {}
111
112 var i: usize = 0;
113 while (i < avail and src[i] != 0) : (i += 1) {
114 dest[dest_end + i] = src[i];
115 }
116 dest[dest_end + i] = 0;
117
118 return dest;
119}
120
121test "strncat" {
122 var s1: [9:0]u8 = undefined;
123
124 s1[0] = 0;
125 _ = strncat(&s1, "foo1111", 3);
126 _ = strncat(&s1, "bar1111", 3);
127 _ = strncat(&s1, "baz1111", 3);
128 try std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.sliceTo(&s1, 0));
129}
130
131fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int {
132 return std.cstr.cmp(s1, s2);
133}
134
135fn strlen(s: [*:0]const u8) callconv(.C) usize {
136 return std.mem.len(s);
137}
138
139fn strncmp(_l: [*:0]const u8, _r: [*:0]const u8, _n: usize) callconv(.C) c_int {
140 if (_n == 0) return 0;
141 var l = _l;
142 var r = _r;
143 var n = _n - 1;
144 while (l[0] != 0 and r[0] != 0 and n != 0 and l[0] == r[0]) {
145 l += 1;
146 r += 1;
147 n -= 1;
148 }
149 return @as(c_int, l[0]) - @as(c_int, r[0]);
150}
151
152fn strerror(errnum: c_int) callconv(.C) [*:0]const u8 {
153 _ = errnum;
154 return "TODO strerror implementation";
155}
156
157test "strncmp" {
158 try std.testing.expect(strncmp("a", "b", 1) == -1);
159 try std.testing.expect(strncmp("a", "c", 1) == -2);
160 try std.testing.expect(strncmp("b", "a", 1) == 1);
161 try std.testing.expect(strncmp("\xff", "\x02", 1) == 253);
162}
163
164export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 {
165 @setRuntimeSafety(false);
166
167 if (@ptrToInt(dest) < @ptrToInt(src)) {
168 var index: usize = 0;
169 while (index != n) : (index += 1) {
170 dest.?[index] = src.?[index];
171 }
172 } else {
173 var index = n;
174 while (index != 0) {
175 index -= 1;
176 dest.?[index] = src.?[index];
177 }
178 }
179
180 return dest;
181}
182
183export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) c_int {
184 @setRuntimeSafety(false);
185
186 var index: usize = 0;
187 while (index != n) : (index += 1) {
188 const compare_val = @bitCast(i8, vl.?[index] -% vr.?[index]);
189 if (compare_val != 0) {
190 return compare_val;
191 }
192 }
193
194 return 0;
195}
196
197test "memcmp" {
198 const base_arr = &[_]u8{ 1, 1, 1 };
199 const arr1 = &[_]u8{ 1, 1, 1 };
200 const arr2 = &[_]u8{ 1, 0, 1 };
201 const arr3 = &[_]u8{ 1, 2, 1 };
202
203 try std.testing.expect(memcmp(base_arr[0..], arr1[0..], base_arr.len) == 0);
204 try std.testing.expect(memcmp(base_arr[0..], arr2[0..], base_arr.len) > 0);
205 try std.testing.expect(memcmp(base_arr[0..], arr3[0..], base_arr.len) < 0);
206}
207
208export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) callconv(.C) c_int {
209 @setRuntimeSafety(false);
210
211 var index: usize = 0;
212 while (index != n) : (index += 1) {
213 if (vl[index] != vr[index]) {
214 return 1;
215 }
216 }
217
218 return 0;
219}
220
221test "bcmp" {
222 const base_arr = &[_]u8{ 1, 1, 1 };
223 const arr1 = &[_]u8{ 1, 1, 1 };
224 const arr2 = &[_]u8{ 1, 0, 1 };
225 const arr3 = &[_]u8{ 1, 2, 1 };
226
227 try std.testing.expect(bcmp(base_arr[0..], arr1[0..], base_arr.len) == 0);
228 try std.testing.expect(bcmp(base_arr[0..], arr2[0..], base_arr.len) != 0);
229 try std.testing.expect(bcmp(base_arr[0..], arr3[0..], base_arr.len) != 0);
230}
231
232comptime {
233 if (native_os == .linux) {
234 @export(clone, .{ .name = "clone" });
235 }
236}
237
238// TODO we should be able to put this directly in std/linux/x86_64.zig but
239// it causes a segfault in release mode. this is a workaround of calling it
240// across .o file boundaries. fix comptime @ptrCast of nakedcc functions.
241fn clone() callconv(.Naked) void {
242 switch (native_arch) {
243 .i386 => {
244 // __clone(func, stack, flags, arg, ptid, tls, ctid)
245 // +8, +12, +16, +20, +24, +28, +32
246 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
247 // eax, ebx, ecx, edx, esi, edi
248 asm volatile (
249 \\ push %%ebp
250 \\ mov %%esp,%%ebp
251 \\ push %%ebx
252 \\ push %%esi
253 \\ push %%edi
254 \\ // Setup the arguments
255 \\ mov 16(%%ebp),%%ebx
256 \\ mov 12(%%ebp),%%ecx
257 \\ and $-16,%%ecx
258 \\ sub $20,%%ecx
259 \\ mov 20(%%ebp),%%eax
260 \\ mov %%eax,4(%%ecx)
261 \\ mov 8(%%ebp),%%eax
262 \\ mov %%eax,0(%%ecx)
263 \\ mov 24(%%ebp),%%edx
264 \\ mov 28(%%ebp),%%esi
265 \\ mov 32(%%ebp),%%edi
266 \\ mov $120,%%eax
267 \\ int $128
268 \\ test %%eax,%%eax
269 \\ jnz 1f
270 \\ pop %%eax
271 \\ xor %%ebp,%%ebp
272 \\ call *%%eax
273 \\ mov %%eax,%%ebx
274 \\ xor %%eax,%%eax
275 \\ inc %%eax
276 \\ int $128
277 \\ hlt
278 \\1:
279 \\ pop %%edi
280 \\ pop %%esi
281 \\ pop %%ebx
282 \\ pop %%ebp
283 \\ ret
284 );
285 },
286 .x86_64 => {
287 asm volatile (
288 \\ xor %%eax,%%eax
289 \\ mov $56,%%al // SYS_clone
290 \\ mov %%rdi,%%r11
291 \\ mov %%rdx,%%rdi
292 \\ mov %%r8,%%rdx
293 \\ mov %%r9,%%r8
294 \\ mov 8(%%rsp),%%r10
295 \\ mov %%r11,%%r9
296 \\ and $-16,%%rsi
297 \\ sub $8,%%rsi
298 \\ mov %%rcx,(%%rsi)
299 \\ syscall
300 \\ test %%eax,%%eax
301 \\ jnz 1f
302 \\ xor %%ebp,%%ebp
303 \\ pop %%rdi
304 \\ call *%%r9
305 \\ mov %%eax,%%edi
306 \\ xor %%eax,%%eax
307 \\ mov $60,%%al // SYS_exit
308 \\ syscall
309 \\ hlt
310 \\1: ret
311 \\
312 );
313 },
314 .aarch64 => {
315 // __clone(func, stack, flags, arg, ptid, tls, ctid)
316 // x0, x1, w2, x3, x4, x5, x6
317
318 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
319 // x8, x0, x1, x2, x3, x4
320 asm volatile (
321 \\ // align stack and save func,arg
322 \\ and x1,x1,#-16
323 \\ stp x0,x3,[x1,#-16]!
324 \\
325 \\ // syscall
326 \\ uxtw x0,w2
327 \\ mov x2,x4
328 \\ mov x3,x5
329 \\ mov x4,x6
330 \\ mov x8,#220 // SYS_clone
331 \\ svc #0
332 \\
333 \\ cbz x0,1f
334 \\ // parent
335 \\ ret
336 \\ // child
337 \\1: ldp x1,x0,[sp],#16
338 \\ blr x1
339 \\ mov x8,#93 // SYS_exit
340 \\ svc #0
341 );
342 },
343 .arm, .thumb => {
344 // __clone(func, stack, flags, arg, ptid, tls, ctid)
345 // r0, r1, r2, r3, +0, +4, +8
346
347 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
348 // r7 r0, r1, r2, r3, r4
349 asm volatile (
350 \\ stmfd sp!,{r4,r5,r6,r7}
351 \\ mov r7,#120
352 \\ mov r6,r3
353 \\ mov r5,r0
354 \\ mov r0,r2
355 \\ and r1,r1,#-16
356 \\ ldr r2,[sp,#16]
357 \\ ldr r3,[sp,#20]
358 \\ ldr r4,[sp,#24]
359 \\ svc 0
360 \\ tst r0,r0
361 \\ beq 1f
362 \\ ldmfd sp!,{r4,r5,r6,r7}
363 \\ bx lr
364 \\
365 \\1: mov r0,r6
366 \\ bl 3f
367 \\2: mov r7,#1
368 \\ svc 0
369 \\ b 2b
370 \\3: bx r5
371 );
372 },
373 .riscv64 => {
374 // __clone(func, stack, flags, arg, ptid, tls, ctid)
375 // a0, a1, a2, a3, a4, a5, a6
376
377 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
378 // a7 a0, a1, a2, a3, a4
379 asm volatile (
380 \\ # Save func and arg to stack
381 \\ addi a1, a1, -16
382 \\ sd a0, 0(a1)
383 \\ sd a3, 8(a1)
384 \\
385 \\ # Call SYS_clone
386 \\ mv a0, a2
387 \\ mv a2, a4
388 \\ mv a3, a5
389 \\ mv a4, a6
390 \\ li a7, 220 # SYS_clone
391 \\ ecall
392 \\
393 \\ beqz a0, 1f
394 \\ # Parent
395 \\ ret
396 \\
397 \\ # Child
398 \\1: ld a1, 0(sp)
399 \\ ld a0, 8(sp)
400 \\ jalr a1
401 \\
402 \\ # Exit
403 \\ li a7, 93 # SYS_exit
404 \\ ecall
405 );
406 },
407 .mips, .mipsel => {
408 // __clone(func, stack, flags, arg, ptid, tls, ctid)
409 // 3, 4, 5, 6, 7, 8, 9
410
411 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
412 // 2 4, 5, 6, 7, 8
413 asm volatile (
414 \\ # Save function pointer and argument pointer on new thread stack
415 \\ and $5, $5, -8
416 \\ subu $5, $5, 16
417 \\ sw $4, 0($5)
418 \\ sw $7, 4($5)
419 \\ # Shuffle (fn,sp,fl,arg,ptid,tls,ctid) to (fl,sp,ptid,tls,ctid)
420 \\ move $4, $6
421 \\ lw $6, 16($sp)
422 \\ lw $7, 20($sp)
423 \\ lw $9, 24($sp)
424 \\ subu $sp, $sp, 16
425 \\ sw $9, 16($sp)
426 \\ li $2, 4120
427 \\ syscall
428 \\ beq $7, $0, 1f
429 \\ nop
430 \\ addu $sp, $sp, 16
431 \\ jr $ra
432 \\ subu $2, $0, $2
433 \\1:
434 \\ beq $2, $0, 1f
435 \\ nop
436 \\ addu $sp, $sp, 16
437 \\ jr $ra
438 \\ nop
439 \\1:
440 \\ lw $25, 0($sp)
441 \\ lw $4, 4($sp)
442 \\ jalr $25
443 \\ nop
444 \\ move $4, $2
445 \\ li $2, 4001
446 \\ syscall
447 );
448 },
449 .powerpc => {
450 // __clone(func, stack, flags, arg, ptid, tls, ctid)
451 // 3, 4, 5, 6, 7, 8, 9
452
453 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
454 // 0 3, 4, 5, 6, 7
455 asm volatile (
456 \\# store non-volatile regs r30, r31 on stack in order to put our
457 \\# start func and its arg there
458 \\stwu 30, -16(1)
459 \\stw 31, 4(1)
460 \\
461 \\# save r3 (func) into r30, and r6(arg) into r31
462 \\mr 30, 3
463 \\mr 31, 6
464 \\
465 \\# create initial stack frame for new thread
466 \\clrrwi 4, 4, 4
467 \\li 0, 0
468 \\stwu 0, -16(4)
469 \\
470 \\#move c into first arg
471 \\mr 3, 5
472 \\#mr 4, 4
473 \\mr 5, 7
474 \\mr 6, 8
475 \\mr 7, 9
476 \\
477 \\# move syscall number into r0
478 \\li 0, 120
479 \\
480 \\sc
481 \\
482 \\# check for syscall error
483 \\bns+ 1f # jump to label 1 if no summary overflow.
484 \\#else
485 \\neg 3, 3 #negate the result (errno)
486 \\1:
487 \\# compare sc result with 0
488 \\cmpwi cr7, 3, 0
489 \\
490 \\# if not 0, jump to end
491 \\bne cr7, 2f
492 \\
493 \\#else: we're the child
494 \\#call funcptr: move arg (d) into r3
495 \\mr 3, 31
496 \\#move r30 (funcptr) into CTR reg
497 \\mtctr 30
498 \\# call CTR reg
499 \\bctrl
500 \\# mov SYS_exit into r0 (the exit param is already in r3)
501 \\li 0, 1
502 \\sc
503 \\
504 \\2:
505 \\
506 \\# restore stack
507 \\lwz 30, 0(1)
508 \\lwz 31, 4(1)
509 \\addi 1, 1, 16
510 \\
511 \\blr
512 );
513 },
514 .powerpc64, .powerpc64le => {
515 // __clone(func, stack, flags, arg, ptid, tls, ctid)
516 // 3, 4, 5, 6, 7, 8, 9
517
518 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
519 // 0 3, 4, 5, 6, 7
520 asm volatile (
521 \\ # create initial stack frame for new thread
522 \\ clrrdi 4, 4, 4
523 \\ li 0, 0
524 \\ stdu 0,-32(4)
525 \\
526 \\ # save fn and arg to child stack
527 \\ std 3, 8(4)
528 \\ std 6, 16(4)
529 \\
530 \\ # shuffle args into correct registers and call SYS_clone
531 \\ mr 3, 5
532 \\ #mr 4, 4
533 \\ mr 5, 7
534 \\ mr 6, 8
535 \\ mr 7, 9
536 \\ li 0, 120 # SYS_clone = 120
537 \\ sc
538 \\
539 \\ # if error, negate return (errno)
540 \\ bns+ 1f
541 \\ neg 3, 3
542 \\
543 \\1:
544 \\ # if we're the parent, return
545 \\ cmpwi cr7, 3, 0
546 \\ bnelr cr7
547 \\
548 \\ # we're the child. call fn(arg)
549 \\ ld 3, 16(1)
550 \\ ld 12, 8(1)
551 \\ mtctr 12
552 \\ bctrl
553 \\
554 \\ # call SYS_exit. exit code is already in r3 from fn return value
555 \\ li 0, 1 # SYS_exit = 1
556 \\ sc
557 );
558 },
559 .sparcv9 => {
560 // __clone(func, stack, flags, arg, ptid, tls, ctid)
561 // i0, i1, i2, i3, i4, i5, sp
562 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
563 // g1 o0, o1, o2, o3, o4
564 asm volatile (
565 \\ save %%sp, -192, %%sp
566 \\ # Save the func pointer and the arg pointer
567 \\ mov %%i0, %%g2
568 \\ mov %%i3, %%g3
569 \\ # Shuffle the arguments
570 \\ mov 217, %%g1
571 \\ mov %%i2, %%o0
572 \\ # Add some extra space for the initial frame
573 \\ sub %%i1, 176 + 2047, %%o1
574 \\ mov %%i4, %%o2
575 \\ mov %%i5, %%o3
576 \\ ldx [%%fp + 0x8af], %%o4
577 \\ t 0x6d
578 \\ bcs,pn %%xcc, 2f
579 \\ nop
580 \\ # The child pid is returned in o0 while o1 tells if this
581 \\ # process is # the child (=1) or the parent (=0).
582 \\ brnz %%o1, 1f
583 \\ nop
584 \\ # Parent process, return the child pid
585 \\ mov %%o0, %%i0
586 \\ ret
587 \\ restore
588 \\1:
589 \\ # Child process, call func(arg)
590 \\ mov %%g0, %%fp
591 \\ call %%g2
592 \\ mov %%g3, %%o0
593 \\ # Exit
594 \\ mov 1, %%g1
595 \\ t 0x6d
596 \\2:
597 \\ # The syscall failed
598 \\ sub %%g0, %%o0, %%i0
599 \\ ret
600 \\ restore
601 );
602 },
603 else => @compileError("Implement clone() for this arch."),
604 }
605}
606
607const math = std.math;
608
609export fn fmodf(x: f32, y: f32) f32 {
610 return generic_fmod(f32, x, y);
611}
612export fn fmod(x: f64, y: f64) f64 {
613 return generic_fmod(f64, x, y);
614}
615
616export fn fmaf(a: f32, b: f32, c: f32) f32 {
617 return math.fma(f32, a, b, c);
618}
619
620export fn fma(a: f64, b: f64, c: f64) f64 {
621 return math.fma(f64, a, b, c);
622}
623export fn fmal(a: c_longdouble, b: c_longdouble, c: c_longdouble) c_longdouble {
624 if (!long_double_is_f128) {
625 @panic("TODO implement this");
626 }
627 return math.fma(c_longdouble, a, b, c);
628}
629
630export fn sincos(a: f64, r_sin: *f64, r_cos: *f64) void {
631 r_sin.* = math.sin(a);
632 r_cos.* = math.cos(a);
633}
634
635export fn sincosf(a: f32, r_sin: *f32, r_cos: *f32) void {
636 r_sin.* = math.sin(a);
637 r_cos.* = math.cos(a);
638}
639
640export fn fabs(a: f64) f64 {
641 return math.fabs(a);
642}
643
644export fn fabsf(a: f32) f32 {
645 return math.fabs(a);
646}
647
648export fn round(a: f64) f64 {
649 return math.round(a);
650}
651
652export fn roundf(a: f32) f32 {
653 return math.round(a);
654}
655
656fn generic_fmod(comptime T: type, x: T, y: T) T {
657 @setRuntimeSafety(false);
658
659 const bits = @typeInfo(T).Float.bits;
660 const uint = std.meta.Int(.unsigned, bits);
661 const log2uint = math.Log2Int(uint);
662 const digits = if (T == f32) 23 else 52;
663 const exp_bits = if (T == f32) 9 else 12;
664 const bits_minus_1 = bits - 1;
665 const mask = if (T == f32) 0xff else 0x7ff;
666 var ux = @bitCast(uint, x);
667 var uy = @bitCast(uint, y);
668 var ex = @intCast(i32, (ux >> digits) & mask);
669 var ey = @intCast(i32, (uy >> digits) & mask);
670 const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1);
671 var i: uint = undefined;
672
673 if (uy << 1 == 0 or isNan(@bitCast(T, uy)) or ex == mask)
674 return (x * y) / (x * y);
675
676 if (ux << 1 <= uy << 1) {
677 if (ux << 1 == uy << 1)
678 return 0 * x;
679 return x;
680 }
681
682 // normalize x and y
683 if (ex == 0) {
684 i = ux << exp_bits;
685 while (i >> bits_minus_1 == 0) : ({
686 ex -= 1;
687 i <<= 1;
688 }) {}
689 ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1));
690 } else {
691 ux &= maxInt(uint) >> exp_bits;
692 ux |= 1 << digits;
693 }
694 if (ey == 0) {
695 i = uy << exp_bits;
696 while (i >> bits_minus_1 == 0) : ({
697 ey -= 1;
698 i <<= 1;
699 }) {}
700 uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1));
701 } else {
702 uy &= maxInt(uint) >> exp_bits;
703 uy |= 1 << digits;
704 }
705
706 // x mod y
707 while (ex > ey) : (ex -= 1) {
708 i = ux -% uy;
709 if (i >> bits_minus_1 == 0) {
710 if (i == 0)
711 return 0 * x;
712 ux = i;
713 }
714 ux <<= 1;
715 }
716 i = ux -% uy;
717 if (i >> bits_minus_1 == 0) {
718 if (i == 0)
719 return 0 * x;
720 ux = i;
721 }
722 while (ux >> digits == 0) : ({
723 ux <<= 1;
724 ex -= 1;
725 }) {}
726
727 // scale result up
728 if (ex > 0) {
729 ux -%= 1 << digits;
730 ux |= @as(uint, @bitCast(u32, ex)) << digits;
731 } else {
732 ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1));
733 }
734 if (T == f32) {
735 ux |= sx;
736 } else {
737 ux |= @intCast(uint, sx) << bits_minus_1;
738 }
739 return @bitCast(T, ux);
740}
741
742test "fmod, fmodf" {
743 inline for ([_]type{ f32, f64 }) |T| {
744 const nan_val = math.nan(T);
745 const inf_val = math.inf(T);
746
747 try std.testing.expect(isNan(generic_fmod(T, nan_val, 1.0)));
748 try std.testing.expect(isNan(generic_fmod(T, 1.0, nan_val)));
749 try std.testing.expect(isNan(generic_fmod(T, inf_val, 1.0)));
750 try std.testing.expect(isNan(generic_fmod(T, 0.0, 0.0)));
751 try std.testing.expect(isNan(generic_fmod(T, 1.0, 0.0)));
752
753 try std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0));
754 try std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0));
755
756 try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0));
757 try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0));
758 try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0));
759 try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0));
760 }
761}
762
763fn generic_fmin(comptime T: type, x: T, y: T) T {
764 if (isNan(x))
765 return y;
766 if (isNan(y))
767 return x;
768 return if (x < y) x else y;
769}
770
771export fn fminf(x: f32, y: f32) callconv(.C) f32 {
772 return generic_fmin(f32, x, y);
773}
774
775export fn fmin(x: f64, y: f64) callconv(.C) f64 {
776 return generic_fmin(f64, x, y);
777}
778
779test "fmin, fminf" {
780 inline for ([_]type{ f32, f64 }) |T| {
781 const nan_val = math.nan(T);
782
783 try std.testing.expect(isNan(generic_fmin(T, nan_val, nan_val)));
784 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, nan_val, 1.0));
785 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, nan_val));
786
787 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, 10.0));
788 try std.testing.expectEqual(@as(T, -1.0), generic_fmin(T, 1.0, -1.0));
789 }
790}
791
792fn generic_fmax(comptime T: type, x: T, y: T) T {
793 if (isNan(x))
794 return y;
795 if (isNan(y))
796 return x;
797 return if (x < y) y else x;
798}
799
800export fn fmaxf(x: f32, y: f32) callconv(.C) f32 {
801 return generic_fmax(f32, x, y);
802}
803
804export fn fmax(x: f64, y: f64) callconv(.C) f64 {
805 return generic_fmax(f64, x, y);
806}
807
808test "fmax, fmaxf" {
809 inline for ([_]type{ f32, f64 }) |T| {
810 const nan_val = math.nan(T);
811
812 try std.testing.expect(isNan(generic_fmax(T, nan_val, nan_val)));
813 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, nan_val, 1.0));
814 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, nan_val));
815
816 try std.testing.expectEqual(@as(T, 10.0), generic_fmax(T, 1.0, 10.0));
817 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, -1.0));
818 }
819}
820
821// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
822// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
823// potentially some edge cases remaining that are not handled in the same way.
824export fn sqrt(x: f64) f64 {
825 const tiny: f64 = 1.0e-300;
826 const sign: u32 = 0x80000000;
827 const u = @bitCast(u64, x);
828
829 var ix0 = @intCast(u32, u >> 32);
830 var ix1 = @intCast(u32, u & 0xFFFFFFFF);
831
832 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
833 if (ix0 & 0x7FF00000 == 0x7FF00000) {
834 return x * x + x;
835 }
836
837 // sqrt(+-0) = +-0
838 if (x == 0.0) {
839 return x;
840 }
841 // sqrt(-ve) = snan
842 if (ix0 & sign != 0) {
843 return math.snan(f64);
844 }
845
846 // normalize x
847 var m = @intCast(i32, ix0 >> 20);
848 if (m == 0) {
849 // subnormal
850 while (ix0 == 0) {
851 m -= 21;
852 ix0 |= ix1 >> 11;
853 ix1 <<= 21;
854 }
855
856 // subnormal
857 var i: u32 = 0;
858 while (ix0 & 0x00100000 == 0) : (i += 1) {
859 ix0 <<= 1;
860 }
861 m -= @intCast(i32, i) - 1;
862 ix0 |= ix1 >> @intCast(u5, 32 - i);
863 ix1 <<= @intCast(u5, i);
864 }
865
866 // unbias exponent
867 m -= 1023;
868 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
869 if (m & 1 != 0) {
870 ix0 += ix0 + (ix1 >> 31);
871 ix1 = ix1 +% ix1;
872 }
873 m >>= 1;
874
875 // sqrt(x) bit by bit
876 ix0 += ix0 + (ix1 >> 31);
877 ix1 = ix1 +% ix1;
878
879 var q: u32 = 0;
880 var q1: u32 = 0;
881 var s0: u32 = 0;
882 var s1: u32 = 0;
883 var r: u32 = 0x00200000;
884 var t: u32 = undefined;
885 var t1: u32 = undefined;
886
887 while (r != 0) {
888 t = s0 +% r;
889 if (t <= ix0) {
890 s0 = t + r;
891 ix0 -= t;
892 q += r;
893 }
894 ix0 = ix0 +% ix0 +% (ix1 >> 31);
895 ix1 = ix1 +% ix1;
896 r >>= 1;
897 }
898
899 r = sign;
900 while (r != 0) {
901 t1 = s1 +% r;
902 t = s0;
903 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
904 s1 = t1 +% r;
905 if (t1 & sign == sign and s1 & sign == 0) {
906 s0 += 1;
907 }
908 ix0 -= t;
909 if (ix1 < t1) {
910 ix0 -= 1;
911 }
912 ix1 = ix1 -% t1;
913 q1 += r;
914 }
915 ix0 = ix0 +% ix0 +% (ix1 >> 31);
916 ix1 = ix1 +% ix1;
917 r >>= 1;
918 }
919
920 // rounding direction
921 if (ix0 | ix1 != 0) {
922 var z = 1.0 - tiny; // raise inexact
923 if (z >= 1.0) {
924 z = 1.0 + tiny;
925 if (q1 == 0xFFFFFFFF) {
926 q1 = 0;
927 q += 1;
928 } else if (z > 1.0) {
929 if (q1 == 0xFFFFFFFE) {
930 q += 1;
931 }
932 q1 += 2;
933 } else {
934 q1 += q1 & 1;
935 }
936 }
937 }
938
939 ix0 = (q >> 1) + 0x3FE00000;
940 ix1 = q1 >> 1;
941 if (q & 1 != 0) {
942 ix1 |= 0x80000000;
943 }
944
945 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
946 // behaviour at least.
947 var iix0 = @intCast(i32, ix0);
948 iix0 = iix0 +% (m << 20);
949
950 const uz = (@intCast(u64, iix0) << 32) | ix1;
951 return @bitCast(f64, uz);
952}
953
954test "sqrt" {
955 const V = [_]f64{
956 0.0,
957 4.089288054930154,
958 7.538757127071935,
959 8.97780793672623,
960 5.304443821913729,
961 5.682408965311888,
962 0.5846878579110049,
963 3.650338664297043,
964 0.3178091951800732,
965 7.1505232436382835,
966 3.6589165881946464,
967 };
968
969 // Note that @sqrt will either generate the sqrt opcode (if supported by the
970 // target ISA) or a call to `sqrtf` otherwise.
971 for (V) |val|
972 try std.testing.expectEqual(@sqrt(val), sqrt(val));
973}
974
975test "sqrt special" {
976 try std.testing.expect(std.math.isPositiveInf(sqrt(std.math.inf(f64))));
977 try std.testing.expect(sqrt(0.0) == 0.0);
978 try std.testing.expect(sqrt(-0.0) == -0.0);
979 try std.testing.expect(isNan(sqrt(-1.0)));
980 try std.testing.expect(isNan(sqrt(std.math.nan(f64))));
981}
982
983export fn sqrtf(x: f32) f32 {
984 const tiny: f32 = 1.0e-30;
985 const sign: i32 = @bitCast(i32, @as(u32, 0x80000000));
986 var ix: i32 = @bitCast(i32, x);
987
988 if ((ix & 0x7F800000) == 0x7F800000) {
989 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
990 }
991
992 // zero
993 if (ix <= 0) {
994 if (ix & ~sign == 0) {
995 return x; // sqrt (+-0) = +-0
996 }
997 if (ix < 0) {
998 return math.snan(f32);
999 }
1000 }
1001
1002 // normalize
1003 var m = ix >> 23;
1004 if (m == 0) {
1005 // subnormal
1006 var i: i32 = 0;
1007 while (ix & 0x00800000 == 0) : (i += 1) {
1008 ix <<= 1;
1009 }
1010 m -= i - 1;
1011 }
1012
1013 m -= 127; // unbias exponent
1014 ix = (ix & 0x007FFFFF) | 0x00800000;
1015
1016 if (m & 1 != 0) { // odd m, double x to even
1017 ix += ix;
1018 }
1019
1020 m >>= 1; // m = [m / 2]
1021
1022 // sqrt(x) bit by bit
1023 ix += ix;
1024 var q: i32 = 0; // q = sqrt(x)
1025 var s: i32 = 0;
1026 var r: i32 = 0x01000000; // r = moving bit right -> left
1027
1028 while (r != 0) {
1029 const t = s + r;
1030 if (t <= ix) {
1031 s = t + r;
1032 ix -= t;
1033 q += r;
1034 }
1035 ix += ix;
1036 r >>= 1;
1037 }
1038
1039 // floating add to find rounding direction
1040 if (ix != 0) {
1041 var z = 1.0 - tiny; // inexact
1042 if (z >= 1.0) {
1043 z = 1.0 + tiny;
1044 if (z > 1.0) {
1045 q += 2;
1046 } else {
1047 if (q & 1 != 0) {
1048 q += 1;
1049 }
1050 }
1051 }
1052 }
1053
1054 ix = (q >> 1) + 0x3f000000;
1055 ix += m << 23;
1056 return @bitCast(f32, ix);
1057}
1058
1059test "sqrtf" {
1060 const V = [_]f32{
1061 0.0,
1062 4.089288054930154,
1063 7.538757127071935,
1064 8.97780793672623,
1065 5.304443821913729,
1066 5.682408965311888,
1067 0.5846878579110049,
1068 3.650338664297043,
1069 0.3178091951800732,
1070 7.1505232436382835,
1071 3.6589165881946464,
1072 };
1073
1074 // Note that @sqrt will either generate the sqrt opcode (if supported by the
1075 // target ISA) or a call to `sqrtf` otherwise.
1076 for (V) |val|
1077 try std.testing.expectEqual(@sqrt(val), sqrtf(val));
1078}
1079
1080test "sqrtf special" {
1081 try std.testing.expect(std.math.isPositiveInf(sqrtf(std.math.inf(f32))));
1082 try std.testing.expect(sqrtf(0.0) == 0.0);
1083 try std.testing.expect(sqrtf(-0.0) == -0.0);
1084 try std.testing.expect(isNan(sqrtf(-1.0)));
1085 try std.testing.expect(isNan(sqrtf(std.math.nan(f32))));
1086}
lib/std/special/compiler_rt.zig+3-10
......@@ -23,9 +23,7 @@ const long_double_is_f128 = builtin.target.longDoubleIsF128();
2323
2424comptime {
2525 // These files do their own comptime exporting logic.
26 if (builtin.zig_backend == .stage1) {
27 _ = @import("compiler_rt/atomics.zig");
28 }
26 _ = @import("compiler_rt/atomics.zig");
2927 if (builtin.zig_backend != .stage2_llvm) { // TODO
3028 _ = @import("compiler_rt/clear_cache.zig").clear_cache;
3129 }
......@@ -201,7 +199,7 @@ comptime {
201199 const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2;
202200 @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage });
203201
204 if (builtin.zig_backend == .stage1) {
202 if (builtin.zig_backend == .stage1) { // TODO
205203 switch (arch) {
206204 .i386,
207205 .x86_64,
......@@ -304,7 +302,7 @@ comptime {
304302
305303 const __floatunsisf = @import("compiler_rt/floatunsisf.zig").__floatunsisf;
306304 @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage });
307 if (builtin.zig_backend == .stage1) {
305 if (builtin.zig_backend == .stage1) { // TODO it's crashing on a switch expression
308306 const __floatundisf = @import("compiler_rt/floatundisf.zig").__floatundisf;
309307 @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage });
310308 }
......@@ -789,11 +787,6 @@ fn floorl(x: c_longdouble) callconv(.C) c_longdouble {
789787pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn {
790788 _ = error_return_trace;
791789 @setCold(true);
792 if (builtin.zig_backend != .stage1) {
793 while (true) {
794 @breakpoint();
795 }
796 }
797790 if (is_test) {
798791 std.debug.panic("{s}", .{msg});
799792 } else {