[turbofan] Induction variable bound analysis.

The new phase will detect loop variable, infer bounds and bake them into
the type.

Review-Url: https://codereview.chromium.org/2164263003
Cr-Commit-Position: refs/heads/master@{#38077}
This commit is contained in:
jarin 2016-07-26 22:50:39 -07:00 committed by Commit bot
parent 3deb71fbad
commit 398a114357
14 changed files with 684 additions and 32 deletions

View File

@ -1057,6 +1057,9 @@ v8_source_set("v8_base") {
"src/compiler/loop-analysis.cc",
"src/compiler/loop-analysis.h",
"src/compiler/loop-peeling.cc",
"src/compiler/loop-peeling.h",
"src/compiler/loop-variable-optimizer.cc",
"src/compiler/loop-variable-optimizer.h",
"src/compiler/machine-operator-reducer.cc",
"src/compiler/machine-operator-reducer.h",
"src/compiler/machine-operator.cc",

View File

@ -276,6 +276,11 @@ std::ostream& operator<<(std::ostream& os,
V(5) \
V(6)
#define CACHED_INDUCTION_VARIABLE_PHI_LIST(V) \
V(4) \
V(5) \
V(6) \
V(7)
#define CACHED_LOOP_LIST(V) \
V(1) \
@ -473,6 +478,20 @@ struct CommonOperatorGlobalCache final {
CACHED_PHI_LIST(CACHED_PHI)
#undef CACHED_PHI
template <int kInputCount>
struct InductionVariablePhiOperator final : public Operator {
InductionVariablePhiOperator()
: Operator( //--
IrOpcode::kInductionVariablePhi, Operator::kPure, // opcode
"InductionVariablePhi", // name
kInputCount, 0, 1, 1, 0, 0) {} // counts
};
#define CACHED_INDUCTION_VARIABLE_PHI(input_count) \
InductionVariablePhiOperator<input_count> \
kInductionVariablePhi##input_count##Operator;
CACHED_INDUCTION_VARIABLE_PHI_LIST(CACHED_INDUCTION_VARIABLE_PHI)
#undef CACHED_INDUCTION_VARIABLE_PHI
template <int kIndex>
struct ParameterOperator final : public Operator1<ParameterInfo> {
ParameterOperator()
@ -853,6 +872,25 @@ const Operator* CommonOperatorBuilder::EffectPhi(int effect_input_count) {
0, effect_input_count, 1, 0, 1, 0); // counts
}
const Operator* CommonOperatorBuilder::InductionVariablePhi(int input_count) {
DCHECK(input_count >= 4); // There must be always the entry, backedge,
// increment and at least one bound.
switch (input_count) {
#define CACHED_INDUCTION_VARIABLE_PHI(input_count) \
case input_count: \
return &cache_.kInductionVariablePhi##input_count##Operator;
CACHED_INDUCTION_VARIABLE_PHI_LIST(CACHED_INDUCTION_VARIABLE_PHI)
#undef CACHED_INDUCTION_VARIABLE_PHI
default:
break;
}
// Uncached.
return new (zone()) Operator( // --
IrOpcode::kInductionVariablePhi, Operator::kPure, // opcode
"InductionVariablePhi", // name
input_count, 0, 1, 1, 0, 0); // counts
}
const Operator* CommonOperatorBuilder::BeginRegion(
RegionObservability region_observability) {
switch (region_observability) {

View File

@ -232,6 +232,7 @@ class CommonOperatorBuilder final : public ZoneObject {
const Operator* Phi(MachineRepresentation representation,
int value_input_count);
const Operator* EffectPhi(int effect_input_count);
const Operator* InductionVariablePhi(int value_input_count);
const Operator* LoopExit();
const Operator* LoopExitValue();
const Operator* LoopExitEffect();

View File

@ -509,7 +509,7 @@ LoopTree* LoopFinder::BuildLoopTree(Graph* graph, Zone* zone) {
new (graph->zone()) LoopTree(graph->NodeCount(), graph->zone());
LoopFinderImpl finder(graph, loop_tree, zone);
finder.Run();
if (FLAG_trace_turbo_graph) {
if (FLAG_trace_turbo_loop) {
finder.Print();
}
return loop_tree;

View File

@ -186,7 +186,7 @@ bool LoopPeeler::CanPeel(LoopTree* loop_tree, LoopTree::Loop* loop) {
unmarked_exit = (use->opcode() != IrOpcode::kTerminate);
}
if (unmarked_exit) {
if (FLAG_trace_turbo_graph) {
if (FLAG_trace_turbo_loop) {
Node* loop_node = loop_tree->GetLoopControl(loop);
PrintF(
"Cannot peel loop %i. Loop exit without explicit mark: Node %i "
@ -312,11 +312,12 @@ void PeelInnerLoops(Graph* graph, CommonOperatorBuilder* common,
}
// Only peel small-enough loops.
if (loop->TotalSize() > LoopPeeler::kMaxPeeledNodes) return;
if (FLAG_trace_turbo_graph) {
if (FLAG_trace_turbo_loop) {
PrintF("Peeling loop with header: ");
for (Node* node : loop_tree->HeaderNodes(loop)) {
PrintF("%i ", node->id());
}
PrintF("\n");
}
LoopPeeler::Peel(graph, common, loop_tree, loop, temp_zone);

View File

@ -0,0 +1,390 @@
// 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.
#include "src/compiler/loop-variable-optimizer.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/graph.h"
#include "src/compiler/node-marker.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/node.h"
#include "src/zone-containers.h"
#include "src/zone.h"
namespace v8 {
namespace internal {
namespace compiler {
// Macro for outputting trace information from representation inference.
#define TRACE(...) \
do { \
if (FLAG_trace_turbo_loop) PrintF(__VA_ARGS__); \
} while (false)
LoopVariableOptimizer::LoopVariableOptimizer(Graph* graph,
CommonOperatorBuilder* common,
Zone* zone)
: graph_(graph),
common_(common),
zone_(zone),
limits_(zone),
induction_vars_(zone) {}
void LoopVariableOptimizer::Run() {
ZoneQueue<Node*> queue(zone());
queue.push(graph()->start());
NodeMarker<bool> queued(graph(), 2);
while (!queue.empty()) {
Node* node = queue.front();
queue.pop();
queued.Set(node, false);
DCHECK(limits_.find(node->id()) == limits_.end());
bool all_inputs_visited = true;
int inputs_end = (node->opcode() == IrOpcode::kLoop)
? kFirstBackedge
: node->op()->ControlInputCount();
for (int i = 0; i < inputs_end; i++) {
auto input = limits_.find(NodeProperties::GetControlInput(node, i)->id());
if (input == limits_.end()) {
all_inputs_visited = false;
break;
}
}
if (!all_inputs_visited) continue;
VisitNode(node);
DCHECK(limits_.find(node->id()) != limits_.end());
// Queue control outputs.
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsControlEdge(edge) &&
edge.from()->op()->ControlOutputCount() > 0) {
Node* use = edge.from();
if (use->opcode() == IrOpcode::kLoop &&
edge.index() != kAssumedLoopEntryIndex) {
VisitBackedge(node, use);
} else if (!queued.Get(use)) {
queue.push(use);
queued.Set(use, true);
}
}
}
}
}
class LoopVariableOptimizer::Constraint : public ZoneObject {
public:
InductionVariable::ConstraintKind kind() const { return kind_; }
Node* left() const { return left_; }
Node* right() const { return right_; }
const Constraint* next() const { return next_; }
Constraint(Node* left, InductionVariable::ConstraintKind kind, Node* right,
const Constraint* next)
: left_(left), right_(right), kind_(kind), next_(next) {}
private:
Node* left_;
Node* right_;
InductionVariable::ConstraintKind kind_;
const Constraint* next_;
};
class LoopVariableOptimizer::VariableLimits : public ZoneObject {
public:
static VariableLimits* Empty(Zone* zone) {
return new (zone) VariableLimits();
}
VariableLimits* Copy(Zone* zone) const {
return new (zone) VariableLimits(this);
}
void Add(Node* left, InductionVariable::ConstraintKind kind, Node* right,
Zone* zone) {
head_ = new (zone) Constraint(left, kind, right, head_);
limit_count_++;
}
void Merge(const VariableLimits* other) {
// Change the current condition list to a longest common tail
// of this condition list and the other list. (The common tail
// should correspond to the list from the common dominator.)
// First, we throw away the prefix of the longer list, so that
// we have lists of the same length.
size_t other_size = other->limit_count_;
const Constraint* other_limit = other->head_;
while (other_size > limit_count_) {
other_limit = other_limit->next();
other_size--;
}
while (limit_count_ > other_size) {
head_ = head_->next();
limit_count_--;
}
// Then we go through both lists in lock-step until we find
// the common tail.
while (head_ != other_limit) {
DCHECK(limit_count_ > 0);
limit_count_--;
other_limit = other_limit->next();
head_ = head_->next();
}
}
const Constraint* head() const { return head_; }
private:
VariableLimits() {}
explicit VariableLimits(const VariableLimits* other)
: head_(other->head_), limit_count_(other->limit_count_) {}
const Constraint* head_ = nullptr;
size_t limit_count_ = 0;
};
void InductionVariable::AddUpperBound(Node* bound,
InductionVariable::ConstraintKind kind,
Zone* graph_zone) {
if (FLAG_trace_turbo_loop) {
OFStream os(stdout);
os << "New upper bound for " << phi()->id() << " (loop "
<< NodeProperties::GetControlInput(phi())->id() << "): " << *bound
<< std::endl;
}
upper_bounds_.push_back(Bound(bound, kind));
}
void InductionVariable::AddLowerBound(Node* bound,
InductionVariable::ConstraintKind kind,
Zone* graph_zone) {
if (FLAG_trace_turbo_loop) {
OFStream os(stdout);
os << "New lower bound for " << phi()->id() << " (loop "
<< NodeProperties::GetControlInput(phi())->id() << "): " << *bound;
}
lower_bounds_.push_back(Bound(bound, kind));
}
void LoopVariableOptimizer::VisitBackedge(Node* from, Node* loop) {
if (loop->op()->ControlInputCount() != 2) return;
// Go through the constraints, and update the induction variables in
// this loop if they are involved in the constraint.
const VariableLimits* limits = limits_[from->id()];
for (const Constraint* constraint = limits->head(); constraint != nullptr;
constraint = constraint->next()) {
if (constraint->left()->opcode() == IrOpcode::kPhi &&
NodeProperties::GetControlInput(constraint->left()) == loop) {
auto var = induction_vars_.find(constraint->left()->id());
if (var != induction_vars_.end()) {
var->second->AddUpperBound(constraint->right(), constraint->kind(),
graph()->zone());
}
}
if (constraint->right()->opcode() == IrOpcode::kPhi &&
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(),
graph()->zone());
}
}
}
}
void LoopVariableOptimizer::VisitNode(Node* node) {
switch (node->opcode()) {
case IrOpcode::kMerge:
return VisitMerge(node);
case IrOpcode::kLoop:
return VisitLoop(node);
case IrOpcode::kIfFalse:
return VisitIf(node, false);
case IrOpcode::kIfTrue:
return VisitIf(node, true);
case IrOpcode::kStart:
return VisitStart(node);
case IrOpcode::kLoopExit:
return VisitLoopExit(node);
default:
return VisitOtherControl(node);
}
}
void LoopVariableOptimizer::VisitMerge(Node* node) {
// Merge the limits of all incoming edges.
VariableLimits* merged = limits_[node->InputAt(0)->id()]->Copy(zone());
for (int i = 1; i < node->InputCount(); i++) {
merged->Merge(limits_[node->InputAt(0)->id()]);
}
limits_[node->id()] = merged;
}
void LoopVariableOptimizer::VisitLoop(Node* node) {
DetectInductionVariables(node);
// Conservatively take the limits from the loop entry here.
return TakeConditionsFromFirstControl(node);
}
void LoopVariableOptimizer::VisitIf(Node* node, bool polarity) {
Node* branch = node->InputAt(0);
Node* cond = branch->InputAt(0);
VariableLimits* limits = limits_[branch->id()]->Copy(zone());
// Normalize to less than comparison.
switch (cond->opcode()) {
case IrOpcode::kJSLessThan:
AddCmpToLimits(limits, cond, InductionVariable::kStrict, polarity);
break;
case IrOpcode::kJSGreaterThan:
AddCmpToLimits(limits, cond, InductionVariable::kNonStrict, !polarity);
break;
case IrOpcode::kJSLessThanOrEqual:
AddCmpToLimits(limits, cond, InductionVariable::kNonStrict, polarity);
break;
case IrOpcode::kJSGreaterThanOrEqual:
AddCmpToLimits(limits, cond, InductionVariable::kStrict, !polarity);
break;
default:
break;
}
limits_[node->id()] = limits;
}
void LoopVariableOptimizer::AddCmpToLimits(
VariableLimits* limits, Node* node, InductionVariable::ConstraintKind kind,
bool polarity) {
Node* left = node->InputAt(0);
Node* right = node->InputAt(1);
if (FindInductionVariable(left) || FindInductionVariable(right)) {
if (polarity) {
limits->Add(left, kind, right, zone());
} else {
kind = (kind == InductionVariable::kStrict)
? InductionVariable::kNonStrict
: InductionVariable::kStrict;
limits->Add(right, kind, left, zone());
}
}
}
void LoopVariableOptimizer::VisitStart(Node* node) {
limits_[node->id()] = VariableLimits::Empty(zone());
}
void LoopVariableOptimizer::VisitLoopExit(Node* node) {
return TakeConditionsFromFirstControl(node);
}
void LoopVariableOptimizer::VisitOtherControl(Node* node) {
DCHECK_EQ(1, node->op()->ControlInputCount());
return TakeConditionsFromFirstControl(node);
}
void LoopVariableOptimizer::TakeConditionsFromFirstControl(Node* node) {
const VariableLimits* limits =
limits_[NodeProperties::GetControlInput(node, 0)->id()];
DCHECK_NOT_NULL(limits);
limits_[node->id()] = limits;
}
const InductionVariable* LoopVariableOptimizer::FindInductionVariable(
Node* node) {
auto var = induction_vars_.find(node->id());
if (var != induction_vars_.end()) {
return var->second;
}
return nullptr;
}
InductionVariable* LoopVariableOptimizer::TryGetInductionVariable(Node* phi) {
DCHECK_EQ(2, phi->op()->ValueInputCount());
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) {
return nullptr;
}
// TODO(jarin) Support both sides.
if (arith->InputAt(0) != phi) {
if (arith->InputAt(0)->opcode() != IrOpcode::kJSToNumber ||
arith->InputAt(0)->InputAt(0) != phi) {
return nullptr;
}
}
Node* incr = arith->InputAt(1);
return new (zone()) InductionVariable(phi, arith, incr, initial, zone());
}
void LoopVariableOptimizer::DetectInductionVariables(Node* loop) {
if (loop->op()->ControlInputCount() != 2) return;
TRACE("Loop variables for loop %i:", loop->id());
for (Edge edge : loop->use_edges()) {
if (NodeProperties::IsControlEdge(edge) &&
edge.from()->opcode() == IrOpcode::kPhi) {
Node* phi = edge.from();
InductionVariable* induction_var = TryGetInductionVariable(phi);
if (induction_var) {
induction_vars_[phi->id()] = induction_var;
TRACE(" %i", induction_var->phi()->id());
}
}
}
TRACE("\n");
}
void LoopVariableOptimizer::ChangeToInductionVariablePhis() {
for (auto entry : induction_vars_) {
// It only make sense to analyze the induction variables if
// there is a bound.
InductionVariable* induction_var = entry.second;
DCHECK_EQ(MachineRepresentation::kTagged,
PhiRepresentationOf(induction_var->phi()->op()));
if (induction_var->upper_bounds().size() == 0 &&
induction_var->lower_bounds().size() == 0) {
continue;
}
// Insert the increment value to the value inputs.
induction_var->phi()->InsertInput(graph()->zone(),
induction_var->phi()->InputCount() - 1,
induction_var->increment());
// Insert the bound inputs to the value inputs.
for (auto bound : induction_var->lower_bounds()) {
induction_var->phi()->InsertInput(
graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound);
}
for (auto bound : induction_var->upper_bounds()) {
induction_var->phi()->InsertInput(
graph()->zone(), induction_var->phi()->InputCount() - 1, bound.bound);
}
NodeProperties::ChangeOp(
induction_var->phi(),
common()->InductionVariablePhi(induction_var->phi()->InputCount() - 1));
}
}
void LoopVariableOptimizer::ChangeFromInductionVariablePhis() {
for (auto entry : induction_vars_) {
InductionVariable* induction_var = entry.second;
if (induction_var->phi()->opcode() == IrOpcode::kInductionVariablePhi) {
int value_count = 2;
Node* control = NodeProperties::GetControlInput(induction_var->phi());
DCHECK_EQ(value_count, control->op()->ControlInputCount());
induction_var->phi()->TrimInputCount(value_count + 1);
induction_var->phi()->ReplaceInput(value_count, control);
NodeProperties::ChangeOp(
induction_var->phi(),
common()->Phi(MachineRepresentation::kTagged, value_count));
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,112 @@
// 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.
#ifndef V8_COMPILER_LOOP_VARIABLE_OPTIMIZER_H_
#define V8_COMPILER_LOOP_VARIABLE_OPTIMIZER_H_
#include "src/zone-containers.h"
namespace v8 {
namespace internal {
namespace compiler {
class CommonOperatorBuilder;
class Graph;
class Node;
class InductionVariable : public ZoneObject {
public:
Node* phi() const { return phi_; }
Node* arith() const { return arith_; }
Node* increment() const { return increment_; }
Node* init_value() const { return init_value_; }
enum ConstraintKind { kStrict, kNonStrict };
struct Bound {
Bound(Node* bound, ConstraintKind kind) : bound(bound), kind(kind) {}
Node* bound;
ConstraintKind kind;
};
const ZoneVector<Bound>& lower_bounds() { return lower_bounds_; }
const ZoneVector<Bound>& upper_bounds() { return upper_bounds_; }
private:
friend class LoopVariableOptimizer;
InductionVariable(Node* phi, Node* arith, Node* increment, Node* init_value,
Zone* zone)
: phi_(phi),
arith_(arith),
increment_(increment),
init_value_(init_value),
lower_bounds_(zone),
upper_bounds_(zone) {}
void AddUpperBound(Node* bound, ConstraintKind kind, Zone* graph_zone);
void AddLowerBound(Node* bound, ConstraintKind kind, Zone* graph_zone);
Node* phi_;
Node* arith_;
Node* increment_;
Node* init_value_;
ZoneVector<Bound> lower_bounds_;
ZoneVector<Bound> upper_bounds_;
};
class LoopVariableOptimizer {
public:
void Run();
LoopVariableOptimizer(Graph* graph, CommonOperatorBuilder* common,
Zone* zone);
const ZoneMap<int, InductionVariable*>& induction_variables() {
return induction_vars_;
}
void ChangeToInductionVariablePhis();
void ChangeFromInductionVariablePhis();
private:
const int kAssumedLoopEntryIndex = 0;
const int kFirstBackedge = 1;
class Constraint;
class VariableLimits;
void VisitBackedge(Node* from, Node* loop);
void VisitNode(Node* node);
void VisitMerge(Node* node);
void VisitLoop(Node* node);
void VisitIf(Node* node, bool polarity);
void VisitStart(Node* node);
void VisitLoopExit(Node* node);
void VisitOtherControl(Node* node);
void AddCmpToLimits(VariableLimits* limits, Node* node,
InductionVariable::ConstraintKind kind, bool polarity);
void TakeConditionsFromFirstControl(Node* node);
const InductionVariable* FindInductionVariable(Node* node);
InductionVariable* TryGetInductionVariable(Node* phi);
void DetectInductionVariables(Node* loop);
Graph* graph() { return graph_; }
CommonOperatorBuilder* common() { return common_; }
Zone* zone() { return zone_; }
Graph* graph_;
CommonOperatorBuilder* common_;
Zone* zone_;
ZoneMap<int, const VariableLimits*> limits_;
ZoneMap<int, InductionVariable*> induction_vars_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_LOOP_VARIABLE_OPTIMIZER_H_

View File

@ -43,23 +43,24 @@
V(RelocatableInt32Constant) \
V(RelocatableInt64Constant)
#define INNER_OP_LIST(V) \
V(Select) \
V(Phi) \
V(EffectPhi) \
V(Checkpoint) \
V(BeginRegion) \
V(FinishRegion) \
V(FrameState) \
V(StateValues) \
V(TypedStateValues) \
V(ObjectState) \
V(Call) \
V(Parameter) \
V(OsrValue) \
V(LoopExit) \
V(LoopExitValue) \
V(LoopExitEffect) \
#define INNER_OP_LIST(V) \
V(Select) \
V(Phi) \
V(EffectPhi) \
V(InductionVariablePhi) \
V(Checkpoint) \
V(BeginRegion) \
V(FinishRegion) \
V(FrameState) \
V(StateValues) \
V(TypedStateValues) \
V(ObjectState) \
V(Call) \
V(Parameter) \
V(OsrValue) \
V(LoopExit) \
V(LoopExitValue) \
V(LoopExitEffect) \
V(Projection)
#define COMMON_OP_LIST(V) \

View File

@ -45,6 +45,7 @@
#include "src/compiler/load-elimination.h"
#include "src/compiler/loop-analysis.h"
#include "src/compiler/loop-peeling.h"
#include "src/compiler/loop-variable-optimizer.h"
#include "src/compiler/machine-operator-reducer.h"
#include "src/compiler/memory-optimizer.h"
#include "src/compiler/move-optimizer.h"
@ -834,7 +835,10 @@ struct TyperPhase {
void Run(PipelineData* data, Zone* temp_zone, Typer* typer) {
NodeVector roots(temp_zone);
data->jsgraph()->GetCachedNodes(&roots);
typer->Run(roots);
LoopVariableOptimizer induction_vars(data->jsgraph()->graph(),
data->common(), temp_zone);
if (FLAG_turbo_loop_variable) induction_vars.Run();
typer->Run(roots, &induction_vars);
}
};

View File

@ -10,6 +10,7 @@
#include "src/compiler/common-operator.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/loop-variable-optimizer.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/node.h"
#include "src/compiler/operation-typer.h"
@ -77,8 +78,10 @@ Typer::~Typer() {
class Typer::Visitor : public Reducer {
public:
explicit Visitor(Typer* typer)
: typer_(typer), weakened_nodes_(typer->zone()) {}
explicit Visitor(Typer* typer, LoopVariableOptimizer* induction_vars)
: typer_(typer),
induction_vars_(induction_vars),
weakened_nodes_(typer->zone()) {}
Reduction Reduce(Node* node) override {
if (node->op()->ValueOutputCount() == 0) return NoChange();
@ -189,6 +192,7 @@ class Typer::Visitor : public Reducer {
private:
Typer* typer_;
LoopVariableOptimizer* induction_vars_;
ZoneSet<NodeId> weakened_nodes_;
#define DECLARE_METHOD(x) inline Type* Type##x(Node* node);
@ -308,18 +312,23 @@ class Typer::Visitor : public Reducer {
}
};
void Typer::Run() { Run(NodeVector(zone()), nullptr); }
void Typer::Run() { Run(NodeVector(zone())); }
void Typer::Run(const NodeVector& roots) {
Visitor visitor(this);
void Typer::Run(const NodeVector& roots,
LoopVariableOptimizer* induction_vars) {
if (induction_vars != nullptr) {
induction_vars->ChangeToInductionVariablePhis();
}
Visitor visitor(this, induction_vars);
GraphReducer graph_reducer(zone(), graph());
graph_reducer.AddReducer(&visitor);
for (Node* const root : roots) graph_reducer.ReduceNode(root);
graph_reducer.ReduceGraph();
}
if (induction_vars != nullptr) {
induction_vars->ChangeFromInductionVariablePhis();
}
}
void Typer::Decorator::Decorate(Node* node) {
if (node->op()->ValueOutputCount() > 0) {
@ -327,7 +336,7 @@ void Typer::Decorator::Decorate(Node* node) {
// Other cases will generally require a proper fixpoint iteration with Run.
bool is_typed = NodeProperties::IsTyped(node);
if (is_typed || NodeProperties::AllValueInputsAreTyped(node)) {
Visitor typing(typer_);
Visitor typing(typer_, nullptr);
Type* type = typing.TypeNode(node);
if (is_typed) {
type = Type::Intersect(type, NodeProperties::GetType(node),
@ -736,7 +745,6 @@ Type* Typer::Visitor::TypeSelect(Node* node) {
return Type::Union(Operand(node, 1), Operand(node, 2), zone());
}
Type* Typer::Visitor::TypePhi(Node* node) {
int arity = node->op()->ValueInputCount();
Type* type = Operand(node, 0);
@ -746,6 +754,89 @@ Type* Typer::Visitor::TypePhi(Node* node) {
return type;
}
Type* Typer::Visitor::TypeInductionVariablePhi(Node* node) {
int arity = NodeProperties::GetControlInput(node)->op()->ControlInputCount();
DCHECK_EQ(IrOpcode::kLoop, NodeProperties::GetControlInput(node)->opcode());
DCHECK_EQ(2, NodeProperties::GetControlInput(node)->InputCount());
Type* initial_type = Operand(node, 0);
Type* increment_type = Operand(node, 2);
// We only handle integer induction variables (otherwise ranges
// do not apply and we cannot do anything).
if (!initial_type->Is(typer_->cache_.kInteger) ||
!increment_type->Is(typer_->cache_.kInteger)) {
// Fallback to normal phi typing.
Type* type = Operand(node, 0);
for (int i = 1; i < arity; ++i) {
type = Type::Union(type, Operand(node, i), zone());
}
return type;
}
// If we do not have enough type information for the initial value or
// the increment, just return the initial value's type.
if (!initial_type->IsInhabited() || !increment_type->IsInhabited()) {
return initial_type;
}
// Now process the bounds.
auto res = induction_vars_->induction_variables().find(node->id());
DCHECK(res != induction_vars_->induction_variables().end());
InductionVariable* induction_var = res->second;
double min = -V8_INFINITY;
double max = V8_INFINITY;
if (increment_type->Min() >= 0) {
min = initial_type->Min();
for (auto bound : induction_var->upper_bounds()) {
Type* bound_type = TypeOrNone(bound.bound);
// If the type is not an integer, just skip the bound.
if (!bound_type->Is(typer_->cache_.kInteger)) continue;
// If the type is not inhabited, then we can take the initial value.
if (!bound_type->IsInhabited()) {
max = initial_type->Max();
break;
}
double bound_max = bound_type->Max();
if (bound.kind == InductionVariable::kStrict) {
bound_max -= 1;
}
max = std::min(max, bound_max + increment_type->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) {
max = initial_type->Max();
for (auto bound : induction_var->lower_bounds()) {
Type* bound_type = TypeOrNone(bound.bound);
// If the type is not an integer, just skip the bound.
if (!bound_type->Is(typer_->cache_.kInteger)) continue;
// If the type is not inhabited, then we can take the initial value.
if (!bound_type->IsInhabited()) {
min = initial_type->Min();
break;
}
double bound_min = bound_type->Min();
if (bound.kind == InductionVariable::kStrict) {
bound_min += 1;
}
min = std::max(min, bound_min + increment_type->Min());
}
// The lower bound must be at most the initial value's lower bound.
min = std::min(min, initial_type->Min());
} else {
// Shortcut: If the increment can be both positive and negative,
// the variable can go arbitrarily far, so just return integer.
return typer_->cache_.kInteger;
}
if (FLAG_trace_turbo_loop) {
OFStream os(stdout);
os << "Loop (" << NodeProperties::GetControlInput(node)->id()
<< ") variable bounds for phi " << node->id() << ": (" << min << ", "
<< max << ")\n";
}
return Type::Range(min, max, typer_->zone());
}
Type* Typer::Visitor::TypeEffectPhi(Node* node) {
UNREACHABLE();

View File

@ -19,6 +19,7 @@ class TypeCache;
namespace compiler {
class LoopVariableOptimizer;
class OperationTyper;
class Typer {
@ -36,7 +37,8 @@ class Typer {
void Run();
// TODO(bmeurer,jarin): Remove this once we have a notion of "roots" on Graph.
void Run(const ZoneVector<Node*>& roots);
void Run(const ZoneVector<Node*>& roots,
LoopVariableOptimizer* induction_vars);
private:
class Visitor;

View File

@ -386,6 +386,11 @@ void Verifier::Visitor::Check(Node* node) {
*/
break;
}
case IrOpcode::kInductionVariablePhi: {
// This is only a temporary node for the typer.
UNREACHABLE();
break;
}
case IrOpcode::kEffectPhi: {
// EffectPhi input count matches parent control node.
CHECK_EQ(0, value_count);

View File

@ -446,6 +446,7 @@ DEFINE_BOOL(trace_turbo_reduction, false, "trace TurboFan's various reducers")
DEFINE_BOOL(trace_turbo_trimming, false, "trace TurboFan's graph trimmer")
DEFINE_BOOL(trace_turbo_jt, false, "trace TurboFan's jump threading")
DEFINE_BOOL(trace_turbo_ceq, false, "trace TurboFan's control equivalence")
DEFINE_BOOL(trace_turbo_loop, false, "trace TurboFan's loop optimizations")
DEFINE_BOOL(turbo_asm, true, "enable TurboFan for asm.js code")
DEFINE_BOOL(turbo_asm_deoptimization, false,
"enable deoptimization in TurboFan for asm.js code")
@ -477,6 +478,7 @@ DEFINE_BOOL(turbo_jt, true, "enable jump threading in TurboFan")
DEFINE_BOOL(turbo_stress_loop_peeling, false,
"stress loop peeling optimization")
DEFINE_BOOL(turbo_loop_peeling, false, "Turbofan loop peeling")
DEFINE_BOOL(turbo_loop_variable, false, "Turbofan loop variable optimization")
DEFINE_BOOL(turbo_cf_optimization, true, "optimize control flow in TurboFan")
DEFINE_BOOL(turbo_frame_elision, true, "elide frames in TurboFan")
DEFINE_BOOL(turbo_cache_shared_code, true, "cache context-independent code")

View File

@ -657,6 +657,8 @@
'compiler/loop-analysis.h',
'compiler/loop-peeling.cc',
'compiler/loop-peeling.h',
'compiler/loop-variable-optimizer.cc',
'compiler/loop-variable-optimizer.h',
'compiler/machine-operator-reducer.cc',
'compiler/machine-operator-reducer.h',
'compiler/machine-operator.cc',