| ... | @@ -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 variable | 19 | /// Index of the local variable |
| 20 | local: u32, | 20 | local: u32, |
| 21 | /// A constant instruction | 21 | /// Instruction holding a constant `Value` |
| 22 | constant: *Inst, | 22 | constant: *Inst, |
| 23 | /// Each newly created wasm block have a label | 23 | /// Block label |
| 24 | /// in the form of an index. | | |
| 25 | block_idx: u32, | 24 | block_idx: u32, |
| 26 | }; | 25 | }; |
| 27 | | 26 | |
| 28 | pub const ValueTable = std.AutoArrayHashMap(*Inst, WValue); | 27 | /// Hashmap to store generated `WValue` for each `Inst` |
| | 28 | pub const ValueTable = std.AutoHashMap(*Inst, WValue); |
| 29 | | 29 | |
| 30 | /// Using a given Zig type, returns the corresponding wasm value type | 30 | /// Using a given `Type`, returns the corresponding wasm value type |
| 31 | fn genValtype(ty: Type) ?u8 { | 31 | fn 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 { |
| 40 | | 40 | |
| 41 | /// Code represents the `Code` section of wasm that | 41 | /// Code represents the `Code` section of wasm that |
| 42 | /// belongs to a function | 42 | /// belongs to a function |
| 43 | pub const Code = struct { | 43 | pub const Context = struct { |
| 44 | /// Reference to the function declaration the code | 44 | /// Reference to the function declaration the code |
| 45 | /// section belongs to | 45 | /// 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 emitted | 50 | /// `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 function | 52 | /// 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 have | 55 | /// The index the next local generated will have |
| 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 | /// The index the next argument generated will have | | |
| 60 | arg_index: u32 = 0, | | |
| 61 | /// If codegen fails, an error messages will be allocated and saved | 59 | /// 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 | }; |
| 69 | | 67 | |
| 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 | } |
| 74 | | 73 | |
| 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 instead | 75 | /// 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 | } |
| 84 | | 83 | |
| 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.set | 90 | 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 stack | 93 | .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 | } |
| 101 | | 96 | |
| 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(); |
| 105 | | 100 | |
| ... | @@ -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) orelse | 111 | 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) orelse | 123 | 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 | } |
| 134 | | 129 | |
| 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(); |
| 157 | | 152 | |
| 158 | const wasm_type = genValtype(elem_type) orelse | 153 | 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()}); |
| 160 | | 155 | |
| 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); // valtype | 164 | try leb.writeULEB128(writer, local); // valtype |
| 170 | } | 165 | } |
| 171 | | 166 | |
| 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 | } | | |
| 179 | | 168 | |
| 180 | // Write 'end' opcode | 169 | try writer.writeByte(0x0B); // end |
| 181 | try writer.writeByte(0x0B); | | |
| 182 | | 170 | |
| 183 | // Fill in the size of the generated code to the reserved space at the | 171 | // 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 | } |
| 188 | | 176 | |
| 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 | } |
| 203 | | 192 | |
| 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 | } |
| 209 | | 205 | |
| 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; |
| 215 | | 211 | |
| 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 | } |
| 220 | | 216 | |
| ... | @@ -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 | } |
| 232 | | 228 | |
| 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 | } |
| 237 | | 233 | |
| 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(); |
| 240 | | 236 | |
| 241 | const lhs = try self.resolveInst(inst.lhs); | 237 | const lhs = self.resolveInst(inst.lhs); |
| 242 | | 238 | const rhs = self.resolveInst(inst.rhs); |
| 243 | const rhs = try self.resolveInst(inst.rhs); | | |
| 244 | try self.emitWValue(rhs); | 239 | try self.emitWValue(rhs); |
| 245 | | 240 | |
| 246 | try writer.writeByte(0x21); // local.set | 241 | 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 | } |
| 251 | | 245 | |
| 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); |
| 254 | | 248 | try self.emitWValue(operand); |
| 255 | // ensure index to local | 249 | return WValue.none; |
| 256 | return WValue{ .local = operand.local }; | | |
| 257 | } | 250 | } |
| 258 | | 251 | |
| 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 locals | 253 | // 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 | } |
| 264 | | 257 | |
| 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 => { |