Commit ac873367b9

Luuk de Gram <luuk@degram.dev>
2022-04-05 18:35:38
wasm: Use 'select' instruction for max/min
Rather than using blocks and control flow to check which operand is the maximum or minimum, we use wasm's `select` instruction which returns us the operand based on a result from a comparison. This saves us the need of control flow, as well as reduce the instruction count from 13 to 7.
1 parent 5fafcc2
Changed files (3)
src/arch/wasm/CodeGen.zig
@@ -3889,27 +3889,26 @@ fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerErro
     const lhs = try self.resolveInst(bin_op.lhs);
     const rhs = try self.resolveInst(bin_op.rhs);
 
-    const result = try self.allocLocal(ty);
-
-    try self.startBlock(.block, wasm.block_empty);
-    try self.startBlock(.block, wasm.block_empty);
-
-    // check if LHS is greater/lesser than RHS
-    const cmp_result = try self.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt);
-    try self.addLabel(.local_get, cmp_result.local);
-    try self.addLabel(.br_if, 0); // break to outer loop if LHS is greater/lesser than RHS
-
-    // set RHS as max/min
+    // operands to select from
+    try self.emitWValue(lhs);
     try self.emitWValue(rhs);
-    try self.addLabel(.local_set, result.local);
-    try self.addLabel(.br, 1); // break out of all blocks
-    try self.endBlock();
 
-    // set LHS as max/min
+    // operands to compare
     try self.emitWValue(lhs);
-    try self.addLabel(.local_set, result.local);
-    try self.endBlock();
+    try self.emitWValue(rhs);
+    const opcode = buildOpcode(.{
+        .op = if (op == .max) .gt else .lt,
+        .signedness = if (ty.isSignedInt()) .signed else .unsigned,
+        .valtype1 = typeToValtype(ty, self.target),
+    });
+    try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
+
+    // based on the result from comparison, return operand 0 or 1.
+    try self.addTag(.select);
 
+    // store result in local
+    const result = try self.allocLocal(ty);
+    try self.addLabel(.local_set, result.local);
     return result;
 }
 
src/arch/wasm/Emit.zig
@@ -96,6 +96,8 @@ pub fn emitMir(emit: *Emit) InnerError!void {
             .@"return" => try emit.emitTag(tag),
             .@"unreachable" => try emit.emitTag(tag),
 
+            .select => try emit.emitTag(tag),
+
             // arithmetic
             .i32_eqz => try emit.emitTag(tag),
             .i32_eq => try emit.emitTag(tag),
src/arch/wasm/Mir.zig
@@ -77,6 +77,10 @@ pub const Inst = struct {
         ///
         /// Uses `label`
         call_indirect = 0x11,
+        /// Pops three values from the stack and pushes
+        /// the first or second value dependent on the third value.
+        /// Uses `tag`
+        select = 0x1B,
         /// Loads a local at given index onto the stack.
         ///
         /// Uses `label`