Commit 52fb044669

Luuk de Gram <luuk@degram.dev>
2021-12-02 22:33:36
wasm: Implement trunc, fix sliceLen and write undefined
1 parent 96a4692
Changed files (3)
src/arch/wasm/CodeGen.zig
@@ -551,7 +551,7 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{},
 initial_stack_value: WValue = .none,
 /// Arguments of this function declaration
 /// This will be set after `resolveCallingConventionValues`
-args: []WValue = undefined,
+args: []WValue = &.{},
 /// This will only be `.none` if the function returns void, or returns an immediate.
 /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated
 /// before this function returns its execution to the caller.
@@ -1117,6 +1117,13 @@ fn moveStack(self: *Self, offset: u32, local: u32) !void {
     try self.addLabel(.global_set, 0);
 }
 
+/// From given zig bitsize, returns the wasm bitsize
+fn toWasmIntBits(bits: u16) ?u16 {
+    return for ([_]u16{ 32, 64 }) |wasm_bits| {
+        if (bits <= wasm_bits) return wasm_bits;
+    } else null;
+}
+
 fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
     const air_tags = self.air.instructions.items(.tag);
     return switch (air_tags[inst]) {
@@ -1563,6 +1570,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
 }
 
 fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
+    if (val.isUndefDeep()) return self.emitUndefined(ty);
     switch (ty.zigTypeTag()) {
         .Int => {
             const int_info = ty.intInfo(self.target);
@@ -1668,6 +1676,22 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
     }
 }
 
+fn emitUndefined(self: *Self, ty: Type) InnerError!void {
+    switch (ty.zigTypeTag()) {
+        .Int => switch (ty.intInfo(self.target).bits) {
+            0...32 => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))),
+            33...64 => try self.addImm64(0xaaaaaaaaaaaaaaaa),
+            else => |bits| return self.fail("Wasm TODO: emitUndefined for integer bitsize: {d}", .{bits}),
+        },
+        .Float => switch (ty.floatBits(self.target)) {
+            0...32 => try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) } }),
+            33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))),
+            else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}),
+        },
+        else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}),
+    }
+}
+
 /// Returns a `Value` as a signed 32 bit value.
 /// It's illegal to provide a value with a type that cannot be represented
 /// as an integer value.
@@ -2233,19 +2257,21 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
     const operand = self.resolveInst(ty_op.operand);
     const pointer_width = self.target.cpu.arch.ptrBitWidth() / 8;
 
-    // Get pointer to slice
-    try self.emitWValue(operand);
-    // length of slice is stored after the pointer of the slice
-    const extra_index = try self.addExtra(Mir.MemArg{
-        .offset = pointer_width,
-        .alignment = pointer_width,
-    });
-    try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } });
+    return try self.load(operand, Type.usize, pointer_width);
 
-    const result = try self.allocLocal(Type.initTag(.i32)); // pointer is always i32
-    // store slice length in local
-    try self.addLabel(.local_set, result.local);
-    return result;
+    // // Get pointer to slice
+    // try self.emitWValue(operand);
+    // // length of slice is stored after the pointer of the slice
+    // const extra_index = try self.addExtra(Mir.MemArg{
+    //     .offset = pointer_width,
+    //     .alignment = pointer_width,
+    // });
+    // try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } });
+
+    // const result = try self.allocLocal(Type.initTag(.i32)); // pointer is always i32
+    // // store slice length in local
+    // try self.addLabel(.local_set, result.local);
+    // return result;
 }
 
 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
@@ -2272,3 +2298,70 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
     try self.addLabel(.local_set, result.local);
     return result;
 }
+
+fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+    if (self.liveness.isUnused(inst)) return WValue.none;
+    const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+    const operand = self.resolveInst(ty_op.operand);
+    return try self.load(operand, Type.usize, 0);
+}
+
+fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+    if (self.liveness.isUnused(inst)) return WValue.none;
+    const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+    const operand = self.resolveInst(ty_op.operand);
+    const op_ty = self.air.typeOf(ty_op.operand);
+    const int_info = self.air.getRefType(ty_op.ty).intInfo(self.target);
+    const wanted_bits = int_info.bits;
+    const result = try self.allocLocal(self.air.getRefType(ty_op.ty));
+    const op_bits = op_ty.intInfo(self.target).bits;
+
+    const wasm_bits = toWasmIntBits(wanted_bits) orelse
+        return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{wanted_bits});
+
+    // Use wasm's instruction to wrap from 64bit to 32bit integer when possible
+    if (op_bits == 64 and wanted_bits == 32) {
+        try self.emitWValue(operand);
+        try self.addTag(.i32_wrap_i64);
+        try self.addLabel(.local_set, result.local);
+        return result;
+    }
+
+    // Any other truncation must be done manually
+    if (int_info.signedness == .unsigned) {
+        const mask = (@as(u65, 1) << @intCast(u7, wanted_bits)) - 1;
+        try self.emitWValue(operand);
+        switch (wasm_bits) {
+            32 => {
+                try self.addImm32(@bitCast(i32, @intCast(u32, mask)));
+                try self.addTag(.i32_and);
+            },
+            64 => {
+                try self.addImm64(@intCast(u64, mask));
+                try self.addTag(.i64_and);
+            },
+            else => unreachable,
+        }
+    } else {
+        const shift_bits = wasm_bits - wanted_bits;
+        try self.emitWValue(operand);
+        switch (wasm_bits) {
+            32 => {
+                try self.addImm32(@bitCast(i16, shift_bits));
+                try self.addTag(.i32_shl);
+                try self.addImm32(@bitCast(i16, shift_bits));
+                try self.addTag(.i32_shr_s);
+            },
+            64 => {
+                try self.addImm64(shift_bits);
+                try self.addTag(.i64_shl);
+                try self.addImm64(shift_bits);
+                try self.addTag(.i64_shr_s);
+            },
+            else => unreachable,
+        }
+    }
+
+    try self.addLabel(.local_set, result.local);
+    return result;
+}
src/arch/wasm/Emit.zig
@@ -147,6 +147,11 @@ pub fn emitMir(emit: *Emit) InnerError!void {
             .i64_div_s => try emit.emitTag(tag),
             .i64_div_u => try emit.emitTag(tag),
             .i64_and => try emit.emitTag(tag),
+            .i64_or => try emit.emitTag(tag),
+            .i64_xor => try emit.emitTag(tag),
+            .i64_shl => try emit.emitTag(tag),
+            .i64_shr_s => try emit.emitTag(tag),
+            .i64_shr_u => try emit.emitTag(tag),
             .i32_wrap_i64 => try emit.emitTag(tag),
             .i64_extend_i32_s => try emit.emitTag(tag),
             .i64_extend_i32_u => try emit.emitTag(tag),
src/arch/wasm/Mir.zig
@@ -348,6 +348,16 @@ pub const Inst = struct {
         /// Uses `tag`
         i64_and = 0x83,
         /// Uses `tag`
+        i64_or = 0x84,
+        /// Uses `tag`
+        i64_xor = 0x85,
+        /// Uses `tag`
+        i64_shl = 0x86,
+        /// Uses `tag`
+        i64_shr_s = 0x87,
+        /// Uses `tag`
+        i64_shr_u = 0x88,
+        /// Uses `tag`
         i32_wrap_i64 = 0xA7,
         /// Uses `tag`
         i64_extend_i32_s = 0xAC,