Commit c946ff89 authored by Clemens Backes's avatar Clemens Backes Committed by V8 LUCI CQ

[liftoff] Optimize comparisons with constants

i32 comparisons often compare against constants, in order to implement
conditional branches. This CL optimizes such code by not loading the
constant into a register first, but directly emitting the comparison.
The code is shared for implementing {if} and {br_if} (and thereby makes
those two methods more readable).

R=thibaudm@chromium.org

Change-Id: I3f2f071a1c9e4b02c7368a2757bf4aae2920bd69
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3172765Reviewed-by: 's avatarThibaud Michaud <thibaudm@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77008}
parent 41578273
......@@ -45,8 +45,8 @@ enum LiftoffCondition {
kUnsignedGreaterEqual
};
inline constexpr LiftoffCondition Negate(LiftoffCondition liftoff_cond) {
switch (liftoff_cond) {
inline constexpr LiftoffCondition Negate(LiftoffCondition cond) {
switch (cond) {
case kEqual:
return kUnequal;
case kUnequal:
......@@ -70,6 +70,31 @@ inline constexpr LiftoffCondition Negate(LiftoffCondition liftoff_cond) {
}
}
inline constexpr LiftoffCondition Flip(LiftoffCondition cond) {
switch (cond) {
case kEqual:
return kEqual;
case kUnequal:
return kUnequal;
case kSignedLessThan:
return kSignedGreaterThan;
case kSignedLessEqual:
return kSignedGreaterEqual;
case kSignedGreaterEqual:
return kSignedLessEqual;
case kSignedGreaterThan:
return kSignedLessThan;
case kUnsignedLessThan:
return kUnsignedGreaterThan;
case kUnsignedLessEqual:
return kUnsignedGreaterEqual;
case kUnsignedGreaterEqual:
return kUnsignedLessEqual;
case kUnsignedGreaterThan:
return kUnsignedLessThan;
}
}
class LiftoffAssembler : public TurboAssembler {
public:
// Each slot in our stack frame currently has exactly 8 bytes.
......@@ -980,8 +1005,8 @@ class LiftoffAssembler : public TurboAssembler {
inline void emit_cond_jump(LiftoffCondition, Label*, ValueKind value,
Register lhs, Register rhs = no_reg);
inline void emit_i32_cond_jumpi(LiftoffCondition liftoff_cond, Label* label,
Register lhs, int imm);
inline void emit_i32_cond_jumpi(LiftoffCondition, Label*, Register lhs,
int imm);
// Set {dst} to 1 if condition holds, 0 otherwise.
inline void emit_i32_eqz(Register dst, Register src);
inline void emit_i32_set_cond(LiftoffCondition, Register dst, Register lhs,
......
......@@ -1262,6 +1262,46 @@ class LiftoffCompiler {
}
}
void JumpIfFalse(FullDecoder* decoder, Label* false_dst) {
LiftoffCondition cond =
test_and_reset_outstanding_op(kExprI32Eqz) ? kNotEqualZero : kEqualZero;
if (!has_outstanding_op()) {
// Unary comparison.
Register value = __ PopToRegister().gp();
__ emit_cond_jump(cond, false_dst, kI32, value);
return;
}
// Binary comparison of i32 values.
cond = Negate(GetCompareCondition(outstanding_op_));
outstanding_op_ = kNoOutstandingOp;
LiftoffAssembler::VarState rhs_slot = __ cache_state()->stack_state.back();
if (rhs_slot.is_const()) {
// Compare to a constant.
int32_t rhs_imm = rhs_slot.i32_const();
__ cache_state()->stack_state.pop_back();
Register lhs = __ PopToRegister().gp();
__ emit_i32_cond_jumpi(cond, false_dst, lhs, rhs_imm);
return;
}
Register rhs = __ PopToRegister().gp();
LiftoffAssembler::VarState lhs_slot = __ cache_state()->stack_state.back();
if (lhs_slot.is_const()) {
// Compare a constant to an arbitrary value.
int32_t lhs_imm = lhs_slot.i32_const();
__ cache_state()->stack_state.pop_back();
// Flip the condition, because {lhs} and {rhs} are swapped.
__ emit_i32_cond_jumpi(Flip(cond), false_dst, rhs, lhs_imm);
return;
}
// Compare two arbitrary values.
Register lhs = __ PopToRegister(LiftoffRegList::ForRegs(rhs)).gp();
__ emit_cond_jump(cond, false_dst, kI32, lhs, rhs);
}
void If(FullDecoder* decoder, const Value& cond, Control* if_block) {
DCHECK_EQ(if_block, decoder->control_at(0));
DCHECK(if_block->is_if());
......@@ -1269,24 +1309,8 @@ class LiftoffCompiler {
// Allocate the else state.
if_block->else_state = std::make_unique<ElseState>();
// Test the condition, jump to else if zero.
Register value = __ PopToRegister().gp();
if (!has_outstanding_op()) {
__ emit_cond_jump(kEqualZero, if_block->else_state->label.get(), kI32,
value);
} else if (outstanding_op_ == kExprI32Eqz) {
__ emit_cond_jump(kNotEqualZero, if_block->else_state->label.get(), kI32,
value);
outstanding_op_ = kNoOutstandingOp;
} else {
// Otherwise, it's an i32 compare opcode.
LiftoffCondition cond = Negate(GetCompareCondition(outstanding_op_));
Register rhs = value;
Register lhs = __ PopToRegister(LiftoffRegList::ForRegs(rhs)).gp();
__ emit_cond_jump(cond, if_block->else_state->label.get(), kI32, lhs,
rhs);
outstanding_op_ = kNoOutstandingOp;
}
// Test the condition on the value stack, jump to else if zero.
JumpIfFalse(decoder, if_block->else_state->label.get());
// Store the state (after popping the value) for executing the else branch.
if_block->else_state->state.Split(*__ cache_state());
......@@ -2500,21 +2524,9 @@ class LiftoffCompiler {
}
Label cont_false;
Register value = __ PopToRegister().gp();
if (!has_outstanding_op()) {
__ emit_cond_jump(kEqualZero, &cont_false, kI32, value);
} else if (outstanding_op_ == kExprI32Eqz) {
__ emit_cond_jump(kNotEqualZero, &cont_false, kI32, value);
outstanding_op_ = kNoOutstandingOp;
} else {
// Otherwise, it's an i32 compare opcode.
LiftoffCondition cond = Negate(GetCompareCondition(outstanding_op_));
Register rhs = value;
Register lhs = __ PopToRegister(LiftoffRegList::ForRegs(rhs)).gp();
__ emit_cond_jump(cond, &cont_false, kI32, lhs, rhs);
outstanding_op_ = kNoOutstandingOp;
}
// Test the condition on the value stack, jump to {cont_false} if zero.
JumpIfFalse(decoder, &cont_false);
BrOrRet(decoder, depth, 0);
__ bind(&cont_false);
......@@ -6147,6 +6159,13 @@ class LiftoffCompiler {
return outstanding_op_ != kNoOutstandingOp;
}
bool test_and_reset_outstanding_op(WasmOpcode opcode) {
DCHECK_NE(kNoOutstandingOp, opcode);
if (outstanding_op_ != opcode) return false;
outstanding_op_ = kNoOutstandingOp;
return true;
}
void TraceCacheState(FullDecoder* decoder) const {
if (!FLAG_trace_liftoff) return;
StdoutStream os;
......
......@@ -180,6 +180,65 @@ static void TestInt32Binop(TestExecutionTier execution_tier, WasmOpcode opcode,
}
}
}
FOR_INT32_INPUTS(i) {
WasmRunner<ctype, ctype> r(execution_tier);
// Apply {opcode} on constant and parameter.
BUILD(r, WASM_BINOP(opcode, WASM_I32V(i), WASM_LOCAL_GET(0)));
FOR_INT32_INPUTS(j) {
CHECK_EQ(expected(i, j), r.Call(j));
}
}
FOR_INT32_INPUTS(j) {
WasmRunner<ctype, ctype> r(execution_tier);
// Apply {opcode} on parameter and constant.
BUILD(r, WASM_BINOP(opcode, WASM_LOCAL_GET(0), WASM_I32V(j)));
FOR_INT32_INPUTS(i) {
CHECK_EQ(expected(i, j), r.Call(i));
}
}
auto to_bool = [](ctype value) -> ctype {
return value == static_cast<ctype>(0xDEADBEEF) ? value : !!value;
};
FOR_INT32_INPUTS(i) {
WasmRunner<ctype, ctype> r(execution_tier);
// Apply {opcode} on constant and parameter, followed by {if}.
BUILD(r, WASM_IF(WASM_BINOP(opcode, WASM_I32V(i), WASM_LOCAL_GET(0)),
WASM_RETURN(WASM_ONE)),
WASM_ZERO);
FOR_INT32_INPUTS(j) {
CHECK_EQ(to_bool(expected(i, j)), r.Call(j));
}
}
FOR_INT32_INPUTS(j) {
WasmRunner<ctype, ctype> r(execution_tier);
// Apply {opcode} on parameter and constant, followed by {if}.
BUILD(r, WASM_IF(WASM_BINOP(opcode, WASM_LOCAL_GET(0), WASM_I32V(j)),
WASM_RETURN(WASM_ONE)),
WASM_ZERO);
FOR_INT32_INPUTS(i) {
CHECK_EQ(to_bool(expected(i, j)), r.Call(i));
}
}
FOR_INT32_INPUTS(i) {
WasmRunner<ctype, ctype> r(execution_tier);
// Apply {opcode} on constant and parameter, followed by {br_if}.
BUILD(r, WASM_BR_IFD(0, WASM_ONE,
WASM_BINOP(opcode, WASM_I32V(i), WASM_LOCAL_GET(0))),
WASM_ZERO);
FOR_INT32_INPUTS(j) {
CHECK_EQ(to_bool(expected(i, j)), r.Call(j));
}
}
FOR_INT32_INPUTS(j) {
WasmRunner<ctype, ctype> r(execution_tier);
// Apply {opcode} on parameter and constant, followed by {br_if}.
BUILD(r, WASM_BR_IFD(0, WASM_ONE,
WASM_BINOP(opcode, WASM_LOCAL_GET(0), WASM_I32V(j))),
WASM_ZERO);
FOR_INT32_INPUTS(i) {
CHECK_EQ(to_bool(expected(i, j)), r.Call(i));
}
}
}
// clang-format on
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment