Commit 4b2538f72c

Luuk de Gram <Luukdegram@users.noreply.github.com>
2021-01-15 23:24:47
Cleanup and 'add' instruction for bigger test area
1 parent bb74f72
Changed files (2)
src
codegen
link
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);
-
-            if (result != .none) {
-                try self.values.putNoClobber(inst, result);
-            }
-        }
+        try self.genBody(mod_fn.body);
 
-        // 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 => {
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(