diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index 97703ee42a..a67e3000e9 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -18,16 +18,16 @@ const WValue = union(enum) { none: void, /// Index of the local variable local: u32, - /// A constant instruction + /// Instruction holding a constant `Value` constant: *Inst, - /// Each newly created wasm block have a label - /// in the form of an index. + /// Block label block_idx: u32, }; -pub const ValueTable = std.AutoArrayHashMap(*Inst, WValue); +/// Hashmap to store generated `WValue` for each `Inst` +pub const ValueTable = std.AutoHashMap(*Inst, WValue); -/// Using a given Zig type, returns the corresponding wasm value type +/// Using a given `Type`, returns the corresponding wasm value type fn genValtype(ty: Type) ?u8 { return switch (ty.tag()) { .f32 => 0x7D, @@ -40,24 +40,22 @@ fn genValtype(ty: Type) ?u8 { /// Code represents the `Code` section of wasm that /// belongs to a function -pub const Code = struct { +pub const Context = struct { /// Reference to the function declaration the code /// section belongs to decl: *Decl, gpa: *mem.Allocator, /// Table to save `WValue`'s generated by an `Inst` values: ValueTable, - /// `bytes` contains the wasm instructions that have been emitted - /// this is what will be emitted after codegen to write the wasm binary + /// `bytes` contains the wasm bytecode belonging to the 'code' section. bytes: ArrayList(u8), /// Contains the generated function type bytecode for the current function + /// found in `decl` func_type_data: ArrayList(u8), /// The index the next local generated will have /// NOTE: arguments share the index with locals therefore the first variable /// will have the index that comes after the last argument's index local_index: u32 = 0, - /// The index the next argument generated will have - arg_index: u32 = 0, /// If codegen fails, an error messages will be allocated and saved /// in `err_msg` err_msg: *Compilation.ErrorMsg, @@ -67,14 +65,15 @@ pub const Code = struct { CodegenFail, }; - fn fail(self: *Code, src: usize, comptime fmt: []const u8, args: anytype) InnerError { + /// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig + fn fail(self: *Context, src: usize, comptime fmt: []const u8, args: anytype) InnerError { self.err_msg = try Compilation.ErrorMsg.create(self.gpa, src, fmt, args); return error.CodegenFail; } - /// Returns the `WValue` for the given `inst` - /// creates a new WValue for constants and returns that instead - fn resolveInst(self: Code, inst: *Inst) !WValue { + /// Resolves the `WValue` for the given instruction `inst` + /// When the given instruction has a `Value`, it returns a constant instead + fn resolveInst(self: Context, inst: *Inst) WValue { if (inst.value()) |_| { return WValue{ .constant = inst }; } @@ -83,23 +82,19 @@ pub const Code = struct { } /// Writes the bytecode depending on the given `WValue` in `val` - fn emitWValue(self: *Code, val: WValue) !void { + fn emitWValue(self: *Context, val: WValue) InnerError!void { const writer = self.bytes.writer(); switch (val) { - .none => unreachable, - .block_idx => unreachable, - // loads the local onto the stack at the given index + .none, .block_idx => {}, .local => |idx| { - // local.set - try writer.writeByte(0x20); + try writer.writeByte(0x20); // local.get try leb.writeULEB128(writer, idx); }, - // creates a new constant onto the stack - .constant => |inst| try self.emitConstant(inst.castTag(.constant).?), + .constant => |inst| try self.emitConstant(inst.castTag(.constant).?), // creates a new constant onto the stack } } - fn genFunctype(self: *Code) !void { + fn genFunctype(self: *Context) InnerError!void { const ty = self.decl.typed_value.most_recent.typed_value.ty; const writer = self.func_type_data.writer(); @@ -114,7 +109,7 @@ pub const Code = struct { ty.fnParamTypes(params); for (params) |param_type| { const val_type = genValtype(param_type) orelse - return self.fail(self.decl.src(), "TODO: Wasm generate wasm type value for type '{s}'", .{param_type.tag()}); + return self.fail(self.decl.src(), "TODO: Wasm codegen - arg type value for type '{s}'", .{param_type.tag()}); try writer.writeByte(val_type); } } @@ -126,14 +121,14 @@ pub const Code = struct { else => |ret_type| { try leb.writeULEB128(writer, @as(u32, 1)); const val_type = genValtype(return_type) orelse - return self.fail(self.decl.src(), "TODO: Wasm generate wasm return type value for type '{s}'", .{ret_type}); + return self.fail(self.decl.src(), "TODO: Wasm codegen - return type value for type '{s}'", .{ret_type}); try writer.writeByte(val_type); }, } } /// Generates the wasm bytecode for the given `code` - pub fn gen(self: *Code) !void { + pub fn gen(self: *Context) InnerError!void { assert(self.bytes.items.len == 0); try self.genFunctype(); const writer = self.bytes.writer(); @@ -156,7 +151,7 @@ pub const Code = struct { const elem_type = alloc.base.ty.elemType(); const wasm_type = genValtype(elem_type) orelse - return self.fail(inst.src, "TODO: Wasm generate wasm type value for type '{s}'", .{elem_type.tag()}); + return self.fail(inst.src, "TODO: Wasm codegen - valtype for type '{s}'", .{elem_type.tag()}); try locals.append(wasm_type); } @@ -169,16 +164,9 @@ pub const Code = struct { try leb.writeULEB128(writer, local); // valtype } - for (mod_fn.body.instructions) |inst| { - const result = try self.genInst(inst); + try self.genBody(mod_fn.body); - if (result != .none) { - try self.values.putNoClobber(inst, result); - } - } - - // Write 'end' opcode - try writer.writeByte(0x0B); + try writer.writeByte(0x0B); // end // Fill in the size of the generated code to the reserved space at the // beginning of the buffer. @@ -186,8 +174,9 @@ pub const Code = struct { leb.writeUnsignedFixed(5, self.bytes.items[0..5], @intCast(u32, size)); } - fn genInst(self: *Code, inst: *Inst) !WValue { + fn genInst(self: *Context, inst: *Inst) InnerError!WValue { return switch (inst.tag) { + .add => self.genAdd(inst.castTag(.add).?), .alloc => self.genAlloc(inst.castTag(.alloc).?), .arg => self.genArg(inst.castTag(.arg).?), .call => self.genCall(inst.castTag(.call).?), @@ -201,20 +190,27 @@ pub const Code = struct { }; } - fn genRet(self: *Code, inst: *Inst.UnOp) !WValue { - const operand = try self.resolveInst(inst.operand); + fn genBody(self: *Context, body: ir.Body) InnerError!void { + for (body.instructions) |inst| { + const result = try self.genInst(inst); + try self.values.putNoClobber(inst, result); + } + } + + fn genRet(self: *Context, inst: *Inst.UnOp) InnerError!WValue { + const operand = self.resolveInst(inst.operand); try self.emitWValue(operand); return WValue.none; } - fn genCall(self: *Code, inst: *Inst.Call) !WValue { + fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue { const func_inst = inst.func.castTag(.constant).?; const func = func_inst.val.castTag(.function).?.data; const target = func.owner_decl; const target_ty = target.typed_value.most_recent.typed_value.ty; for (inst.args) |arg| { - const arg_val = try self.resolveInst(arg); + const arg_val = self.resolveInst(arg); try self.emitWValue(arg_val); } @@ -230,39 +226,47 @@ pub const Code = struct { return WValue.none; } - fn genAlloc(self: *Code, inst: *Inst.NoOp) !WValue { + fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue { defer self.local_index += 1; return WValue{ .local = self.local_index }; } - fn genStore(self: *Code, inst: *Inst.BinOp) !WValue { + fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { const writer = self.bytes.writer(); - const lhs = try self.resolveInst(inst.lhs); - - const rhs = try self.resolveInst(inst.rhs); + const lhs = self.resolveInst(inst.lhs); + const rhs = self.resolveInst(inst.rhs); try self.emitWValue(rhs); try writer.writeByte(0x21); // local.set try leb.writeULEB128(writer, lhs.local); - return WValue.none; } - fn genLoad(self: *Code, inst: *Inst.UnOp) !WValue { + fn genLoad(self: *Context, inst: *Inst.UnOp) InnerError!WValue { const operand = self.resolveInst(inst.operand); - - // ensure index to local - return WValue{ .local = operand.local }; + try self.emitWValue(operand); + return WValue.none; } - fn genArg(self: *Code, inst: *Inst.Arg) !WValue { + fn genArg(self: *Context, inst: *Inst.Arg) InnerError!WValue { // arguments share the index with locals defer self.local_index += 1; return WValue{ .local = self.local_index }; } - fn emitConstant(self: *Code, inst: *Inst.Constant) !void { + fn genAdd(self: *Context, inst: *Inst.BinOp) InnerError!WValue { + const lhs = self.resolveInst(inst.lhs); + const rhs = self.resolveInst(inst.rhs); + + try self.emitWValue(lhs); + try self.emitWValue(rhs); + + try self.bytes.append(0x6A); // i32.add + return WValue.none; + } + + fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void { const writer = self.bytes.writer(); switch (inst.base.ty.tag()) { .u32 => { diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index c5e86ada3d..8e3d79d4db 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -119,7 +119,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { var managed_functype = fn_data.functype.toManaged(self.base.allocator); var managed_code = fn_data.code.toManaged(self.base.allocator); - var code = codegen.Code{ + var context = codegen.Context{ .gpa = self.base.allocator, .values = codegen.ValueTable.init(self.base.allocator), .bytes = managed_code, @@ -127,20 +127,20 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { .decl = decl, .err_msg = undefined, }; - defer code.values.deinit(); + defer context.values.deinit(); // generate the 'code' section for the function declaration - code.gen() catch |err| switch (err) { + context.gen() catch |err| switch (err) { error.CodegenFail => { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, code.err_msg); + try module.failed_decls.put(module.gpa, decl, context.err_msg); return; }, else => |e| return err, }; - fn_data.functype = code.func_type_data.toUnmanaged(); - fn_data.code = code.bytes.toUnmanaged(); + fn_data.functype = context.func_type_data.toUnmanaged(); + fn_data.code = context.bytes.toUnmanaged(); } pub fn updateDeclExports(