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...@@ -3889,27 +3889,26 @@ fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerErro
3889 const lhs = try self.resolveInst(bin_op.lhs);3889 const lhs = try self.resolveInst(bin_op.lhs);
3890 const rhs = try self.resolveInst(bin_op.rhs);3890 const rhs = try self.resolveInst(bin_op.rhs);
38913891
3892 const result = try self.allocLocal(ty);3892 // operands to select from
38933893 try self.emitWValue(lhs);
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
3903 try self.emitWValue(rhs);3894 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/min3896 // operands to compare
3909 try self.emitWValue(lhs);3897 try self.emitWValue(lhs);
3910 try self.addLabel(.local_set, result.local);3898 try self.emitWValue(rhs);
3911 try self.endBlock();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);
3913 return result;3912 return result;
3914}3913}
39153914
src/arch/wasm/Emit.zig+2
...@@ -96,6 +96,8 @@ pub fn emitMir(emit: *Emit) InnerError!void {...@@ -96,6 +96,8 @@ pub fn emitMir(emit: *Emit) InnerError!void {
96 .@"return" => try emit.emitTag(tag),96 .@"return" => try emit.emitTag(tag),
97 .@"unreachable" => try emit.emitTag(tag),97 .@"unreachable" => try emit.emitTag(tag),
9898
99 .select => try emit.emitTag(tag),
100
99 // arithmetic101 // arithmetic
100 .i32_eqz => try emit.emitTag(tag),102 .i32_eqz => try emit.emitTag(tag),
101 .i32_eq => try emit.emitTag(tag),103 .i32_eq => try emit.emitTag(tag),
src/arch/wasm/Mir.zig+4
...@@ -77,6 +77,10 @@ pub const Inst = struct {...@@ -77,6 +77,10 @@ pub const Inst = struct {
77 ///77 ///
78 /// Uses `label`78 /// Uses `label`
79 call_indirect = 0x11,79 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,
80 /// Loads a local at given index onto the stack.84 /// Loads a local at given index onto the stack.
81 ///85 ///
82 /// Uses `label`86 /// Uses `label`