| 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,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 function | 52 | /// 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 variable | 56 | /// NOTE: arguments share the index with locals therefore the first variable |
| 57 | /// will have the index that comes after the last argument's index | 57 | /// 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 saved | 59 | /// 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, |
| 62 | 61 | ||
| 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 instead | 74 | /// 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 { |
| 83 | 84 | ||
| 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 | } |
| 129 | 130 | ||
| 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(); |
| 135 | 136 | ||
| 136 | // Reserve space to write the size after generating the code | 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 | // Write instructions | 140 | // Write instructions |
| 140 | // TODO: check for and handle death of instructions | 141 | // TODO: check for and handle death of instructions |
| ... | @@ -170,8 +171,8 @@ pub const Context = struct { | ... | @@ -170,8 +171,8 @@ pub const Context = struct { |
| 170 | 171 | ||
| 171 | // Fill in the size of the generated code to the reserved space at the | 172 | // 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 | } |
| 176 | 177 | ||
| 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 | } |
| 199 | 200 | ||
| 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 | } |
| 216 | 218 | ||
| 217 | try self.bytes.append(0x10); // call | 219 | try self.code.append(0x10); // call |
| 218 | 220 | ||
| 219 | // The function index immediate argument will be filled in using this data | 221 | // 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 | }); |
| 225 | 227 | ||
| ... | @@ -232,7 +234,7 @@ pub const Context = struct { | ... | @@ -232,7 +234,7 @@ pub const Context = struct { |
| 232 | } | 234 | } |
| 233 | 235 | ||
| 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(); |
| 236 | 238 | ||
| 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); |
| 264 | 266 | ||
| 265 | try self.bytes.append(0x6A); // i32.add | 267 | try self.code.append(0x6A); // i32.add |
| 266 | return WValue.none; | 268 | return WValue.none; |
| 267 | } | 269 | } |
| 268 | 270 | ||
| 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.const | 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,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 | }; |
| 141 | 141 | ||
| 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 | } |
| 145 | 145 | ||
| 146 | pub fn updateDeclExports( | 146 | pub 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 | }; |
| 23 | 23 | ||
| 24 | const wasi = std.zig.CrossTarget{ | ||
| 25 | .cpu_arch = .wasm32, | ||
| 26 | .os_tag = .wasi, | ||
| 27 | }; | ||
| 28 | |||
| 29 | pub fn addCases(ctx: *TestContext) !void { | 24 | pub 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); | ||
| 35 | 31 | ||
| 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 | } |
| 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 | 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 @@ | ||
| 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 | } | ||