| author | |
| committer | |
| log | 659c1bdeeebce7bf32e122be6a728fe727112c56 |
| tree | 774feeeee06ea881be48caf769cd0e96944d5345 |
| parent | cb38bd0a1436bd18de8ed57c45ffc890c8ddfb78 |
| parent | 0f0d01a0374dd62d55a0fe3eed72b9ec009af410 |
| signature |
7 files changed, 168 insertions(+), 101 deletions(-)
lib/std/build.zig+8| ... | @@ -1064,6 +1064,9 @@ pub const LibExeObjStep = struct { | ... | @@ -1064,6 +1064,9 @@ pub const LibExeObjStep = struct { |
| 1064 | /// Uses system QEMU installation to run cross compiled foreign architecture build artifacts. | 1064 | /// Uses system QEMU installation to run cross compiled foreign architecture build artifacts. |
| 1065 | enable_qemu: bool = false, | 1065 | enable_qemu: bool = false, |
| 1066 | 1066 | ||
| 1067 | /// Uses system Wasmtime installation to run cross compiled wasm/wasi build artifacts. | ||
| 1068 | enable_wasmtime: bool = false, | ||
| 1069 | |||
| 1067 | /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, | 1070 | /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, |
| 1068 | /// this will be the directory $glibc-build-dir/install/glibcs | 1071 | /// this will be the directory $glibc-build-dir/install/glibcs |
| 1069 | /// Given the example of the aarch64 target, this is the directory | 1072 | /// Given the example of the aarch64 target, this is the directory |
| ... | @@ -1863,6 +1866,11 @@ pub const LibExeObjStep = struct { | ... | @@ -1863,6 +1866,11 @@ pub const LibExeObjStep = struct { |
| 1863 | try zig_args.append(bin_name); | 1866 | try zig_args.append(bin_name); |
| 1864 | try zig_args.append("--test-cmd-bin"); | 1867 | try zig_args.append("--test-cmd-bin"); |
| 1865 | }, | 1868 | }, |
| 1869 | .wasmtime => |bin_name| if (self.enable_wasmtime) { | ||
| 1870 | try zig_args.append("--test-cmd"); | ||
| 1871 | try zig_args.append(bin_name); | ||
| 1872 | try zig_args.append("--test-cmd-bin"); | ||
| 1873 | }, | ||
| 1866 | } | 1874 | } |
| 1867 | for (self.packages.toSliceConst()) |pkg| { | 1875 | for (self.packages.toSliceConst()) |pkg| { |
| 1868 | zig_args.append("--pkg-begin") catch unreachable; | 1876 | zig_args.append("--pkg-begin") catch unreachable; |
lib/std/debug.zig+2-1| ... | @@ -213,7 +213,8 @@ pub fn assert(ok: bool) void { | ... | @@ -213,7 +213,8 @@ pub fn assert(ok: bool) void { |
| 213 | 213 | ||
| 214 | pub fn panic(comptime format: []const u8, args: ...) noreturn { | 214 | pub fn panic(comptime format: []const u8, args: ...) noreturn { |
| 215 | @setCold(true); | 215 | @setCold(true); |
| 216 | const first_trace_addr = @returnAddress(); | 216 | // TODO: remove conditional once wasi / LLVM defines __builtin_return_address |
| 217 | const first_trace_addr = if (builtin.os == .wasi) null else @returnAddress(); | ||
| 217 | panicExtra(null, first_trace_addr, format, args); | 218 | panicExtra(null, first_trace_addr, format, args); |
| 218 | } | 219 | } |
| 219 | 220 |
lib/std/heap.zig+91-98| ... | @@ -33,11 +33,19 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new | ... | @@ -33,11 +33,19 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new |
| 33 | 33 | ||
| 34 | /// This allocator makes a syscall directly for every allocation and free. | 34 | /// This allocator makes a syscall directly for every allocation and free. |
| 35 | /// Thread-safe and lock-free. | 35 | /// Thread-safe and lock-free. |
| 36 | pub const page_allocator = &page_allocator_state; | 36 | pub const page_allocator = if (std.Target.current.isWasm()) |
| 37 | &wasm_page_allocator_state | ||
| 38 | else | ||
| 39 | &page_allocator_state; | ||
| 40 | |||
| 37 | var page_allocator_state = Allocator{ | 41 | var page_allocator_state = Allocator{ |
| 38 | .reallocFn = PageAllocator.realloc, | 42 | .reallocFn = PageAllocator.realloc, |
| 39 | .shrinkFn = PageAllocator.shrink, | 43 | .shrinkFn = PageAllocator.shrink, |
| 40 | }; | 44 | }; |
| 45 | var wasm_page_allocator_state = Allocator{ | ||
| 46 | .reallocFn = WasmPageAllocator.realloc, | ||
| 47 | .shrinkFn = WasmPageAllocator.shrink, | ||
| 48 | }; | ||
| 41 | 49 | ||
| 42 | /// Deprecated. Use `page_allocator`. | 50 | /// Deprecated. Use `page_allocator`. |
| 43 | pub const direct_allocator = page_allocator; | 51 | pub const direct_allocator = page_allocator; |
| ... | @@ -238,6 +246,88 @@ const PageAllocator = struct { | ... | @@ -238,6 +246,88 @@ const PageAllocator = struct { |
| 238 | } | 246 | } |
| 239 | }; | 247 | }; |
| 240 | 248 | ||
| 249 | // TODO Exposed LLVM intrinsics is a bug | ||
| 250 | // See: https://github.com/ziglang/zig/issues/2291 | ||
| 251 | extern fn @"llvm.wasm.memory.size.i32"(u32) u32; | ||
| 252 | extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32; | ||
| 253 | |||
| 254 | /// TODO: make this re-use freed pages, and cooperate with other callers of these global intrinsics | ||
| 255 | /// by better utilizing the return value of grow() | ||
| 256 | const WasmPageAllocator = struct { | ||
| 257 | var start_ptr: [*]u8 = undefined; | ||
| 258 | var num_pages: usize = 0; | ||
| 259 | var end_index: usize = 0; | ||
| 260 | |||
| 261 | comptime { | ||
| 262 | if (builtin.arch != .wasm32) { | ||
| 263 | @compileError("WasmPageAllocator is only available for wasm32 arch"); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | fn alloc(allocator: *Allocator, size: usize, alignment: u29) ![]u8 { | ||
| 268 | const addr = @ptrToInt(start_ptr) + end_index; | ||
| 269 | const adjusted_addr = mem.alignForward(addr, alignment); | ||
| 270 | const adjusted_index = end_index + (adjusted_addr - addr); | ||
| 271 | const new_end_index = adjusted_index + size; | ||
| 272 | |||
| 273 | if (new_end_index > num_pages * mem.page_size) { | ||
| 274 | const required_memory = new_end_index - (num_pages * mem.page_size); | ||
| 275 | |||
| 276 | var num_pages: usize = required_memory / mem.page_size; | ||
| 277 | if (required_memory % mem.page_size != 0) { | ||
| 278 | num_pages += 1; | ||
| 279 | } | ||
| 280 | |||
| 281 | const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, num_pages)); | ||
| 282 | if (prev_page == -1) { | ||
| 283 | return error.OutOfMemory; | ||
| 284 | } | ||
| 285 | |||
| 286 | num_pages += num_pages; | ||
| 287 | } | ||
| 288 | |||
| 289 | const result = start_ptr[adjusted_index..new_end_index]; | ||
| 290 | end_index = new_end_index; | ||
| 291 | |||
| 292 | return result; | ||
| 293 | } | ||
| 294 | |||
| 295 | // Check if memory is the last "item" and is aligned correctly | ||
| 296 | fn is_last_item(memory: []u8, alignment: u29) bool { | ||
| 297 | return memory.ptr == start_ptr + end_index - memory.len and mem.alignForward(@ptrToInt(memory.ptr), alignment) == @ptrToInt(memory.ptr); | ||
| 298 | } | ||
| 299 | |||
| 300 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { | ||
| 301 | // Initialize start_ptr at the first realloc | ||
| 302 | if (num_pages == 0) { | ||
| 303 | start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * mem.page_size); | ||
| 304 | } | ||
| 305 | |||
| 306 | if (is_last_item(old_mem, new_align)) { | ||
| 307 | const start_index = end_index - old_mem.len; | ||
| 308 | const new_end_index = start_index + new_size; | ||
| 309 | |||
| 310 | if (new_end_index > num_pages * mem.page_size) { | ||
| 311 | _ = try alloc(allocator, new_end_index - end_index, new_align); | ||
| 312 | } | ||
| 313 | const result = start_ptr[start_index..new_end_index]; | ||
| 314 | |||
| 315 | end_index = new_end_index; | ||
| 316 | return result; | ||
| 317 | } else if (new_size <= old_mem.len and new_align <= old_align) { | ||
| 318 | return error.OutOfMemory; | ||
| 319 | } else { | ||
| 320 | const result = try alloc(allocator, new_size, new_align); | ||
| 321 | mem.copy(u8, result, old_mem); | ||
| 322 | return result; | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { | ||
| 327 | return old_mem[0..new_size]; | ||
| 328 | } | ||
| 329 | }; | ||
| 330 | |||
| 241 | pub const HeapAllocator = switch (builtin.os) { | 331 | pub const HeapAllocator = switch (builtin.os) { |
| 242 | .windows => struct { | 332 | .windows => struct { |
| 243 | allocator: Allocator, | 333 | allocator: Allocator, |
| ... | @@ -487,103 +577,6 @@ pub const FixedBufferAllocator = struct { | ... | @@ -487,103 +577,6 @@ pub const FixedBufferAllocator = struct { |
| 487 | } | 577 | } |
| 488 | }; | 578 | }; |
| 489 | 579 | ||
| 490 | // TODO Exposed LLVM intrinsics is a bug | ||
| 491 | // See: https://github.com/ziglang/zig/issues/2291 | ||
| 492 | extern fn @"llvm.wasm.memory.size.i32"(u32) u32; | ||
| 493 | extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32; | ||
| 494 | |||
| 495 | pub const wasm_allocator = &wasm_allocator_state.allocator; | ||
| 496 | var wasm_allocator_state = WasmAllocator{ | ||
| 497 | .allocator = Allocator{ | ||
| 498 | .reallocFn = WasmAllocator.realloc, | ||
| 499 | .shrinkFn = WasmAllocator.shrink, | ||
| 500 | }, | ||
| 501 | .start_ptr = undefined, | ||
| 502 | .num_pages = 0, | ||
| 503 | .end_index = 0, | ||
| 504 | }; | ||
| 505 | |||
| 506 | const WasmAllocator = struct { | ||
| 507 | allocator: Allocator, | ||
| 508 | start_ptr: [*]u8, | ||
| 509 | num_pages: usize, | ||
| 510 | end_index: usize, | ||
| 511 | |||
| 512 | comptime { | ||
| 513 | if (builtin.arch != .wasm32) { | ||
| 514 | @compileError("WasmAllocator is only available for wasm32 arch"); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | fn alloc(allocator: *Allocator, size: usize, alignment: u29) ![]u8 { | ||
| 519 | const self = @fieldParentPtr(WasmAllocator, "allocator", allocator); | ||
| 520 | |||
| 521 | const addr = @ptrToInt(self.start_ptr) + self.end_index; | ||
| 522 | const adjusted_addr = mem.alignForward(addr, alignment); | ||
| 523 | const adjusted_index = self.end_index + (adjusted_addr - addr); | ||
| 524 | const new_end_index = adjusted_index + size; | ||
| 525 | |||
| 526 | if (new_end_index > self.num_pages * mem.page_size) { | ||
| 527 | const required_memory = new_end_index - (self.num_pages * mem.page_size); | ||
| 528 | |||
| 529 | var num_pages: usize = required_memory / mem.page_size; | ||
| 530 | if (required_memory % mem.page_size != 0) { | ||
| 531 | num_pages += 1; | ||
| 532 | } | ||
| 533 | |||
| 534 | const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, num_pages)); | ||
| 535 | if (prev_page == -1) { | ||
| 536 | return error.OutOfMemory; | ||
| 537 | } | ||
| 538 | |||
| 539 | self.num_pages += num_pages; | ||
| 540 | } | ||
| 541 | |||
| 542 | const result = self.start_ptr[adjusted_index..new_end_index]; | ||
| 543 | self.end_index = new_end_index; | ||
| 544 | |||
| 545 | return result; | ||
| 546 | } | ||
| 547 | |||
| 548 | // Check if memory is the last "item" and is aligned correctly | ||
| 549 | fn is_last_item(allocator: *Allocator, memory: []u8, alignment: u29) bool { | ||
| 550 | const self = @fieldParentPtr(WasmAllocator, "allocator", allocator); | ||
| 551 | return memory.ptr == self.start_ptr + self.end_index - memory.len and mem.alignForward(@ptrToInt(memory.ptr), alignment) == @ptrToInt(memory.ptr); | ||
| 552 | } | ||
| 553 | |||
| 554 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { | ||
| 555 | const self = @fieldParentPtr(WasmAllocator, "allocator", allocator); | ||
| 556 | |||
| 557 | // Initialize start_ptr at the first realloc | ||
| 558 | if (self.num_pages == 0) { | ||
| 559 | self.start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * mem.page_size); | ||
| 560 | } | ||
| 561 | |||
| 562 | if (is_last_item(allocator, old_mem, new_align)) { | ||
| 563 | const start_index = self.end_index - old_mem.len; | ||
| 564 | const new_end_index = start_index + new_size; | ||
| 565 | |||
| 566 | if (new_end_index > self.num_pages * mem.page_size) { | ||
| 567 | _ = try alloc(allocator, new_end_index - self.end_index, new_align); | ||
| 568 | } | ||
| 569 | const result = self.start_ptr[start_index..new_end_index]; | ||
| 570 | |||
| 571 | self.end_index = new_end_index; | ||
| 572 | return result; | ||
| 573 | } else if (new_size <= old_mem.len and new_align <= old_align) { | ||
| 574 | return error.OutOfMemory; | ||
| 575 | } else { | ||
| 576 | const result = try alloc(allocator, new_size, new_align); | ||
| 577 | mem.copy(u8, result, old_mem); | ||
| 578 | return result; | ||
| 579 | } | ||
| 580 | } | ||
| 581 | |||
| 582 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { | ||
| 583 | return old_mem[0..new_size]; | ||
| 584 | } | ||
| 585 | }; | ||
| 586 | |||
| 587 | pub const ThreadSafeFixedBufferAllocator = blk: { | 580 | pub const ThreadSafeFixedBufferAllocator = blk: { |
| 588 | if (builtin.single_threaded) { | 581 | if (builtin.single_threaded) { |
| 589 | break :blk FixedBufferAllocator; | 582 | break :blk FixedBufferAllocator; |
lib/std/os.zig+1-1| ... | @@ -1527,7 +1527,7 @@ pub fn isatty(handle: fd_t) bool { | ... | @@ -1527,7 +1527,7 @@ pub fn isatty(handle: fd_t) bool { |
| 1527 | return system.isatty(handle) != 0; | 1527 | return system.isatty(handle) != 0; |
| 1528 | } | 1528 | } |
| 1529 | if (builtin.os == .wasi) { | 1529 | if (builtin.os == .wasi) { |
| 1530 | @compileError("TODO implement std.os.isatty for WASI"); | 1530 | return system.isatty(handle); |
| 1531 | } | 1531 | } |
| 1532 | if (builtin.os == .linux) { | 1532 | if (builtin.os == .linux) { |
| 1533 | var wsz: linux.winsize = undefined; | 1533 | var wsz: linux.winsize = undefined; |
lib/std/os/bits/wasi.zig+7-1| ... | @@ -138,7 +138,7 @@ pub const FDFLAG_NONBLOCK: fdflags_t = 0x0004; | ... | @@ -138,7 +138,7 @@ pub const FDFLAG_NONBLOCK: fdflags_t = 0x0004; |
| 138 | pub const FDFLAG_RSYNC: fdflags_t = 0x0008; | 138 | pub const FDFLAG_RSYNC: fdflags_t = 0x0008; |
| 139 | pub const FDFLAG_SYNC: fdflags_t = 0x0010; | 139 | pub const FDFLAG_SYNC: fdflags_t = 0x0010; |
| 140 | 140 | ||
| 141 | const fdstat_t = extern struct { | 141 | pub const fdstat_t = extern struct { |
| 142 | fs_filetype: filetype_t, | 142 | fs_filetype: filetype_t, |
| 143 | fs_flags: fdflags_t, | 143 | fs_flags: fdflags_t, |
| 144 | fs_rights_base: rights_t, | 144 | fs_rights_base: rights_t, |
| ... | @@ -298,6 +298,7 @@ pub const subscription_t = extern struct { | ... | @@ -298,6 +298,7 @@ pub const subscription_t = extern struct { |
| 298 | }; | 298 | }; |
| 299 | 299 | ||
| 300 | pub const timestamp_t = u64; | 300 | pub const timestamp_t = u64; |
| 301 | pub const time_t = i64; // match https://github.com/CraneStation/wasi-libc | ||
| 301 | 302 | ||
| 302 | pub const userdata_t = u64; | 303 | pub const userdata_t = u64; |
| 303 | 304 | ||
| ... | @@ -305,3 +306,8 @@ pub const whence_t = u8; | ... | @@ -305,3 +306,8 @@ pub const whence_t = u8; |
| 305 | pub const WHENCE_CUR: whence_t = 0; | 306 | pub const WHENCE_CUR: whence_t = 0; |
| 306 | pub const WHENCE_END: whence_t = 1; | 307 | pub const WHENCE_END: whence_t = 1; |
| 307 | pub const WHENCE_SET: whence_t = 2; | 308 | pub const WHENCE_SET: whence_t = 2; |
| 309 | |||
| 310 | pub const timespec = extern struct { | ||
| 311 | tv_sec: time_t, | ||
| 312 | tv_nsec: isize, | ||
| 313 | }; |
lib/std/os/wasi.zig+51| ... | @@ -76,3 +76,54 @@ pub extern "wasi_unstable" fn sched_yield() errno_t; | ... | @@ -76,3 +76,54 @@ pub extern "wasi_unstable" fn sched_yield() errno_t; |
| 76 | pub extern "wasi_unstable" fn sock_recv(sock: fd_t, ri_data: *const iovec_t, ri_data_len: usize, ri_flags: riflags_t, ro_datalen: *usize, ro_flags: *roflags_t) errno_t; | 76 | pub extern "wasi_unstable" fn sock_recv(sock: fd_t, ri_data: *const iovec_t, ri_data_len: usize, ri_flags: riflags_t, ro_datalen: *usize, ro_flags: *roflags_t) errno_t; |
| 77 | pub extern "wasi_unstable" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t; | 77 | pub extern "wasi_unstable" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t; |
| 78 | pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; | 78 | pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; |
| 79 | |||
| 80 | /// Get the errno from a syscall return value, or 0 for no error. | ||
| 81 | pub fn getErrno(r: usize) usize { | ||
| 82 | const signed_r = @bitCast(isize, r); | ||
| 83 | return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; | ||
| 84 | } | ||
| 85 | |||
| 86 | pub fn clock_getres(clock_id: i32, res: *timespec) errno_t { | ||
| 87 | var ts: timestamp_t = undefined; | ||
| 88 | const err = clock_res_get(@bitCast(u32, clock_id), &ts); | ||
| 89 | if (err != 0) { | ||
| 90 | return err; | ||
| 91 | } | ||
| 92 | res.* = .{ | ||
| 93 | .tv_sec = @intCast(i64, ts / std.time.ns_per_s), | ||
| 94 | .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), | ||
| 95 | }; | ||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 99 | pub fn clock_gettime(clock_id: i32, tp: *timespec) errno_t { | ||
| 100 | var ts: timestamp_t = undefined; | ||
| 101 | const err = clock_time_get(@bitCast(u32, clock_id), 1, &ts); | ||
| 102 | if (err != 0) { | ||
| 103 | return err; | ||
| 104 | } | ||
| 105 | tp.* = .{ | ||
| 106 | .tv_sec = @intCast(i64, ts / std.time.ns_per_s), | ||
| 107 | .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), | ||
| 108 | }; | ||
| 109 | return 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | pub fn isatty(fd: fd_t) bool { | ||
| 113 | var statbuf: fdstat_t = undefined; | ||
| 114 | const err = fd_fdstat_get(fd, &statbuf); | ||
| 115 | if (err != 0) { | ||
| 116 | // errno = err; | ||
| 117 | return false; | ||
| 118 | } | ||
| 119 | |||
| 120 | // A tty is a character device that we can't seek or tell on. | ||
| 121 | if (statbuf.fs_filetype != FILETYPE_CHARACTER_DEVICE or | ||
| 122 | (statbuf.fs_rights_base & (RIGHT_FD_SEEK | RIGHT_FD_TELL)) != 0) | ||
| 123 | { | ||
| 124 | // errno = ENOTTY; | ||
| 125 | return false; | ||
| 126 | } | ||
| 127 | |||
| 128 | return true; | ||
| 129 | } |
lib/std/target.zig+8| ... | @@ -611,6 +611,7 @@ pub const Target = union(enum) { | ... | @@ -611,6 +611,7 @@ pub const Target = union(enum) { |
| 611 | native, | 611 | native, |
| 612 | qemu: []const u8, | 612 | qemu: []const u8, |
| 613 | wine: []const u8, | 613 | wine: []const u8, |
| 614 | wasmtime: []const u8, | ||
| 614 | unavailable, | 615 | unavailable, |
| 615 | }; | 616 | }; |
| 616 | 617 | ||
| ... | @@ -649,6 +650,13 @@ pub const Target = union(enum) { | ... | @@ -649,6 +650,13 @@ pub const Target = union(enum) { |
| 649 | } | 650 | } |
| 650 | } | 651 | } |
| 651 | 652 | ||
| 653 | if (self.isWasm()) { | ||
| 654 | switch (self.getArchPtrBitWidth()) { | ||
| 655 | 32 => return Executor{ .wasmtime = "wasmtime" }, | ||
| 656 | else => return .unavailable, | ||
| 657 | } | ||
| 658 | } | ||
| 659 | |||
| 652 | return .unavailable; | 660 | return .unavailable; |
| 653 | } | 661 | } |
| 654 | }; | 662 | }; |