authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-01-16 14:47:06+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-01-16 14:58:04+01:00
log6c19aeddca7f1f2c25c7d34dd9f6011b495670a9
tree5aa3c281df8ec09cb13acadf60c905e84eb36702
parent4b2538f72c9190b12c11c45a6d18cdc831b41015
signaturelock-open Commit is signed but in an unrecognized format.

Add tests and move tests to wasm's own file


4 files changed, 145 insertions(+), 78 deletions(-)

src/codegen/wasm.zig+17-15
...@@ -48,7 +48,7 @@ pub const Context = struct {...@@ -48,7 +48,7 @@ pub const Context = struct {
48 /// Table to save `WValue`'s generated by an `Inst`48 /// Table to save `WValue`'s generated by an `Inst`
49 values: ValueTable,49 values: ValueTable,
50 /// `bytes` contains the wasm bytecode belonging to the 'code' section.50 /// `bytes` contains the wasm bytecode belonging to the 'code' section.
51 bytes: ArrayList(u8),51 code: ArrayList(u8),
52 /// Contains the generated function type bytecode for the current function52 /// Contains the generated function type bytecode for the current function
53 /// found in `decl`53 /// found in `decl`
54 func_type_data: ArrayList(u8),54 func_type_data: ArrayList(u8),
...@@ -56,8 +56,7 @@ pub const Context = struct {...@@ -56,8 +56,7 @@ pub const Context = struct {
56 /// NOTE: arguments share the index with locals therefore the first variable56 /// NOTE: arguments share the index with locals therefore the first variable
57 /// will have the index that comes after the last argument's index57 /// will have the index that comes after the last argument's index
58 local_index: u32 = 0,58 local_index: u32 = 0,
59 /// If codegen fails, an error messages will be allocated and saved59 /// If codegen fails, an error messages will be allocated and saved in `err_msg`
60 /// in `err_msg`
61 err_msg: *Compilation.ErrorMsg,60 err_msg: *Compilation.ErrorMsg,
6261
63 const InnerError = error{62 const InnerError = error{
...@@ -74,6 +73,8 @@ pub const Context = struct {...@@ -74,6 +73,8 @@ pub const Context = struct {
74 /// Resolves the `WValue` for the given instruction `inst`73 /// Resolves the `WValue` for the given instruction `inst`
75 /// When the given instruction has a `Value`, it returns a constant instead74 /// When the given instruction has a `Value`, it returns a constant instead
76 fn resolveInst(self: Context, inst: *Inst) WValue {75 fn resolveInst(self: Context, inst: *Inst) WValue {
76 if (!inst.ty.hasCodeGenBits()) return .none;
77
77 if (inst.value()) |_| {78 if (inst.value()) |_| {
78 return WValue{ .constant = inst };79 return WValue{ .constant = inst };
79 }80 }
...@@ -83,7 +84,7 @@ pub const Context = struct {...@@ -83,7 +84,7 @@ pub const Context = struct {
8384
84 /// Writes the bytecode depending on the given `WValue` in `val`85 /// Writes the bytecode depending on the given `WValue` in `val`
85 fn emitWValue(self: *Context, val: WValue) InnerError!void {86 fn emitWValue(self: *Context, val: WValue) InnerError!void {
86 const writer = self.bytes.writer();87 const writer = self.code.writer();
87 switch (val) {88 switch (val) {
88 .none, .block_idx => {},89 .none, .block_idx => {},
89 .local => |idx| {90 .local => |idx| {
...@@ -127,14 +128,14 @@ pub const Context = struct {...@@ -127,14 +128,14 @@ pub const Context = struct {
127 }128 }
128 }129 }
129130
130 /// Generates the wasm bytecode for the given `code`131 /// Generates the wasm bytecode for the function declaration belonging to `Context`
131 pub fn gen(self: *Context) InnerError!void {132 pub fn gen(self: *Context) InnerError!void {
132 assert(self.bytes.items.len == 0);133 assert(self.code.items.len == 0);
133 try self.genFunctype();134 try self.genFunctype();
134 const writer = self.bytes.writer();135 const writer = self.code.writer();
135136
136 // Reserve space to write the size after generating the code137 // Reserve space to write the size after generating the code
137 try self.bytes.resize(5);138 try self.code.resize(5);
138139
139 // Write instructions140 // Write instructions
140 // TODO: check for and handle death of instructions141 // TODO: check for and handle death of instructions
...@@ -170,8 +171,8 @@ pub const Context = struct {...@@ -170,8 +171,8 @@ pub const Context = struct {
170171
171 // Fill in the size of the generated code to the reserved space at the172 // Fill in the size of the generated code to the reserved space at the
172 // beginning of the buffer.173 // beginning of the buffer.
173 const size = self.bytes.items.len - 5 + self.decl.fn_link.wasm.?.idx_refs.items.len * 5;174 const size = self.code.items.len - 5 + self.decl.fn_link.wasm.?.idx_refs.items.len * 5;
174 leb.writeUnsignedFixed(5, self.bytes.items[0..5], @intCast(u32, size));175 leb.writeUnsignedFixed(5, self.code.items[0..5], @intCast(u32, size));
175 }176 }
176177
177 fn genInst(self: *Context, inst: *Inst) InnerError!WValue {178 fn genInst(self: *Context, inst: *Inst) InnerError!WValue {
...@@ -198,6 +199,7 @@ pub const Context = struct {...@@ -198,6 +199,7 @@ pub const Context = struct {
198 }199 }
199200
200 fn genRet(self: *Context, inst: *Inst.UnOp) InnerError!WValue {201 fn genRet(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
202 // TODO: Implement tail calls
201 const operand = self.resolveInst(inst.operand);203 const operand = self.resolveInst(inst.operand);
202 try self.emitWValue(operand);204 try self.emitWValue(operand);
203 return WValue.none;205 return WValue.none;
...@@ -214,12 +216,12 @@ pub const Context = struct {...@@ -214,12 +216,12 @@ pub const Context = struct {
214 try self.emitWValue(arg_val);216 try self.emitWValue(arg_val);
215 }217 }
216218
217 try self.bytes.append(0x10); // call219 try self.code.append(0x10); // call
218220
219 // The function index immediate argument will be filled in using this data221 // The function index immediate argument will be filled in using this data
220 // in link.Wasm.flush().222 // in link.Wasm.flush().
221 try self.decl.fn_link.wasm.?.idx_refs.append(self.gpa, .{223 try self.decl.fn_link.wasm.?.idx_refs.append(self.gpa, .{
222 .offset = @intCast(u32, self.bytes.items.len),224 .offset = @intCast(u32, self.code.items.len),
223 .decl = target,225 .decl = target,
224 });226 });
225227
...@@ -232,7 +234,7 @@ pub const Context = struct {...@@ -232,7 +234,7 @@ pub const Context = struct {
232 }234 }
233235
234 fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue {236 fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue {
235 const writer = self.bytes.writer();237 const writer = self.code.writer();
236238
237 const lhs = self.resolveInst(inst.lhs);239 const lhs = self.resolveInst(inst.lhs);
238 const rhs = self.resolveInst(inst.rhs);240 const rhs = self.resolveInst(inst.rhs);
...@@ -262,12 +264,12 @@ pub const Context = struct {...@@ -262,12 +264,12 @@ pub const Context = struct {
262 try self.emitWValue(lhs);264 try self.emitWValue(lhs);
263 try self.emitWValue(rhs);265 try self.emitWValue(rhs);
264266
265 try self.bytes.append(0x6A); // i32.add267 try self.code.append(0x6A); // i32.add
266 return WValue.none;268 return WValue.none;
267 }269 }
268270
269 fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void {271 fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void {
270 const writer = self.bytes.writer();272 const writer = self.code.writer();
271 switch (inst.base.ty.tag()) {273 switch (inst.base.ty.tag()) {
272 .u32 => {274 .u32 => {
273 try writer.writeByte(0x41); // i32.const275 try writer.writeByte(0x41); // i32.const
src/link/Wasm.zig+2-2
...@@ -122,7 +122,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {...@@ -122,7 +122,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
122 var context = codegen.Context{122 var context = codegen.Context{
123 .gpa = self.base.allocator,123 .gpa = self.base.allocator,
124 .values = codegen.ValueTable.init(self.base.allocator),124 .values = codegen.ValueTable.init(self.base.allocator),
125 .bytes = managed_code,125 .code = managed_code,
126 .func_type_data = managed_functype,126 .func_type_data = managed_functype,
127 .decl = decl,127 .decl = decl,
128 .err_msg = undefined,128 .err_msg = undefined,
...@@ -140,7 +140,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {...@@ -140,7 +140,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
140 };140 };
141141
142 fn_data.functype = context.func_type_data.toUnmanaged();142 fn_data.functype = context.func_type_data.toUnmanaged();
143 fn_data.code = context.bytes.toUnmanaged();143 fn_data.code = context.code.toUnmanaged();
144}144}
145145
146pub fn updateDeclExports(146pub fn updateDeclExports(
test/stage2/test.zig+1-61
...@@ -21,17 +21,13 @@ const linux_riscv64 = std.zig.CrossTarget{...@@ -21,17 +21,13 @@ const linux_riscv64 = std.zig.CrossTarget{
21 .os_tag = .linux,21 .os_tag = .linux,
22};22};
2323
24const wasi = std.zig.CrossTarget{
25 .cpu_arch = .wasm32,
26 .os_tag = .wasi,
27};
28
29pub fn addCases(ctx: *TestContext) !void {24pub fn addCases(ctx: *TestContext) !void {
30 try @import("cbe.zig").addCases(ctx);25 try @import("cbe.zig").addCases(ctx);
31 try @import("spu-ii.zig").addCases(ctx);26 try @import("spu-ii.zig").addCases(ctx);
32 try @import("arm.zig").addCases(ctx);27 try @import("arm.zig").addCases(ctx);
33 try @import("aarch64.zig").addCases(ctx);28 try @import("aarch64.zig").addCases(ctx);
34 try @import("llvm.zig").addCases(ctx);29 try @import("llvm.zig").addCases(ctx);
30 try @import("wasm.zig").addCases(ctx);
3531
36 {32 {
37 var case = ctx.exe("hello world with updates", linux_x64);33 var case = ctx.exe("hello world with updates", linux_x64);
...@@ -1158,62 +1154,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1158,62 +1154,6 @@ pub fn addCases(ctx: *TestContext) !void {
1158 });1154 });
1159 }1155 }
11601156
1161 {
1162 var case = ctx.exe("wasm function calls", wasi);
1163
1164 case.addCompareOutput(
1165 \\export fn _start() u32 {
1166 \\ foo();
1167 \\ bar();
1168 \\ return 42;
1169 \\}
1170 \\fn foo() void {
1171 \\ bar();
1172 \\ bar();
1173 \\}
1174 \\fn bar() void {}
1175 ,
1176 "42\n",
1177 );
1178
1179 case.addCompareOutput(
1180 \\export fn _start() i64 {
1181 \\ bar();
1182 \\ foo();
1183 \\ foo();
1184 \\ bar();
1185 \\ foo();
1186 \\ bar();
1187 \\ return 42;
1188 \\}
1189 \\fn foo() void {
1190 \\ bar();
1191 \\}
1192 \\fn bar() void {}
1193 ,
1194 "42\n",
1195 );
1196
1197 case.addCompareOutput(
1198 \\export fn _start() f32 {
1199 \\ bar();
1200 \\ foo();
1201 \\ return 42.0;
1202 \\}
1203 \\fn foo() void {
1204 \\ bar();
1205 \\ bar();
1206 \\ bar();
1207 \\}
1208 \\fn bar() void {}
1209 ,
1210 // This is what you get when you take the bits of the IEE-754
1211 // representation of 42.0 and reinterpret them as an unsigned
1212 // integer. Guess that's a bug in wasmtime.
1213 "1109917696\n",
1214 );
1215 }
1216
1217 ctx.compileError("function redefinition", linux_x64,1157 ctx.compileError("function redefinition", linux_x64,
1218 \\fn entry() void {}1158 \\fn entry() void {}
1219 \\fn entry() void {}1159 \\fn entry() void {}
test/stage2/wasm.zig created+125
...@@ -0,0 +1,125 @@
1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;
3
4const wasi = std.zig.CrossTarget{
5 .cpu_arch = .wasm32,
6 .os_tag = .wasi,
7};
8
9pub fn addCases(ctx: *TestContext) !void {
10 {
11 var case = ctx.exe("wasm function calls", wasi);
12
13 case.addCompareOutput(
14 \\export fn _start() u32 {
15 \\ foo();
16 \\ bar();
17 \\ return 42;
18 \\}
19 \\fn foo() void {
20 \\ bar();
21 \\ bar();
22 \\}
23 \\fn bar() void {}
24 ,
25 "42\n",
26 );
27
28 case.addCompareOutput(
29 \\export fn _start() i64 {
30 \\ bar();
31 \\ foo();
32 \\ foo();
33 \\ bar();
34 \\ foo();
35 \\ bar();
36 \\ return 42;
37 \\}
38 \\fn foo() void {
39 \\ bar();
40 \\}
41 \\fn bar() void {}
42 ,
43 "42\n",
44 );
45
46 case.addCompareOutput(
47 \\export fn _start() f32 {
48 \\ bar();
49 \\ foo();
50 \\ return 42.0;
51 \\}
52 \\fn foo() void {
53 \\ bar();
54 \\ bar();
55 \\ bar();
56 \\}
57 \\fn bar() void {}
58 ,
59 // This is what you get when you take the bits of the IEE-754
60 // representation of 42.0 and reinterpret them as an unsigned
61 // integer. Guess that's a bug in wasmtime.
62 "1109917696\n",
63 );
64
65 case.addCompareOutput(
66 \\export fn _start() u32 {
67 \\ foo(10, 20);
68 \\ return 5;
69 \\}
70 \\fn foo(x: u32, y: u32) void {}
71 , "5\n");
72 }
73
74 {
75 var case = ctx.exe("wasm locals", wasi);
76
77 case.addCompareOutput(
78 \\export fn _start() u32 {
79 \\ var i: u32 = 5;
80 \\ var y: f32 = 42.0;
81 \\ var x: u32 = 10;
82 \\ return i;
83 \\}
84 , "5\n");
85
86 case.addCompareOutput(
87 \\export fn _start() u32 {
88 \\ var i: u32 = 5;
89 \\ var y: f32 = 42.0;
90 \\ var x: u32 = 10;
91 \\ foo(i, x);
92 \\ i = x;
93 \\ return i;
94 \\}
95 \\fn foo(x: u32, y: u32) void {
96 \\ var i: u32 = 10;
97 \\ i = x;
98 \\}
99 , "10\n");
100 }
101
102 {
103 var case = ctx.exe("wasm binary operands", wasi);
104
105 case.addCompareOutput(
106 \\export fn _start() u32 {
107 \\ var i: u32 = 5;
108 \\ i += 20;
109 \\ return i;
110 \\}
111 , "25\n");
112
113 case.addCompareOutput(
114 \\export fn _start() u32 {
115 \\ var i: u32 = 5;
116 \\ i += 20;
117 \\ var result: u32 = foo(i, 10);
118 \\ return result;
119 \\}
120 \\fn foo(x: u32, y: u32) u32 {
121 \\ return x + y;
122 \\}
123 , "35\n");
124 }
125}