Commit 398a1143 authored by jarin's avatar jarin Committed by Commit bot

[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}
parent 3deb71fb
......@@ -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",
......
......@@ -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) {
......
......@@ -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();
......
......@@ -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;
......
......@@ -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);
......
This diff is collapsed.
// 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_
......@@ -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) \
......
......@@ -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);
}
};
......
......@@ -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();
......
......@@ -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;
......
......@@ -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);
......
......@@ -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")
......
......@@ -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',
......
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