| author | |
| committer | |
| log | 418105589a2723ca372596e5893e0e1e030efe87 |
| tree | ab6195e8b58bf58442a8d855d23accfca417b141 |
| parent | cc4d38ed574690e0b212fc47431324325edc7921 |
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_thread4 files changed, 1372 insertions(+), 1191 deletions(-)
lib/std/special/c.zig+43-1174| ... | ... | @@ -1,177 +1,36 @@ |
| 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. | |
| 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 | 6 | |
| 7 | 7 | const std = @import("std"); |
| 8 | const builtin = std.builtin; | |
| 9 | const maxInt = std.math.maxInt; | |
| 10 | const isNan = std.math.isNan; | |
| 11 | const native_arch = std.Target.current.cpu.arch; | |
| 12 | const native_abi = std.Target.current.abi; | |
| 13 | const native_os = std.Target.current.os.tag; | |
| 8 | const builtin = @import("builtin"); | |
| 9 | const native_os = builtin.os.tag; | |
| 14 | 10 | |
| 15 | const is_wasm = switch (native_arch) { | |
| 16 | .wasm32, .wasm64 => true, | |
| 17 | else => false, | |
| 18 | }; | |
| 19 | const is_msvc = switch (native_abi) { | |
| 20 | .msvc => true, | |
| 21 | else => false, | |
| 22 | }; | |
| 23 | const is_freestanding = switch (native_os) { | |
| 24 | .freestanding => true, | |
| 25 | else => false, | |
| 26 | }; | |
| 27 | 11 | comptime { |
| 28 | if (is_freestanding and is_wasm and builtin.link_libc) { | |
| 29 | @export(wasm_start, .{ .name = "_start", .linkage = .Strong }); | |
| 30 | } | |
| 31 | if (builtin.link_libc) { | |
| 32 | @export(strcmp, .{ .name = "strcmp", .linkage = .Strong }); | |
| 33 | @export(strncmp, .{ .name = "strncmp", .linkage = .Strong }); | |
| 34 | @export(strerror, .{ .name = "strerror", .linkage = .Strong }); | |
| 35 | @export(strlen, .{ .name = "strlen", .linkage = .Strong }); | |
| 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 | ||
| 45 | var _fltused: c_int = 1; | |
| 46 | ||
| 47 | extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; | |
| 48 | fn wasm_start() callconv(.C) void { | |
| 49 | _ = main(0, undefined); | |
| 50 | } | |
| 51 | ||
| 52 | fn 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 | ||
| 62 | test "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 | ||
| 70 | fn 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 | ||
| 82 | test "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 | ||
| 90 | fn 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 | ||
| 103 | test "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 | ||
| 113 | fn 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 | ||
| 126 | test "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 | ||
| 136 | fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int { | |
| 137 | return std.cstr.cmp(s1, s2); | |
| 138 | } | |
| 139 | ||
| 140 | fn strlen(s: [*:0]const u8) callconv(.C) usize { | |
| 141 | return std.mem.len(s); | |
| 142 | } | |
| 143 | ||
| 144 | fn 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; | |
| 12 | // When the self-hosted compiler is further along, all the logic from c_stage1.zig will | |
| 13 | // be migrated to this file and then c_stage1.zig will be deleted. Until then we have a | |
| 14 | // simpler implementation of c.zig that only uses features already implemented in self-hosted. | |
| 15 | if (builtin.zig_is_stage2) { | |
| 16 | @export(memset, .{ .name = "memset", .linkage = .Strong }); | |
| 17 | @export(memcpy, .{ .name = "memcpy", .linkage = .Strong }); | |
| 18 | } else { | |
| 19 | _ = @import("c_stage1.zig"); | |
| 153 | 20 | } |
| 154 | return @as(c_int, l[0]) - @as(c_int, r[0]); | |
| 155 | } | |
| 156 | ||
| 157 | fn strerror(errnum: c_int) callconv(.C) [*:0]const u8 { | |
| 158 | _ = errnum; | |
| 159 | return "TODO strerror implementation"; | |
| 160 | } | |
| 161 | ||
| 162 | test "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 | } |
| 168 | 22 | |
| 169 | 23 | // Avoid dragging in the runtime safety mechanisms into this .o file, |
| 170 | 24 | // unless we're trying to test this file. |
| 171 | pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { | |
| 25 | pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { | |
| 26 | @setCold(true); | |
| 172 | 27 | _ = error_return_trace; |
| 28 | if (builtin.zig_is_stage2) { | |
| 29 | while (true) { | |
| 30 | @breakpoint(); | |
| 31 | } | |
| 32 | } | |
| 173 | 33 | if (builtin.is_test) { |
| 174 | @setCold(true); | |
| 175 | 34 | std.debug.panic("{s}", .{msg}); |
| 176 | 35 | } |
| 177 | 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 | 39 | while (true) {} |
| 181 | 40 | } |
| 182 | 41 | |
| 183 | export fn memset(dest: ?[*]u8, c: u8, n: 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 | ||
| 193 | export 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 | ||
| 199 | export 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 | ||
| 209 | export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 { | |
| 42 | fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.C) ?[*]u8 { | |
| 210 | 43 | @setRuntimeSafety(false); |
| 211 | 44 | |
| 212 | if (@ptrToInt(dest) < @ptrToInt(src)) { | |
| 213 | var index: usize = 0; | |
| 214 | while (index != n) : (index += 1) { | |
| 215 | dest.?[index] = src.?[index]; | |
| 216 | } | |
| 217 | } else { | |
| 218 | var index = n; | |
| 219 | while (index != 0) { | |
| 220 | index -= 1; | |
| 221 | dest.?[index] = src.?[index]; | |
| 45 | if (len != 0) { | |
| 46 | var d = dest.?; | |
| 47 | var n = len; | |
| 48 | while (true) { | |
| 49 | d.* = c; | |
| 50 | n -= 1; | |
| 51 | if (n == 0) break; | |
| 52 | d += 1; | |
| 222 | 53 | } |
| 223 | 54 | } |
| 224 | 55 | |
| 225 | 56 | return dest; |
| 226 | 57 | } |
| 227 | 58 | |
| 228 | export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) c_int { | |
| 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 | ||
| 242 | test "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 | ||
| 253 | export 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 | ||
| 266 | test "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 | ||
| 277 | comptime { | |
| 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. | |
| 286 | fn 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 | ||
| 652 | const math = std.math; | |
| 653 | ||
| 654 | export fn fmodf(x: f32, y: f32) f32 { | |
| 655 | return generic_fmod(f32, x, y); | |
| 656 | } | |
| 657 | export 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 | |
| 663 | export fn floorf(x: f32) f32 { | |
| 664 | return math.floor(x); | |
| 665 | } | |
| 666 | ||
| 667 | export fn ceilf(x: f32) f32 { | |
| 668 | return math.ceil(x); | |
| 669 | } | |
| 670 | ||
| 671 | export fn floor(x: f64) f64 { | |
| 672 | return math.floor(x); | |
| 673 | } | |
| 674 | ||
| 675 | export fn ceil(x: f64) f64 { | |
| 676 | return math.ceil(x); | |
| 677 | } | |
| 678 | ||
| 679 | export fn fma(a: f64, b: f64, c: f64) f64 { | |
| 680 | return math.fma(f64, a, b, c); | |
| 681 | } | |
| 682 | ||
| 683 | export fn fmaf(a: f32, b: f32, c: f32) f32 { | |
| 684 | return math.fma(f32, a, b, c); | |
| 685 | } | |
| 686 | ||
| 687 | export fn sin(a: f64) f64 { | |
| 688 | return math.sin(a); | |
| 689 | } | |
| 690 | ||
| 691 | export fn sinf(a: f32) f32 { | |
| 692 | return math.sin(a); | |
| 693 | } | |
| 694 | ||
| 695 | export fn cos(a: f64) f64 { | |
| 696 | return math.cos(a); | |
| 697 | } | |
| 698 | ||
| 699 | export fn cosf(a: f32) f32 { | |
| 700 | return math.cos(a); | |
| 701 | } | |
| 702 | ||
| 703 | export 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 | ||
| 708 | export 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 | ||
| 713 | export fn exp(a: f64) f64 { | |
| 714 | return math.exp(a); | |
| 715 | } | |
| 716 | ||
| 717 | export fn expf(a: f32) f32 { | |
| 718 | return math.exp(a); | |
| 719 | } | |
| 720 | ||
| 721 | export fn exp2(a: f64) f64 { | |
| 722 | return math.exp2(a); | |
| 723 | } | |
| 724 | ||
| 725 | export fn exp2f(a: f32) f32 { | |
| 726 | return math.exp2(a); | |
| 727 | } | |
| 728 | ||
| 729 | export fn log(a: f64) f64 { | |
| 730 | return math.ln(a); | |
| 731 | } | |
| 732 | ||
| 733 | export fn logf(a: f32) f32 { | |
| 734 | return math.ln(a); | |
| 735 | } | |
| 736 | ||
| 737 | export fn log2(a: f64) f64 { | |
| 738 | return math.log2(a); | |
| 739 | } | |
| 740 | ||
| 741 | export fn log2f(a: f32) f32 { | |
| 742 | return math.log2(a); | |
| 743 | } | |
| 744 | ||
| 745 | export fn log10(a: f64) f64 { | |
| 746 | return math.log10(a); | |
| 747 | } | |
| 748 | ||
| 749 | export fn log10f(a: f32) f32 { | |
| 750 | return math.log10(a); | |
| 751 | } | |
| 752 | ||
| 753 | export fn fabs(a: f64) f64 { | |
| 754 | return math.fabs(a); | |
| 755 | } | |
| 756 | ||
| 757 | export fn fabsf(a: f32) f32 { | |
| 758 | return math.fabs(a); | |
| 759 | } | |
| 760 | ||
| 761 | export fn trunc(a: f64) f64 { | |
| 762 | return math.trunc(a); | |
| 763 | } | |
| 764 | ||
| 765 | export fn truncf(a: f32) f32 { | |
| 766 | return math.trunc(a); | |
| 767 | } | |
| 768 | ||
| 769 | export fn round(a: f64) f64 { | |
| 770 | return math.round(a); | |
| 771 | } | |
| 772 | ||
| 773 | export fn roundf(a: f32) f32 { | |
| 774 | return math.round(a); | |
| 775 | } | |
| 776 | ||
| 777 | fn generic_fmod(comptime T: type, x: T, y: T) T { | |
| 59 | fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, len: usize) callconv(.C) ?[*]u8 { | |
| 778 | 60 | @setRuntimeSafety(false); |
| 779 | 61 | |
| 780 | const bits = @typeInfo(T).Float.bits; | |
| 781 | const uint = std.meta.Int(.unsigned, bits); | |
| 782 | const log2uint = math.Log2Int(uint); | |
| 783 | const digits = if (T == f32) 23 else 52; | |
| 784 | const exp_bits = if (T == f32) 9 else 12; | |
| 785 | const bits_minus_1 = bits - 1; | |
| 786 | const mask = if (T == f32) 0xff else 0x7ff; | |
| 787 | var ux = @bitCast(uint, x); | |
| 788 | var uy = @bitCast(uint, y); | |
| 789 | var ex = @intCast(i32, (ux >> digits) & mask); | |
| 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 | ||
| 863 | test "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 | ||
| 884 | fn 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 | ||
| 892 | export fn fminf(x: f32, y: f32) callconv(.C) f32 { | |
| 893 | return generic_fmin(f32, x, y); | |
| 894 | } | |
| 895 | ||
| 896 | export fn fmin(x: f64, y: f64) callconv(.C) f64 { | |
| 897 | return generic_fmin(f64, x, y); | |
| 898 | } | |
| 899 | ||
| 900 | test "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 | ||
| 913 | fn 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 | ||
| 921 | export fn fmaxf(x: f32, y: f32) callconv(.C) f32 { | |
| 922 | return generic_fmax(f32, x, y); | |
| 923 | } | |
| 924 | ||
| 925 | export fn fmax(x: f64, y: f64) callconv(.C) f64 { | |
| 926 | return generic_fmax(f64, x, y); | |
| 927 | } | |
| 928 | ||
| 929 | test "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. | |
| 945 | export 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; | |
| 62 | if (len != 0) { | |
| 63 | var d = dest.?; | |
| 64 | var s = src.?; | |
| 65 | var n = len; | |
| 66 | while (true) { | |
| 67 | d.* = s.*; | |
| 68 | n -= 1; | |
| 69 | if (n == 0) break; | |
| 70 | d += 1; | |
| 71 | s += 1; | |
| 981 | 72 | } |
| 982 | m -= @intCast(i32, i) - 1; | |
| 983 | ix0 |= ix1 >> @intCast(u5, 32 - i); | |
| 984 | ix1 <<= @intCast(u5, i); | |
| 985 | 73 | } |
| 986 | 74 | |
| 987 | // unbias exponent | |
| 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 | ||
| 1075 | test "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 | ||
| 1096 | test "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 | ||
| 1104 | export 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 | ||
| 1180 | test "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 | ||
| 1201 | test "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)))); | |
| 75 | return dest; | |
| 1207 | 76 | } |
lib/std/special/c_stage1.zig created+1187| ... | ... | @@ -0,0 +1,1187 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const maxInt = std.math.maxInt; | |
| 4 | const isNan = std.math.isNan; | |
| 5 | const native_arch = builtin.cpu.arch; | |
| 6 | const native_abi = builtin.abi; | |
| 7 | const native_os = builtin.os.tag; | |
| 8 | ||
| 9 | const is_wasm = switch (native_arch) { | |
| 10 | .wasm32, .wasm64 => true, | |
| 11 | else => false, | |
| 12 | }; | |
| 13 | const is_msvc = switch (native_abi) { | |
| 14 | .msvc => true, | |
| 15 | else => false, | |
| 16 | }; | |
| 17 | const is_freestanding = switch (native_os) { | |
| 18 | .freestanding => true, | |
| 19 | else => false, | |
| 20 | }; | |
| 21 | comptime { | |
| 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 | ||
| 39 | var _fltused: c_int = 1; | |
| 40 | ||
| 41 | extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; | |
| 42 | fn wasm_start() callconv(.C) void { | |
| 43 | _ = main(0, undefined); | |
| 44 | } | |
| 45 | ||
| 46 | fn 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 | ||
| 56 | test "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 | ||
| 64 | fn 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 | ||
| 76 | test "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 | ||
| 84 | fn 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 | ||
| 97 | test "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 | ||
| 107 | fn 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 | ||
| 120 | test "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 | ||
| 130 | fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int { | |
| 131 | return std.cstr.cmp(s1, s2); | |
| 132 | } | |
| 133 | ||
| 134 | fn strlen(s: [*:0]const u8) callconv(.C) usize { | |
| 135 | return std.mem.len(s); | |
| 136 | } | |
| 137 | ||
| 138 | fn 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 | ||
| 151 | fn strerror(errnum: c_int) callconv(.C) [*:0]const u8 { | |
| 152 | _ = errnum; | |
| 153 | return "TODO strerror implementation"; | |
| 154 | } | |
| 155 | ||
| 156 | test "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 | ||
| 163 | export 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 | ||
| 173 | export 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 | ||
| 179 | export 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 | ||
| 189 | export 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 | ||
| 208 | export 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 | ||
| 222 | test "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 | ||
| 233 | export 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 | ||
| 246 | test "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 | ||
| 257 | comptime { | |
| 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. | |
| 266 | fn 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 | ||
| 632 | const math = std.math; | |
| 633 | ||
| 634 | export fn fmodf(x: f32, y: f32) f32 { | |
| 635 | return generic_fmod(f32, x, y); | |
| 636 | } | |
| 637 | export 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 | |
| 643 | export fn floorf(x: f32) f32 { | |
| 644 | return math.floor(x); | |
| 645 | } | |
| 646 | ||
| 647 | export fn ceilf(x: f32) f32 { | |
| 648 | return math.ceil(x); | |
| 649 | } | |
| 650 | ||
| 651 | export fn floor(x: f64) f64 { | |
| 652 | return math.floor(x); | |
| 653 | } | |
| 654 | ||
| 655 | export fn ceil(x: f64) f64 { | |
| 656 | return math.ceil(x); | |
| 657 | } | |
| 658 | ||
| 659 | export fn fma(a: f64, b: f64, c: f64) f64 { | |
| 660 | return math.fma(f64, a, b, c); | |
| 661 | } | |
| 662 | ||
| 663 | export fn fmaf(a: f32, b: f32, c: f32) f32 { | |
| 664 | return math.fma(f32, a, b, c); | |
| 665 | } | |
| 666 | ||
| 667 | export fn sin(a: f64) f64 { | |
| 668 | return math.sin(a); | |
| 669 | } | |
| 670 | ||
| 671 | export fn sinf(a: f32) f32 { | |
| 672 | return math.sin(a); | |
| 673 | } | |
| 674 | ||
| 675 | export fn cos(a: f64) f64 { | |
| 676 | return math.cos(a); | |
| 677 | } | |
| 678 | ||
| 679 | export fn cosf(a: f32) f32 { | |
| 680 | return math.cos(a); | |
| 681 | } | |
| 682 | ||
| 683 | export 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 | ||
| 688 | export 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 | ||
| 693 | export fn exp(a: f64) f64 { | |
| 694 | return math.exp(a); | |
| 695 | } | |
| 696 | ||
| 697 | export fn expf(a: f32) f32 { | |
| 698 | return math.exp(a); | |
| 699 | } | |
| 700 | ||
| 701 | export fn exp2(a: f64) f64 { | |
| 702 | return math.exp2(a); | |
| 703 | } | |
| 704 | ||
| 705 | export fn exp2f(a: f32) f32 { | |
| 706 | return math.exp2(a); | |
| 707 | } | |
| 708 | ||
| 709 | export fn log(a: f64) f64 { | |
| 710 | return math.ln(a); | |
| 711 | } | |
| 712 | ||
| 713 | export fn logf(a: f32) f32 { | |
| 714 | return math.ln(a); | |
| 715 | } | |
| 716 | ||
| 717 | export fn log2(a: f64) f64 { | |
| 718 | return math.log2(a); | |
| 719 | } | |
| 720 | ||
| 721 | export fn log2f(a: f32) f32 { | |
| 722 | return math.log2(a); | |
| 723 | } | |
| 724 | ||
| 725 | export fn log10(a: f64) f64 { | |
| 726 | return math.log10(a); | |
| 727 | } | |
| 728 | ||
| 729 | export fn log10f(a: f32) f32 { | |
| 730 | return math.log10(a); | |
| 731 | } | |
| 732 | ||
| 733 | export fn fabs(a: f64) f64 { | |
| 734 | return math.fabs(a); | |
| 735 | } | |
| 736 | ||
| 737 | export fn fabsf(a: f32) f32 { | |
| 738 | return math.fabs(a); | |
| 739 | } | |
| 740 | ||
| 741 | export fn trunc(a: f64) f64 { | |
| 742 | return math.trunc(a); | |
| 743 | } | |
| 744 | ||
| 745 | export fn truncf(a: f32) f32 { | |
| 746 | return math.trunc(a); | |
| 747 | } | |
| 748 | ||
| 749 | export fn round(a: f64) f64 { | |
| 750 | return math.round(a); | |
| 751 | } | |
| 752 | ||
| 753 | export fn roundf(a: f32) f32 { | |
| 754 | return math.round(a); | |
| 755 | } | |
| 756 | ||
| 757 | fn 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 | ||
| 843 | test "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 | ||
| 864 | fn 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 | ||
| 872 | export fn fminf(x: f32, y: f32) callconv(.C) f32 { | |
| 873 | return generic_fmin(f32, x, y); | |
| 874 | } | |
| 875 | ||
| 876 | export fn fmin(x: f64, y: f64) callconv(.C) f64 { | |
| 877 | return generic_fmin(f64, x, y); | |
| 878 | } | |
| 879 | ||
| 880 | test "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 | ||
| 893 | fn 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 | ||
| 901 | export fn fmaxf(x: f32, y: f32) callconv(.C) f32 { | |
| 902 | return generic_fmax(f32, x, y); | |
| 903 | } | |
| 904 | ||
| 905 | export fn fmax(x: f64, y: f64) callconv(.C) f64 { | |
| 906 | return generic_fmax(f64, x, y); | |
| 907 | } | |
| 908 | ||
| 909 | test "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. | |
| 925 | export 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 | ||
| 1055 | test "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 | ||
| 1076 | test "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 | ||
| 1084 | export 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 | ||
| 1160 | test "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 | ||
| 1181 | test "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 | 370 | } |
| 371 | 371 | |
| 372 | 372 | // This gets the LLVM values from the function and stores them in `dg.args`. |
| 373 | const fn_param_len = decl.ty.fnParamLen(); | |
| 374 | var args = try dg.gpa.alloc(*const llvm.Value, fn_param_len); | |
| 373 | const fn_info = decl.ty.fnInfo(); | |
| 374 | var args = try dg.gpa.alloc(*const llvm.Value, fn_info.param_types.len); | |
| 375 | 375 | |
| 376 | 376 | for (args) |*arg, i| { |
| 377 | 377 | arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i)); |
| 378 | 378 | } |
| 379 | 379 | |
| 380 | // We remove all the basic blocks of a function to support incremental | |
| 381 | // compilation! | |
| 382 | // TODO: remove all basic blocks if functions can have more than one | |
| 383 | if (llvm_func.getFirstBasicBlock()) |bb| { | |
| 380 | // Remove all the basic blocks of a function in order to start over, generating | |
| 381 | // LLVM IR from an empty function body. | |
| 382 | while (llvm_func.getFirstBasicBlock()) |bb| { | |
| 384 | 383 | bb.deleteBasicBlock(); |
| 385 | 384 | } |
| 386 | 385 | |
| ... | ... | @@ -545,20 +544,16 @@ pub const DeclGen = struct { |
| 545 | 544 | |
| 546 | 545 | assert(decl.has_tv); |
| 547 | 546 | const zig_fn_type = decl.ty; |
| 548 | const return_type = zig_fn_type.fnReturnType(); | |
| 549 | const fn_param_len = zig_fn_type.fnParamLen(); | |
| 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); | |
| 547 | const fn_info = zig_fn_type.fnInfo(); | |
| 548 | const return_type = fn_info.return_type; | |
| 554 | 549 | |
| 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 | 551 | defer self.gpa.free(llvm_param_buffer); |
| 557 | 552 | |
| 558 | 553 | var llvm_params_len: c_uint = 0; |
| 559 | for (fn_param_types) |fn_param| { | |
| 560 | if (fn_param.hasCodeGenBits()) { | |
| 561 | llvm_param_buffer[llvm_params_len] = try self.llvmType(fn_param); | |
| 554 | for (fn_info.param_types) |param_ty| { | |
| 555 | if (param_ty.hasCodeGenBits()) { | |
| 556 | llvm_param_buffer[llvm_params_len] = try self.llvmType(param_ty); | |
| 562 | 557 | llvm_params_len += 1; |
| 563 | 558 | } |
| 564 | 559 | } |
| ... | ... | @@ -583,8 +578,85 @@ pub const DeclGen = struct { |
| 583 | 578 | llvm_fn.setUnnamedAddr(.True); |
| 584 | 579 | } |
| 585 | 580 | |
| 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 | } | |
| 587 | 643 | |
| 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 | 660 | if (return_type.isNoReturn()) { |
| 589 | 661 | self.addFnAttr(llvm_fn, "noreturn"); |
| 590 | 662 | } |
src/codegen/llvm/bindings.zig+53| ... | ... | @@ -145,6 +145,12 @@ pub const Value = opaque { |
| 145 | 145 | |
| 146 | 146 | pub const setAlignment = LLVMSetAlignment; |
| 147 | 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 | }; |
| 149 | 155 | |
| 150 | 156 | pub const Type = opaque { |
| ... | ... | @@ -1028,6 +1034,53 @@ pub const TypeKind = enum(c_int) { |
| 1028 | 1034 | X86_AMX, |
| 1029 | 1035 | }; |
| 1030 | 1036 | |
| 1037 | pub 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 | ||
| 1031 | 1084 | pub const address_space = struct { |
| 1032 | 1085 | pub const default: c_uint = 0; |
| 1033 | 1086 |