From 6c19aeddca7f1f2c25c7d34dd9f6011b495670a9 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 16 Jan 2021 14:47:06 +0100 Subject: [PATCH] Add tests and move tests to wasm's own file --- src/codegen/wasm.zig | 32 +++++------ src/link/Wasm.zig | 4 +- test/stage2/test.zig | 62 +-------------------- test/stage2/wasm.zig | 125 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 78 deletions(-) create mode 100644 test/stage2/wasm.zig diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index a67e3000e9..b5e044b258 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -48,7 +48,7 @@ pub const Context = struct { /// Table to save `WValue`'s generated by an `Inst` values: ValueTable, /// `bytes` contains the wasm bytecode belonging to the 'code' section. - bytes: ArrayList(u8), + code: ArrayList(u8), /// Contains the generated function type bytecode for the current function /// found in `decl` func_type_data: ArrayList(u8), @@ -56,8 +56,7 @@ pub const Context = struct { /// NOTE: arguments share the index with locals therefore the first variable /// will have the index that comes after the last argument's index local_index: u32 = 0, - /// If codegen fails, an error messages will be allocated and saved - /// in `err_msg` + /// If codegen fails, an error messages will be allocated and saved in `err_msg` err_msg: *Compilation.ErrorMsg, const InnerError = error{ @@ -74,6 +73,8 @@ pub const Context = struct { /// Resolves the `WValue` for the given instruction `inst` /// When the given instruction has a `Value`, it returns a constant instead fn resolveInst(self: Context, inst: *Inst) WValue { + if (!inst.ty.hasCodeGenBits()) return .none; + if (inst.value()) |_| { return WValue{ .constant = inst }; } @@ -83,7 +84,7 @@ pub const Context = struct { /// Writes the bytecode depending on the given `WValue` in `val` fn emitWValue(self: *Context, val: WValue) InnerError!void { - const writer = self.bytes.writer(); + const writer = self.code.writer(); switch (val) { .none, .block_idx => {}, .local => |idx| { @@ -127,14 +128,14 @@ pub const Context = struct { } } - /// Generates the wasm bytecode for the given `code` + /// Generates the wasm bytecode for the function declaration belonging to `Context` pub fn gen(self: *Context) InnerError!void { - assert(self.bytes.items.len == 0); + assert(self.code.items.len == 0); try self.genFunctype(); - const writer = self.bytes.writer(); + const writer = self.code.writer(); // Reserve space to write the size after generating the code - try self.bytes.resize(5); + try self.code.resize(5); // Write instructions // TODO: check for and handle death of instructions @@ -170,8 +171,8 @@ pub const Context = struct { // Fill in the size of the generated code to the reserved space at the // beginning of the buffer. - const size = self.bytes.items.len - 5 + self.decl.fn_link.wasm.?.idx_refs.items.len * 5; - leb.writeUnsignedFixed(5, self.bytes.items[0..5], @intCast(u32, size)); + const size = self.code.items.len - 5 + self.decl.fn_link.wasm.?.idx_refs.items.len * 5; + leb.writeUnsignedFixed(5, self.code.items[0..5], @intCast(u32, size)); } fn genInst(self: *Context, inst: *Inst) InnerError!WValue { @@ -198,6 +199,7 @@ pub const Context = struct { } fn genRet(self: *Context, inst: *Inst.UnOp) InnerError!WValue { + // TODO: Implement tail calls const operand = self.resolveInst(inst.operand); try self.emitWValue(operand); return WValue.none; @@ -214,12 +216,12 @@ pub const Context = struct { try self.emitWValue(arg_val); } - try self.bytes.append(0x10); // call + try self.code.append(0x10); // call // The function index immediate argument will be filled in using this data // in link.Wasm.flush(). try self.decl.fn_link.wasm.?.idx_refs.append(self.gpa, .{ - .offset = @intCast(u32, self.bytes.items.len), + .offset = @intCast(u32, self.code.items.len), .decl = target, }); @@ -232,7 +234,7 @@ pub const Context = struct { } fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { - const writer = self.bytes.writer(); + const writer = self.code.writer(); const lhs = self.resolveInst(inst.lhs); const rhs = self.resolveInst(inst.rhs); @@ -262,12 +264,12 @@ pub const Context = struct { try self.emitWValue(lhs); try self.emitWValue(rhs); - try self.bytes.append(0x6A); // i32.add + try self.code.append(0x6A); // i32.add return WValue.none; } fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void { - const writer = self.bytes.writer(); + const writer = self.code.writer(); switch (inst.base.ty.tag()) { .u32 => { try writer.writeByte(0x41); // i32.const diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 8e3d79d4db..4640c9f1af 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -122,7 +122,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { var context = codegen.Context{ .gpa = self.base.allocator, .values = codegen.ValueTable.init(self.base.allocator), - .bytes = managed_code, + .code = managed_code, .func_type_data = managed_functype, .decl = decl, .err_msg = undefined, @@ -140,7 +140,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { }; fn_data.functype = context.func_type_data.toUnmanaged(); - fn_data.code = context.bytes.toUnmanaged(); + fn_data.code = context.code.toUnmanaged(); } pub fn updateDeclExports( diff --git a/test/stage2/test.zig b/test/stage2/test.zig index f25f07adbf..afe006574f 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -21,17 +21,13 @@ const linux_riscv64 = std.zig.CrossTarget{ .os_tag = .linux, }; -const wasi = std.zig.CrossTarget{ - .cpu_arch = .wasm32, - .os_tag = .wasi, -}; - pub fn addCases(ctx: *TestContext) !void { try @import("cbe.zig").addCases(ctx); try @import("spu-ii.zig").addCases(ctx); try @import("arm.zig").addCases(ctx); try @import("aarch64.zig").addCases(ctx); try @import("llvm.zig").addCases(ctx); + try @import("wasm.zig").addCases(ctx); { var case = ctx.exe("hello world with updates", linux_x64); @@ -1158,62 +1154,6 @@ pub fn addCases(ctx: *TestContext) !void { }); } - { - var case = ctx.exe("wasm function calls", wasi); - - case.addCompareOutput( - \\export fn _start() u32 { - \\ foo(); - \\ bar(); - \\ return 42; - \\} - \\fn foo() void { - \\ bar(); - \\ bar(); - \\} - \\fn bar() void {} - , - "42\n", - ); - - case.addCompareOutput( - \\export fn _start() i64 { - \\ bar(); - \\ foo(); - \\ foo(); - \\ bar(); - \\ foo(); - \\ bar(); - \\ return 42; - \\} - \\fn foo() void { - \\ bar(); - \\} - \\fn bar() void {} - , - "42\n", - ); - - case.addCompareOutput( - \\export fn _start() f32 { - \\ bar(); - \\ foo(); - \\ return 42.0; - \\} - \\fn foo() void { - \\ bar(); - \\ bar(); - \\ bar(); - \\} - \\fn bar() void {} - , - // This is what you get when you take the bits of the IEE-754 - // representation of 42.0 and reinterpret them as an unsigned - // integer. Guess that's a bug in wasmtime. - "1109917696\n", - ); - } - ctx.compileError("function redefinition", linux_x64, \\fn entry() void {} \\fn entry() void {} diff --git a/test/stage2/wasm.zig b/test/stage2/wasm.zig new file mode 100644 index 0000000000..f522db8809 --- /dev/null +++ b/test/stage2/wasm.zig @@ -0,0 +1,125 @@ +const std = @import("std"); +const TestContext = @import("../../src/test.zig").TestContext; + +const wasi = std.zig.CrossTarget{ + .cpu_arch = .wasm32, + .os_tag = .wasi, +}; + +pub fn addCases(ctx: *TestContext) !void { + { + var case = ctx.exe("wasm function calls", wasi); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ foo(); + \\ bar(); + \\ return 42; + \\} + \\fn foo() void { + \\ bar(); + \\ bar(); + \\} + \\fn bar() void {} + , + "42\n", + ); + + case.addCompareOutput( + \\export fn _start() i64 { + \\ bar(); + \\ foo(); + \\ foo(); + \\ bar(); + \\ foo(); + \\ bar(); + \\ return 42; + \\} + \\fn foo() void { + \\ bar(); + \\} + \\fn bar() void {} + , + "42\n", + ); + + case.addCompareOutput( + \\export fn _start() f32 { + \\ bar(); + \\ foo(); + \\ return 42.0; + \\} + \\fn foo() void { + \\ bar(); + \\ bar(); + \\ bar(); + \\} + \\fn bar() void {} + , + // This is what you get when you take the bits of the IEE-754 + // representation of 42.0 and reinterpret them as an unsigned + // integer. Guess that's a bug in wasmtime. + "1109917696\n", + ); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ foo(10, 20); + \\ return 5; + \\} + \\fn foo(x: u32, y: u32) void {} + , "5\n"); + } + + { + var case = ctx.exe("wasm locals", wasi); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 5; + \\ var y: f32 = 42.0; + \\ var x: u32 = 10; + \\ return i; + \\} + , "5\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 5; + \\ var y: f32 = 42.0; + \\ var x: u32 = 10; + \\ foo(i, x); + \\ i = x; + \\ return i; + \\} + \\fn foo(x: u32, y: u32) void { + \\ var i: u32 = 10; + \\ i = x; + \\} + , "10\n"); + } + + { + var case = ctx.exe("wasm binary operands", wasi); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 5; + \\ i += 20; + \\ return i; + \\} + , "25\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 5; + \\ i += 20; + \\ var result: u32 = foo(i, 10); + \\ return result; + \\} + \\fn foo(x: u32, y: u32) u32 { + \\ return x + y; + \\} + , "35\n"); + } +}