| Index: src/compiler/loop-variable-optimizer.cc
|
| diff --git a/src/compiler/loop-variable-optimizer.cc b/src/compiler/loop-variable-optimizer.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9db42c907e5a0aa03f3fd842dd1fa47ee543d3db
|
| --- /dev/null
|
| +++ b/src/compiler/loop-variable-optimizer.cc
|
| @@ -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
|
|
|