summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/loop-variable-optimizer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/loop-variable-optimizer.cc')
-rw-r--r--deps/v8/src/compiler/loop-variable-optimizer.cc406
1 files changed, 406 insertions, 0 deletions
diff --git a/deps/v8/src/compiler/loop-variable-optimizer.cc b/deps/v8/src/compiler/loop-variable-optimizer.cc
new file mode 100644
index 0000000000..8331963a7d
--- /dev/null
+++ b/deps/v8/src/compiler/loop-variable-optimizer.cc
@@ -0,0 +1,406 @@
+// 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) {
+ 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) {
+ 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());
+ }
+ }
+ 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->AddLowerBound(constraint->left(), constraint->kind());
+ }
+ }
+ }
+}
+
+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(i)->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);
+ 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 ||
+ arith->InputAt(0)->InputAt(0) != phi) {
+ return nullptr;
+ }
+ }
+ Node* incr = arith->InputAt(1);
+ return new (zone())
+ InductionVariable(phi, arith, incr, initial, zone(), arithmeticType);
+}
+
+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::ChangeToPhisAndInsertGuards() {
+ for (auto entry : induction_vars_) {
+ InductionVariable* induction_var = entry.second;
+ if (induction_var->phi()->opcode() == IrOpcode::kInductionVariablePhi) {
+ // Turn the induction variable phi back to normal phi.
+ 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));
+
+ // If the backedge is not a subtype of the phi's type, we insert a sigma
+ // to get the typing right.
+ Node* backedge_value = induction_var->phi()->InputAt(1);
+ Type* backedge_type = NodeProperties::GetType(backedge_value);
+ Type* phi_type = NodeProperties::GetType(induction_var->phi());
+ if (!backedge_type->Is(phi_type)) {
+ Node* backedge_control =
+ NodeProperties::GetControlInput(induction_var->phi())->InputAt(1);
+ Node* rename = graph()->NewNode(common()->TypeGuard(phi_type),
+ backedge_value, backedge_control);
+ induction_var->phi()->ReplaceInput(1, rename);
+ }
+ }
+ }
+}
+
+} // namespace compiler
+} // namespace internal
+} // namespace v8