Commit ce7ec6ef authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Refactor the CheckedInt32Div/CheckedUint32Div lowering.

Improve the lowering of CheckedInt32Div and CheckedUint32Div for the
case that the right hand side is a known (positive) power of two, as
in that case it's sufficient to just check the relevant bits on the
left hand side and then shift by the appropriate amount of bits.

This is significantly faster than what TurboFan is able to generate
from the general lowering, even with all the MachineOperatorReducer
magic (it even shows as a steady ~1.5% overall improvement on the
Kraken crypto ccm benchmark).

Also turn the general CheckedInt32Div lowering into readable code again,
and make sure that all the bailout cases are properly covered by mjsunit
tests (i.e. the "division by zero" bailout was not covered properly).

Bug: v8:8015
Change-Id: Ibfdd367a6ee5d70dcaa48801858042c5029b7004
Reviewed-on: https://chromium-review.googlesource.com/1236954Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56115}
parent 1f395638
......@@ -1688,63 +1688,87 @@ Node* EffectControlLinearizer::LowerCheckedInt32Div(Node* node,
Node* frame_state) {
Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1);
auto if_not_positive = __ MakeDeferredLabel();
auto if_is_minint = __ MakeDeferredLabel();
auto done = __ MakeLabel(MachineRepresentation::kWord32);
auto minint_check_done = __ MakeLabel();
Node* zero = __ Int32Constant(0);
// Check if {rhs} is positive (and not zero).
Node* check0 = __ Int32LessThan(zero, rhs);
__ GotoIfNot(check0, &if_not_positive);
// Fast case, no additional checking required.
__ Goto(&done, __ Int32Div(lhs, rhs));
// Check if the {rhs} is a known power of two.
Int32Matcher m(rhs);
if (m.IsPowerOf2()) {
// Since we know that {rhs} is a power of two, we can perform a fast
// check to see if the relevant least significant bits of the {lhs}
// are all zero, and if so we know that we can perform a division
// safely (and fast by doing an arithmetic - aka sign preserving -
// right shift on {lhs}).
int32_t divisor = m.Value();
Node* mask = __ Int32Constant(divisor - 1);
Node* shift = __ Int32Constant(WhichPowerOf2(divisor));
Node* check = __ Word32Equal(__ Word32And(lhs, mask), zero);
__ DeoptimizeIfNot(DeoptimizeReason::kLostPrecision, VectorSlotPair(),
check, frame_state);
return __ Word32Sar(lhs, shift);
} else {
auto if_rhs_positive = __ MakeLabel();
auto if_rhs_negative = __ MakeDeferredLabel();
auto done = __ MakeLabel(MachineRepresentation::kWord32);
{
__ Bind(&if_not_positive);
// Check if {rhs} is positive (and not zero).
Node* check_rhs_positive = __ Int32LessThan(zero, rhs);
__ Branch(check_rhs_positive, &if_rhs_positive, &if_rhs_negative);
// Check if {rhs} is zero.
Node* check = __ Word32Equal(rhs, zero);
__ DeoptimizeIf(DeoptimizeReason::kDivisionByZero, VectorSlotPair(), check,
frame_state);
__ Bind(&if_rhs_positive);
{
// Fast case, no additional checking required.
__ Goto(&done, __ Int32Div(lhs, rhs));
}
// Check if {lhs} is zero, as that would produce minus zero.
check = __ Word32Equal(lhs, zero);
__ DeoptimizeIf(DeoptimizeReason::kMinusZero, VectorSlotPair(), check,
frame_state);
__ Bind(&if_rhs_negative);
{
auto if_lhs_minint = __ MakeDeferredLabel();
auto if_lhs_notminint = __ MakeLabel();
// Check if {rhs} is zero.
Node* check_rhs_zero = __ Word32Equal(rhs, zero);
__ DeoptimizeIf(DeoptimizeReason::kDivisionByZero, VectorSlotPair(),
check_rhs_zero, frame_state);
// Check if {lhs} is zero, as that would produce minus zero.
Node* check_lhs_zero = __ Word32Equal(lhs, zero);
__ DeoptimizeIf(DeoptimizeReason::kMinusZero, VectorSlotPair(),
check_lhs_zero, frame_state);
// Check if {lhs} is kMinInt and {rhs} is -1, in which case we'd have
// to return -kMinInt, which is not representable as Word32.
Node* check_lhs_minint = graph()->NewNode(machine()->Word32Equal(), lhs,
__ Int32Constant(kMinInt));
__ Branch(check_lhs_minint, &if_lhs_minint, &if_lhs_notminint);
__ Bind(&if_lhs_minint);
{
// Check that {rhs} is not -1, otherwise result would be -kMinInt.
Node* check_rhs_minusone = __ Word32Equal(rhs, __ Int32Constant(-1));
__ DeoptimizeIf(DeoptimizeReason::kOverflow, VectorSlotPair(),
check_rhs_minusone, frame_state);
// Check if {lhs} is kMinInt and {rhs} is -1, in which case we'd have
// to return -kMinInt, which is not representable.
Node* minint = __ Int32Constant(std::numeric_limits<int32_t>::min());
Node* check1 = graph()->NewNode(machine()->Word32Equal(), lhs, minint);
__ GotoIf(check1, &if_is_minint);
__ Goto(&minint_check_done);
__ Bind(&if_is_minint);
// Check if {rhs} is -1.
Node* minusone = __ Int32Constant(-1);
Node* is_minus_one = __ Word32Equal(rhs, minusone);
__ DeoptimizeIf(DeoptimizeReason::kOverflow, VectorSlotPair(), is_minus_one,
frame_state);
__ Goto(&minint_check_done);
// Perform the actual integer division.
__ Goto(&done, __ Int32Div(lhs, rhs));
}
__ Bind(&minint_check_done);
// Perform the actual integer division.
__ Goto(&done, __ Int32Div(lhs, rhs));
}
__ Bind(&if_lhs_notminint);
{
// Perform the actual integer division.
__ Goto(&done, __ Int32Div(lhs, rhs));
}
}
__ Bind(&done);
Node* value = done.PhiAt(0);
__ Bind(&done);
Node* value = done.PhiAt(0);
// Check if the remainder is non-zero.
Node* check = __ Word32Equal(lhs, __ Int32Mul(rhs, value));
__ DeoptimizeIfNot(DeoptimizeReason::kLostPrecision, VectorSlotPair(), check,
frame_state);
// Check if the remainder is non-zero.
Node* check = __ Word32Equal(lhs, __ Int32Mul(value, rhs));
__ DeoptimizeIfNot(DeoptimizeReason::kLostPrecision, VectorSlotPair(),
check, frame_state);
return value;
return value;
}
}
Node* EffectControlLinearizer::BuildUint32Mod(Node* lhs, Node* rhs) {
......@@ -1855,22 +1879,38 @@ Node* EffectControlLinearizer::LowerCheckedUint32Div(Node* node,
Node* frame_state) {
Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1);
Node* zero = __ Int32Constant(0);
// Ensure that {rhs} is not zero, otherwise we'd have to return NaN.
Node* check = __ Word32Equal(rhs, zero);
__ DeoptimizeIf(DeoptimizeReason::kDivisionByZero, VectorSlotPair(), check,
frame_state);
// Check if the {rhs} is a known power of two.
Uint32Matcher m(rhs);
if (m.IsPowerOf2()) {
// Since we know that {rhs} is a power of two, we can perform a fast
// check to see if the relevant least significant bits of the {lhs}
// are all zero, and if so we know that we can perform a division
// safely (and fast by doing a logical - aka zero extending - right
// shift on {lhs}).
uint32_t divisor = m.Value();
Node* mask = __ Uint32Constant(divisor - 1);
Node* shift = __ Uint32Constant(WhichPowerOf2(divisor));
Node* check = __ Word32Equal(__ Word32And(lhs, mask), zero);
__ DeoptimizeIfNot(DeoptimizeReason::kLostPrecision, VectorSlotPair(),
check, frame_state);
return __ Word32Shr(lhs, shift);
} else {
// Ensure that {rhs} is not zero, otherwise we'd have to return NaN.
Node* check = __ Word32Equal(rhs, zero);
__ DeoptimizeIf(DeoptimizeReason::kDivisionByZero, VectorSlotPair(), check,
frame_state);
// Perform the actual unsigned integer division.
Node* value = __ Uint32Div(lhs, rhs);
// Perform the actual unsigned integer division.
Node* value = __ Uint32Div(lhs, rhs);
// Check if the remainder is non-zero.
check = __ Word32Equal(lhs, __ Int32Mul(rhs, value));
__ DeoptimizeIfNot(DeoptimizeReason::kLostPrecision, VectorSlotPair(), check,
frame_state);
return value;
// Check if the remainder is non-zero.
check = __ Word32Equal(lhs, __ Int32Mul(rhs, value));
__ DeoptimizeIfNot(DeoptimizeReason::kLostPrecision, VectorSlotPair(),
check, frame_state);
return value;
}
}
Node* EffectControlLinearizer::LowerCheckedUint32Mod(Node* node,
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --opt
// Flags: --allow-natives-syntax --opt --noalways-opt
// Test that NumberDivide with Number feedback works if only in the
// end SimplifiedLowering figures out that the inputs to this operation
......@@ -61,3 +61,147 @@
assertEquals(2, foo(4));
assertOptimized(foo);
})();
// Test that SpeculativeNumberDivide turns into CheckedInt32Div, and
// that the "known power of two divisor" optimization works correctly.
(function() {
function foo(x) { return (x | 0) / 2; }
// Warmup with proper int32 divisions.
assertEquals(1, foo(2));
assertEquals(2, foo(4));
%OptimizeFunctionOnNextCall(foo);
assertEquals(3, foo(6));
assertOptimized(foo);
// Make optimized code fail.
assertEquals(0.5, foo(1));
assertUnoptimized(foo);
// Try again with the new feedback, and now it should stay optimized.
%OptimizeFunctionOnNextCall(foo);
assertEquals(4, foo(8));
assertOptimized(foo);
assertEquals(0.5, foo(1));
assertOptimized(foo);
})();
// Test that SpeculativeNumberDivide turns into CheckedInt32Div, and
// that the optimized code properly bails out on "division by zero".
(function() {
function foo(x, y) { return x / y; }
// Warmup with proper int32 divisions.
assertEquals(2, foo(4, 2));
assertEquals(2, foo(8, 4));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo(2, 2));
assertOptimized(foo);
// Make optimized code fail.
assertEquals(Infinity, foo(1, 0));
assertUnoptimized(foo);
// Try again with the new feedback, and now it should stay optimized.
%OptimizeFunctionOnNextCall(foo);
assertEquals(2, foo(2, 1));
assertOptimized(foo);
assertEquals(Infinity, foo(1, 0));
assertOptimized(foo);
})();
// Test that SpeculativeNumberDivide turns into CheckedInt32Div, and
// that the optimized code properly bails out on minus zero.
(function() {
function foo(x, y) { return x / y; }
// Warmup with proper int32 divisions.
assertEquals(2, foo(4, 2));
assertEquals(2, foo(8, 4));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo(2, 2));
assertOptimized(foo);
// Make optimized code fail.
assertEquals(-0, foo(0, -1));
assertUnoptimized(foo);
// Try again with the new feedback, and now it should stay optimized.
%OptimizeFunctionOnNextCall(foo);
assertEquals(2, foo(2, 1));
assertOptimized(foo);
assertEquals(-0, foo(0, -1));
assertOptimized(foo);
})();
// Test that SpeculativeNumberDivide turns into CheckedInt32Div, and
// that the optimized code properly bails out if result is -kMinInt.
(function() {
function foo(x, y) { return x / y; }
// Warmup with proper int32 divisions.
assertEquals(2, foo(4, 2));
assertEquals(2, foo(8, 4));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo(2, 2));
assertOptimized(foo);
// Make optimized code fail.
assertEquals(2147483648, foo(-2147483648, -1));
assertUnoptimized(foo);
// Try again with the new feedback, and now it should stay optimized.
%OptimizeFunctionOnNextCall(foo);
assertEquals(2, foo(2, 1));
assertOptimized(foo);
assertEquals(2147483648, foo(-2147483648, -1));
assertOptimized(foo);
})();
// Test that SpeculativeNumberDivide turns into CheckedUint32Div, and
// that the "known power of two divisor" optimization works correctly.
(function() {
function foo(s) { return s.length / 2; }
// Warmup with proper uint32 divisions.
assertEquals(1, foo("ab".repeat(1)));
assertEquals(2, foo("ab".repeat(2)));
%OptimizeFunctionOnNextCall(foo);
assertEquals(3, foo("ab".repeat(3)));
assertOptimized(foo);
// Make optimized code fail.
assertEquals(0.5, foo("a"));
assertUnoptimized(foo);
// Try again with the new feedback, and now it should stay optimized.
%OptimizeFunctionOnNextCall(foo);
assertEquals(4, foo("ab".repeat(4)));
assertOptimized(foo);
assertEquals(0.5, foo("a"));
assertOptimized(foo);
})();
// Test that SpeculativeNumberDivide turns into CheckedUint32Div, and
// that the optimized code properly bails out on "division by zero".
(function() {
function foo(x, y) { return (x >>> 0) / (y >>> 0); }
// Warmup with proper uint32 divisions.
assertEquals(2, foo(4, 2));
assertEquals(2, foo(8, 4));
%OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo(2, 2));
assertOptimized(foo);
// Make optimized code fail.
assertEquals(Infinity, foo(1, 0));
assertUnoptimized(foo);
// Try again with the new feedback, and now it should stay optimized.
%OptimizeFunctionOnNextCall(foo);
assertEquals(2, foo(2, 1));
assertOptimized(foo);
assertEquals(Infinity, foo(1, 0));
assertOptimized(foo);
})();
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