From 747529e96bf61f1a2017224f956d4029e55fce42 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Tue, 19 Nov 2019 00:31:26 -0600 Subject: [PATCH 1/8] Use wasm_allocator --- lib/std/heap.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 24ab39573..f251c8398 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -33,7 +33,7 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new /// This allocator makes a syscall directly for every allocation and free. /// Thread-safe and lock-free. -pub const direct_allocator = &direct_allocator_state; +pub const direct_allocator = if (builtin.arch == .wasm32) wasm_allocator else &direct_allocator_state; var direct_allocator_state = Allocator{ .reallocFn = DirectAllocator.realloc, .shrinkFn = DirectAllocator.shrink, From c3d93cd9f2a6ebc4e7104435a8bf24213eb4ba34 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Tue, 19 Nov 2019 16:12:26 -0600 Subject: [PATCH 2/8] WASI time_t / timespec --- lib/std/os/bits/wasi.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/std/os/bits/wasi.zig b/lib/std/os/bits/wasi.zig index 93d2a82fd..36944e563 100644 --- a/lib/std/os/bits/wasi.zig +++ b/lib/std/os/bits/wasi.zig @@ -298,6 +298,7 @@ pub const subscription_t = extern struct { }; pub const timestamp_t = u64; +pub const time_t = i64; // match https://github.com/CraneStation/wasi-libc pub const userdata_t = u64; @@ -305,3 +306,8 @@ pub const whence_t = u8; pub const WHENCE_CUR: whence_t = 0; pub const WHENCE_END: whence_t = 1; pub const WHENCE_SET: whence_t = 2; + +pub const timespec = extern struct { + tv_sec: time_t, + tv_nsec: isize, +}; From 14e9c7d1f2f9a1b583237626e6b834c8e86d6cf7 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Tue, 19 Nov 2019 19:44:19 -0600 Subject: [PATCH 3/8] WASI clock functions --- lib/std/os/wasi.zig | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/std/os/wasi.zig b/lib/std/os/wasi.zig index fce29ef0b..7f030510e 100644 --- a/lib/std/os/wasi.zig +++ b/lib/std/os/wasi.zig @@ -76,3 +76,35 @@ pub extern "wasi_unstable" fn sched_yield() errno_t; 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; 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; pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; + +/// Get the errno from a syscall return value, or 0 for no error. +pub fn getErrno(r: usize) usize { + const signed_r = @bitCast(isize, r); + return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; +} + +pub fn clock_getres(clock_id: i32, res: *timespec) errno_t { + var ts: timestamp_t = undefined; + const err = clock_res_get(@bitCast(u32, clock_id), &ts); + if (err != 0) { + return err; + } + res.* = .{ + .tv_sec = @intCast(i64, ts / 1000000000), + .tv_nsec = @intCast(isize, ts % 1000000000), + }; + return 0; +} + +pub fn clock_gettime(clock_id: i32, tp: *timespec) errno_t { + var ts: timestamp_t = undefined; + const err = clock_time_get(@bitCast(u32, clock_id), 1, &ts); + if (err != 0) { + return err; + } + tp.* = .{ + .tv_sec = @intCast(i64, ts / 1000000000), + .tv_nsec = @intCast(isize, ts % 1000000000), + }; + return 0; +} From b88bb93af3cf22f5a994100e037dbb4091144f0c Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Tue, 19 Nov 2019 20:17:00 -0600 Subject: [PATCH 4/8] WASI isatty --- lib/std/os.zig | 2 +- lib/std/os/bits/wasi.zig | 2 +- lib/std/os/wasi.zig | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 68a3a6e9f..36c1c8dbf 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1522,7 +1522,7 @@ pub fn isatty(handle: fd_t) bool { return system.isatty(handle) != 0; } if (builtin.os == .wasi) { - @compileError("TODO implement std.os.isatty for WASI"); + return system.isatty(handle); } if (builtin.os == .linux) { var wsz: linux.winsize = undefined; diff --git a/lib/std/os/bits/wasi.zig b/lib/std/os/bits/wasi.zig index 36944e563..139418ded 100644 --- a/lib/std/os/bits/wasi.zig +++ b/lib/std/os/bits/wasi.zig @@ -138,7 +138,7 @@ pub const FDFLAG_NONBLOCK: fdflags_t = 0x0004; pub const FDFLAG_RSYNC: fdflags_t = 0x0008; pub const FDFLAG_SYNC: fdflags_t = 0x0010; -const fdstat_t = extern struct { +pub const fdstat_t = extern struct { fs_filetype: filetype_t, fs_flags: fdflags_t, fs_rights_base: rights_t, diff --git a/lib/std/os/wasi.zig b/lib/std/os/wasi.zig index 7f030510e..e8f4d052e 100644 --- a/lib/std/os/wasi.zig +++ b/lib/std/os/wasi.zig @@ -108,3 +108,22 @@ pub fn clock_gettime(clock_id: i32, tp: *timespec) errno_t { }; return 0; } + +pub fn isatty(fd: fd_t) bool { + var statbuf: fdstat_t = undefined; + const err = fd_fdstat_get(fd, &statbuf); + if (err != 0) { + // errno = err; + return false; + } + + // A tty is a character device that we can't seek or tell on. + if (statbuf.fs_filetype != FILETYPE_CHARACTER_DEVICE or + (statbuf.fs_rights_base & (RIGHT_FD_SEEK | RIGHT_FD_TELL)) != 0) + { + // errno = ENOTTY; + return false; + } + + return true; +} From 218f9ff34e94511489d42f41831bc249464ed853 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Tue, 19 Nov 2019 20:55:55 -0600 Subject: [PATCH 5/8] Work around WASI's nonexistent @returnAddress() --- lib/std/debug.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 3cf4306ea..86d652224 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -213,7 +213,8 @@ pub fn assert(ok: bool) void { pub fn panic(comptime format: []const u8, args: ...) noreturn { @setCold(true); - const first_trace_addr = @returnAddress(); + // TODO: remove conditional once wasi / LLVM defines __builtin_return_address + const first_trace_addr = if (builtin.os == .wasi) null else @returnAddress(); panicExtra(null, first_trace_addr, format, args); } From d27721f58cfeff5fcd805125875e9c55b54b65bd Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Wed, 20 Nov 2019 00:56:34 -0600 Subject: [PATCH 6/8] Add .enable_wasmtime build flag --- lib/std/build.zig | 8 ++++++++ lib/std/target.zig | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/std/build.zig b/lib/std/build.zig index b124b12fe..c4095a7fd 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1064,6 +1064,9 @@ pub const LibExeObjStep = struct { /// Uses system QEMU installation to run cross compiled foreign architecture build artifacts. enable_qemu: bool = false, + /// Uses system Wasmtime installation to run cross compiled wasm/wasi build artifacts. + enable_wasmtime: bool = false, + /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, /// this will be the directory $glibc-build-dir/install/glibcs /// Given the example of the aarch64 target, this is the directory @@ -1863,6 +1866,11 @@ pub const LibExeObjStep = struct { try zig_args.append(bin_name); try zig_args.append("--test-cmd-bin"); }, + .wasmtime => |bin_name| if (self.enable_wasmtime) { + try zig_args.append("--test-cmd"); + try zig_args.append(bin_name); + try zig_args.append("--test-cmd-bin"); + }, } for (self.packages.toSliceConst()) |pkg| { zig_args.append("--pkg-begin") catch unreachable; diff --git a/lib/std/target.zig b/lib/std/target.zig index 5c0ac3d90..9652d5b6e 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -611,6 +611,7 @@ pub const Target = union(enum) { native, qemu: []const u8, wine: []const u8, + wasmtime: []const u8, unavailable, }; @@ -649,6 +650,13 @@ pub const Target = union(enum) { } } + if (self.isWasm()) { + switch (self.getArchPtrBitWidth()) { + 32 => return Executor{ .wasmtime = "wasmtime" }, + else => return .unavailable, + } + } + return .unavailable; } }; From 0f0d01a0374dd62d55a0fe3eed72b9ec009af410 Mon Sep 17 00:00:00 2001 From: Benjamin Feng Date: Thu, 21 Nov 2019 18:41:02 -0600 Subject: [PATCH 7/8] Replace magic numbers with named constants --- lib/std/os/wasi.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/os/wasi.zig b/lib/std/os/wasi.zig index e8f4d052e..7ba2339e0 100644 --- a/lib/std/os/wasi.zig +++ b/lib/std/os/wasi.zig @@ -90,8 +90,8 @@ pub fn clock_getres(clock_id: i32, res: *timespec) errno_t { return err; } res.* = .{ - .tv_sec = @intCast(i64, ts / 1000000000), - .tv_nsec = @intCast(isize, ts % 1000000000), + .tv_sec = @intCast(i64, ts / std.time.ns_per_s), + .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), }; return 0; } @@ -103,8 +103,8 @@ pub fn clock_gettime(clock_id: i32, tp: *timespec) errno_t { return err; } tp.* = .{ - .tv_sec = @intCast(i64, ts / 1000000000), - .tv_nsec = @intCast(isize, ts % 1000000000), + .tv_sec = @intCast(i64, ts / std.time.ns_per_s), + .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), }; return 0; } From 4261fa3c49be715355c9623102bad0bf93d537a3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Nov 2019 18:46:17 -0500 Subject: [PATCH 8/8] move logic to the appropriate layers; add new compile error --- lib/std/heap.zig | 8 ++-- lib/std/os.zig | 44 +++++++++++++++++++++- lib/std/os/wasi.zig | 50 +------------------------ lib/std/target.zig | 6 ++- src/analyze.cpp | 3 +- src/ir.cpp | 8 ++++ src/target.cpp | 6 ++- test/compile_errors.zig | 19 +++++++++- test/stage1/behavior/asm.zig | 3 +- test/stage1/behavior/new_stack_call.zig | 3 ++ 10 files changed, 91 insertions(+), 59 deletions(-) diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 259c29f6e..997f1fa06 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -273,17 +273,17 @@ const WasmPageAllocator = struct { if (new_end_index > num_pages * mem.page_size) { const required_memory = new_end_index - (num_pages * mem.page_size); - var num_pages: usize = required_memory / mem.page_size; + var inner_num_pages: usize = required_memory / mem.page_size; if (required_memory % mem.page_size != 0) { - num_pages += 1; + inner_num_pages += 1; } - const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, num_pages)); + const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, inner_num_pages)); if (prev_page == -1) { return error.OutOfMemory; } - num_pages += num_pages; + num_pages += inner_num_pages; } const result = start_ptr[adjusted_index..new_end_index]; diff --git a/lib/std/os.zig b/lib/std/os.zig index a616fbc49..22d52b24a 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1527,7 +1527,22 @@ pub fn isatty(handle: fd_t) bool { return system.isatty(handle) != 0; } if (builtin.os == .wasi) { - return system.isatty(handle); + var statbuf: fdstat_t = undefined; + const err = system.fd_fdstat_get(handle, &statbuf); + if (err != 0) { + // errno = err; + return false; + } + + // A tty is a character device that we can't seek or tell on. + if (statbuf.fs_filetype != FILETYPE_CHARACTER_DEVICE or + (statbuf.fs_rights_base & (RIGHT_FD_SEEK | RIGHT_FD_TELL)) != 0) + { + // errno = ENOTTY; + return false; + } + + return true; } if (builtin.os == .linux) { var wsz: linux.winsize = undefined; @@ -2720,6 +2735,20 @@ pub fn dl_iterate_phdr( pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError; pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { + if (comptime std.Target.current.getOs() == .wasi) { + var ts: timestamp_t = undefined; + switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) { + 0 => { + tp.* = .{ + .tv_sec = @intCast(i64, ts / std.time.ns_per_s), + .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), + }; + }, + EINVAL => return error.UnsupportedClock, + else => |err| return unexpectedErrno(err), + } + return; + } switch (errno(system.clock_gettime(clk_id, tp))) { 0 => return, EFAULT => unreachable, @@ -2729,6 +2758,19 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { } pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void { + if (comptime std.Target.current.getOs() == .wasi) { + var ts: timestamp_t = undefined; + switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) { + 0 => res.* = .{ + .tv_sec = @intCast(i64, ts / std.time.ns_per_s), + .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), + }, + EINVAL => return error.UnsupportedClock, + else => |err| return unexpectedErrno(err), + } + return; + } + switch (errno(system.clock_getres(clk_id, res))) { 0 => return, EFAULT => unreachable, diff --git a/lib/std/os/wasi.zig b/lib/std/os/wasi.zig index 1ffc9610f..c417097c7 100644 --- a/lib/std/os/wasi.zig +++ b/lib/std/os/wasi.zig @@ -78,52 +78,6 @@ pub extern "wasi_unstable" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; /// Get the errno from a syscall return value, or 0 for no error. -pub fn getErrno(r: usize) usize { - const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; -} - -pub fn clock_getres(clock_id: i32, res: *timespec) errno_t { - var ts: timestamp_t = undefined; - const err = clock_res_get(@bitCast(u32, clock_id), &ts); - if (err != 0) { - return err; - } - res.* = .{ - .tv_sec = @intCast(i64, ts / std.time.ns_per_s), - .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), - }; - return 0; -} - -pub fn clock_gettime(clock_id: i32, tp: *timespec) errno_t { - var ts: timestamp_t = undefined; - const err = clock_time_get(@bitCast(u32, clock_id), 1, &ts); - if (err != 0) { - return err; - } - tp.* = .{ - .tv_sec = @intCast(i64, ts / std.time.ns_per_s), - .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), - }; - return 0; -} - -pub fn isatty(fd: fd_t) bool { - var statbuf: fdstat_t = undefined; - const err = fd_fdstat_get(fd, &statbuf); - if (err != 0) { - // errno = err; - return false; - } - - // A tty is a character device that we can't seek or tell on. - if (statbuf.fs_filetype != FILETYPE_CHARACTER_DEVICE or - (statbuf.fs_rights_base & (RIGHT_FD_SEEK | RIGHT_FD_TELL)) != 0) - { - // errno = ENOTTY; - return false; - } - - return true; +pub fn getErrno(r: errno_t) usize { + return r; } diff --git a/lib/std/target.zig b/lib/std/target.zig index 9652d5b6e..4d19fa627 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -607,6 +607,10 @@ pub const Target = union(enum) { } } + pub fn supportsNewStackCall(self: Target) bool { + return !self.isWasm(); + } + pub const Executor = union(enum) { native, qemu: []const u8, @@ -650,7 +654,7 @@ pub const Target = union(enum) { } } - if (self.isWasm()) { + if (self.getOs() == .wasi) { switch (self.getArchPtrBitWidth()) { 32 => return Executor{ .wasmtime = "wasmtime" }, else => return .unavailable, diff --git a/src/analyze.cpp b/src/analyze.cpp index e47b67a49..e1b086059 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -978,7 +978,8 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) { if (g->zig_target->arch == ZigLLVM_x86 || g->zig_target->arch == ZigLLVM_x86_64 || target_is_arm(g->zig_target) || - target_is_riscv(g->zig_target)) + target_is_riscv(g->zig_target) || + target_is_wasm(g->zig_target)) { X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type); return abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval; diff --git a/src/ir.cpp b/src/ir.cpp index f4bcfb633..7e867f1c9 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -17095,6 +17095,14 @@ static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstructionCall if (call_instruction->new_stack == nullptr) return nullptr; + if (!call_instruction->is_async_call_builtin && + arch_stack_pointer_register_name(ira->codegen->zig_target->arch) == nullptr) + { + ir_add_error(ira, &call_instruction->base, + buf_sprintf("target arch '%s' does not support @newStackCall", + target_arch_name(ira->codegen->zig_target->arch))); + } + IrInstruction *new_stack = call_instruction->new_stack->child; if (type_is_invalid(new_stack->value->type)) return ira->codegen->invalid_instruction; diff --git a/src/target.cpp b/src/target.cpp index 8fd7d7055..538f836f2 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -1458,6 +1458,10 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) { case ZigLLVM_mipsel: return "sp"; + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + return nullptr; // known to be not available + case ZigLLVM_amdgcn: case ZigLLVM_amdil: case ZigLLVM_amdil64: @@ -1491,8 +1495,6 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) { case ZigLLVM_systemz: case ZigLLVM_tce: case ZigLLVM_tcele: - case ZigLLVM_wasm32: - case ZigLLVM_wasm64: case ZigLLVM_xcore: case ZigLLVM_ppc: case ZigLLVM_ppc64: diff --git a/test/compile_errors.zig b/test/compile_errors.zig index dd0f80b25..c1e2d579a 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,24 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addCase(x: { + var tc = cases.create("@newStackCall on unsupported target", + \\export fn entry() void { + \\ var buf: [10]u8 align(16) = undefined; + \\ @newStackCall(&buf, foo); + \\} + \\fn foo() void {} + , "tmp.zig:3:5: error: target arch 'wasm32' does not support @newStackCall"); + tc.target = tests.Target{ + .Cross = tests.CrossTarget{ + .arch = .wasm32, + .os = .wasi, + .abi = .none, + }, + }; + break :x tc; + }); + cases.add( "incompatible sentinels", \\export fn entry1(ptr: [*:255]u8) [*:0]u8 { @@ -26,7 +44,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:8:35: note: destination array requires a terminating '0' sentinel, but source array has a terminating '255' sentinel", "tmp.zig:11:31: error: expected type '[2:0]u8', found '[2]u8'", "tmp.zig:11:31: note: destination array requires a terminating '0' sentinel", - ); cases.add( diff --git a/test/stage1/behavior/asm.zig b/test/stage1/behavior/asm.zig index 9278d45d5..d1404c6f4 100644 --- a/test/stage1/behavior/asm.zig +++ b/test/stage1/behavior/asm.zig @@ -1,5 +1,6 @@ +const std = @import("std"); const config = @import("builtin"); -const expect = @import("std").testing.expect; +const expect = std.testing.expect; comptime { if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { diff --git a/test/stage1/behavior/new_stack_call.zig b/test/stage1/behavior/new_stack_call.zig index a2f3773af..b057566d9 100644 --- a/test/stage1/behavior/new_stack_call.zig +++ b/test/stage1/behavior/new_stack_call.zig @@ -12,6 +12,9 @@ test "calling a function with a new stack" { // TODO: https://github.com/ziglang/zig/issues/3338 return error.SkipZigTest; } + if (comptime !std.Target.current.supportsNewStackCall()) { + return error.SkipZigTest; + } const arg = 1234;