| author | |
| committer | |
| log | 14cdb01f35b35972b06e95a3a438f9f7910b97f8 |
| tree | 652a48bb218b86c581ebe7c747167c47a0236541 |
| parent | 3aa43dc31c468b2f80bf065f850618a981c5fca2 |
* 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) { |
| 776 | 776 | ||
| 777 | static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path, OutType child_out_type) { | 777 | static 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 assertion | 778 | // 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/1535 | 781 | // 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)); |
| 789 | 789 | ||
| 790 | // This is so that compiler_rt and builtin libraries know whether they | 790 | // This is so that compiler_rt and libc.zig libraries know whether they |
| 791 | // will eventually be linked with libc. They make different decisions | 791 | // 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) { |
| 1002 | 1002 | ||
| 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 | } |
| 1008 | 1008 | ||
| 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) { |
| 1092 | 1092 | ||
| 1093 | lj->args.append("-error-limit=0"); | 1093 | lj->args.append("-error-limit=0"); |
| 1094 | 1094 | ||
| 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)); |
| 1101 | 1113 | ||
| 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 files | 1114 | // .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 | } |
| 1113 | 1118 | ||
| 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 | } | ||
| 1119 | 1122 | ||
| 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) { |
| 1356 | 1359 | ||
| 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 | } |
| 1362 | 1365 | ||
| 1363 | // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage | 1366 | // 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 | } |
| 1377 | 1377 | ||
| 1378 | ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { | 1378 | ZigLLVM_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 | } |
| 1509 | 1513 | ||
| 1510 | const char *target_libc_generic_name(const ZigTarget *target) { | 1514 | const 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 | |||
| 4 | const std = @import("std"); | ||
| 5 | const builtin = @import("builtin"); | ||
| 6 | const 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. | ||
| 10 | pub 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 | |||
| 19 | export 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 | |||
| 29 | export 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 | |||
| 39 | export 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 | |||
| 58 | export 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 | |||
| 72 | test "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 | |||
| 83 | comptime { | ||
| 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 | } | ||
| 94 | extern 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. | ||
| 101 | nakedcc 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 | |||
| 162 | const math = std.math; | ||
| 163 | |||
| 164 | export fn fmodf(x: f32, y: f32) f32 { | ||
| 165 | return generic_fmod(f32, x, y); | ||
| 166 | } | ||
| 167 | export 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 | ||
| 173 | export fn floorf(x: f32) f32 { | ||
| 174 | return math.floor(x); | ||
| 175 | } | ||
| 176 | export fn ceilf(x: f32) f32 { | ||
| 177 | return math.ceil(x); | ||
| 178 | } | ||
| 179 | export fn floor(x: f64) f64 { | ||
| 180 | return math.floor(x); | ||
| 181 | } | ||
| 182 | export fn ceil(x: f64) f64 { | ||
| 183 | return math.ceil(x); | ||
| 184 | } | ||
| 185 | |||
| 186 | fn 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 | |||
| 271 | fn 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. | ||
| 286 | export 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 | |||
| 416 | export 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 | |||
| 7 | const std = @import("std"); | ||
| 8 | const builtin = @import("builtin"); | ||
| 9 | const maxInt = std.math.maxInt; | ||
| 10 | |||
| 11 | const is_wasm = switch (builtin.arch) { .wasm32, .wasm64 => true, else => false}; | ||
| 12 | const is_freestanding = switch (builtin.os) { .freestanding => true, else => false }; | ||
| 13 | comptime { | ||
| 14 | if (is_freestanding and is_wasm) { | ||
| 15 | @export("_start", wasm_start, .Strong); | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | extern fn main(argc: c_int, argv: [*][*]u8) c_int; | ||
| 20 | extern 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. | ||
| 26 | pub 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 | |||
| 35 | export 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 | |||
| 45 | export 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 | |||
| 55 | export 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 | |||
| 74 | export 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 | |||
| 88 | test "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 | |||
| 99 | comptime { | ||
| 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 | } | ||
| 110 | extern 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. | ||
| 117 | nakedcc 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 | |||
| 178 | const math = std.math; | ||
| 179 | |||
| 180 | export fn fmodf(x: f32, y: f32) f32 { | ||
| 181 | return generic_fmod(f32, x, y); | ||
| 182 | } | ||
| 183 | export 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 | ||
| 189 | export fn floorf(x: f32) f32 { | ||
| 190 | return math.floor(x); | ||
| 191 | } | ||
| 192 | export fn ceilf(x: f32) f32 { | ||
| 193 | return math.ceil(x); | ||
| 194 | } | ||
| 195 | export fn floor(x: f64) f64 { | ||
| 196 | return math.floor(x); | ||
| 197 | } | ||
| 198 | export fn ceil(x: f64) f64 { | ||
| 199 | return math.ceil(x); | ||
| 200 | } | ||
| 201 | |||
| 202 | fn 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 | |||
| 287 | fn 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. | ||
| 302 | export 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 | |||
| 432 | export 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 | } | ||