Commit 2c40b37f79

Luuk de Gram <luuk@degram.dev>
2022-04-02 17:56:11
wasm: Implement `@ctz` for bitsize <= 64
Implements the `ctz` AIR instruction for integers with bitsize <= 64. When the bitsize of the integer does not match the bitsize of a wasm type, we first XOR the value with the value of (1<<bitsize) to set the right bits and ensure we will only count the trailing zeroes of the integer with the correct bitsize.
1 parent bd27fe2
Changed files (1)
src
arch
src/arch/wasm/CodeGen.zig
@@ -1317,6 +1317,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
         .mul_with_overflow => self.airBinOpOverflow(inst, .mul),
 
         .clz => self.airClz(inst),
+        .ctz => self.airCtz(inst),
 
         .cmp_eq => self.airCmp(inst, .eq),
         .cmp_gte => self.airCmp(inst, .gte),
@@ -1440,7 +1441,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
         .shl_sat,
         .ret_addr,
         .frame_addr,
-        .ctz,
         .byte_swap,
         .bit_reverse,
         .is_err_ptr,
@@ -3978,3 +3978,44 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
     try self.addLabel(.local_set, result.local);
     return result;
 }
+
+fn airCtz(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 ty = self.air.typeOf(ty_op.operand);
+    const result_ty = self.air.typeOfIndex(inst);
+
+    if (ty.zigTypeTag() == .Vector) {
+        return self.fail("TODO: `@ctz` for vectors", .{});
+    }
+
+    const operand = try self.resolveInst(ty_op.operand);
+    const int_info = ty.intInfo(self.target);
+    const wasm_bits = toWasmBits(int_info.bits) orelse {
+        return self.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits});
+    };
+
+    switch (wasm_bits) {
+        32 => {
+            if (wasm_bits != int_info.bits) {
+                const val: u32 = @as(u32, 1) << @intCast(u5, int_info.bits);
+                const bin_op = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or");
+                try self.emitWValue(bin_op);
+            } else try self.emitWValue(operand);
+            try self.addTag(.i32_ctz);
+        },
+        64 => {
+            if (wasm_bits != int_info.bits) {
+                const val: u64 = @as(u64, 1) << @intCast(u6, int_info.bits);
+                const bin_op = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or");
+                try self.emitWValue(bin_op);
+            } else try self.emitWValue(operand);
+            try self.addTag(.i64_ctz);
+        },
+        else => unreachable,
+    }
+
+    const result = try self.allocLocal(result_ty);
+    try self.addLabel(.local_set, result.local);
+    return result;
+}