Commit 6e665b09 authored by franzih's avatar franzih Committed by Commit bot

[turbofan] Induction variable bound analysis for decrements.

This detects loops with integer decrements.

Drive-by fix: Add lower bounds to lower_bounds
zone vector instead of upper_bounds.

BUG=

Review-Url: https://codereview.chromium.org/2260153002
Cr-Commit-Position: refs/heads/master@{#38772}
parent 72b78442
......@@ -188,7 +188,7 @@ void LoopVariableOptimizer::VisitBackedge(Node* from, Node* loop) {
NodeProperties::GetControlInput(constraint->right()) == loop) {
auto var = induction_vars_.find(constraint->right()->id());
if (var != induction_vars_.end()) {
var->second->AddUpperBound(constraint->left(), constraint->kind());
var->second->AddLowerBound(constraint->left(), constraint->kind());
}
}
}
......@@ -303,10 +303,15 @@ InductionVariable* LoopVariableOptimizer::TryGetInductionVariable(Node* phi) {
DCHECK_EQ(IrOpcode::kLoop, NodeProperties::GetControlInput(phi)->opcode());
Node* initial = phi->InputAt(0);
Node* arith = phi->InputAt(1);
// TODO(jarin) Support subtraction.
if (arith->opcode() != IrOpcode::kJSAdd) {
InductionVariable::ArithmeticType arithmeticType;
if (arith->opcode() == IrOpcode::kJSAdd) {
arithmeticType = InductionVariable::ArithmeticType::kAddition;
} else if (arith->opcode() == IrOpcode::kJSSubtract) {
arithmeticType = InductionVariable::ArithmeticType::kSubtraction;
} else {
return nullptr;
}
// TODO(jarin) Support both sides.
if (arith->InputAt(0) != phi) {
if (arith->InputAt(0)->opcode() != IrOpcode::kJSToNumber ||
......@@ -315,7 +320,8 @@ InductionVariable* LoopVariableOptimizer::TryGetInductionVariable(Node* phi) {
}
}
Node* incr = arith->InputAt(1);
return new (zone()) InductionVariable(phi, arith, incr, initial, zone());
return new (zone())
InductionVariable(phi, arith, incr, initial, zone(), arithmeticType);
}
void LoopVariableOptimizer::DetectInductionVariables(Node* loop) {
......
......@@ -23,6 +23,7 @@ class InductionVariable : public ZoneObject {
Node* init_value() const { return init_value_; }
enum ConstraintKind { kStrict, kNonStrict };
enum ArithmeticType { kAddition, kSubtraction };
struct Bound {
Bound(Node* bound, ConstraintKind kind) : bound(bound), kind(kind) {}
......@@ -33,17 +34,20 @@ class InductionVariable : public ZoneObject {
const ZoneVector<Bound>& lower_bounds() { return lower_bounds_; }
const ZoneVector<Bound>& upper_bounds() { return upper_bounds_; }
ArithmeticType Type() { return arithmeticType_; }
private:
friend class LoopVariableOptimizer;
InductionVariable(Node* phi, Node* arith, Node* increment, Node* init_value,
Zone* zone)
Zone* zone, ArithmeticType arithmeticType)
: phi_(phi),
arith_(arith),
increment_(increment),
init_value_(init_value),
lower_bounds_(zone),
upper_bounds_(zone) {}
upper_bounds_(zone),
arithmeticType_(arithmeticType) {}
void AddUpperBound(Node* bound, ConstraintKind kind);
void AddLowerBound(Node* bound, ConstraintKind kind);
......@@ -54,6 +58,7 @@ class InductionVariable : public ZoneObject {
Node* init_value_;
ZoneVector<Bound> lower_bounds_;
ZoneVector<Bound> upper_bounds_;
ArithmeticType arithmeticType_;
};
class LoopVariableOptimizer {
......
......@@ -651,9 +651,24 @@ Type* Typer::Visitor::TypeInductionVariablePhi(Node* node) {
DCHECK(res != induction_vars_->induction_variables().end());
InductionVariable* induction_var = res->second;
InductionVariable::ArithmeticType arithmetic_type = induction_var->Type();
double min = -V8_INFINITY;
double max = V8_INFINITY;
if (increment_type->Min() >= 0) {
double increment_min;
double increment_max;
if (arithmetic_type == InductionVariable::ArithmeticType::kAddition) {
increment_min = increment_type->Min();
increment_max = increment_type->Max();
} else {
DCHECK(arithmetic_type == InductionVariable::ArithmeticType::kSubtraction);
increment_min = -increment_type->Max();
increment_max = -increment_type->Min();
}
if (increment_min >= 0) {
// increasing sequence
min = initial_type->Min();
for (auto bound : induction_var->upper_bounds()) {
Type* bound_type = TypeOrNone(bound.bound);
......@@ -668,11 +683,12 @@ Type* Typer::Visitor::TypeInductionVariablePhi(Node* node) {
if (bound.kind == InductionVariable::kStrict) {
bound_max -= 1;
}
max = std::min(max, bound_max + increment_type->Max());
max = std::min(max, bound_max + increment_max);
}
// The upper bound must be at least the initial value's upper bound.
max = std::max(max, initial_type->Max());
} else if (increment_type->Max() <= 0) {
} else if (increment_max <= 0) {
// decreasing sequence
max = initial_type->Max();
for (auto bound : induction_var->lower_bounds()) {
Type* bound_type = TypeOrNone(bound.bound);
......@@ -687,7 +703,7 @@ Type* Typer::Visitor::TypeInductionVariablePhi(Node* node) {
if (bound.kind == InductionVariable::kStrict) {
bound_min += 1;
}
min = std::max(min, bound_min + increment_type->Min());
min = std::max(min, bound_min + increment_min);
}
// The lower bound must be at most the initial value's lower bound.
min = std::min(min, initial_type->Min());
......@@ -700,8 +716,11 @@ Type* Typer::Visitor::TypeInductionVariablePhi(Node* node) {
OFStream os(stdout);
os << std::setprecision(10);
os << "Loop (" << NodeProperties::GetControlInput(node)->id()
<< ") variable bounds for phi " << node->id() << ": (" << min << ", "
<< max << ")\n";
<< ") variable bounds in "
<< (arithmetic_type == InductionVariable::ArithmeticType::kAddition
? "addition"
: "subtraction")
<< " for phi " << node->id() << ": (" << min << ", " << max << ")\n";
}
return Type::Range(min, max, typer_->zone());
}
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --turbo
// TurboFan optimizes integer loops. These tests check that we compute
// the correct upper and lower bounds.
function positive_increment() {
for (var i = 5; i < 10; i++) {
if (i < 0) return false;
if (i > 20) return false;
if (i === 7) return true;
}
return false;
}
function positive_increment_strict() {
for (var i = 5; i < 10; i++) {
if (i < 0) return false;
if (i === 10) return false;
}
return true;
}
function positive_increment_non_strict() {
for (var i = 5; i <= 10; i++) {
if (i < 0) return false;
if (i === 10) return true;
}
return false;
}
function negative_increment() {
for (var i = 10; i > 5;) {
if (i < 0) return false;
if (i > 20) return false;
if (i === 7) return true;
i -= 1;
}
return false;
}
function positive_decrement() {
for (var i = 10; i > 5; i--) {
if (i < 0) return false;
if (i === 7) return true;
}
return false;
}
function positive_decrement_strict() {
for (var i = 10; i > 5; i--) {
if (i < 0) return false;
if (i === 5) return false;
}
return true;
}
function positive_decrement_non_strict() {
for (var i = 10; i >= 5; i--) {
if (i < 0) return false;
if (i === 5) return true;
}
return false;
}
function negative_decrement() {
for (var i = 5; i < 10;) {
if (i < 0) return false;
if (i === 7) return true;
i -= -1;
}
return false;
}
function variable_bound() {
for (var i = 5; i < 10; i++) {
for (var j = 5; j < i; j++) {
if (j < 0) return false;
if (j === 7) return true;
}
}
return false;
}
function test(f) {
f();
assertTrue(f());
%OptimizeFunctionOnNextCall(f);
assertTrue(f());
}
test(positive_increment);
test(positive_increment_strict);
test(positive_increment_non_strict);
test(negative_increment);
test(positive_decrement);
test(positive_decrement_strict);
test(positive_decrement_non_strict);
test(negative_decrement);
test(variable_bound);
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