authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-25 18:46:35-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-25 18:46:35-05:00
log80f79cc9e8ed02f631c9d59f68e5f20a909e85fc
treeba8da3ef0611a547fe67c61ed2d42d4ea67e6d2e
parentcb38bd0a1436bd18de8ed57c45ffc890c8ddfb78
parent4261fa3c49be715355c9623102bad0bf93d537a3
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'fengb-wasi-run-tests'

closes #3730

13 files changed, 205 insertions(+), 106 deletions(-)

lib/std/build.zig+8
......@@ -1064,6 +1064,9 @@ pub const LibExeObjStep = struct {
10641064 /// Uses system QEMU installation to run cross compiled foreign architecture build artifacts.
10651065 enable_qemu: bool = false,
10661066
1067 /// Uses system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
1068 enable_wasmtime: bool = false,
1069
10671070 /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
10681071 /// this will be the directory $glibc-build-dir/install/glibcs
10691072 /// Given the example of the aarch64 target, this is the directory
......@@ -1863,6 +1866,11 @@ pub const LibExeObjStep = struct {
18631866 try zig_args.append(bin_name);
18641867 try zig_args.append("--test-cmd-bin");
18651868 },
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 },
18661874 }
18671875 for (self.packages.toSliceConst()) |pkg| {
18681876 zig_args.append("--pkg-begin") catch unreachable;
lib/std/debug.zig+2-1
......@@ -213,7 +213,8 @@ pub fn assert(ok: bool) void {
213213
214214pub fn panic(comptime format: []const u8, args: ...) noreturn {
215215 @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();
217218 panicExtra(null, first_trace_addr, format, args);
218219}
219220
lib/std/heap.zig+91-98
......@@ -33,11 +33,19 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new
3333
3434/// This allocator makes a syscall directly for every allocation and free.
3535/// Thread-safe and lock-free.
36pub const page_allocator = &page_allocator_state;
36pub const page_allocator = if (std.Target.current.isWasm())
37 &wasm_page_allocator_state
38else
39 &page_allocator_state;
40
3741var page_allocator_state = Allocator{
3842 .reallocFn = PageAllocator.realloc,
3943 .shrinkFn = PageAllocator.shrink,
4044};
45var wasm_page_allocator_state = Allocator{
46 .reallocFn = WasmPageAllocator.realloc,
47 .shrinkFn = WasmPageAllocator.shrink,
48};
4149
4250/// Deprecated. Use `page_allocator`.
4351pub const direct_allocator = page_allocator;
......@@ -238,6 +246,88 @@ const PageAllocator = struct {
238246 }
239247};
240248
249// TODO Exposed LLVM intrinsics is a bug
250// See: https://github.com/ziglang/zig/issues/2291
251extern fn @"llvm.wasm.memory.size.i32"(u32) u32;
252extern 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()
256const 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 inner_num_pages: usize = required_memory / mem.page_size;
277 if (required_memory % mem.page_size != 0) {
278 inner_num_pages += 1;
279 }
280
281 const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, inner_num_pages));
282 if (prev_page == -1) {
283 return error.OutOfMemory;
284 }
285
286 num_pages += inner_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
241331pub const HeapAllocator = switch (builtin.os) {
242332 .windows => struct {
243333 allocator: Allocator,
......@@ -487,103 +577,6 @@ pub const FixedBufferAllocator = struct {
487577 }
488578};
489579
490// TODO Exposed LLVM intrinsics is a bug
491// See: https://github.com/ziglang/zig/issues/2291
492extern fn @"llvm.wasm.memory.size.i32"(u32) u32;
493extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32;
494
495pub const wasm_allocator = &wasm_allocator_state.allocator;
496var 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
506const 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
587580pub const ThreadSafeFixedBufferAllocator = blk: {
588581 if (builtin.single_threaded) {
589582 break :blk FixedBufferAllocator;
lib/std/os.zig+43-1
......@@ -1527,7 +1527,22 @@ pub fn isatty(handle: fd_t) bool {
15271527 return system.isatty(handle) != 0;
15281528 }
15291529 if (builtin.os == .wasi) {
1530 @compileError("TODO implement std.os.isatty for WASI");
1530 var statbuf: fdstat_t = undefined;
1531 const err = system.fd_fdstat_get(handle, &statbuf);
1532 if (err != 0) {
1533 // errno = err;
1534 return false;
1535 }
1536
1537 // A tty is a character device that we can't seek or tell on.
1538 if (statbuf.fs_filetype != FILETYPE_CHARACTER_DEVICE or
1539 (statbuf.fs_rights_base & (RIGHT_FD_SEEK | RIGHT_FD_TELL)) != 0)
1540 {
1541 // errno = ENOTTY;
1542 return false;
1543 }
1544
1545 return true;
15311546 }
15321547 if (builtin.os == .linux) {
15331548 var wsz: linux.winsize = undefined;
......@@ -2720,6 +2735,20 @@ pub fn dl_iterate_phdr(
27202735pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
27212736
27222737pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
2738 if (comptime std.Target.current.getOs() == .wasi) {
2739 var ts: timestamp_t = undefined;
2740 switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) {
2741 0 => {
2742 tp.* = .{
2743 .tv_sec = @intCast(i64, ts / std.time.ns_per_s),
2744 .tv_nsec = @intCast(isize, ts % std.time.ns_per_s),
2745 };
2746 },
2747 EINVAL => return error.UnsupportedClock,
2748 else => |err| return unexpectedErrno(err),
2749 }
2750 return;
2751 }
27232752 switch (errno(system.clock_gettime(clk_id, tp))) {
27242753 0 => return,
27252754 EFAULT => unreachable,
......@@ -2729,6 +2758,19 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
27292758}
27302759
27312760pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void {
2761 if (comptime std.Target.current.getOs() == .wasi) {
2762 var ts: timestamp_t = undefined;
2763 switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) {
2764 0 => res.* = .{
2765 .tv_sec = @intCast(i64, ts / std.time.ns_per_s),
2766 .tv_nsec = @intCast(isize, ts % std.time.ns_per_s),
2767 },
2768 EINVAL => return error.UnsupportedClock,
2769 else => |err| return unexpectedErrno(err),
2770 }
2771 return;
2772 }
2773
27322774 switch (errno(system.clock_getres(clk_id, res))) {
27332775 0 => return,
27342776 EFAULT => unreachable,
lib/std/os/bits/wasi.zig+7-1
......@@ -138,7 +138,7 @@ pub const FDFLAG_NONBLOCK: fdflags_t = 0x0004;
138138pub const FDFLAG_RSYNC: fdflags_t = 0x0008;
139139pub const FDFLAG_SYNC: fdflags_t = 0x0010;
140140
141const fdstat_t = extern struct {
141pub const fdstat_t = extern struct {
142142 fs_filetype: filetype_t,
143143 fs_flags: fdflags_t,
144144 fs_rights_base: rights_t,
......@@ -298,6 +298,7 @@ pub const subscription_t = extern struct {
298298};
299299
300300pub const timestamp_t = u64;
301pub const time_t = i64; // match https://github.com/CraneStation/wasi-libc
301302
302303pub const userdata_t = u64;
303304
......@@ -305,3 +306,8 @@ pub const whence_t = u8;
305306pub const WHENCE_CUR: whence_t = 0;
306307pub const WHENCE_END: whence_t = 1;
307308pub const WHENCE_SET: whence_t = 2;
309
310pub const timespec = extern struct {
311 tv_sec: time_t,
312 tv_nsec: isize,
313};
lib/std/os/wasi.zig+5
......@@ -76,3 +76,8 @@ pub extern "wasi_unstable" fn sched_yield() errno_t;
7676pub 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;
7777pub 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;
7878pub 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.
81pub fn getErrno(r: errno_t) usize {
82 return r;
83}
lib/std/target.zig+12
......@@ -607,10 +607,15 @@ pub const Target = union(enum) {
607607 }
608608 }
609609
610 pub fn supportsNewStackCall(self: Target) bool {
611 return !self.isWasm();
612 }
613
610614 pub const Executor = union(enum) {
611615 native,
612616 qemu: []const u8,
613617 wine: []const u8,
618 wasmtime: []const u8,
614619 unavailable,
615620 };
616621
......@@ -649,6 +654,13 @@ pub const Target = union(enum) {
649654 }
650655 }
651656
657 if (self.getOs() == .wasi) {
658 switch (self.getArchPtrBitWidth()) {
659 32 => return Executor{ .wasmtime = "wasmtime" },
660 else => return .unavailable,
661 }
662 }
663
652664 return .unavailable;
653665 }
654666};
src/analyze.cpp+2-1
......@@ -978,7 +978,8 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
978978 if (g->zig_target->arch == ZigLLVM_x86 ||
979979 g->zig_target->arch == ZigLLVM_x86_64 ||
980980 target_is_arm(g->zig_target) ||
981 target_is_riscv(g->zig_target))
981 target_is_riscv(g->zig_target) ||
982 target_is_wasm(g->zig_target))
982983 {
983984 X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
984985 return abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval;
src/ir.cpp+8
......@@ -17095,6 +17095,14 @@ static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstructionCall
1709517095 if (call_instruction->new_stack == nullptr)
1709617096 return nullptr;
1709717097
17098 if (!call_instruction->is_async_call_builtin &&
17099 arch_stack_pointer_register_name(ira->codegen->zig_target->arch) == nullptr)
17100 {
17101 ir_add_error(ira, &call_instruction->base,
17102 buf_sprintf("target arch '%s' does not support @newStackCall",
17103 target_arch_name(ira->codegen->zig_target->arch)));
17104 }
17105
1709817106 IrInstruction *new_stack = call_instruction->new_stack->child;
1709917107 if (type_is_invalid(new_stack->value->type))
1710017108 return ira->codegen->invalid_instruction;
src/target.cpp+4-2
......@@ -1458,6 +1458,10 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {
14581458 case ZigLLVM_mipsel:
14591459 return "sp";
14601460
1461 case ZigLLVM_wasm32:
1462 case ZigLLVM_wasm64:
1463 return nullptr; // known to be not available
1464
14611465 case ZigLLVM_amdgcn:
14621466 case ZigLLVM_amdil:
14631467 case ZigLLVM_amdil64:
......@@ -1491,8 +1495,6 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {
14911495 case ZigLLVM_systemz:
14921496 case ZigLLVM_tce:
14931497 case ZigLLVM_tcele:
1494 case ZigLLVM_wasm32:
1495 case ZigLLVM_wasm64:
14961498 case ZigLLVM_xcore:
14971499 case ZigLLVM_ppc:
14981500 case ZigLLVM_ppc64:
test/compile_errors.zig+18-1
......@@ -2,6 +2,24 @@ const tests = @import("tests.zig");
22const builtin = @import("builtin");
33
44pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.addCase(x: {
6 var tc = cases.create("@newStackCall on unsupported target",
7 \\export fn entry() void {
8 \\ var buf: [10]u8 align(16) = undefined;
9 \\ @newStackCall(&buf, foo);
10 \\}
11 \\fn foo() void {}
12 , "tmp.zig:3:5: error: target arch 'wasm32' does not support @newStackCall");
13 tc.target = tests.Target{
14 .Cross = tests.CrossTarget{
15 .arch = .wasm32,
16 .os = .wasi,
17 .abi = .none,
18 },
19 };
20 break :x tc;
21 });
22
523 cases.add(
624 "incompatible sentinels",
725 \\export fn entry1(ptr: [*:255]u8) [*:0]u8 {
......@@ -26,7 +44,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2644 "tmp.zig:8:35: note: destination array requires a terminating '0' sentinel, but source array has a terminating '255' sentinel",
2745 "tmp.zig:11:31: error: expected type '[2:0]u8', found '[2]u8'",
2846 "tmp.zig:11:31: note: destination array requires a terminating '0' sentinel",
29
3047 );
3148
3249 cases.add(
test/stage1/behavior/asm.zig+2-1
......@@ -1,5 +1,6 @@
1const std = @import("std");
12const config = @import("builtin");
2const expect = @import("std").testing.expect;
3const expect = std.testing.expect;
34
45comptime {
56 if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) {
test/stage1/behavior/new_stack_call.zig+3
......@@ -12,6 +12,9 @@ test "calling a function with a new stack" {
1212 // TODO: https://github.com/ziglang/zig/issues/3338
1313 return error.SkipZigTest;
1414 }
15 if (comptime !std.Target.current.supportsNewStackCall()) {
16 return error.SkipZigTest;
17 }
1518
1619 const arg = 1234;
1720