authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-23 20:01:45-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-23 20:16:57-07:00
log418105589a2723ca372596e5893e0e1e030efe87
treeab6195e8b58bf58442a8d855d23accfca417b141
parentcc4d38ed574690e0b212fc47431324325edc7921

stage2: prepare for building freestanding libc

Extracts lib/std/special/c_stage1.zig from lib/std/special/c.zig. When the self-hosted compiler is further along, all the logic from c_stage1.zig will be migrated back c.zig and then c_stage1.zig will be deleted. Until then we have a simpler implementation of c.zig that only uses features already implemented in self-hosted. So far it only contains memcpy and memset, with slightly different (arguably more correct!) implementations that are compatible with self-hosted. Additionally, this commit improves the LLVM backend: * use the more efficient and convenient fnInfo() when lowering function type info. * fix incremental compilation not deleting all basic blocks of a function. * hook up calling conventions * hook up the following function attributes: - noredzone, nounwind, uwtable, minsize, optsize, sanitize_thread

4 files changed, 1372 insertions(+), 1191 deletions(-)

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