Commit 47aaac64 authored by mvstanton's avatar mvstanton Committed by Commit bot

[Turbofan]: Eliminate the check for -0 if it's not possible/observable.

In int32 multiplication, if we have a positive integer as input, then we know we can't produce a -0 answer. The same is true if truncation is applied (x * y | 0). Without this information, we have to rather annoyingly check if the result of multiplication is 0, then OR the inputs to check for negativity, and possibly return -0. In TurboFan, we'll deopt in this case.

BUG=

Review-Url: https://codereview.chromium.org/2154073002
Cr-Commit-Position: refs/heads/master@{#37831}
parent 98080817
...@@ -1325,6 +1325,7 @@ EffectControlLinearizer::LowerCheckedUint32Mod(Node* node, Node* frame_state, ...@@ -1325,6 +1325,7 @@ EffectControlLinearizer::LowerCheckedUint32Mod(Node* node, Node* frame_state,
EffectControlLinearizer::ValueEffectControl EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckedInt32Mul(Node* node, Node* frame_state, EffectControlLinearizer::LowerCheckedInt32Mul(Node* node, Node* frame_state,
Node* effect, Node* control) { Node* effect, Node* control) {
CheckForMinusZeroMode mode = CheckMinusZeroModeOf(node->op());
Node* zero = jsgraph()->Int32Constant(0); Node* zero = jsgraph()->Int32Constant(0);
Node* lhs = node->InputAt(0); Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1); Node* rhs = node->InputAt(1);
...@@ -1339,28 +1340,30 @@ EffectControlLinearizer::LowerCheckedInt32Mul(Node* node, Node* frame_state, ...@@ -1339,28 +1340,30 @@ EffectControlLinearizer::LowerCheckedInt32Mul(Node* node, Node* frame_state,
Node* value = graph()->NewNode(common()->Projection(0), projection, control); Node* value = graph()->NewNode(common()->Projection(0), projection, control);
Node* check_zero = graph()->NewNode(machine()->Word32Equal(), value, zero); if (mode == CheckForMinusZeroMode::kCheckForMinusZero) {
Node* branch_zero = graph()->NewNode(common()->Branch(BranchHint::kFalse), Node* check_zero = graph()->NewNode(machine()->Word32Equal(), value, zero);
check_zero, control); Node* branch_zero = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check_zero, control);
Node* if_zero = graph()->NewNode(common()->IfTrue(), branch_zero); Node* if_zero = graph()->NewNode(common()->IfTrue(), branch_zero);
Node* e_if_zero = effect; Node* e_if_zero = effect;
{ {
// We may need to return negative zero. // We may need to return negative zero.
Node* or_inputs = graph()->NewNode(machine()->Word32Or(), lhs, rhs); Node* or_inputs = graph()->NewNode(machine()->Word32Or(), lhs, rhs);
Node* check_or = Node* check_or =
graph()->NewNode(machine()->Int32LessThan(), or_inputs, zero); graph()->NewNode(machine()->Int32LessThan(), or_inputs, zero);
if_zero = e_if_zero = if_zero = e_if_zero =
graph()->NewNode(common()->DeoptimizeIf(DeoptimizeReason::kMinusZero), graph()->NewNode(common()->DeoptimizeIf(DeoptimizeReason::kMinusZero),
check_or, frame_state, e_if_zero, if_zero); check_or, frame_state, e_if_zero, if_zero);
} }
Node* if_not_zero = graph()->NewNode(common()->IfFalse(), branch_zero); Node* if_not_zero = graph()->NewNode(common()->IfFalse(), branch_zero);
Node* e_if_not_zero = effect; Node* e_if_not_zero = effect;
control = graph()->NewNode(common()->Merge(2), if_zero, if_not_zero); control = graph()->NewNode(common()->Merge(2), if_zero, if_not_zero);
effect = graph()->NewNode(common()->EffectPhi(2), e_if_zero, e_if_not_zero, effect = graph()->NewNode(common()->EffectPhi(2), e_if_zero, e_if_not_zero,
control); control);
}
return ValueEffectControl(value, effect, control); return ValueEffectControl(value, effect, control);
} }
......
...@@ -627,8 +627,6 @@ const Operator* RepresentationChanger::Int32OverflowOperatorFor( ...@@ -627,8 +627,6 @@ const Operator* RepresentationChanger::Int32OverflowOperatorFor(
return simplified()->CheckedInt32Div(); return simplified()->CheckedInt32Div();
case IrOpcode::kSpeculativeNumberModulus: case IrOpcode::kSpeculativeNumberModulus:
return simplified()->CheckedInt32Mod(); return simplified()->CheckedInt32Mod();
case IrOpcode::kSpeculativeNumberMultiply:
return simplified()->CheckedInt32Mul();
default: default:
UNREACHABLE(); UNREACHABLE();
return nullptr; return nullptr;
......
...@@ -1077,6 +1077,22 @@ class RepresentationSelector { ...@@ -1077,6 +1077,22 @@ class RepresentationSelector {
return jsgraph_->simplified(); return jsgraph_->simplified();
} }
void LowerToCheckedInt32Mul(Node* node, Truncation truncation,
Type* input0_type, Type* input1_type) {
// If one of the inputs is positive and/or truncation is being applied,
// there is no need to return -0.
CheckForMinusZeroMode mz_mode =
truncation.TruncatesToWord32() ||
(input0_type->Is(Type::OrderedNumber()) &&
input0_type->Min() > 0) ||
(input1_type->Is(Type::OrderedNumber()) &&
input1_type->Min() > 0)
? CheckForMinusZeroMode::kDontCheckForMinusZero
: CheckForMinusZeroMode::kCheckForMinusZero;
NodeProperties::ChangeOp(node, simplified()->CheckedInt32Mul(mz_mode));
}
void ChangeToInt32OverflowOp(Node* node) { void ChangeToInt32OverflowOp(Node* node) {
NodeProperties::ChangeOp(node, Int32OverflowOp(node)); NodeProperties::ChangeOp(node, Int32OverflowOp(node));
} }
...@@ -1333,6 +1349,8 @@ class RepresentationSelector { ...@@ -1333,6 +1349,8 @@ class RepresentationSelector {
} }
// Try to use type feedback. // Try to use type feedback.
BinaryOperationHints::Hint hint = BinaryOperationHintOf(node->op()); BinaryOperationHints::Hint hint = BinaryOperationHintOf(node->op());
Type* input0_type = TypeOf(node->InputAt(0));
Type* input1_type = TypeOf(node->InputAt(1));
// Handle the case when no int32 checks on inputs are necessary // Handle the case when no int32 checks on inputs are necessary
// (but an overflow check is needed on the output). // (but an overflow check is needed on the output).
...@@ -1342,7 +1360,10 @@ class RepresentationSelector { ...@@ -1342,7 +1360,10 @@ class RepresentationSelector {
hint == BinaryOperationHints::kSigned32) { hint == BinaryOperationHints::kSigned32) {
VisitBinop(node, UseInfo::TruncatingWord32(), VisitBinop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32, Type::Signed32()); MachineRepresentation::kWord32, Type::Signed32());
if (lower()) ChangeToInt32OverflowOp(node); if (lower()) {
LowerToCheckedInt32Mul(node, truncation, input0_type,
input1_type);
}
return; return;
} }
} }
...@@ -1351,7 +1372,9 @@ class RepresentationSelector { ...@@ -1351,7 +1372,9 @@ class RepresentationSelector {
hint == BinaryOperationHints::kSigned32) { hint == BinaryOperationHints::kSigned32) {
VisitBinop(node, UseInfo::CheckedSigned32AsWord32(), VisitBinop(node, UseInfo::CheckedSigned32AsWord32(),
MachineRepresentation::kWord32, Type::Signed32()); MachineRepresentation::kWord32, Type::Signed32());
if (lower()) ChangeToInt32OverflowOp(node); if (lower()) {
LowerToCheckedInt32Mul(node, truncation, input0_type, input1_type);
}
return; return;
} }
......
...@@ -201,6 +201,26 @@ CheckFloat64HoleMode CheckFloat64HoleModeOf(const Operator* op) { ...@@ -201,6 +201,26 @@ CheckFloat64HoleMode CheckFloat64HoleModeOf(const Operator* op) {
return OpParameter<CheckFloat64HoleMode>(op); return OpParameter<CheckFloat64HoleMode>(op);
} }
CheckForMinusZeroMode CheckMinusZeroModeOf(const Operator* op) {
DCHECK_EQ(IrOpcode::kCheckedInt32Mul, op->opcode());
return OpParameter<CheckForMinusZeroMode>(op);
}
size_t hash_value(CheckForMinusZeroMode mode) {
return static_cast<size_t>(mode);
}
std::ostream& operator<<(std::ostream& os, CheckForMinusZeroMode mode) {
switch (mode) {
case CheckForMinusZeroMode::kCheckForMinusZero:
return os << "check-for-minus-zero";
case CheckForMinusZeroMode::kDontCheckForMinusZero:
return os << "dont-check-for-minus-zero";
}
UNREACHABLE();
return os;
}
size_t hash_value(CheckTaggedHoleMode mode) { size_t hash_value(CheckTaggedHoleMode mode) {
return static_cast<size_t>(mode); return static_cast<size_t>(mode);
} }
...@@ -334,7 +354,6 @@ CompareOperationHints::Hint CompareOperationHintOf(const Operator* op) { ...@@ -334,7 +354,6 @@ CompareOperationHints::Hint CompareOperationHintOf(const Operator* op) {
V(CheckedInt32Mod, 2, 1) \ V(CheckedInt32Mod, 2, 1) \
V(CheckedUint32Div, 2, 1) \ V(CheckedUint32Div, 2, 1) \
V(CheckedUint32Mod, 2, 1) \ V(CheckedUint32Mod, 2, 1) \
V(CheckedInt32Mul, 2, 1) \
V(CheckedUint32ToInt32, 1, 1) \ V(CheckedUint32ToInt32, 1, 1) \
V(CheckedFloat64ToInt32, 1, 1) \ V(CheckedFloat64ToInt32, 1, 1) \
V(CheckedTaggedToInt32, 1, 1) \ V(CheckedTaggedToInt32, 1, 1) \
...@@ -362,6 +381,20 @@ struct SimplifiedOperatorGlobalCache final { ...@@ -362,6 +381,20 @@ struct SimplifiedOperatorGlobalCache final {
CHECKED_OP_LIST(CHECKED) CHECKED_OP_LIST(CHECKED)
#undef CHECKED #undef CHECKED
template <CheckForMinusZeroMode kMode>
struct CheckedInt32MulOperator final
: public Operator1<CheckForMinusZeroMode> {
CheckedInt32MulOperator()
: Operator1<CheckForMinusZeroMode>(
IrOpcode::kCheckedInt32Mul,
Operator::kFoldable | Operator::kNoThrow, "CheckedInt32Mul", 2, 1,
1, 1, 1, 0, kMode) {}
};
CheckedInt32MulOperator<CheckForMinusZeroMode::kCheckForMinusZero>
kCheckedInt32MulCheckForMinusZeroOperator;
CheckedInt32MulOperator<CheckForMinusZeroMode::kDontCheckForMinusZero>
kCheckedInt32MulDontCheckForMinusZeroOperator;
template <CheckFloat64HoleMode kMode> template <CheckFloat64HoleMode kMode>
struct CheckFloat64HoleNaNOperator final struct CheckFloat64HoleNaNOperator final
: public Operator1<CheckFloat64HoleMode> { : public Operator1<CheckFloat64HoleMode> {
...@@ -441,6 +474,18 @@ PURE_OP_LIST(GET_FROM_CACHE) ...@@ -441,6 +474,18 @@ PURE_OP_LIST(GET_FROM_CACHE)
CHECKED_OP_LIST(GET_FROM_CACHE) CHECKED_OP_LIST(GET_FROM_CACHE)
#undef GET_FROM_CACHE #undef GET_FROM_CACHE
const Operator* SimplifiedOperatorBuilder::CheckedInt32Mul(
CheckForMinusZeroMode mode) {
switch (mode) {
case CheckForMinusZeroMode::kCheckForMinusZero:
return &cache_.kCheckedInt32MulCheckForMinusZeroOperator;
case CheckForMinusZeroMode::kDontCheckForMinusZero:
return &cache_.kCheckedInt32MulDontCheckForMinusZeroOperator;
}
UNREACHABLE();
return nullptr;
}
const Operator* SimplifiedOperatorBuilder::CheckFloat64Hole( const Operator* SimplifiedOperatorBuilder::CheckFloat64Hole(
CheckFloat64HoleMode mode) { CheckFloat64HoleMode mode) {
switch (mode) { switch (mode) {
......
...@@ -129,6 +129,17 @@ std::ostream& operator<<(std::ostream&, CheckTaggedHoleMode); ...@@ -129,6 +129,17 @@ std::ostream& operator<<(std::ostream&, CheckTaggedHoleMode);
CheckTaggedHoleMode CheckTaggedHoleModeOf(const Operator*) WARN_UNUSED_RESULT; CheckTaggedHoleMode CheckTaggedHoleModeOf(const Operator*) WARN_UNUSED_RESULT;
enum class CheckForMinusZeroMode : uint8_t {
kCheckForMinusZero,
kDontCheckForMinusZero,
};
size_t hash_value(CheckForMinusZeroMode);
std::ostream& operator<<(std::ostream&, CheckForMinusZeroMode);
CheckForMinusZeroMode CheckMinusZeroModeOf(const Operator*) WARN_UNUSED_RESULT;
Type* TypeOf(const Operator* op) WARN_UNUSED_RESULT; Type* TypeOf(const Operator* op) WARN_UNUSED_RESULT;
BinaryOperationHints::Hint BinaryOperationHintOf(const Operator* op); BinaryOperationHints::Hint BinaryOperationHintOf(const Operator* op);
...@@ -261,7 +272,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject { ...@@ -261,7 +272,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* CheckedInt32Mod(); const Operator* CheckedInt32Mod();
const Operator* CheckedUint32Div(); const Operator* CheckedUint32Div();
const Operator* CheckedUint32Mod(); const Operator* CheckedUint32Mod();
const Operator* CheckedInt32Mul(); const Operator* CheckedInt32Mul(CheckForMinusZeroMode);
const Operator* CheckedUint32ToInt32(); const Operator* CheckedUint32ToInt32();
const Operator* CheckedFloat64ToInt32(); const Operator* CheckedFloat64ToInt32();
const Operator* CheckedTaggedToInt32(); const Operator* CheckedTaggedToInt32();
......
...@@ -4,6 +4,20 @@ ...@@ -4,6 +4,20 @@
// Flags: --allow-natives-syntax // Flags: --allow-natives-syntax
// For TurboFan, make sure we can eliminate the -0 return value check
// by recognizing a constant value.
function gotaconstant(y) { return 15 * y; }
assertEquals(45, gotaconstant(3));
gotaconstant(3);
%OptimizeFunctionOnNextCall(gotaconstant);
gotaconstant(3);
function gotaconstant_truncated(x, y) { return x * y | 0; }
assertEquals(45, gotaconstant_truncated(3, 15));
gotaconstant_truncated(3, 15);
%OptimizeFunctionOnNextCall(gotaconstant_truncated);
gotaconstant_truncated(3, 15);
function test(x, y) { return x * y; } function test(x, y) { return x * y; }
assertEquals(12, test(3, 4)); assertEquals(12, test(3, 4));
......
...@@ -246,10 +246,7 @@ ...@@ -246,10 +246,7 @@
'getters-on-elements': [PASS, NO_IGNITION], 'getters-on-elements': [PASS, NO_IGNITION],
'harmony/do-expressions': [PASS, NO_IGNITION], 'harmony/do-expressions': [PASS, NO_IGNITION],
'math-floor-of-div-minus-zero': [PASS, NO_IGNITION], 'math-floor-of-div-minus-zero': [PASS, NO_IGNITION],
'regress/regress-2132': [PASS, NO_IGNITION],
# TODO(mvstanton): restore NO_IGNITION.
'regress/regress-2132': [PASS, NO_VARIANTS],
'regress/regress-2339': [PASS, NO_IGNITION], 'regress/regress-2339': [PASS, NO_IGNITION],
'regress/regress-3176': [PASS, NO_IGNITION], 'regress/regress-3176': [PASS, NO_IGNITION],
'regress/regress-3709': [PASS, NO_IGNITION], 'regress/regress-3709': [PASS, NO_IGNITION],
......
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