[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:
parent
007a73548b
commit
630a992b8d
@ -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 {
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user