authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-01-15 23:24:47+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-01-15 23:27:38+01:00
log4b2538f72c9190b12c11c45a6d18cdc831b41015
treea17d40caaaba49fe6a08ff24f1f019443d9cb809
parentbb74f72e97f3d503c4801bb7ca3d6412ad01b28c
signaturelock-open Commit is signed but in an unrecognized format.

Cleanup and 'add' instruction for bigger test area


2 files changed, 63 insertions(+), 59 deletions(-)

src/codegen/wasm.zig+57-53
...@@ -18,16 +18,16 @@ const WValue = union(enum) {...@@ -18,16 +18,16 @@ const WValue = union(enum) {
18 none: void,18 none: void,
19 /// Index of the local variable19 /// Index of the local variable
20 local: u32,20 local: u32,
21 /// A constant instruction21 /// Instruction holding a constant `Value`
22 constant: *Inst,22 constant: *Inst,
23 /// Each newly created wasm block have a label23 /// Block label
24 /// in the form of an index.
25 block_idx: u32,24 block_idx: u32,
26};25};
2726
28pub const ValueTable = std.AutoArrayHashMap(*Inst, WValue);27/// Hashmap to store generated `WValue` for each `Inst`
28pub const ValueTable = std.AutoHashMap(*Inst, WValue);
2929
30/// Using a given Zig type, returns the corresponding wasm value type30/// Using a given `Type`, returns the corresponding wasm value type
31fn genValtype(ty: Type) ?u8 {31fn genValtype(ty: Type) ?u8 {
32 return switch (ty.tag()) {32 return switch (ty.tag()) {
33 .f32 => 0x7D,33 .f32 => 0x7D,
...@@ -40,24 +40,22 @@ fn genValtype(ty: Type) ?u8 {...@@ -40,24 +40,22 @@ fn genValtype(ty: Type) ?u8 {
4040
41/// Code represents the `Code` section of wasm that41/// Code represents the `Code` section of wasm that
42/// belongs to a function42/// belongs to a function
43pub const Code = struct {43pub const Context = struct {
44 /// Reference to the function declaration the code44 /// Reference to the function declaration the code
45 /// section belongs to45 /// section belongs to
46 decl: *Decl,46 decl: *Decl,
47 gpa: *mem.Allocator,47 gpa: *mem.Allocator,
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 instructions that have been emitted50 /// `bytes` contains the wasm bytecode belonging to the 'code' section.
51 /// this is what will be emitted after codegen to write the wasm binary
52 bytes: ArrayList(u8),51 bytes: ArrayList(u8),
53 /// Contains the generated function type bytecode for the current function52 /// Contains the generated function type bytecode for the current function
53 /// found in `decl`
54 func_type_data: ArrayList(u8),54 func_type_data: ArrayList(u8),
55 /// The index the next local generated will have55 /// The index the next local generated will have
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 /// The index the next argument generated will have
60 arg_index: u32 = 0,
61 /// If codegen fails, an error messages will be allocated and saved59 /// If codegen fails, an error messages will be allocated and saved
62 /// in `err_msg`60 /// in `err_msg`
63 err_msg: *Compilation.ErrorMsg,61 err_msg: *Compilation.ErrorMsg,
...@@ -67,14 +65,15 @@ pub const Code = struct {...@@ -67,14 +65,15 @@ pub const Code = struct {
67 CodegenFail,65 CodegenFail,
68 };66 };
6967
70 fn fail(self: *Code, src: usize, comptime fmt: []const u8, args: anytype) InnerError {68 /// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig
69 fn fail(self: *Context, src: usize, comptime fmt: []const u8, args: anytype) InnerError {
71 self.err_msg = try Compilation.ErrorMsg.create(self.gpa, src, fmt, args);70 self.err_msg = try Compilation.ErrorMsg.create(self.gpa, src, fmt, args);
72 return error.CodegenFail;71 return error.CodegenFail;
73 }72 }
7473
75 /// Returns the `WValue` for the given `inst`74 /// Resolves the `WValue` for the given instruction `inst`
76 /// creates a new WValue for constants and returns that instead75 /// When the given instruction has a `Value`, it returns a constant instead
77 fn resolveInst(self: Code, inst: *Inst) !WValue {76 fn resolveInst(self: Context, inst: *Inst) WValue {
78 if (inst.value()) |_| {77 if (inst.value()) |_| {
79 return WValue{ .constant = inst };78 return WValue{ .constant = inst };
80 }79 }
...@@ -83,23 +82,19 @@ pub const Code = struct {...@@ -83,23 +82,19 @@ pub const Code = struct {
83 }82 }
8483
85 /// Writes the bytecode depending on the given `WValue` in `val`84 /// Writes the bytecode depending on the given `WValue` in `val`
86 fn emitWValue(self: *Code, val: WValue) !void {85 fn emitWValue(self: *Context, val: WValue) InnerError!void {
87 const writer = self.bytes.writer();86 const writer = self.bytes.writer();
88 switch (val) {87 switch (val) {
89 .none => unreachable,88 .none, .block_idx => {},
90 .block_idx => unreachable,
91 // loads the local onto the stack at the given index
92 .local => |idx| {89 .local => |idx| {
93 // local.set90 try writer.writeByte(0x20); // local.get
94 try writer.writeByte(0x20);
95 try leb.writeULEB128(writer, idx);91 try leb.writeULEB128(writer, idx);
96 },92 },
97 // creates a new constant onto the stack93 .constant => |inst| try self.emitConstant(inst.castTag(.constant).?), // creates a new constant onto the stack
98 .constant => |inst| try self.emitConstant(inst.castTag(.constant).?),
99 }94 }
100 }95 }
10196
102 fn genFunctype(self: *Code) !void {97 fn genFunctype(self: *Context) InnerError!void {
103 const ty = self.decl.typed_value.most_recent.typed_value.ty;98 const ty = self.decl.typed_value.most_recent.typed_value.ty;
104 const writer = self.func_type_data.writer();99 const writer = self.func_type_data.writer();
105100
...@@ -114,7 +109,7 @@ pub const Code = struct {...@@ -114,7 +109,7 @@ pub const Code = struct {
114 ty.fnParamTypes(params);109 ty.fnParamTypes(params);
115 for (params) |param_type| {110 for (params) |param_type| {
116 const val_type = genValtype(param_type) orelse111 const val_type = genValtype(param_type) orelse
117 return self.fail(self.decl.src(), "TODO: Wasm generate wasm type value for type '{s}'", .{param_type.tag()});112 return self.fail(self.decl.src(), "TODO: Wasm codegen - arg type value for type '{s}'", .{param_type.tag()});
118 try writer.writeByte(val_type);113 try writer.writeByte(val_type);
119 }114 }
120 }115 }
...@@ -126,14 +121,14 @@ pub const Code = struct {...@@ -126,14 +121,14 @@ pub const Code = struct {
126 else => |ret_type| {121 else => |ret_type| {
127 try leb.writeULEB128(writer, @as(u32, 1));122 try leb.writeULEB128(writer, @as(u32, 1));
128 const val_type = genValtype(return_type) orelse123 const val_type = genValtype(return_type) orelse
129 return self.fail(self.decl.src(), "TODO: Wasm generate wasm return type value for type '{s}'", .{ret_type});124 return self.fail(self.decl.src(), "TODO: Wasm codegen - return type value for type '{s}'", .{ret_type});
130 try writer.writeByte(val_type);125 try writer.writeByte(val_type);
131 },126 },
132 }127 }
133 }128 }
134129
135 /// Generates the wasm bytecode for the given `code`130 /// Generates the wasm bytecode for the given `code`
136 pub fn gen(self: *Code) !void {131 pub fn gen(self: *Context) InnerError!void {
137 assert(self.bytes.items.len == 0);132 assert(self.bytes.items.len == 0);
138 try self.genFunctype();133 try self.genFunctype();
139 const writer = self.bytes.writer();134 const writer = self.bytes.writer();
...@@ -156,7 +151,7 @@ pub const Code = struct {...@@ -156,7 +151,7 @@ pub const Code = struct {
156 const elem_type = alloc.base.ty.elemType();151 const elem_type = alloc.base.ty.elemType();
157152
158 const wasm_type = genValtype(elem_type) orelse153 const wasm_type = genValtype(elem_type) orelse
159 return self.fail(inst.src, "TODO: Wasm generate wasm type value for type '{s}'", .{elem_type.tag()});154 return self.fail(inst.src, "TODO: Wasm codegen - valtype for type '{s}'", .{elem_type.tag()});
160155
161 try locals.append(wasm_type);156 try locals.append(wasm_type);
162 }157 }
...@@ -169,16 +164,9 @@ pub const Code = struct {...@@ -169,16 +164,9 @@ pub const Code = struct {
169 try leb.writeULEB128(writer, local); // valtype164 try leb.writeULEB128(writer, local); // valtype
170 }165 }
171166
172 for (mod_fn.body.instructions) |inst| {167 try self.genBody(mod_fn.body);
173 const result = try self.genInst(inst);
174
175 if (result != .none) {
176 try self.values.putNoClobber(inst, result);
177 }
178 }
179168
180 // Write 'end' opcode169 try writer.writeByte(0x0B); // end
181 try writer.writeByte(0x0B);
182170
183 // Fill in the size of the generated code to the reserved space at the171 // Fill in the size of the generated code to the reserved space at the
184 // beginning of the buffer.172 // beginning of the buffer.
...@@ -186,8 +174,9 @@ pub const Code = struct {...@@ -186,8 +174,9 @@ pub const Code = struct {
186 leb.writeUnsignedFixed(5, self.bytes.items[0..5], @intCast(u32, size));174 leb.writeUnsignedFixed(5, self.bytes.items[0..5], @intCast(u32, size));
187 }175 }
188176
189 fn genInst(self: *Code, inst: *Inst) !WValue {177 fn genInst(self: *Context, inst: *Inst) InnerError!WValue {
190 return switch (inst.tag) {178 return switch (inst.tag) {
179 .add => self.genAdd(inst.castTag(.add).?),
191 .alloc => self.genAlloc(inst.castTag(.alloc).?),180 .alloc => self.genAlloc(inst.castTag(.alloc).?),
192 .arg => self.genArg(inst.castTag(.arg).?),181 .arg => self.genArg(inst.castTag(.arg).?),
193 .call => self.genCall(inst.castTag(.call).?),182 .call => self.genCall(inst.castTag(.call).?),
...@@ -201,20 +190,27 @@ pub const Code = struct {...@@ -201,20 +190,27 @@ pub const Code = struct {
201 };190 };
202 }191 }
203192
204 fn genRet(self: *Code, inst: *Inst.UnOp) !WValue {193 fn genBody(self: *Context, body: ir.Body) InnerError!void {
205 const operand = try self.resolveInst(inst.operand);194 for (body.instructions) |inst| {
195 const result = try self.genInst(inst);
196 try self.values.putNoClobber(inst, result);
197 }
198 }
199
200 fn genRet(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
201 const operand = self.resolveInst(inst.operand);
206 try self.emitWValue(operand);202 try self.emitWValue(operand);
207 return WValue.none;203 return WValue.none;
208 }204 }
209205
210 fn genCall(self: *Code, inst: *Inst.Call) !WValue {206 fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue {
211 const func_inst = inst.func.castTag(.constant).?;207 const func_inst = inst.func.castTag(.constant).?;
212 const func = func_inst.val.castTag(.function).?.data;208 const func = func_inst.val.castTag(.function).?.data;
213 const target = func.owner_decl;209 const target = func.owner_decl;
214 const target_ty = target.typed_value.most_recent.typed_value.ty;210 const target_ty = target.typed_value.most_recent.typed_value.ty;
215211
216 for (inst.args) |arg| {212 for (inst.args) |arg| {
217 const arg_val = try self.resolveInst(arg);213 const arg_val = self.resolveInst(arg);
218 try self.emitWValue(arg_val);214 try self.emitWValue(arg_val);
219 }215 }
220216
...@@ -230,39 +226,47 @@ pub const Code = struct {...@@ -230,39 +226,47 @@ pub const Code = struct {
230 return WValue.none;226 return WValue.none;
231 }227 }
232228
233 fn genAlloc(self: *Code, inst: *Inst.NoOp) !WValue {229 fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue {
234 defer self.local_index += 1;230 defer self.local_index += 1;
235 return WValue{ .local = self.local_index };231 return WValue{ .local = self.local_index };
236 }232 }
237233
238 fn genStore(self: *Code, inst: *Inst.BinOp) !WValue {234 fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue {
239 const writer = self.bytes.writer();235 const writer = self.bytes.writer();
240236
241 const lhs = try self.resolveInst(inst.lhs);237 const lhs = self.resolveInst(inst.lhs);
242238 const rhs = self.resolveInst(inst.rhs);
243 const rhs = try self.resolveInst(inst.rhs);
244 try self.emitWValue(rhs);239 try self.emitWValue(rhs);
245240
246 try writer.writeByte(0x21); // local.set241 try writer.writeByte(0x21); // local.set
247 try leb.writeULEB128(writer, lhs.local);242 try leb.writeULEB128(writer, lhs.local);
248
249 return WValue.none;243 return WValue.none;
250 }244 }
251245
252 fn genLoad(self: *Code, inst: *Inst.UnOp) !WValue {246 fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
253 const operand = self.resolveInst(inst.operand);247 const operand = self.resolveInst(inst.operand);
254248 try self.emitWValue(operand);
255 // ensure index to local249 return WValue.none;
256 return WValue{ .local = operand.local };
257 }250 }
258251
259 fn genArg(self: *Code, inst: *Inst.Arg) !WValue {252 fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue {
260 // arguments share the index with locals253 // arguments share the index with locals
261 defer self.local_index += 1;254 defer self.local_index += 1;
262 return WValue{ .local = self.local_index };255 return WValue{ .local = self.local_index };
263 }256 }
264257
265 fn emitConstant(self: *Code, inst: *Inst.Constant) !void {258 fn genAdd(self: *Context, inst: *Inst.BinOp) InnerError!WValue {
259 const lhs = self.resolveInst(inst.lhs);
260 const rhs = self.resolveInst(inst.rhs);
261
262 try self.emitWValue(lhs);
263 try self.emitWValue(rhs);
264
265 try self.bytes.append(0x6A); // i32.add
266 return WValue.none;
267 }
268
269 fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void {
266 const writer = self.bytes.writer();270 const writer = self.bytes.writer();
267 switch (inst.base.ty.tag()) {271 switch (inst.base.ty.tag()) {
268 .u32 => {272 .u32 => {
src/link/Wasm.zig+6-6
...@@ -119,7 +119,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {...@@ -119,7 +119,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
119 var managed_functype = fn_data.functype.toManaged(self.base.allocator);119 var managed_functype = fn_data.functype.toManaged(self.base.allocator);
120 var managed_code = fn_data.code.toManaged(self.base.allocator);120 var managed_code = fn_data.code.toManaged(self.base.allocator);
121121
122 var code = codegen.Code{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 .bytes = managed_code,
...@@ -127,20 +127,20 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {...@@ -127,20 +127,20 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
127 .decl = decl,127 .decl = decl,
128 .err_msg = undefined,128 .err_msg = undefined,
129 };129 };
130 defer code.values.deinit();130 defer context.values.deinit();
131131
132 // generate the 'code' section for the function declaration132 // generate the 'code' section for the function declaration
133 code.gen() catch |err| switch (err) {133 context.gen() catch |err| switch (err) {
134 error.CodegenFail => {134 error.CodegenFail => {
135 decl.analysis = .codegen_failure;135 decl.analysis = .codegen_failure;
136 try module.failed_decls.put(module.gpa, decl, code.err_msg);136 try module.failed_decls.put(module.gpa, decl, context.err_msg);
137 return;137 return;
138 },138 },
139 else => |e| return err,139 else => |e| return err,
140 };140 };
141141
142 fn_data.functype = code.func_type_data.toUnmanaged();142 fn_data.functype = context.func_type_data.toUnmanaged();
143 fn_data.code = code.bytes.toUnmanaged();143 fn_data.code = context.bytes.toUnmanaged();
144}144}
145145
146pub fn updateDeclExports(146pub fn updateDeclExports(