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) {
case IrOpcode::kCheckedTaggedToInt32:
case IrOpcode::kCheckedUint32ToInt32:
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:
return ReduceEffectPhi(node);
case IrOpcode::kDead:
......@@ -133,6 +138,17 @@ Node* RedundancyElimination::EffectPathChecks::LookupCheck(Node* node) const {
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::PathChecksForEffectNodes::Get(Node* node) const {
size_t const id = node->id();
......@@ -158,10 +174,41 @@ Reduction RedundancyElimination::ReduceCheckNode(Node* node) {
ReplaceWithValue(node, check);
return Replace(check);
}
// Learn from this check.
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) {
Node* const control = NodeProperties::GetControlInput(node);
if (control->opcode() == IrOpcode::kLoop) {
......
......@@ -34,6 +34,7 @@ class RedundancyElimination final : public AdvancedReducer {
EffectPathChecks const* AddCheck(Zone* zone, Node* node) const;
Node* LookupCheck(Node* node) const;
Node* LookupBoundsCheckFor(Node* node) const;
private:
EffectPathChecks(Check* head, size_t size) : head_(head), size_(size) {}
......@@ -62,6 +63,8 @@ class RedundancyElimination final : public AdvancedReducer {
Reduction TakeChecksFromFirstEffect(Node* node);
Reduction UpdateChecks(Node* node, EffectPathChecks const* checks);
Reduction TryReuseBoundsCheckForFirstInput(Node* node);
Zone* zone() const { return zone_; }
PathChecksForEffectNodes node_checks_;
......
......@@ -209,8 +209,30 @@ class InputUseInfos {
#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 {
public:
......@@ -1164,6 +1186,7 @@ class RepresentationSelector {
if (BothInputsAre(node, Type::PlainPrimitive())) {
if (truncation.IsUnused()) return VisitUnused(node);
}
if (BothInputsAre(node, type_cache_.kAdditiveSafeIntegerOrMinusZero) &&
(GetUpperBound(node)->Is(Type::Signed32()) ||
GetUpperBound(node)->Is(Type::Unsigned32()) ||
......@@ -1177,33 +1200,38 @@ class RepresentationSelector {
// Try to use type feedback.
NumberOperationHint hint = NumberOperationHintOf(node->op());
// Handle the case when no int32 checks on inputs are necessary
// (but an overflow check is needed on the output).
if (BothInputsAre(node, Type::Signed32()) ||
(BothInputsAre(node, Type::Signed32OrMinusZero()) &&
NodeProperties::GetType(node)->Is(type_cache_.kSafeInteger))) {
// If both the inputs the feedback are int32, use the overflow op.
if (hint == NumberOperationHint::kSignedSmall ||
hint == NumberOperationHint::kSigned32) {
if (hint == NumberOperationHint::kSignedSmall ||
hint == NumberOperationHint::kSigned32) {
Type* left_feedback_type = TypeOf(node->InputAt(0));
Type* right_feedback_type = TypeOf(node->InputAt(1));
// Handle the case when no int32 checks on inputs are necessary (but
// an overflow check is needed on the output).
// TODO(jarin) We should not look at the upper bound because the typer
// 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(),
MachineRepresentation::kWord32, Type::Signed32());
if (lower()) ChangeToInt32OverflowOp(node);
return;
} else {
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;
}
......
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