Commit cd0c2592 authored by jarin's avatar jarin Committed by Commit bot

[turbofan] Use bounds checks to eliminate subsequent inc/dec overflow checks.

This has two parts:
- in redundancy elimination, if we see addition with left hand side that
  was bounds-checked, we reconnect the lhs to the bounds check if it has better
  type.
- in representation inference, eliminate overflow checks if the input types
  guarantee no overflow.

Review-Url: https://codereview.chromium.org/2527083002
Cr-Commit-Position: refs/heads/master@{#41260}
parent abdbfc95
...@@ -36,6 +36,11 @@ Reduction RedundancyElimination::Reduce(Node* node) { ...@@ -36,6 +36,11 @@ Reduction RedundancyElimination::Reduce(Node* node) {
case IrOpcode::kCheckedTaggedToInt32: case IrOpcode::kCheckedTaggedToInt32:
case IrOpcode::kCheckedUint32ToInt32: case IrOpcode::kCheckedUint32ToInt32:
return ReduceCheckNode(node); return ReduceCheckNode(node);
case IrOpcode::kSpeculativeNumberAdd:
case IrOpcode::kSpeculativeNumberSubtract:
// For increments and decrements by a constant, try to learn from the last
// bounds check.
return TryReuseBoundsCheckForFirstInput(node);
case IrOpcode::kEffectPhi: case IrOpcode::kEffectPhi:
return ReduceEffectPhi(node); return ReduceEffectPhi(node);
case IrOpcode::kDead: case IrOpcode::kDead:
...@@ -133,6 +138,17 @@ Node* RedundancyElimination::EffectPathChecks::LookupCheck(Node* node) const { ...@@ -133,6 +138,17 @@ Node* RedundancyElimination::EffectPathChecks::LookupCheck(Node* node) const {
return nullptr; return nullptr;
} }
Node* RedundancyElimination::EffectPathChecks::LookupBoundsCheckFor(
Node* node) const {
for (Check const* check = head_; check != nullptr; check = check->next) {
if (check->node->opcode() == IrOpcode::kCheckBounds &&
check->node->InputAt(0) == node) {
return check->node;
}
}
return nullptr;
}
RedundancyElimination::EffectPathChecks const* RedundancyElimination::EffectPathChecks const*
RedundancyElimination::PathChecksForEffectNodes::Get(Node* node) const { RedundancyElimination::PathChecksForEffectNodes::Get(Node* node) const {
size_t const id = node->id(); size_t const id = node->id();
...@@ -158,10 +174,41 @@ Reduction RedundancyElimination::ReduceCheckNode(Node* node) { ...@@ -158,10 +174,41 @@ Reduction RedundancyElimination::ReduceCheckNode(Node* node) {
ReplaceWithValue(node, check); ReplaceWithValue(node, check);
return Replace(check); return Replace(check);
} }
// Learn from this check. // Learn from this check.
return UpdateChecks(node, checks->AddCheck(zone(), node)); return UpdateChecks(node, checks->AddCheck(zone(), node));
} }
Reduction RedundancyElimination::TryReuseBoundsCheckForFirstInput(Node* node) {
DCHECK(node->opcode() == IrOpcode::kSpeculativeNumberAdd ||
node->opcode() == IrOpcode::kSpeculativeNumberSubtract);
DCHECK_EQ(1, node->op()->EffectInputCount());
DCHECK_EQ(1, node->op()->EffectOutputCount());
Node* const effect = NodeProperties::GetEffectInput(node);
EffectPathChecks const* checks = node_checks_.Get(effect);
// If we do not know anything about the predecessor, do not propagate just yet
// because we will have to recompute anyway once we compute the predecessor.
if (checks == nullptr) return NoChange();
Node* left = node->InputAt(0);
Node* right = node->InputAt(1);
// Only use bounds checks for increments/decrements by a constant.
if (right->opcode() == IrOpcode::kNumberConstant) {
if (Node* bounds_check = checks->LookupBoundsCheckFor(left)) {
// Only use the bounds checked type if it is better.
if (NodeProperties::GetType(bounds_check)
->Is(NodeProperties::GetType(left))) {
node->ReplaceInput(0, bounds_check);
}
}
}
return UpdateChecks(node, checks);
}
Reduction RedundancyElimination::ReduceEffectPhi(Node* node) { Reduction RedundancyElimination::ReduceEffectPhi(Node* node) {
Node* const control = NodeProperties::GetControlInput(node); Node* const control = NodeProperties::GetControlInput(node);
if (control->opcode() == IrOpcode::kLoop) { if (control->opcode() == IrOpcode::kLoop) {
......
...@@ -34,6 +34,7 @@ class RedundancyElimination final : public AdvancedReducer { ...@@ -34,6 +34,7 @@ class RedundancyElimination final : public AdvancedReducer {
EffectPathChecks const* AddCheck(Zone* zone, Node* node) const; EffectPathChecks const* AddCheck(Zone* zone, Node* node) const;
Node* LookupCheck(Node* node) const; Node* LookupCheck(Node* node) const;
Node* LookupBoundsCheckFor(Node* node) const;
private: private:
EffectPathChecks(Check* head, size_t size) : head_(head), size_(size) {} EffectPathChecks(Check* head, size_t size) : head_(head), size_(size) {}
...@@ -62,6 +63,8 @@ class RedundancyElimination final : public AdvancedReducer { ...@@ -62,6 +63,8 @@ class RedundancyElimination final : public AdvancedReducer {
Reduction TakeChecksFromFirstEffect(Node* node); Reduction TakeChecksFromFirstEffect(Node* node);
Reduction UpdateChecks(Node* node, EffectPathChecks const* checks); Reduction UpdateChecks(Node* node, EffectPathChecks const* checks);
Reduction TryReuseBoundsCheckForFirstInput(Node* node);
Zone* zone() const { return zone_; } Zone* zone() const { return zone_; }
PathChecksForEffectNodes node_checks_; PathChecksForEffectNodes node_checks_;
......
...@@ -209,8 +209,30 @@ class InputUseInfos { ...@@ -209,8 +209,30 @@ class InputUseInfos {
#endif // DEBUG #endif // DEBUG
} // namespace bool CanOverflowSigned32(const Operator* op, Type* left, Type* right,
Zone* type_zone) {
// We assume the inputs are checked Signed32 (or known statically
// to be Signed32). Technically, theinputs could also be minus zero, but
// that cannot cause overflow.
left = Type::Intersect(left, Type::Signed32(), type_zone);
right = Type::Intersect(right, Type::Signed32(), type_zone);
if (!left->IsInhabited() || !right->IsInhabited()) return false;
switch (op->opcode()) {
case IrOpcode::kSpeculativeNumberAdd:
return (left->Max() + right->Max() > kMaxInt) ||
(left->Min() + right->Min() < kMinInt);
case IrOpcode::kSpeculativeNumberSubtract:
return (left->Max() - right->Min() > kMaxInt) ||
(left->Min() - right->Max() < kMinInt);
default:
UNREACHABLE();
}
return true;
}
} // namespace
class RepresentationSelector { class RepresentationSelector {
public: public:
...@@ -1164,6 +1186,7 @@ class RepresentationSelector { ...@@ -1164,6 +1186,7 @@ class RepresentationSelector {
if (BothInputsAre(node, Type::PlainPrimitive())) { if (BothInputsAre(node, Type::PlainPrimitive())) {
if (truncation.IsUnused()) return VisitUnused(node); if (truncation.IsUnused()) return VisitUnused(node);
} }
if (BothInputsAre(node, type_cache_.kAdditiveSafeIntegerOrMinusZero) && if (BothInputsAre(node, type_cache_.kAdditiveSafeIntegerOrMinusZero) &&
(GetUpperBound(node)->Is(Type::Signed32()) || (GetUpperBound(node)->Is(Type::Signed32()) ||
GetUpperBound(node)->Is(Type::Unsigned32()) || GetUpperBound(node)->Is(Type::Unsigned32()) ||
...@@ -1177,33 +1200,38 @@ class RepresentationSelector { ...@@ -1177,33 +1200,38 @@ class RepresentationSelector {
// Try to use type feedback. // Try to use type feedback.
NumberOperationHint hint = NumberOperationHintOf(node->op()); NumberOperationHint hint = NumberOperationHintOf(node->op());
// Handle the case when no int32 checks on inputs are necessary if (hint == NumberOperationHint::kSignedSmall ||
// (but an overflow check is needed on the output). hint == NumberOperationHint::kSigned32) {
if (BothInputsAre(node, Type::Signed32()) || Type* left_feedback_type = TypeOf(node->InputAt(0));
(BothInputsAre(node, Type::Signed32OrMinusZero()) && Type* right_feedback_type = TypeOf(node->InputAt(1));
NodeProperties::GetType(node)->Is(type_cache_.kSafeInteger))) { // Handle the case when no int32 checks on inputs are necessary (but
// If both the inputs the feedback are int32, use the overflow op. // an overflow check is needed on the output).
if (hint == NumberOperationHint::kSignedSmall || // TODO(jarin) We should not look at the upper bound because the typer
hint == NumberOperationHint::kSigned32) { // could have already baked in some feedback into the upper bound.
if (BothInputsAre(node, Type::Signed32()) ||
(BothInputsAre(node, Type::Signed32OrMinusZero()) &&
GetUpperBound(node)->Is(type_cache_.kSafeInteger))) {
VisitBinop(node, UseInfo::TruncatingWord32(), VisitBinop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32, Type::Signed32()); MachineRepresentation::kWord32, Type::Signed32());
if (lower()) ChangeToInt32OverflowOp(node); } else {
return; UseInfo left_use = CheckedUseInfoAsWord32FromHint(hint);
// For CheckedInt32Add and CheckedInt32Sub, we don't need to do
// a minus zero check for the right hand side, since we already
// know that the left hand side is a proper Signed32 value,
// potentially guarded by a check.
UseInfo right_use = CheckedUseInfoAsWord32FromHint(
hint, CheckForMinusZeroMode::kDontCheckForMinusZero);
VisitBinop(node, left_use, right_use, MachineRepresentation::kWord32,
Type::Signed32());
}
if (lower()) {
if (CanOverflowSigned32(node->op(), left_feedback_type,
right_feedback_type, graph_zone())) {
ChangeToInt32OverflowOp(node);
} else {
ChangeToPureOp(node, Int32Op(node));
}
} }
}
if (hint == NumberOperationHint::kSignedSmall ||
hint == NumberOperationHint::kSigned32) {
UseInfo left_use = CheckedUseInfoAsWord32FromHint(hint);
// For CheckedInt32Add and CheckedInt32Sub, we don't need to do
// a minus zero check for the right hand side, since we already
// know that the left hand side is a proper Signed32 value,
// potentially guarded by a check.
UseInfo right_use = CheckedUseInfoAsWord32FromHint(
hint, CheckForMinusZeroMode::kDontCheckForMinusZero);
VisitBinop(node, left_use, right_use, MachineRepresentation::kWord32,
Type::Signed32());
if (lower()) ChangeToInt32OverflowOp(node);
return; return;
} }
......
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