authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-04-05 18:35:38+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-04-05 21:56:25+02:00
logac873367b9d68e1d7b4cf4e5efbe179960dc7557
tree97476eca40fa2619491c54c1484b4278f8ab5435
parent5fafcc2b629e2ff00755b2bf45e903590f04aa9f

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.

3 files changed, 22 insertions(+), 17 deletions(-)

src/arch/wasm/CodeGen.zig+16-17
......@@ -3889,27 +3889,26 @@ fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerErro
38893889 const lhs = try self.resolveInst(bin_op.lhs);
38903890 const rhs = try self.resolveInst(bin_op.rhs);
38913891
3892 const result = try self.allocLocal(ty);
3893
3894 try self.startBlock(.block, wasm.block_empty);
3895 try self.startBlock(.block, wasm.block_empty);
3896
3897 // check if LHS is greater/lesser than RHS
3898 const cmp_result = try self.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt);
3899 try self.addLabel(.local_get, cmp_result.local);
3900 try self.addLabel(.br_if, 0); // break to outer loop if LHS is greater/lesser than RHS
3901
3902 // set RHS as max/min
3892 // operands to select from
3893 try self.emitWValue(lhs);
39033894 try self.emitWValue(rhs);
3904 try self.addLabel(.local_set, result.local);
3905 try self.addLabel(.br, 1); // break out of all blocks
3906 try self.endBlock();
39073895
3908 // set LHS as max/min
3896 // operands to compare
39093897 try self.emitWValue(lhs);
3910 try self.addLabel(.local_set, result.local);
3911 try self.endBlock();
3898 try self.emitWValue(rhs);
3899 const opcode = buildOpcode(.{
3900 .op = if (op == .max) .gt else .lt,
3901 .signedness = if (ty.isSignedInt()) .signed else .unsigned,
3902 .valtype1 = typeToValtype(ty, self.target),
3903 });
3904 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3905
3906 // based on the result from comparison, return operand 0 or 1.
3907 try self.addTag(.select);
39123908
3909 // store result in local
3910 const result = try self.allocLocal(ty);
3911 try self.addLabel(.local_set, result.local);
39133912 return result;
39143913}
39153914
src/arch/wasm/Emit.zig+2
......@@ -96,6 +96,8 @@ pub fn emitMir(emit: *Emit) InnerError!void {
9696 .@"return" => try emit.emitTag(tag),
9797 .@"unreachable" => try emit.emitTag(tag),
9898
99 .select => try emit.emitTag(tag),
100
99101 // arithmetic
100102 .i32_eqz => try emit.emitTag(tag),
101103 .i32_eq => try emit.emitTag(tag),
src/arch/wasm/Mir.zig+4
......@@ -77,6 +77,10 @@ pub const Inst = struct {
7777 ///
7878 /// Uses `label`
7979 call_indirect = 0x11,
80 /// Pops three values from the stack and pushes
81 /// the first or second value dependent on the third value.
82 /// Uses `tag`
83 select = 0x1B,
8084 /// Loads a local at given index onto the stack.
8185 ///
8286 /// Uses `label`