| author | |
| committer | |
| log | 6c19aeddca7f1f2c25c7d34dd9f6011b495670a9 |
| tree | 5aa3c281df8ec09cb13acadf60c905e84eb36702 |
| parent | 4b2538f72c9190b12c11c45a6d18cdc831b41015 |
| signature |
4 files changed, 145 insertions(+), 78 deletions(-)
src/codegen/wasm.zig+17-15| ... | ... | @@ -48,7 +48,7 @@ pub const Context = struct { |
| 48 | 48 | /// Table to save `WValue`'s generated by an `Inst` |
| 49 | 49 | values: ValueTable, |
| 50 | 50 | /// `bytes` contains the wasm bytecode belonging to the 'code' section. |
| 51 | bytes: ArrayList(u8), | |
| 51 | code: ArrayList(u8), | |
| 52 | 52 | /// Contains the generated function type bytecode for the current function |
| 53 | 53 | /// found in `decl` |
| 54 | 54 | func_type_data: ArrayList(u8), |
| ... | ... | @@ -56,8 +56,7 @@ pub const Context = struct { |
| 56 | 56 | /// NOTE: arguments share the index with locals therefore the first variable |
| 57 | 57 | /// will have the index that comes after the last argument's index |
| 58 | 58 | local_index: u32 = 0, |
| 59 | /// If codegen fails, an error messages will be allocated and saved | |
| 60 | /// in `err_msg` | |
| 59 | /// If codegen fails, an error messages will be allocated and saved in `err_msg` | |
| 61 | 60 | err_msg: *Compilation.ErrorMsg, |
| 62 | 61 | |
| 63 | 62 | const InnerError = error{ |
| ... | ... | @@ -74,6 +73,8 @@ pub const Context = struct { |
| 74 | 73 | /// Resolves the `WValue` for the given instruction `inst` |
| 75 | 74 | /// When the given instruction has a `Value`, it returns a constant instead |
| 76 | 75 | fn resolveInst(self: Context, inst: *Inst) WValue { |
| 76 | if (!inst.ty.hasCodeGenBits()) return .none; | |
| 77 | ||
| 77 | 78 | if (inst.value()) |_| { |
| 78 | 79 | return WValue{ .constant = inst }; |
| 79 | 80 | } |
| ... | ... | @@ -83,7 +84,7 @@ pub const Context = struct { |
| 83 | 84 | |
| 84 | 85 | /// Writes the bytecode depending on the given `WValue` in `val` |
| 85 | 86 | fn emitWValue(self: *Context, val: WValue) InnerError!void { |
| 86 | const writer = self.bytes.writer(); | |
| 87 | const writer = self.code.writer(); | |
| 87 | 88 | switch (val) { |
| 88 | 89 | .none, .block_idx => {}, |
| 89 | 90 | .local => |idx| { |
| ... | ... | @@ -127,14 +128,14 @@ pub const Context = struct { |
| 127 | 128 | } |
| 128 | 129 | } |
| 129 | 130 | |
| 130 | /// Generates the wasm bytecode for the given `code` | |
| 131 | /// Generates the wasm bytecode for the function declaration belonging to `Context` | |
| 131 | 132 | pub fn gen(self: *Context) InnerError!void { |
| 132 | assert(self.bytes.items.len == 0); | |
| 133 | assert(self.code.items.len == 0); | |
| 133 | 134 | try self.genFunctype(); |
| 134 | const writer = self.bytes.writer(); | |
| 135 | const writer = self.code.writer(); | |
| 135 | 136 | |
| 136 | 137 | // Reserve space to write the size after generating the code |
| 137 | try self.bytes.resize(5); | |
| 138 | try self.code.resize(5); | |
| 138 | 139 | |
| 139 | 140 | // Write instructions |
| 140 | 141 | // TODO: check for and handle death of instructions |
| ... | ... | @@ -170,8 +171,8 @@ pub const Context = struct { |
| 170 | 171 | |
| 171 | 172 | // Fill in the size of the generated code to the reserved space at the |
| 172 | 173 | // beginning of the buffer. |
| 173 | const size = self.bytes.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)); | |
| 174 | const size = self.code.items.len - 5 + self.decl.fn_link.wasm.?.idx_refs.items.len * 5; | |
| 175 | leb.writeUnsignedFixed(5, self.code.items[0..5], @intCast(u32, size)); | |
| 175 | 176 | } |
| 176 | 177 | |
| 177 | 178 | fn genInst(self: *Context, inst: *Inst) InnerError!WValue { |
| ... | ... | @@ -198,6 +199,7 @@ pub const Context = struct { |
| 198 | 199 | } |
| 199 | 200 | |
| 200 | 201 | fn genRet(self: *Context, inst: *Inst.UnOp) InnerError!WValue { |
| 202 | // TODO: Implement tail calls | |
| 201 | 203 | const operand = self.resolveInst(inst.operand); |
| 202 | 204 | try self.emitWValue(operand); |
| 203 | 205 | return WValue.none; |
| ... | ... | @@ -214,12 +216,12 @@ pub const Context = struct { |
| 214 | 216 | try self.emitWValue(arg_val); |
| 215 | 217 | } |
| 216 | 218 | |
| 217 | try self.bytes.append(0x10); // call | |
| 219 | try self.code.append(0x10); // call | |
| 218 | 220 | |
| 219 | 221 | // The function index immediate argument will be filled in using this data |
| 220 | 222 | // in link.Wasm.flush(). |
| 221 | 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 | 225 | .decl = target, |
| 224 | 226 | }); |
| 225 | 227 | |
| ... | ... | @@ -232,7 +234,7 @@ pub const Context = struct { |
| 232 | 234 | } |
| 233 | 235 | |
| 234 | 236 | fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { |
| 235 | const writer = self.bytes.writer(); | |
| 237 | const writer = self.code.writer(); | |
| 236 | 238 | |
| 237 | 239 | const lhs = self.resolveInst(inst.lhs); |
| 238 | 240 | const rhs = self.resolveInst(inst.rhs); |
| ... | ... | @@ -262,12 +264,12 @@ pub const Context = struct { |
| 262 | 264 | try self.emitWValue(lhs); |
| 263 | 265 | try self.emitWValue(rhs); |
| 264 | 266 | |
| 265 | try self.bytes.append(0x6A); // i32.add | |
| 267 | try self.code.append(0x6A); // i32.add | |
| 266 | 268 | return WValue.none; |
| 267 | 269 | } |
| 268 | 270 | |
| 269 | 271 | fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void { |
| 270 | const writer = self.bytes.writer(); | |
| 272 | const writer = self.code.writer(); | |
| 271 | 273 | switch (inst.base.ty.tag()) { |
| 272 | 274 | .u32 => { |
| 273 | 275 | 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 | 122 | var context = codegen.Context{ |
| 123 | 123 | .gpa = self.base.allocator, |
| 124 | 124 | .values = codegen.ValueTable.init(self.base.allocator), |
| 125 | .bytes = managed_code, | |
| 125 | .code = managed_code, | |
| 126 | 126 | .func_type_data = managed_functype, |
| 127 | 127 | .decl = decl, |
| 128 | 128 | .err_msg = undefined, |
| ... | ... | @@ -140,7 +140,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { |
| 140 | 140 | }; |
| 141 | 141 | |
| 142 | 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 | } |
| 145 | 145 | |
| 146 | 146 | pub fn updateDeclExports( |
test/stage2/test.zig+1-61| ... | ... | @@ -21,17 +21,13 @@ const linux_riscv64 = std.zig.CrossTarget{ |
| 21 | 21 | .os_tag = .linux, |
| 22 | 22 | }; |
| 23 | 23 | |
| 24 | const wasi = std.zig.CrossTarget{ | |
| 25 | .cpu_arch = .wasm32, | |
| 26 | .os_tag = .wasi, | |
| 27 | }; | |
| 28 | ||
| 29 | 24 | pub fn addCases(ctx: *TestContext) !void { |
| 30 | 25 | try @import("cbe.zig").addCases(ctx); |
| 31 | 26 | try @import("spu-ii.zig").addCases(ctx); |
| 32 | 27 | try @import("arm.zig").addCases(ctx); |
| 33 | 28 | try @import("aarch64.zig").addCases(ctx); |
| 34 | 29 | try @import("llvm.zig").addCases(ctx); |
| 30 | try @import("wasm.zig").addCases(ctx); | |
| 35 | 31 | |
| 36 | 32 | { |
| 37 | 33 | var case = ctx.exe("hello world with updates", linux_x64); |
| ... | ... | @@ -1158,62 +1154,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1158 | 1154 | }); |
| 1159 | 1155 | } |
| 1160 | 1156 | |
| 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 | 1157 | ctx.compileError("function redefinition", linux_x64, |
| 1218 | 1158 | \\fn entry() void {} |
| 1219 | 1159 | \\fn entry() void {} |
test/stage2/wasm.zig created+125| ... | ... | @@ -0,0 +1,125 @@ |
| 1 | const std = @import("std"); | |
| 2 | const TestContext = @import("../../src/test.zig").TestContext; | |
| 3 | ||
| 4 | const wasi = std.zig.CrossTarget{ | |
| 5 | .cpu_arch = .wasm32, | |
| 6 | .os_tag = .wasi, | |
| 7 | }; | |
| 8 | ||
| 9 | pub 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 | } |