[turbofan] LoopVariableOptimizer: use generic FunctionalList implementation

Change-Id: I963215506a87945ae863427c572989c857bca2ff
Reviewed-on: https://chromium-review.googlesource.com/897608
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51039}
This commit is contained in:
Tobias Tebbi 2018-02-01 13:23:16 +01:00 committed by Commit Bot
parent 533820fe83
commit 49a5f6dafd
6 changed files with 167 additions and 213 deletions

View File

@ -1410,6 +1410,7 @@ v8_source_set("v8_base") {
"src/compiler/frame-states.h",
"src/compiler/frame.cc",
"src/compiler/frame.h",
"src/compiler/functional-list.h",
"src/compiler/gap-resolver.cc",
"src/compiler/gap-resolver.h",
"src/compiler/graph-assembler.cc",

View File

@ -706,6 +706,7 @@
'../src/compiler/frame-elider.h',
'../src/compiler/frame-states.cc',
'../src/compiler/frame-states.h',
'../src/compiler/functional-list.h',
'../src/compiler/gap-resolver.cc',
'../src/compiler/gap-resolver.h',
'../src/compiler/graph-assembler.cc',

View File

@ -6,6 +6,7 @@
#define V8_COMPILER_BRANCH_CONDITION_ELIMINATION_H_
#include "src/base/compiler-specific.h"
#include "src/compiler/functional-list.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/node-aux-data.h"
#include "src/globals.h"
@ -18,110 +19,6 @@ namespace compiler {
class CommonOperatorBuilder;
class JSGraph;
// A generic stack implemented as a purely functional singly-linked list, which
// results in an O(1) copy operation. It is the equivalent of functional lists
// in ML-like languages, with the only difference that it also caches the length
// of the list in each node.
// TODO(tebbi): Use this implementation also for RedundancyElimination.
template <class A>
class FunctionalList {
private:
struct Cons : ZoneObject {
Cons(A top, Cons* rest)
: top(std::move(top)), rest(rest), size(1 + (rest ? rest->size : 0)) {}
A const top;
Cons* const rest;
size_t const size;
};
public:
FunctionalList() : elements_(nullptr) {}
bool operator==(const FunctionalList<A>& other) const {
if (Size() != other.Size()) return false;
iterator it = begin();
iterator other_it = other.begin();
while (true) {
if (it == other_it) return true;
if (*it != *other_it) return false;
++it;
++other_it;
}
}
bool operator!=(const FunctionalList<A>& other) const {
return !(*this == other);
}
const A& Front() const {
DCHECK_GT(Size(), 0);
return elements_->top;
}
FunctionalList Rest() const {
FunctionalList result = *this;
result.DropFront();
return result;
}
void DropFront() {
CHECK_GT(Size(), 0);
elements_ = elements_->rest;
}
void PushFront(A a, Zone* zone) {
elements_ = new (zone) Cons(std::move(a), elements_);
}
// If {hint} happens to be exactly what we want to allocate, avoid allocation
// by reusing {hint}.
void PushFront(A a, Zone* zone, FunctionalList hint) {
if (hint.Size() == Size() + 1 && hint.Front() == a &&
hint.Rest() == *this) {
*this = hint;
} else {
PushFront(a, zone);
}
}
// Drop elements until the current stack is equal to the tail shared with
// {other}. The shared tail must not only be equal, but also refer to the
// same memory.
void ResetToCommonAncestor(FunctionalList other) {
while (other.Size() > Size()) other.DropFront();
while (other.Size() < Size()) DropFront();
while (elements_ != other.elements_) {
DropFront();
other.DropFront();
}
}
size_t Size() const { return elements_ ? elements_->size : 0; }
class iterator {
public:
explicit iterator(Cons* cur) : current_(cur) {}
const A& operator*() const { return current_->top; }
iterator& operator++() {
current_ = current_->rest;
return *this;
}
bool operator==(const iterator& other) const {
return this->current_ == other.current_;
}
bool operator!=(const iterator& other) const { return !(*this == other); }
private:
Cons* current_;
};
iterator begin() const { return iterator(elements_); }
iterator end() const { return iterator(nullptr); }
private:
Cons* elements_;
};
class V8_EXPORT_PRIVATE BranchElimination final
: public NON_EXPORTED_BASE(AdvancedReducer) {
public:

View File

@ -0,0 +1,122 @@
// Copyright 2018 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_FUNCTIONAL_LIST_H_
#define V8_COMPILER_FUNCTIONAL_LIST_H_
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace compiler {
// A generic stack implemented as a purely functional singly-linked list, which
// results in an O(1) copy operation. It is the equivalent of functional lists
// in ML-like languages, with the only difference that it also caches the length
// of the list in each node.
// TODO(tebbi): Use this implementation also for RedundancyElimination.
template <class A>
class FunctionalList {
private:
struct Cons : ZoneObject {
Cons(A top, Cons* rest)
: top(std::move(top)), rest(rest), size(1 + (rest ? rest->size : 0)) {}
A const top;
Cons* const rest;
size_t const size;
};
public:
FunctionalList() : elements_(nullptr) {}
bool operator==(const FunctionalList<A>& other) const {
if (Size() != other.Size()) return false;
iterator it = begin();
iterator other_it = other.begin();
while (true) {
if (it == other_it) return true;
if (*it != *other_it) return false;
++it;
++other_it;
}
}
bool operator!=(const FunctionalList<A>& other) const {
return !(*this == other);
}
const A& Front() const {
DCHECK_GT(Size(), 0);
return elements_->top;
}
FunctionalList Rest() const {
FunctionalList result = *this;
result.DropFront();
return result;
}
void DropFront() {
CHECK_GT(Size(), 0);
elements_ = elements_->rest;
}
void PushFront(A a, Zone* zone) {
elements_ = new (zone) Cons(std::move(a), elements_);
}
// If {hint} happens to be exactly what we want to allocate, avoid allocation
// by reusing {hint}.
void PushFront(A a, Zone* zone, FunctionalList hint) {
if (hint.Size() == Size() + 1 && hint.Front() == a &&
hint.Rest() == *this) {
*this = hint;
} else {
PushFront(a, zone);
}
}
// Drop elements until the current stack is equal to the tail shared with
// {other}. The shared tail must not only be equal, but also refer to the
// same memory.
void ResetToCommonAncestor(FunctionalList other) {
while (other.Size() > Size()) other.DropFront();
while (other.Size() < Size()) DropFront();
while (elements_ != other.elements_) {
DropFront();
other.DropFront();
}
}
size_t Size() const { return elements_ ? elements_->size : 0; }
class iterator {
public:
explicit iterator(Cons* cur) : current_(cur) {}
const A& operator*() const { return current_->top; }
iterator& operator++() {
current_ = current_->rest;
return *this;
}
bool operator==(const iterator& other) const {
return this->current_ == other.current_;
}
bool operator!=(const iterator& other) const { return !(*this == other); }
private:
Cons* current_;
};
iterator begin() const { return iterator(elements_); }
iterator end() const { return iterator(nullptr); }
private:
Cons* elements_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_FUNCTIONAL_LIST_H_

View File

@ -29,6 +29,7 @@ LoopVariableOptimizer::LoopVariableOptimizer(Graph* graph,
common_(common),
zone_(zone),
limits_(graph->NodeCount(), zone),
reduced_(graph->NodeCount(), zone),
induction_vars_(zone) {}
void LoopVariableOptimizer::Run() {
@ -40,13 +41,13 @@ void LoopVariableOptimizer::Run() {
queue.pop();
queued.Set(node, false);
DCHECK_NULL(limits_[node->id()]);
DCHECK(!reduced_.Get(node));
bool all_inputs_visited = true;
int inputs_end = (node->opcode() == IrOpcode::kLoop)
? kFirstBackedge
: node->op()->ControlInputCount();
for (int i = 0; i < inputs_end; i++) {
if (limits_[NodeProperties::GetControlInput(node, i)->id()] == nullptr) {
if (!reduced_.Get(NodeProperties::GetControlInput(node, i))) {
all_inputs_visited = false;
break;
}
@ -54,7 +55,7 @@ void LoopVariableOptimizer::Run() {
if (!all_inputs_visited) continue;
VisitNode(node);
DCHECK_NOT_NULL(limits_[node->id()]);
reduced_.Set(node, true);
// Queue control outputs.
for (Edge edge : node->use_edges()) {
@ -73,80 +74,6 @@ void LoopVariableOptimizer::Run() {
}
}
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_LT(0, limit_count_);
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) {
@ -173,21 +100,19 @@ void LoopVariableOptimizer::VisitBackedge(Node* from, Node* loop) {
// 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());
for (Constraint constraint : limits_.Get(from)) {
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());
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 (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());
var->second->AddLowerBound(constraint.left, constraint.kind);
}
}
}
@ -214,11 +139,11 @@ void LoopVariableOptimizer::VisitNode(Node* node) {
void LoopVariableOptimizer::VisitMerge(Node* node) {
// Merge the limits of all incoming edges.
VariableLimits* merged = limits_[node->InputAt(0)->id()]->Copy(zone());
VariableLimits merged = limits_.Get(node->InputAt(0));
for (int i = 1; i < node->InputCount(); i++) {
merged->Merge(limits_[node->InputAt(i)->id()]);
merged.ResetToCommonAncestor(limits_.Get(node->InputAt(i)));
}
limits_[node->id()] = merged;
limits_.Set(node, merged);
}
void LoopVariableOptimizer::VisitLoop(Node* node) {
@ -230,27 +155,27 @@ void LoopVariableOptimizer::VisitLoop(Node* 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());
VariableLimits limits = limits_.Get(branch);
// Normalize to less than comparison.
switch (cond->opcode()) {
case IrOpcode::kJSLessThan:
case IrOpcode::kSpeculativeNumberLessThan:
AddCmpToLimits(limits, cond, InductionVariable::kStrict, polarity);
AddCmpToLimits(&limits, cond, InductionVariable::kStrict, polarity);
break;
case IrOpcode::kJSGreaterThan:
AddCmpToLimits(limits, cond, InductionVariable::kNonStrict, !polarity);
AddCmpToLimits(&limits, cond, InductionVariable::kNonStrict, !polarity);
break;
case IrOpcode::kJSLessThanOrEqual:
case IrOpcode::kSpeculativeNumberLessThanOrEqual:
AddCmpToLimits(limits, cond, InductionVariable::kNonStrict, polarity);
AddCmpToLimits(&limits, cond, InductionVariable::kNonStrict, polarity);
break;
case IrOpcode::kJSGreaterThanOrEqual:
AddCmpToLimits(limits, cond, InductionVariable::kStrict, !polarity);
AddCmpToLimits(&limits, cond, InductionVariable::kStrict, !polarity);
break;
default:
break;
}
limits_[node->id()] = limits;
limits_.Set(node, limits);
}
void LoopVariableOptimizer::AddCmpToLimits(
@ -260,19 +185,17 @@ void LoopVariableOptimizer::AddCmpToLimits(
Node* right = node->InputAt(1);
if (FindInductionVariable(left) || FindInductionVariable(right)) {
if (polarity) {
limits->Add(left, kind, right, zone());
limits->PushFront(Constraint{left, kind, right}, zone());
} else {
kind = (kind == InductionVariable::kStrict)
? InductionVariable::kNonStrict
: InductionVariable::kStrict;
limits->Add(right, kind, left, zone());
limits->PushFront(Constraint{right, kind, left}, zone());
}
}
}
void LoopVariableOptimizer::VisitStart(Node* node) {
limits_[node->id()] = VariableLimits::Empty(zone());
}
void LoopVariableOptimizer::VisitStart(Node* node) { limits_.Set(node, {}); }
void LoopVariableOptimizer::VisitLoopExit(Node* node) {
return TakeConditionsFromFirstControl(node);
@ -284,10 +207,7 @@ void LoopVariableOptimizer::VisitOtherControl(Node* node) {
}
void LoopVariableOptimizer::TakeConditionsFromFirstControl(Node* node) {
const VariableLimits* limits =
limits_[NodeProperties::GetControlInput(node, 0)->id()];
DCHECK_NOT_NULL(limits);
limits_[node->id()] = limits;
limits_.Set(node, limits_.Get(NodeProperties::GetControlInput(node, 0)));
}
const InductionVariable* LoopVariableOptimizer::FindInductionVariable(

View File

@ -5,6 +5,8 @@
#ifndef V8_COMPILER_LOOP_VARIABLE_OPTIMIZER_H_
#define V8_COMPILER_LOOP_VARIABLE_OPTIMIZER_H_
#include "src/compiler/functional-list.h"
#include "src/compiler/node-aux-data.h"
#include "src/zone/zone-containers.h"
namespace v8 {
@ -82,8 +84,17 @@ class LoopVariableOptimizer {
const int kAssumedLoopEntryIndex = 0;
const int kFirstBackedge = 1;
class Constraint;
class VariableLimits;
struct Constraint {
Node* left;
InductionVariable::ConstraintKind kind;
Node* right;
bool operator!=(const Constraint& other) const {
return left != other.left || kind != other.kind || right != other.right;
}
};
using VariableLimits = FunctionalList<Constraint>;
void VisitBackedge(Node* from, Node* loop);
void VisitNode(Node* node);
@ -109,7 +120,9 @@ class LoopVariableOptimizer {
Graph* graph_;
CommonOperatorBuilder* common_;
Zone* zone_;
ZoneVector<const VariableLimits*> limits_;
NodeAuxData<VariableLimits> limits_;
NodeAuxData<bool> reduced_;
ZoneMap<int, InductionVariable*> induction_vars_;
};