[turbofan] refactor BranchElimination to use a generic stack implementation

Bug: 
Change-Id: Ibd91a61a9fd4b673db1afe13936d68a2b4a096cd
Reviewed-on: https://chromium-review.googlesource.com/892058
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50983}
This commit is contained in:
Tobias Tebbi 2018-01-31 10:50:17 +01:00 committed by Commit Bot
parent 007a73548b
commit 630a992b8d
3 changed files with 192 additions and 225 deletions

View File

@ -16,7 +16,8 @@ BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph,
Zone* zone)
: AdvancedReducer(editor),
jsgraph_(js_graph),
node_conditions_(zone, js_graph->graph()->NodeCount()),
node_conditions_(js_graph->graph()->NodeCount(), zone),
reduced_(js_graph->graph()->NodeCount(), zone),
zone_(zone),
dead_(js_graph->Dead()) {}
@ -55,26 +56,24 @@ Reduction BranchElimination::Reduce(Node* node) {
Reduction BranchElimination::ReduceBranch(Node* node) {
Node* condition = node->InputAt(0);
Node* control_input = NodeProperties::GetControlInput(node, 0);
const ControlPathConditions* from_input = node_conditions_.Get(control_input);
if (from_input != nullptr) {
Maybe<bool> condition_value = from_input->LookupCondition(condition);
// If we know the condition we can discard the branch.
if (condition_value.IsJust()) {
bool known_value = condition_value.FromJust();
for (Node* const use : node->uses()) {
switch (use->opcode()) {
case IrOpcode::kIfTrue:
Replace(use, known_value ? control_input : dead());
break;
case IrOpcode::kIfFalse:
Replace(use, known_value ? dead() : control_input);
break;
default:
UNREACHABLE();
}
ControlPathConditions from_input = node_conditions_.Get(control_input);
Maybe<bool> condition_value = from_input.LookupCondition(condition);
// If we know the condition we can discard the branch.
if (condition_value.IsJust()) {
bool known_value = condition_value.FromJust();
for (Node* const use : node->uses()) {
switch (use->opcode()) {
case IrOpcode::kIfTrue:
Replace(use, known_value ? control_input : dead());
break;
case IrOpcode::kIfFalse:
Replace(use, known_value ? dead() : control_input);
break;
default:
UNREACHABLE();
}
return Replace(dead());
}
return Replace(dead());
}
return TakeConditionsFromFirstControl(node);
}
@ -88,14 +87,15 @@ Reduction BranchElimination::ReduceDeoptimizeConditional(Node* node) {
Node* frame_state = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
ControlPathConditions const* conditions = node_conditions_.Get(control);
// If we do not know anything about the predecessor, do not propagate just
// yet because we will have to recompute anyway once we compute the
// predecessor.
if (conditions == nullptr) {
return UpdateConditions(node, conditions);
if (!reduced_.Get(control)) {
return NoChange();
}
Maybe<bool> condition_value = conditions->LookupCondition(condition);
ControlPathConditions conditions = node_conditions_.Get(control);
Maybe<bool> condition_value = conditions.LookupCondition(condition);
if (condition_value.IsJust()) {
// If we know the condition we can discard the branch.
if (condition_is_true == condition_value.FromJust()) {
@ -118,12 +118,12 @@ Reduction BranchElimination::ReduceDeoptimizeConditional(Node* node) {
Reduction BranchElimination::ReduceIf(Node* node, bool is_true_branch) {
// Add the condition to the list arriving from the input branch.
Node* branch = NodeProperties::GetControlInput(node, 0);
const ControlPathConditions* from_branch = node_conditions_.Get(branch);
ControlPathConditions from_branch = node_conditions_.Get(branch);
// If we do not know anything about the predecessor, do not propagate just
// yet because we will have to recompute anyway once we compute the
// predecessor.
if (from_branch == nullptr) {
return UpdateConditions(node, nullptr);
if (!reduced_.Get(branch)) {
return NoChange();
}
Node* condition = branch->InputAt(0);
return UpdateConditions(node, from_branch, condition, is_true_branch);
@ -143,8 +143,8 @@ Reduction BranchElimination::ReduceMerge(Node* node) {
// input.
Node::Inputs inputs = node->inputs();
for (Node* input : inputs) {
if (node_conditions_.Get(input) == nullptr) {
return UpdateConditions(node, nullptr);
if (!reduced_.Get(input)) {
return NoChange();
}
}
@ -152,42 +152,23 @@ Reduction BranchElimination::ReduceMerge(Node* node) {
DCHECK_GT(inputs.count(), 0);
const ControlPathConditions* first = node_conditions_.Get(*input_it);
ControlPathConditions conditions = node_conditions_.Get(*input_it);
++input_it;
// Make a copy of the first input's conditions and merge with the conditions
// from other inputs.
ControlPathConditions* conditions =
new (zone_->New(sizeof(ControlPathConditions)))
ControlPathConditions(*first);
// Merge the first input's conditions with the conditions from the other
// inputs.
auto input_end = inputs.end();
for (; input_it != input_end; ++input_it) {
conditions->Merge(*(node_conditions_.Get(*input_it)));
// 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.)
conditions.ResetToCommonAncestor(node_conditions_.Get(*input_it));
}
return UpdateConditions(node, conditions);
}
Reduction BranchElimination::ReduceStart(Node* node) {
return UpdateConditions(node, ControlPathConditions::Empty(zone_));
}
const BranchElimination::ControlPathConditions*
BranchElimination::PathConditionsForControlNodes::Get(Node* node) const {
if (static_cast<size_t>(node->id()) < info_for_node_.size()) {
return info_for_node_[node->id()];
}
return nullptr;
}
void BranchElimination::PathConditionsForControlNodes::Set(
Node* node, const ControlPathConditions* conditions) {
size_t index = static_cast<size_t>(node->id());
if (index >= info_for_node_.size()) {
info_for_node_.resize(index + 1, nullptr);
}
info_for_node_[index] = conditions;
return UpdateConditions(node, {});
}
@ -200,151 +181,48 @@ Reduction BranchElimination::ReduceOtherControl(Node* node) {
Reduction BranchElimination::TakeConditionsFromFirstControl(Node* node) {
// We just propagate the information from the control input (ideally,
// we would only revisit control uses if there is change).
const ControlPathConditions* from_input =
node_conditions_.Get(NodeProperties::GetControlInput(node, 0));
return UpdateConditions(node, from_input);
Node* input = NodeProperties::GetControlInput(node, 0);
if (!reduced_.Get(input)) return NoChange();
return UpdateConditions(node, node_conditions_.Get(input));
}
Reduction BranchElimination::UpdateConditions(
Node* node, const ControlPathConditions* conditions) {
const ControlPathConditions* original = node_conditions_.Get(node);
Node* node, ControlPathConditions conditions) {
// Only signal that the node has Changed if the condition information has
// changed.
if (conditions != original) {
if (conditions == nullptr || original == nullptr ||
*conditions != *original) {
node_conditions_.Set(node, conditions);
return Changed(node);
}
}
return NoChange();
}
Reduction BranchElimination::UpdateConditions(
Node* node, const ControlPathConditions* prev_conditions,
Node* current_condition, bool is_true_branch) {
const ControlPathConditions* original = node_conditions_.Get(node);
DCHECK(prev_conditions != nullptr && current_condition != nullptr);
// The control path for the node is the path obtained by appending the
// current_condition to the prev_conditions. Check if this new control path
// would be the same as the already recorded path (original).
if (original == nullptr || !prev_conditions->EqualsAfterAddingCondition(
original, current_condition, is_true_branch)) {
// If this is the first visit or if the control path is different from the
// recorded path create the new control path and record it.
const ControlPathConditions* new_condition =
prev_conditions->AddCondition(zone_, current_condition, is_true_branch);
node_conditions_.Set(node, new_condition);
if (reduced_.Set(node, true) | node_conditions_.Set(node, conditions)) {
return Changed(node);
}
return NoChange();
}
// static
const BranchElimination::ControlPathConditions*
BranchElimination::ControlPathConditions::Empty(Zone* zone) {
return new (zone->New(sizeof(ControlPathConditions)))
ControlPathConditions(nullptr, 0);
Reduction BranchElimination::UpdateConditions(
Node* node, ControlPathConditions prev_conditions, Node* current_condition,
bool is_true_branch) {
ControlPathConditions original = node_conditions_.Get(node);
// The control path for the node is the path obtained by appending the
// current_condition to the prev_conditions. Use the original control path as
// a hint to avoid allocations.
prev_conditions.AddCondition(zone_, current_condition, is_true_branch,
original);
return UpdateConditions(node, prev_conditions);
}
void BranchElimination::ControlPathConditions::Merge(
const ControlPathConditions& 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.condition_count_;
BranchCondition* other_condition = other.head_;
while (other_size > condition_count_) {
other_condition = other_condition->next;
other_size--;
}
while (condition_count_ > other_size) {
head_ = head_->next;
condition_count_--;
}
// Then we go through both lists in lock-step until we find
// the common tail.
while (head_ != other_condition) {
DCHECK_LT(0, condition_count_);
condition_count_--;
other_condition = other_condition->next;
head_ = head_->next;
}
}
const BranchElimination::ControlPathConditions*
BranchElimination::ControlPathConditions::AddCondition(Zone* zone,
Node* condition,
bool is_true) const {
void BranchElimination::ControlPathConditions::AddCondition(
Zone* zone, Node* condition, bool is_true, ControlPathConditions hint) {
DCHECK(LookupCondition(condition).IsNothing());
BranchCondition* new_head = new (zone->New(sizeof(BranchCondition)))
BranchCondition(condition, is_true, head_);
ControlPathConditions* conditions =
new (zone->New(sizeof(ControlPathConditions)))
ControlPathConditions(new_head, condition_count_ + 1);
return conditions;
PushFront({condition, is_true}, zone, hint);
}
Maybe<bool> BranchElimination::ControlPathConditions::LookupCondition(
Node* condition) const {
for (BranchCondition* current = head_; current != nullptr;
current = current->next) {
if (current->condition == condition) {
return Just<bool>(current->is_true);
}
for (BranchCondition element : *this) {
if (element.condition == condition) return Just<bool>(element.is_true);
}
return Nothing<bool>();
}
bool BranchElimination::ControlPathConditions::IsSamePath(
BranchCondition* this_condition, BranchCondition* other_condition) const {
while (true) {
if (this_condition == other_condition) return true;
if (this_condition->condition != other_condition->condition ||
this_condition->is_true != other_condition->is_true) {
return false;
}
this_condition = this_condition->next;
other_condition = other_condition->next;
}
UNREACHABLE();
}
bool BranchElimination::ControlPathConditions::operator==(
const ControlPathConditions& other) const {
if (condition_count_ != other.condition_count_) return false;
return IsSamePath(head_, other.head_);
}
bool BranchElimination::ControlPathConditions::EqualsAfterAddingCondition(
const ControlPathConditions* other, const Node* new_condition,
bool new_branch_direction) const {
// When an extra condition is added to the current chain, the count of
// the resulting chain would increase by 1. Quick check to see if counts
// match.
if (other->condition_count_ != condition_count_ + 1) return false;
// Check if the head of the other chain is same as the new condition that
// would be added.
if (other->head_->condition != new_condition ||
other->head_->is_true != new_branch_direction) {
return false;
}
// Check if the rest of the path is the same as the prev_condition.
return IsSamePath(other->head_->next, head_);
}
Graph* BranchElimination::graph() const { return jsgraph()->graph(); }
CommonOperatorBuilder* BranchElimination::common() const {

View File

@ -7,6 +7,7 @@
#include "src/base/compiler-specific.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/node-aux-data.h"
#include "src/globals.h"
namespace v8 {
@ -17,6 +18,110 @@ 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:
@ -31,55 +136,24 @@ class V8_EXPORT_PRIVATE BranchElimination final
struct BranchCondition {
Node* condition;
bool is_true;
BranchCondition* next;
BranchCondition(Node* condition, bool is_true, BranchCondition* next)
: condition(condition), is_true(is_true), next(next) {}
bool operator==(BranchCondition other) const {
return condition == other.condition && is_true == other.is_true;
}
bool operator!=(BranchCondition other) const { return !(*this == other); }
};
// Class for tracking information about branch conditions.
// At the moment it is a linked list of conditions and their values
// (true or false).
class ControlPathConditions {
class ControlPathConditions : public FunctionalList<BranchCondition> {
public:
Maybe<bool> LookupCondition(Node* condition) const;
const ControlPathConditions* AddCondition(Zone* zone, Node* condition,
bool is_true) const;
static const ControlPathConditions* Empty(Zone* zone);
void Merge(const ControlPathConditions& other);
bool IsSamePath(BranchCondition* first, BranchCondition* second) const;
bool EqualsAfterAddingCondition(const ControlPathConditions* other,
const Node* new_condition,
bool new_branch_condition) const;
bool operator==(const ControlPathConditions& other) const;
bool operator!=(const ControlPathConditions& other) const {
return !(*this == other);
}
void AddCondition(Zone* zone, Node* condition, bool is_true,
ControlPathConditions hint);
private:
ControlPathConditions(BranchCondition* head, size_t condition_count)
: head_(head), condition_count_(condition_count) {}
BranchCondition* head_;
// We keep track of the list length so that we can find the longest
// common tail easily.
size_t condition_count_;
};
// Maps each control node to the condition information known about the node.
// If the information is nullptr, then we have not calculated the information
// yet.
class PathConditionsForControlNodes {
public:
PathConditionsForControlNodes(Zone* zone, size_t size_hint)
: info_for_node_(size_hint, nullptr, zone) {}
const ControlPathConditions* Get(Node* node) const;
void Set(Node* node, const ControlPathConditions* conditions);
private:
ZoneVector<const ControlPathConditions*> info_for_node_;
using FunctionalList<BranchCondition>::PushFront;
};
Reduction ReduceBranch(Node* node);
@ -91,10 +165,8 @@ class V8_EXPORT_PRIVATE BranchElimination final
Reduction ReduceOtherControl(Node* node);
Reduction TakeConditionsFromFirstControl(Node* node);
Reduction UpdateConditions(Node* node,
const ControlPathConditions* conditions);
Reduction UpdateConditions(Node* node,
const ControlPathConditions* prev_conditions,
Reduction UpdateConditions(Node* node, ControlPathConditions conditions);
Reduction UpdateConditions(Node* node, ControlPathConditions prev_conditions,
Node* current_condition, bool is_true_branch);
Node* dead() const { return dead_; }
@ -103,7 +175,12 @@ class V8_EXPORT_PRIVATE BranchElimination final
CommonOperatorBuilder* common() const;
JSGraph* const jsgraph_;
PathConditionsForControlNodes node_conditions_;
// Maps each control node to the condition information known about the node.
// If the information is nullptr, then we have not calculated the information
// yet.
NodeAuxData<ControlPathConditions> node_conditions_;
NodeAuxData<bool> reduced_;
Zone* zone_;
Node* dead_;
};

View File

@ -15,15 +15,27 @@ namespace compiler {
// Forward declarations.
class Node;
template <class T, T def()>
template <class T>
T DefaultConstruct() {
return T();
}
template <class T, T def() = DefaultConstruct<T>>
class NodeAuxData {
public:
explicit NodeAuxData(Zone* zone) : aux_data_(zone) {}
explicit NodeAuxData(size_t initial_size, Zone* zone)
: aux_data_(initial_size, zone) {}
void Set(Node* node, T const& data) {
// Update entry. Returns true iff entry was changed.
bool Set(Node* node, T const& data) {
size_t const id = node->id();
if (id >= aux_data_.size()) aux_data_.resize(id + 1, def());
aux_data_[id] = data;
if (aux_data_[id] != data) {
aux_data_[id] = data;
return true;
}
return false;
}
T Get(Node* node) const {