authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-15 16:20:16-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-15 20:14:58-04:00
log14cdb01f35b35972b06e95a3a438f9f7910b97f8
tree652a48bb218b86c581ebe7c747167c47a0236541
parent3aa43dc31c468b2f80bf065f850618a981c5fca2

improvements to zig's implementation of libc and WebAssembly

* rename std/special/builtin.zig to std/special/c.zig not to be confused with @import("builtin") which is entirely different, this is zig's multi-target libc implementation. * WebAssembly: build-exe is for executables which have a main(). build-lib is for building libraries of functions to use from, for example, a web browser environment. - for now pass --export-all for libraries when there are any C objects because we have no way to detect the list of exports when compiling C code. - stop passing --no-entry for executables. if you want --no-entry then use build-lib. * make the "musl" ABI the default ABI for wasm32-freestanding. * zig provides libc for wasm32-freestanding-musl.

5 files changed, 536 insertions(+), 514 deletions(-)

CMakeLists.txt+1-1
...@@ -643,7 +643,7 @@ set(ZIG_STD_FILES...@@ -643,7 +643,7 @@ set(ZIG_STD_FILES
643 "special/bootstrap_lib.zig"643 "special/bootstrap_lib.zig"
644 "special/bootstrap_windows_tls.zig"644 "special/bootstrap_windows_tls.zig"
645 "special/build_runner.zig"645 "special/build_runner.zig"
646 "special/builtin.zig"646 "special/c.zig"
647 "special/compiler_rt.zig"647 "special/compiler_rt.zig"
648 "special/compiler_rt/stack_probe.zig"648 "special/compiler_rt/stack_probe.zig"
649 "special/compiler_rt/arm/aeabi_fcmp.zig"649 "special/compiler_rt/arm/aeabi_fcmp.zig"
src/link.cpp+24-21
...@@ -776,8 +776,8 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) {...@@ -776,8 +776,8 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file) {
776776
777static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path, OutType child_out_type) {777static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path, OutType child_out_type) {
778 // The Mach-O LLD code is not well maintained, and trips an assertion778 // The Mach-O LLD code is not well maintained, and trips an assertion
779 // when we link compiler_rt and builtin as libraries rather than objects.779 // when we link compiler_rt and libc.zig as libraries rather than objects.
780 // Here we workaround this by having compiler_rt and builtin be objects.780 // Here we workaround this by having compiler_rt and libc.zig be objects.
781 // TODO write our own linker. https://github.com/ziglang/zig/issues/1535781 // TODO write our own linker. https://github.com/ziglang/zig/issues/1535
782 if (parent_gen->zig_target->os == OsMacOSX) {782 if (parent_gen->zig_target->os == OsMacOSX) {
783 child_out_type = OutTypeObj;783 child_out_type = OutTypeObj;
...@@ -787,7 +787,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path,...@@ -787,7 +787,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path,
787 parent_gen->libc);787 parent_gen->libc);
788 codegen_set_out_name(child_gen, buf_create_from_str(aname));788 codegen_set_out_name(child_gen, buf_create_from_str(aname));
789789
790 // This is so that compiler_rt and builtin libraries know whether they790 // This is so that compiler_rt and libc.zig libraries know whether they
791 // will eventually be linked with libc. They make different decisions791 // will eventually be linked with libc. They make different decisions
792 // about what to export depending on whether libc is linked.792 // about what to export depending on whether libc is linked.
793 if (parent_gen->libc_link_lib != nullptr) {793 if (parent_gen->libc_link_lib != nullptr) {
...@@ -1002,8 +1002,8 @@ static void construct_linker_job_elf(LinkJob *lj) {...@@ -1002,8 +1002,8 @@ static void construct_linker_job_elf(LinkJob *lj) {
10021002
1003 if (!g->is_dummy_so && (g->out_type == OutTypeExe || is_dyn_lib)) {1003 if (!g->is_dummy_so && (g->out_type == OutTypeExe || is_dyn_lib)) {
1004 if (g->libc_link_lib == nullptr) {1004 if (g->libc_link_lib == nullptr) {
1005 Buf *builtin_a_path = build_a(g, "builtin");1005 Buf *libc_a_path = build_a(g, "c");
1006 lj->args.append(buf_ptr(builtin_a_path));1006 lj->args.append(buf_ptr(libc_a_path));
1007 }1007 }
10081008
1009 Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib);1009 Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib);
...@@ -1092,30 +1092,33 @@ static void construct_linker_job_wasm(LinkJob *lj) {...@@ -1092,30 +1092,33 @@ static void construct_linker_job_wasm(LinkJob *lj) {
10921092
1093 lj->args.append("-error-limit=0");1093 lj->args.append("-error-limit=0");
10941094
1095 if (g->zig_target->os != OsWASI) {1095 if (g->out_type != OutTypeExe) {
1096 lj->args.append("--no-entry"); // So lld doesn't look for _start.1096 lj->args.append("--no-entry"); // So lld doesn't look for _start.
1097
1098 // If there are any C source files we cannot rely on individual exports.
1099 if (g->c_source_files.length != 0) {
1100 lj->args.append("--export-all");
1101 } else {
1102 auto export_it = g->exported_symbol_names.entry_iterator();
1103 decltype(g->exported_symbol_names)::Entry *curr_entry = nullptr;
1104 while ((curr_entry = export_it.next()) != nullptr) {
1105 Buf *arg = buf_sprintf("--export=%s", buf_ptr(curr_entry->key));
1106 lj->args.append(buf_ptr(arg));
1107 }
1108 }
1097 }1109 }
1098 lj->args.append("--allow-undefined");1110 lj->args.append("--allow-undefined");
1099 lj->args.append("-o");1111 lj->args.append("-o");
1100 lj->args.append(buf_ptr(&g->output_file_path));1112 lj->args.append(buf_ptr(&g->output_file_path));
11011113
1102 auto export_it = g->exported_symbol_names.entry_iterator();
1103 decltype(g->exported_symbol_names)::Entry *curr_entry = nullptr;
1104 while ((curr_entry = export_it.next()) != nullptr) {
1105 Buf *arg = buf_sprintf("--export=%s", buf_ptr(curr_entry->key));
1106 lj->args.append(buf_ptr(arg));
1107 }
1108
1109 // .o files1114 // .o files
1110 for (size_t i = 0; i < g->link_objects.length; i += 1) {1115 for (size_t i = 0; i < g->link_objects.length; i += 1) {
1111 lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));1116 lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
1112 }1117 }
11131118
1114 if (g->out_type == OutTypeExe) {1119 if (g->out_type != OutTypeObj) {
1115 if (g->libc_link_lib == nullptr) {1120 Buf *libc_a_path = build_a(g, "c");
1116 Buf *builtin_a_path = build_a(g, "builtin");1121 lj->args.append(buf_ptr(libc_a_path));
1117 lj->args.append(buf_ptr(builtin_a_path));
1118 }
11191122
1120 Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib);1123 Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib);
1121 lj->args.append(buf_ptr(compiler_rt_o_path));1124 lj->args.append(buf_ptr(compiler_rt_o_path));
...@@ -1356,8 +1359,8 @@ static void construct_linker_job_coff(LinkJob *lj) {...@@ -1356,8 +1359,8 @@ static void construct_linker_job_coff(LinkJob *lj) {
13561359
1357 if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) {1360 if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) {
1358 if (g->libc_link_lib == nullptr && !g->is_dummy_so) {1361 if (g->libc_link_lib == nullptr && !g->is_dummy_so) {
1359 Buf *builtin_a_path = build_a(g, "builtin");1362 Buf *libc_a_path = build_a(g, "c");
1360 lj->args.append(buf_ptr(builtin_a_path));1363 lj->args.append(buf_ptr(libc_a_path));
1361 }1364 }
13621365
1363 // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage1366 // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
src/target.cpp+5-2
...@@ -1376,6 +1376,9 @@ bool target_is_single_threaded(const ZigTarget *target) {...@@ -1376,6 +1376,9 @@ bool target_is_single_threaded(const ZigTarget *target) {
1376}1376}
13771377
1378ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) {1378ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) {
1379 if (arch == ZigLLVM_wasm32 || arch == ZigLLVM_wasm64) {
1380 return ZigLLVM_Musl;
1381 }
1379 switch (os) {1382 switch (os) {
1380 case OsFreestanding:1383 case OsFreestanding:
1381 case OsAnanas:1384 case OsAnanas:
...@@ -1490,6 +1493,7 @@ static const AvailableLibC libcs_available[] = {...@@ -1490,6 +1493,7 @@ static const AvailableLibC libcs_available[] = {
1490 {ZigLLVM_systemz, OsLinux, ZigLLVM_Musl},1493 {ZigLLVM_systemz, OsLinux, ZigLLVM_Musl},
1491 {ZigLLVM_sparc, OsLinux, ZigLLVM_GNU},1494 {ZigLLVM_sparc, OsLinux, ZigLLVM_GNU},
1492 {ZigLLVM_sparcv9, OsLinux, ZigLLVM_GNU},1495 {ZigLLVM_sparcv9, OsLinux, ZigLLVM_GNU},
1496 {ZigLLVM_wasm32, OsFreestanding, ZigLLVM_Musl},
1493 {ZigLLVM_x86_64, OsLinux, ZigLLVM_GNU},1497 {ZigLLVM_x86_64, OsLinux, ZigLLVM_GNU},
1494 {ZigLLVM_x86_64, OsLinux, ZigLLVM_GNUX32},1498 {ZigLLVM_x86_64, OsLinux, ZigLLVM_GNUX32},
1495 {ZigLLVM_x86_64, OsLinux, ZigLLVM_Musl},1499 {ZigLLVM_x86_64, OsLinux, ZigLLVM_Musl},
...@@ -1508,7 +1512,6 @@ bool target_can_build_libc(const ZigTarget *target) {...@@ -1508,7 +1512,6 @@ bool target_can_build_libc(const ZigTarget *target) {
1508}1512}
15091513
1510const char *target_libc_generic_name(const ZigTarget *target) {1514const char *target_libc_generic_name(const ZigTarget *target) {
1511 assert(target->os == OsLinux);
1512 switch (target->abi) {1515 switch (target->abi) {
1513 case ZigLLVM_GNU:1516 case ZigLLVM_GNU:
1514 case ZigLLVM_GNUABIN32:1517 case ZigLLVM_GNUABIN32:
...@@ -1520,6 +1523,7 @@ const char *target_libc_generic_name(const ZigTarget *target) {...@@ -1520,6 +1523,7 @@ const char *target_libc_generic_name(const ZigTarget *target) {
1520 case ZigLLVM_Musl:1523 case ZigLLVM_Musl:
1521 case ZigLLVM_MuslEABI:1524 case ZigLLVM_MuslEABI:
1522 case ZigLLVM_MuslEABIHF:1525 case ZigLLVM_MuslEABIHF:
1526 case ZigLLVM_UnknownEnvironment:
1523 return "musl";1527 return "musl";
1524 case ZigLLVM_CODE16:1528 case ZigLLVM_CODE16:
1525 case ZigLLVM_EABI:1529 case ZigLLVM_EABI:
...@@ -1530,7 +1534,6 @@ const char *target_libc_generic_name(const ZigTarget *target) {...@@ -1530,7 +1534,6 @@ const char *target_libc_generic_name(const ZigTarget *target) {
1530 case ZigLLVM_Cygnus:1534 case ZigLLVM_Cygnus:
1531 case ZigLLVM_CoreCLR:1535 case ZigLLVM_CoreCLR:
1532 case ZigLLVM_Simulator:1536 case ZigLLVM_Simulator:
1533 case ZigLLVM_UnknownEnvironment:
1534 zig_unreachable();1537 zig_unreachable();
1535 }1538 }
1536 zig_unreachable();1539 zig_unreachable();
std/special/builtin.zig deleted-490
...@@ -1,490 +0,0 @@
1// These functions are provided when not linking against libc because LLVM
2// sometimes generates code that calls them.
3
4const std = @import("std");
5const builtin = @import("builtin");
6const maxInt = std.math.maxInt;
7
8// Avoid dragging in the runtime safety mechanisms into this .o file,
9// unless we're trying to test this file.
10pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
11 if (builtin.is_test) {
12 @setCold(true);
13 std.debug.panic("{}", msg);
14 } else {
15 unreachable;
16 }
17}
18
19export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 {
20 @setRuntimeSafety(false);
21
22 var index: usize = 0;
23 while (index != n) : (index += 1)
24 dest.?[index] = c;
25
26 return dest;
27}
28
29export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]u8 {
30 @setRuntimeSafety(false);
31
32 var index: usize = 0;
33 while (index != n) : (index += 1)
34 dest.?[index] = src.?[index];
35
36 return dest;
37}
38
39export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 {
40 @setRuntimeSafety(false);
41
42 if (@ptrToInt(dest) < @ptrToInt(src)) {
43 var index: usize = 0;
44 while (index != n) : (index += 1) {
45 dest.?[index] = src.?[index];
46 }
47 } else {
48 var index = n;
49 while (index != 0) {
50 index -= 1;
51 dest.?[index] = src.?[index];
52 }
53 }
54
55 return dest;
56}
57
58export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) isize {
59 @setRuntimeSafety(false);
60
61 var index: usize = 0;
62 while (index != n) : (index += 1) {
63 const compare_val = @bitCast(i8, vl.?[index] -% vr.?[index]);
64 if (compare_val != 0) {
65 return compare_val;
66 }
67 }
68
69 return 0;
70}
71
72test "test_memcmp" {
73 const base_arr = []u8{ 1, 1, 1 };
74 const arr1 = []u8{ 1, 1, 1 };
75 const arr2 = []u8{ 1, 0, 1 };
76 const arr3 = []u8{ 1, 2, 1 };
77
78 std.testing.expect(memcmp(base_arr[0..].ptr, arr1[0..].ptr, base_arr.len) == 0);
79 std.testing.expect(memcmp(base_arr[0..].ptr, arr2[0..].ptr, base_arr.len) == 1);
80 std.testing.expect(memcmp(base_arr[0..].ptr, arr3[0..].ptr, base_arr.len) == -1);
81}
82
83comptime {
84 if (builtin.mode != builtin.Mode.ReleaseFast and
85 builtin.mode != builtin.Mode.ReleaseSmall and
86 builtin.os != builtin.Os.windows)
87 {
88 @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong);
89 }
90 if (builtin.os == builtin.Os.linux) {
91 @export("clone", clone, builtin.GlobalLinkage.Strong);
92 }
93}
94extern fn __stack_chk_fail() noreturn {
95 @panic("stack smashing detected");
96}
97
98// TODO we should be able to put this directly in std/linux/x86_64.zig but
99// it causes a segfault in release mode. this is a workaround of calling it
100// across .o file boundaries. fix comptime @ptrCast of nakedcc functions.
101nakedcc fn clone() void {
102 if (builtin.arch == builtin.Arch.x86_64) {
103 asm volatile (
104 \\ xor %%eax,%%eax
105 \\ mov $56,%%al // SYS_clone
106 \\ mov %%rdi,%%r11
107 \\ mov %%rdx,%%rdi
108 \\ mov %%r8,%%rdx
109 \\ mov %%r9,%%r8
110 \\ mov 8(%%rsp),%%r10
111 \\ mov %%r11,%%r9
112 \\ and $-16,%%rsi
113 \\ sub $8,%%rsi
114 \\ mov %%rcx,(%%rsi)
115 \\ syscall
116 \\ test %%eax,%%eax
117 \\ jnz 1f
118 \\ xor %%ebp,%%ebp
119 \\ pop %%rdi
120 \\ call *%%r9
121 \\ mov %%eax,%%edi
122 \\ xor %%eax,%%eax
123 \\ mov $60,%%al // SYS_exit
124 \\ syscall
125 \\ hlt
126 \\1: ret
127 \\
128 );
129 } else if (builtin.arch == builtin.Arch.aarch64) {
130 // __clone(func, stack, flags, arg, ptid, tls, ctid)
131 // x0, x1, w2, x3, x4, x5, x6
132
133 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
134 // x8, x0, x1, x2, x3, x4
135 asm volatile (
136 \\ // align stack and save func,arg
137 \\ and x1,x1,#-16
138 \\ stp x0,x3,[x1,#-16]!
139 \\
140 \\ // syscall
141 \\ uxtw x0,w2
142 \\ mov x2,x4
143 \\ mov x3,x5
144 \\ mov x4,x6
145 \\ mov x8,#220 // SYS_clone
146 \\ svc #0
147 \\
148 \\ cbz x0,1f
149 \\ // parent
150 \\ ret
151 \\ // child
152 \\1: ldp x1,x0,[sp],#16
153 \\ blr x1
154 \\ mov x8,#93 // SYS_exit
155 \\ svc #0
156 );
157 } else {
158 @compileError("Implement clone() for this arch.");
159 }
160}
161
162const math = std.math;
163
164export fn fmodf(x: f32, y: f32) f32 {
165 return generic_fmod(f32, x, y);
166}
167export fn fmod(x: f64, y: f64) f64 {
168 return generic_fmod(f64, x, y);
169}
170
171// TODO add intrinsics for these (and probably the double version too)
172// and have the math stuff use the intrinsic. same as @mod and @rem
173export fn floorf(x: f32) f32 {
174 return math.floor(x);
175}
176export fn ceilf(x: f32) f32 {
177 return math.ceil(x);
178}
179export fn floor(x: f64) f64 {
180 return math.floor(x);
181}
182export fn ceil(x: f64) f64 {
183 return math.ceil(x);
184}
185
186fn generic_fmod(comptime T: type, x: T, y: T) T {
187 @setRuntimeSafety(false);
188
189 const uint = @IntType(false, T.bit_count);
190 const log2uint = math.Log2Int(uint);
191 const digits = if (T == f32) 23 else 52;
192 const exp_bits = if (T == f32) 9 else 12;
193 const bits_minus_1 = T.bit_count - 1;
194 const mask = if (T == f32) 0xff else 0x7ff;
195 var ux = @bitCast(uint, x);
196 var uy = @bitCast(uint, y);
197 var ex = @intCast(i32, (ux >> digits) & mask);
198 var ey = @intCast(i32, (uy >> digits) & mask);
199 const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1);
200 var i: uint = undefined;
201
202 if (uy << 1 == 0 or isNan(uint, uy) or ex == mask)
203 return (x * y) / (x * y);
204
205 if (ux << 1 <= uy << 1) {
206 if (ux << 1 == uy << 1)
207 return 0 * x;
208 return x;
209 }
210
211 // normalize x and y
212 if (ex == 0) {
213 i = ux << exp_bits;
214 while (i >> bits_minus_1 == 0) : (b: {
215 ex -= 1;
216 i <<= 1;
217 }) {}
218 ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1));
219 } else {
220 ux &= maxInt(uint) >> exp_bits;
221 ux |= 1 << digits;
222 }
223 if (ey == 0) {
224 i = uy << exp_bits;
225 while (i >> bits_minus_1 == 0) : (b: {
226 ey -= 1;
227 i <<= 1;
228 }) {}
229 uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1));
230 } else {
231 uy &= maxInt(uint) >> exp_bits;
232 uy |= 1 << digits;
233 }
234
235 // x mod y
236 while (ex > ey) : (ex -= 1) {
237 i = ux -% uy;
238 if (i >> bits_minus_1 == 0) {
239 if (i == 0)
240 return 0 * x;
241 ux = i;
242 }
243 ux <<= 1;
244 }
245 i = ux -% uy;
246 if (i >> bits_minus_1 == 0) {
247 if (i == 0)
248 return 0 * x;
249 ux = i;
250 }
251 while (ux >> digits == 0) : (b: {
252 ux <<= 1;
253 ex -= 1;
254 }) {}
255
256 // scale result up
257 if (ex > 0) {
258 ux -%= 1 << digits;
259 ux |= uint(@bitCast(u32, ex)) << digits;
260 } else {
261 ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1));
262 }
263 if (T == f32) {
264 ux |= sx;
265 } else {
266 ux |= @intCast(uint, sx) << bits_minus_1;
267 }
268 return @bitCast(T, ux);
269}
270
271fn isNan(comptime T: type, bits: T) bool {
272 if (T == u16) {
273 return (bits & 0x7fff) > 0x7c00;
274 } else if (T == u32) {
275 return (bits & 0x7fffffff) > 0x7f800000;
276 } else if (T == u64) {
277 return (bits & (maxInt(u64) >> 1)) > (u64(0x7ff) << 52);
278 } else {
279 unreachable;
280 }
281}
282
283// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
284// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
285// potentially some edge cases remaining that are not handled in the same way.
286export fn sqrt(x: f64) f64 {
287 const tiny: f64 = 1.0e-300;
288 const sign: u32 = 0x80000000;
289 const u = @bitCast(u64, x);
290
291 var ix0 = @intCast(u32, u >> 32);
292 var ix1 = @intCast(u32, u & 0xFFFFFFFF);
293
294 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
295 if (ix0 & 0x7FF00000 == 0x7FF00000) {
296 return x * x + x;
297 }
298
299 // sqrt(+-0) = +-0
300 if (x == 0.0) {
301 return x;
302 }
303 // sqrt(-ve) = snan
304 if (ix0 & sign != 0) {
305 return math.snan(f64);
306 }
307
308 // normalize x
309 var m = @intCast(i32, ix0 >> 20);
310 if (m == 0) {
311 // subnormal
312 while (ix0 == 0) {
313 m -= 21;
314 ix0 |= ix1 >> 11;
315 ix1 <<= 21;
316 }
317
318 // subnormal
319 var i: u32 = 0;
320 while (ix0 & 0x00100000 == 0) : (i += 1) {
321 ix0 <<= 1;
322 }
323 m -= @intCast(i32, i) - 1;
324 ix0 |= ix1 >> @intCast(u5, 32 - i);
325 ix1 <<= @intCast(u5, i);
326 }
327
328 // unbias exponent
329 m -= 1023;
330 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
331 if (m & 1 != 0) {
332 ix0 += ix0 + (ix1 >> 31);
333 ix1 = ix1 +% ix1;
334 }
335 m >>= 1;
336
337 // sqrt(x) bit by bit
338 ix0 += ix0 + (ix1 >> 31);
339 ix1 = ix1 +% ix1;
340
341 var q: u32 = 0;
342 var q1: u32 = 0;
343 var s0: u32 = 0;
344 var s1: u32 = 0;
345 var r: u32 = 0x00200000;
346 var t: u32 = undefined;
347 var t1: u32 = undefined;
348
349 while (r != 0) {
350 t = s0 +% r;
351 if (t <= ix0) {
352 s0 = t + r;
353 ix0 -= t;
354 q += r;
355 }
356 ix0 = ix0 +% ix0 +% (ix1 >> 31);
357 ix1 = ix1 +% ix1;
358 r >>= 1;
359 }
360
361 r = sign;
362 while (r != 0) {
363 t = s1 +% r;
364 t = s0;
365 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
366 s1 = t1 +% r;
367 if (t1 & sign == sign and s1 & sign == 0) {
368 s0 += 1;
369 }
370 ix0 -= t;
371 if (ix1 < t1) {
372 ix0 -= 1;
373 }
374 ix1 = ix1 -% t1;
375 q1 += r;
376 }
377 ix0 = ix0 +% ix0 +% (ix1 >> 31);
378 ix1 = ix1 +% ix1;
379 r >>= 1;
380 }
381
382 // rounding direction
383 if (ix0 | ix1 != 0) {
384 var z = 1.0 - tiny; // raise inexact
385 if (z >= 1.0) {
386 z = 1.0 + tiny;
387 if (q1 == 0xFFFFFFFF) {
388 q1 = 0;
389 q += 1;
390 } else if (z > 1.0) {
391 if (q1 == 0xFFFFFFFE) {
392 q += 1;
393 }
394 q1 += 2;
395 } else {
396 q1 += q1 & 1;
397 }
398 }
399 }
400
401 ix0 = (q >> 1) + 0x3FE00000;
402 ix1 = q1 >> 1;
403 if (q & 1 != 0) {
404 ix1 |= 0x80000000;
405 }
406
407 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
408 // behaviour at least.
409 var iix0 = @intCast(i32, ix0);
410 iix0 = iix0 +% (m << 20);
411
412 const uz = (@intCast(u64, iix0) << 32) | ix1;
413 return @bitCast(f64, uz);
414}
415
416export fn sqrtf(x: f32) f32 {
417 const tiny: f32 = 1.0e-30;
418 const sign: i32 = @bitCast(i32, u32(0x80000000));
419 var ix: i32 = @bitCast(i32, x);
420
421 if ((ix & 0x7F800000) == 0x7F800000) {
422 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
423 }
424
425 // zero
426 if (ix <= 0) {
427 if (ix & ~sign == 0) {
428 return x; // sqrt (+-0) = +-0
429 }
430 if (ix < 0) {
431 return math.snan(f32);
432 }
433 }
434
435 // normalize
436 var m = ix >> 23;
437 if (m == 0) {
438 // subnormal
439 var i: i32 = 0;
440 while (ix & 0x00800000 == 0) : (i += 1) {
441 ix <<= 1;
442 }
443 m -= i - 1;
444 }
445
446 m -= 127; // unbias exponent
447 ix = (ix & 0x007FFFFF) | 0x00800000;
448
449 if (m & 1 != 0) { // odd m, double x to even
450 ix += ix;
451 }
452
453 m >>= 1; // m = [m / 2]
454
455 // sqrt(x) bit by bit
456 ix += ix;
457 var q: i32 = 0; // q = sqrt(x)
458 var s: i32 = 0;
459 var r: i32 = 0x01000000; // r = moving bit right -> left
460
461 while (r != 0) {
462 const t = s + r;
463 if (t <= ix) {
464 s = t + r;
465 ix -= t;
466 q += r;
467 }
468 ix += ix;
469 r >>= 1;
470 }
471
472 // floating add to find rounding direction
473 if (ix != 0) {
474 var z = 1.0 - tiny; // inexact
475 if (z >= 1.0) {
476 z = 1.0 + tiny;
477 if (z > 1.0) {
478 q += 2;
479 } else {
480 if (q & 1 != 0) {
481 q += 1;
482 }
483 }
484 }
485 }
486
487 ix = (q >> 1) + 0x3f000000;
488 ix += m << 23;
489 return @bitCast(f32, ix);
490}
std/special/c.zig created+506
...@@ -0,0 +1,506 @@
1// This is Zig's multi-target implementation of libc.
2// When builtin.link_libc is true, we need to export all the functions and
3// provide an entire C API.
4// Otherwise, only the functions which LLVM generates calls to need to be generated,
5// such as memcpy, memset, and some math functions.
6
7const std = @import("std");
8const builtin = @import("builtin");
9const maxInt = std.math.maxInt;
10
11const is_wasm = switch (builtin.arch) { .wasm32, .wasm64 => true, else => false};
12const is_freestanding = switch (builtin.os) { .freestanding => true, else => false };
13comptime {
14 if (is_freestanding and is_wasm) {
15 @export("_start", wasm_start, .Strong);
16 }
17}
18
19extern fn main(argc: c_int, argv: [*][*]u8) c_int;
20extern fn wasm_start() c_int {
21 return main(0, undefined);
22}
23
24// Avoid dragging in the runtime safety mechanisms into this .o file,
25// unless we're trying to test this file.
26pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
27 if (builtin.is_test) {
28 @setCold(true);
29 std.debug.panic("{}", msg);
30 } else {
31 unreachable;
32 }
33}
34
35export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 {
36 @setRuntimeSafety(false);
37
38 var index: usize = 0;
39 while (index != n) : (index += 1)
40 dest.?[index] = c;
41
42 return dest;
43}
44
45export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]u8 {
46 @setRuntimeSafety(false);
47
48 var index: usize = 0;
49 while (index != n) : (index += 1)
50 dest.?[index] = src.?[index];
51
52 return dest;
53}
54
55export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 {
56 @setRuntimeSafety(false);
57
58 if (@ptrToInt(dest) < @ptrToInt(src)) {
59 var index: usize = 0;
60 while (index != n) : (index += 1) {
61 dest.?[index] = src.?[index];
62 }
63 } else {
64 var index = n;
65 while (index != 0) {
66 index -= 1;
67 dest.?[index] = src.?[index];
68 }
69 }
70
71 return dest;
72}
73
74export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) isize {
75 @setRuntimeSafety(false);
76
77 var index: usize = 0;
78 while (index != n) : (index += 1) {
79 const compare_val = @bitCast(i8, vl.?[index] -% vr.?[index]);
80 if (compare_val != 0) {
81 return compare_val;
82 }
83 }
84
85 return 0;
86}
87
88test "test_memcmp" {
89 const base_arr = []u8{ 1, 1, 1 };
90 const arr1 = []u8{ 1, 1, 1 };
91 const arr2 = []u8{ 1, 0, 1 };
92 const arr3 = []u8{ 1, 2, 1 };
93
94 std.testing.expect(memcmp(base_arr[0..].ptr, arr1[0..].ptr, base_arr.len) == 0);
95 std.testing.expect(memcmp(base_arr[0..].ptr, arr2[0..].ptr, base_arr.len) == 1);
96 std.testing.expect(memcmp(base_arr[0..].ptr, arr3[0..].ptr, base_arr.len) == -1);
97}
98
99comptime {
100 if (builtin.mode != builtin.Mode.ReleaseFast and
101 builtin.mode != builtin.Mode.ReleaseSmall and
102 builtin.os != builtin.Os.windows)
103 {
104 @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong);
105 }
106 if (builtin.os == builtin.Os.linux) {
107 @export("clone", clone, builtin.GlobalLinkage.Strong);
108 }
109}
110extern fn __stack_chk_fail() noreturn {
111 @panic("stack smashing detected");
112}
113
114// TODO we should be able to put this directly in std/linux/x86_64.zig but
115// it causes a segfault in release mode. this is a workaround of calling it
116// across .o file boundaries. fix comptime @ptrCast of nakedcc functions.
117nakedcc fn clone() void {
118 if (builtin.arch == builtin.Arch.x86_64) {
119 asm volatile (
120 \\ xor %%eax,%%eax
121 \\ mov $56,%%al // SYS_clone
122 \\ mov %%rdi,%%r11
123 \\ mov %%rdx,%%rdi
124 \\ mov %%r8,%%rdx
125 \\ mov %%r9,%%r8
126 \\ mov 8(%%rsp),%%r10
127 \\ mov %%r11,%%r9
128 \\ and $-16,%%rsi
129 \\ sub $8,%%rsi
130 \\ mov %%rcx,(%%rsi)
131 \\ syscall
132 \\ test %%eax,%%eax
133 \\ jnz 1f
134 \\ xor %%ebp,%%ebp
135 \\ pop %%rdi
136 \\ call *%%r9
137 \\ mov %%eax,%%edi
138 \\ xor %%eax,%%eax
139 \\ mov $60,%%al // SYS_exit
140 \\ syscall
141 \\ hlt
142 \\1: ret
143 \\
144 );
145 } else if (builtin.arch == builtin.Arch.aarch64) {
146 // __clone(func, stack, flags, arg, ptid, tls, ctid)
147 // x0, x1, w2, x3, x4, x5, x6
148
149 // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
150 // x8, x0, x1, x2, x3, x4
151 asm volatile (
152 \\ // align stack and save func,arg
153 \\ and x1,x1,#-16
154 \\ stp x0,x3,[x1,#-16]!
155 \\
156 \\ // syscall
157 \\ uxtw x0,w2
158 \\ mov x2,x4
159 \\ mov x3,x5
160 \\ mov x4,x6
161 \\ mov x8,#220 // SYS_clone
162 \\ svc #0
163 \\
164 \\ cbz x0,1f
165 \\ // parent
166 \\ ret
167 \\ // child
168 \\1: ldp x1,x0,[sp],#16
169 \\ blr x1
170 \\ mov x8,#93 // SYS_exit
171 \\ svc #0
172 );
173 } else {
174 @compileError("Implement clone() for this arch.");
175 }
176}
177
178const math = std.math;
179
180export fn fmodf(x: f32, y: f32) f32 {
181 return generic_fmod(f32, x, y);
182}
183export fn fmod(x: f64, y: f64) f64 {
184 return generic_fmod(f64, x, y);
185}
186
187// TODO add intrinsics for these (and probably the double version too)
188// and have the math stuff use the intrinsic. same as @mod and @rem
189export fn floorf(x: f32) f32 {
190 return math.floor(x);
191}
192export fn ceilf(x: f32) f32 {
193 return math.ceil(x);
194}
195export fn floor(x: f64) f64 {
196 return math.floor(x);
197}
198export fn ceil(x: f64) f64 {
199 return math.ceil(x);
200}
201
202fn generic_fmod(comptime T: type, x: T, y: T) T {
203 @setRuntimeSafety(false);
204
205 const uint = @IntType(false, T.bit_count);
206 const log2uint = math.Log2Int(uint);
207 const digits = if (T == f32) 23 else 52;
208 const exp_bits = if (T == f32) 9 else 12;
209 const bits_minus_1 = T.bit_count - 1;
210 const mask = if (T == f32) 0xff else 0x7ff;
211 var ux = @bitCast(uint, x);
212 var uy = @bitCast(uint, y);
213 var ex = @intCast(i32, (ux >> digits) & mask);
214 var ey = @intCast(i32, (uy >> digits) & mask);
215 const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1);
216 var i: uint = undefined;
217
218 if (uy << 1 == 0 or isNan(uint, uy) or ex == mask)
219 return (x * y) / (x * y);
220
221 if (ux << 1 <= uy << 1) {
222 if (ux << 1 == uy << 1)
223 return 0 * x;
224 return x;
225 }
226
227 // normalize x and y
228 if (ex == 0) {
229 i = ux << exp_bits;
230 while (i >> bits_minus_1 == 0) : (b: {
231 ex -= 1;
232 i <<= 1;
233 }) {}
234 ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1));
235 } else {
236 ux &= maxInt(uint) >> exp_bits;
237 ux |= 1 << digits;
238 }
239 if (ey == 0) {
240 i = uy << exp_bits;
241 while (i >> bits_minus_1 == 0) : (b: {
242 ey -= 1;
243 i <<= 1;
244 }) {}
245 uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1));
246 } else {
247 uy &= maxInt(uint) >> exp_bits;
248 uy |= 1 << digits;
249 }
250
251 // x mod y
252 while (ex > ey) : (ex -= 1) {
253 i = ux -% uy;
254 if (i >> bits_minus_1 == 0) {
255 if (i == 0)
256 return 0 * x;
257 ux = i;
258 }
259 ux <<= 1;
260 }
261 i = ux -% uy;
262 if (i >> bits_minus_1 == 0) {
263 if (i == 0)
264 return 0 * x;
265 ux = i;
266 }
267 while (ux >> digits == 0) : (b: {
268 ux <<= 1;
269 ex -= 1;
270 }) {}
271
272 // scale result up
273 if (ex > 0) {
274 ux -%= 1 << digits;
275 ux |= uint(@bitCast(u32, ex)) << digits;
276 } else {
277 ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1));
278 }
279 if (T == f32) {
280 ux |= sx;
281 } else {
282 ux |= @intCast(uint, sx) << bits_minus_1;
283 }
284 return @bitCast(T, ux);
285}
286
287fn isNan(comptime T: type, bits: T) bool {
288 if (T == u16) {
289 return (bits & 0x7fff) > 0x7c00;
290 } else if (T == u32) {
291 return (bits & 0x7fffffff) > 0x7f800000;
292 } else if (T == u64) {
293 return (bits & (maxInt(u64) >> 1)) > (u64(0x7ff) << 52);
294 } else {
295 unreachable;
296 }
297}
298
299// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
300// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
301// potentially some edge cases remaining that are not handled in the same way.
302export fn sqrt(x: f64) f64 {
303 const tiny: f64 = 1.0e-300;
304 const sign: u32 = 0x80000000;
305 const u = @bitCast(u64, x);
306
307 var ix0 = @intCast(u32, u >> 32);
308 var ix1 = @intCast(u32, u & 0xFFFFFFFF);
309
310 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
311 if (ix0 & 0x7FF00000 == 0x7FF00000) {
312 return x * x + x;
313 }
314
315 // sqrt(+-0) = +-0
316 if (x == 0.0) {
317 return x;
318 }
319 // sqrt(-ve) = snan
320 if (ix0 & sign != 0) {
321 return math.snan(f64);
322 }
323
324 // normalize x
325 var m = @intCast(i32, ix0 >> 20);
326 if (m == 0) {
327 // subnormal
328 while (ix0 == 0) {
329 m -= 21;
330 ix0 |= ix1 >> 11;
331 ix1 <<= 21;
332 }
333
334 // subnormal
335 var i: u32 = 0;
336 while (ix0 & 0x00100000 == 0) : (i += 1) {
337 ix0 <<= 1;
338 }
339 m -= @intCast(i32, i) - 1;
340 ix0 |= ix1 >> @intCast(u5, 32 - i);
341 ix1 <<= @intCast(u5, i);
342 }
343
344 // unbias exponent
345 m -= 1023;
346 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
347 if (m & 1 != 0) {
348 ix0 += ix0 + (ix1 >> 31);
349 ix1 = ix1 +% ix1;
350 }
351 m >>= 1;
352
353 // sqrt(x) bit by bit
354 ix0 += ix0 + (ix1 >> 31);
355 ix1 = ix1 +% ix1;
356
357 var q: u32 = 0;
358 var q1: u32 = 0;
359 var s0: u32 = 0;
360 var s1: u32 = 0;
361 var r: u32 = 0x00200000;
362 var t: u32 = undefined;
363 var t1: u32 = undefined;
364
365 while (r != 0) {
366 t = s0 +% r;
367 if (t <= ix0) {
368 s0 = t + r;
369 ix0 -= t;
370 q += r;
371 }
372 ix0 = ix0 +% ix0 +% (ix1 >> 31);
373 ix1 = ix1 +% ix1;
374 r >>= 1;
375 }
376
377 r = sign;
378 while (r != 0) {
379 t = s1 +% r;
380 t = s0;
381 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
382 s1 = t1 +% r;
383 if (t1 & sign == sign and s1 & sign == 0) {
384 s0 += 1;
385 }
386 ix0 -= t;
387 if (ix1 < t1) {
388 ix0 -= 1;
389 }
390 ix1 = ix1 -% t1;
391 q1 += r;
392 }
393 ix0 = ix0 +% ix0 +% (ix1 >> 31);
394 ix1 = ix1 +% ix1;
395 r >>= 1;
396 }
397
398 // rounding direction
399 if (ix0 | ix1 != 0) {
400 var z = 1.0 - tiny; // raise inexact
401 if (z >= 1.0) {
402 z = 1.0 + tiny;
403 if (q1 == 0xFFFFFFFF) {
404 q1 = 0;
405 q += 1;
406 } else if (z > 1.0) {
407 if (q1 == 0xFFFFFFFE) {
408 q += 1;
409 }
410 q1 += 2;
411 } else {
412 q1 += q1 & 1;
413 }
414 }
415 }
416
417 ix0 = (q >> 1) + 0x3FE00000;
418 ix1 = q1 >> 1;
419 if (q & 1 != 0) {
420 ix1 |= 0x80000000;
421 }
422
423 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
424 // behaviour at least.
425 var iix0 = @intCast(i32, ix0);
426 iix0 = iix0 +% (m << 20);
427
428 const uz = (@intCast(u64, iix0) << 32) | ix1;
429 return @bitCast(f64, uz);
430}
431
432export fn sqrtf(x: f32) f32 {
433 const tiny: f32 = 1.0e-30;
434 const sign: i32 = @bitCast(i32, u32(0x80000000));
435 var ix: i32 = @bitCast(i32, x);
436
437 if ((ix & 0x7F800000) == 0x7F800000) {
438 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
439 }
440
441 // zero
442 if (ix <= 0) {
443 if (ix & ~sign == 0) {
444 return x; // sqrt (+-0) = +-0
445 }
446 if (ix < 0) {
447 return math.snan(f32);
448 }
449 }
450
451 // normalize
452 var m = ix >> 23;
453 if (m == 0) {
454 // subnormal
455 var i: i32 = 0;
456 while (ix & 0x00800000 == 0) : (i += 1) {
457 ix <<= 1;
458 }
459 m -= i - 1;
460 }
461
462 m -= 127; // unbias exponent
463 ix = (ix & 0x007FFFFF) | 0x00800000;
464
465 if (m & 1 != 0) { // odd m, double x to even
466 ix += ix;
467 }
468
469 m >>= 1; // m = [m / 2]
470
471 // sqrt(x) bit by bit
472 ix += ix;
473 var q: i32 = 0; // q = sqrt(x)
474 var s: i32 = 0;
475 var r: i32 = 0x01000000; // r = moving bit right -> left
476
477 while (r != 0) {
478 const t = s + r;
479 if (t <= ix) {
480 s = t + r;
481 ix -= t;
482 q += r;
483 }
484 ix += ix;
485 r >>= 1;
486 }
487
488 // floating add to find rounding direction
489 if (ix != 0) {
490 var z = 1.0 - tiny; // inexact
491 if (z >= 1.0) {
492 z = 1.0 + tiny;
493 if (z > 1.0) {
494 q += 2;
495 } else {
496 if (q & 1 != 0) {
497 q += 1;
498 }
499 }
500 }
501 }
502
503 ix = (q >> 1) + 0x3f000000;
504 ix += m << 23;
505 return @bitCast(f32, ix);
506}