Commit ec84742c89

gracefu <81774659+gracefuu@users.noreply.github.com>
2021-04-03 10:11:29
stage2 wasm codegen: refactor to use wasm.buildOpcode
1 parent 3648e43
Changed files (2)
src
codegen
link
src/codegen/wasm.zig
@@ -498,6 +498,8 @@ pub const Context = struct {
     /// List of all locals' types generated throughout this declaration
     /// used to emit locals count at start of 'code' section.
     locals: std.ArrayListUnmanaged(u8),
+    /// The Target we're emitting (used to call intInfo)
+    target: std.Target,
 
     const InnerError = error{
         OutOfMemory,
@@ -529,17 +531,22 @@ pub const Context = struct {
         return self.values.get(inst).?; // Instruction does not dominate all uses!
     }
 
-    /// Using a given `Type`, returns the corresponding wasm value type
-    fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {
+    /// Using a given `Type`, returns the corresponding wasm Valtype
+    fn typeToValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!wasm.Valtype {
         return switch (ty.tag()) {
-            .f32 => wasm.valtype(.f32),
-            .f64 => wasm.valtype(.f64),
-            .u32, .i32, .bool => wasm.valtype(.i32),
-            .u64, .i64 => wasm.valtype(.i64),
-            else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}),
+            .f32 => .f32,
+            .f64 => .f64,
+            .u32, .i32, .bool => .i32,
+            .u64, .i64 => .i64,
+            else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.tag()}),
         };
     }
 
+    /// Using a given `Type`, returns the byte representation of its wasm value type
+    fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {
+        return wasm.valtype(try self.typeToValtype(src, ty));
+    }
+
     /// Using a given `Type`, returns the corresponding wasm value type
     /// Differently from `genValtype` this also allows `void` to create a block
     /// with no return type
@@ -643,7 +650,7 @@ pub const Context = struct {
 
     fn genInst(self: *Context, inst: *Inst) InnerError!WValue {
         return switch (inst.tag) {
-            .add => self.genAdd(inst.castTag(.add).?),
+            .add => self.genBinOp(inst.castTag(.add).?, .add),
             .alloc => self.genAlloc(inst.castTag(.alloc).?),
             .arg => self.genArg(inst.castTag(.arg).?),
             .block => self.genBlock(inst.castTag(.block).?),
@@ -661,12 +668,12 @@ pub const Context = struct {
             .dbg_stmt => WValue.none,
             .load => self.genLoad(inst.castTag(.load).?),
             .loop => self.genLoop(inst.castTag(.loop).?),
-            .mul => self.genMul(inst.castTag(.mul).?),
+            .mul => self.genBinOp(inst.castTag(.mul).?, .mul),
             .not => self.genNot(inst.castTag(.not).?),
             .ret => self.genRet(inst.castTag(.ret).?),
             .retvoid => WValue.none,
             .store => self.genStore(inst.castTag(.store).?),
-            .sub => self.genSub(inst.castTag(.sub).?),
+            .sub => self.genBinOp(inst.castTag(.sub).?, .sub),
             .unreach => self.genUnreachable(inst.castTag(.unreach).?),
             else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}),
         };
@@ -747,94 +754,59 @@ pub const Context = struct {
         return WValue{ .local = self.local_index };
     }
 
-    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);
-
-        const opcode: wasm.Opcode = switch (inst.base.ty.tag()) {
-            .u32, .i32 => .i32_add,
-            .u64, .i64 => .i64_add,
-            .f32 => .f32_add,
-            .f64 => .f64_add,
-            else => return self.fail(inst.base.src, "TODO - Implement wasm genAdd for type '{s}'", .{inst.base.ty.tag()}),
-        };
-
-        try self.code.append(wasm.opcode(opcode));
-        return .none;
-    }
-
-    fn genSub(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);
-
-        const opcode: wasm.Opcode = switch (inst.base.ty.tag()) {
-            .u32, .i32 => .i32_sub,
-            .u64, .i64 => .i64_sub,
-            .f32 => .f32_sub,
-            .f64 => .f64_sub,
-            else => return self.fail(inst.base.src, "TODO - Implement wasm genSub for type '{s}'", .{inst.base.ty.tag()}),
-        };
-
-        try self.code.append(wasm.opcode(opcode));
-        return .none;
-    }
-
-    fn genMul(self: *Context, inst: *Inst.BinOp) InnerError!WValue {
+    fn genBinOp(self: *Context, inst: *Inst.BinOp, op: Op) InnerError!WValue {
         const lhs = self.resolveInst(inst.lhs);
         const rhs = self.resolveInst(inst.rhs);
 
         try self.emitWValue(lhs);
         try self.emitWValue(rhs);
 
-        const opcode: wasm.Opcode = switch (inst.base.ty.tag()) {
-            .u32, .i32 => .i32_mul,
-            .u64, .i64 => .i64_mul,
-            .f32 => .f32_mul,
-            .f64 => .f64_mul,
-            else => return self.fail(inst.base.src, "TODO - Implement wasm genMul for type '{s}'", .{inst.base.ty.tag()}),
-        };
-
+        const opcode: wasm.Opcode = buildOpcode(.{
+            .op = op,
+            .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty),
+        });
         try self.code.append(wasm.opcode(opcode));
         return .none;
     }
 
     fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void {
         const writer = self.code.writer();
-        switch (inst.base.ty.tag()) {
-            .u32 => {
-                try writer.writeByte(wasm.opcode(.i32_const));
-                try leb.writeILEB128(writer, inst.val.toUnsignedInt());
+        switch (inst.base.ty.zigTypeTag()) {
+            .Int => {
+                // write opcode
+                const opcode: wasm.Opcode = buildOpcode(.{
+                    .op = .@"const",
+                    .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty),
+                });
+                try writer.writeByte(wasm.opcode(opcode));
+                // write constant
+                switch (inst.base.ty.intInfo(self.target).signedness) {
+                    .signed => try leb.writeILEB128(writer, inst.val.toSignedInt()),
+                    .unsigned => try leb.writeILEB128(writer, inst.val.toUnsignedInt()),
+                }
             },
-            .i32, .bool => {
+            .Bool => {
+                // write opcode
                 try writer.writeByte(wasm.opcode(.i32_const));
+                // write constant
                 try leb.writeILEB128(writer, inst.val.toSignedInt());
             },
-            .u64 => {
-                try writer.writeByte(wasm.opcode(.i64_const));
-                try leb.writeILEB128(writer, inst.val.toUnsignedInt());
-            },
-            .i64 => {
-                try writer.writeByte(wasm.opcode(.i64_const));
-                try leb.writeILEB128(writer, inst.val.toSignedInt());
-            },
-            .f32 => {
-                try writer.writeByte(wasm.opcode(.f32_const));
-                // TODO: enforce LE byte order
-                try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32)));
-            },
-            .f64 => {
-                try writer.writeByte(wasm.opcode(.f64_const));
-                // TODO: enforce LE byte order
-                try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64)));
+            .Float => {
+                // write opcode
+                const opcode: wasm.Opcode = buildOpcode(.{
+                    .op = .@"const",
+                    .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty),
+                });
+                try writer.writeByte(wasm.opcode(opcode));
+                // write constant
+                switch (inst.base.ty.floatBits(self.target)) {
+                    0...32 => try writer.writeIntLittle(u32, @bitCast(u32, inst.val.toFloat(f32))),
+                    64 => try writer.writeIntLittle(u64, @bitCast(u64, inst.val.toFloat(f64))),
+                    else => |bits| return self.fail(inst.base.src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}),
+                }
             },
-            .void => {},
-            else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for type {s}", .{ty}),
+            .Void => {},
+            else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{ty}),
         }
     }
 
@@ -935,62 +907,18 @@ pub const Context = struct {
         try self.emitWValue(lhs);
         try self.emitWValue(rhs);
 
-        const opcode_maybe: ?wasm.Opcode = switch (op) {
-            .lt => @as(?wasm.Opcode, switch (ty) {
-                .i32 => .i32_lt_s,
-                .u32 => .i32_lt_u,
-                .i64 => .i64_lt_s,
-                .u64 => .i64_lt_u,
-                .f32 => .f32_lt,
-                .f64 => .f64_lt,
-                else => null,
-            }),
-            .lte => @as(?wasm.Opcode, switch (ty) {
-                .i32 => .i32_le_s,
-                .u32 => .i32_le_u,
-                .i64 => .i64_le_s,
-                .u64 => .i64_le_u,
-                .f32 => .f32_le,
-                .f64 => .f64_le,
-                else => null,
-            }),
-            .eq => @as(?wasm.Opcode, switch (ty) {
-                .i32, .u32 => .i32_eq,
-                .i64, .u64 => .i64_eq,
-                .f32 => .f32_eq,
-                .f64 => .f64_eq,
-                else => null,
-            }),
-            .gte => @as(?wasm.Opcode, switch (ty) {
-                .i32 => .i32_ge_s,
-                .u32 => .i32_ge_u,
-                .i64 => .i64_ge_s,
-                .u64 => .i64_ge_u,
-                .f32 => .f32_ge,
-                .f64 => .f64_ge,
-                else => null,
-            }),
-            .gt => @as(?wasm.Opcode, switch (ty) {
-                .i32 => .i32_gt_s,
-                .u32 => .i32_gt_u,
-                .i64 => .i64_gt_s,
-                .u64 => .i64_gt_u,
-                .f32 => .f32_gt,
-                .f64 => .f64_gt,
-                else => null,
-            }),
-            .neq => @as(?wasm.Opcode, switch (ty) {
-                .i32, .u32 => .i32_ne,
-                .i64, .u64 => .i64_ne,
-                .f32 => .f32_ne,
-                .f64 => .f64_ne,
-                else => null,
-            }),
-        };
-
-        const opcode = opcode_maybe orelse
-            return self.fail(inst.base.src, "TODO - Wasm genCmp for type '{s}' and operator '{s}'", .{ ty, @tagName(op) });
-
+        const opcode: wasm.Opcode = buildOpcode(.{
+            .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty),
+            .op = switch (op) {
+                .lt => .lt,
+                .lte => .le,
+                .eq => .eq,
+                .neq => .ne,
+                .gte => .ge,
+                .gt => .gt,
+            },
+            .signedness = inst.lhs.ty.intInfo(self.target).signedness,
+        });
         try self.code.append(wasm.opcode(opcode));
         return WValue{ .code_offset = offset };
     }
src/link/Wasm.zig
@@ -127,6 +127,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
         .decl = decl,
         .err_msg = undefined,
         .locals = .{},
+        .target = self.base.options.target,
     };
     defer context.deinit();