| Index: src/compiler/value-numbering-reducer.cc
|
| diff --git a/src/compiler/value-numbering-reducer.cc b/src/compiler/value-numbering-reducer.cc
|
| index 595a4f30171904aa33fd3b6b3d19cf88816a335e..f4cf1d486266c2331fe95e39deb4dbc9130b4c74 100644
|
| --- a/src/compiler/value-numbering-reducer.cc
|
| +++ b/src/compiler/value-numbering-reducer.cc
|
| @@ -4,6 +4,9 @@
|
|
|
| #include "src/compiler/value-numbering-reducer.h"
|
|
|
| +#include <cstring>
|
| +
|
| +#include "src/base/functional.h"
|
| #include "src/compiler/node.h"
|
|
|
| namespace v8 {
|
| @@ -12,7 +15,13 @@ namespace compiler {
|
|
|
| namespace {
|
|
|
| -size_t HashCode(Node* node) { return node->op()->HashCode(); }
|
| +size_t HashCode(Node* node) {
|
| + size_t h = base::hash_combine(node->op()->HashCode(), node->InputCount());
|
| + for (int j = 0; j < node->InputCount(); ++j) {
|
| + h = base::hash_combine(h, node->InputAt(j)->id());
|
| + }
|
| + return h;
|
| +}
|
|
|
|
|
| bool Equals(Node* a, Node* b) {
|
| @@ -33,40 +42,96 @@ bool Equals(Node* a, Node* b) {
|
| } // namespace
|
|
|
|
|
| -class ValueNumberingReducer::Entry FINAL : public ZoneObject {
|
| - public:
|
| - Entry(Node* node, Entry* next) : node_(node), next_(next) {}
|
| +ValueNumberingReducer::ValueNumberingReducer(Zone* zone)
|
| + : entries_(nullptr), capacity_(0), size_(0), zone_(zone) {}
|
|
|
| - Node* node() const { return node_; }
|
| - Entry* next() const { return next_; }
|
|
|
| - private:
|
| - Node* node_;
|
| - Entry* next_;
|
| -};
|
| +ValueNumberingReducer::~ValueNumberingReducer() {}
|
|
|
|
|
| -ValueNumberingReducer::ValueNumberingReducer(Zone* zone) : zone_(zone) {
|
| - for (size_t i = 0; i < arraysize(buckets_); ++i) {
|
| - buckets_[i] = NULL;
|
| +Reduction ValueNumberingReducer::Reduce(Node* node) {
|
| + if (!node->op()->HasProperty(Operator::kEliminatable)) return NoChange();
|
| +
|
| + const size_t hash = HashCode(node);
|
| + if (!entries_) {
|
| + DCHECK(size_ == 0);
|
| + DCHECK(capacity_ == 0);
|
| + // Allocate the initial entries and insert the first entry.
|
| + capacity_ = kInitialCapacity;
|
| + entries_ = zone()->NewArray<Node*>(kInitialCapacity);
|
| + memset(entries_, 0, sizeof(*entries_) * kInitialCapacity);
|
| + entries_[hash & (kInitialCapacity - 1)] = node;
|
| + size_ = 1;
|
| + return NoChange();
|
| }
|
| -}
|
|
|
| + DCHECK(size_ < capacity_);
|
| + DCHECK(size_ * kCapacityToSizeRatio < capacity_);
|
| +
|
| + const size_t mask = capacity_ - 1;
|
| + size_t dead = capacity_;
|
| +
|
| + for (size_t i = hash & mask;; i = (i + 1) & mask) {
|
| + Node* entry = entries_[i];
|
| + if (!entry) {
|
| + if (dead != capacity_) {
|
| + // Reuse dead entry that we discovered on the way.
|
| + entries_[dead] = node;
|
| + } else {
|
| + // Have to insert a new entry.
|
| + entries_[i] = node;
|
| + size_++;
|
| +
|
| + // Resize to keep load factor below 1/kCapacityToSizeRatio.
|
| + if (size_ * kCapacityToSizeRatio >= capacity_) Grow();
|
| + }
|
| + DCHECK(size_ * kCapacityToSizeRatio < capacity_);
|
| + return NoChange();
|
| + }
|
| + if (entry == node) {
|
| + return NoChange();
|
| + }
|
|
|
| -ValueNumberingReducer::~ValueNumberingReducer() {}
|
| + // Skip dead entries, but remember their indices so we can reuse them.
|
| + if (entry->IsDead()) {
|
| + dead = i;
|
| + continue;
|
| + }
|
| + if (Equals(entry, node)) {
|
| + return Replace(entry);
|
| + }
|
| + }
|
| +}
|
|
|
|
|
| -Reduction ValueNumberingReducer::Reduce(Node* node) {
|
| - Entry** head = &buckets_[HashCode(node) % arraysize(buckets_)];
|
| - for (Entry* entry = *head; entry; entry = entry->next()) {
|
| - if (entry->node()->IsDead()) continue;
|
| - if (entry->node() == node) return NoChange();
|
| - if (Equals(node, entry->node())) {
|
| - return Replace(entry->node());
|
| +void ValueNumberingReducer::Grow() {
|
| + // Allocate a new block of entries kCapacityToSizeRatio times the previous
|
| + // capacity.
|
| + Node** const old_entries = entries_;
|
| + size_t const old_capacity = capacity_;
|
| + capacity_ *= kCapacityToSizeRatio;
|
| + entries_ = zone()->NewArray<Node*>(static_cast<int>(capacity_));
|
| + memset(entries_, 0, sizeof(*entries_) * capacity_);
|
| + size_ = 0;
|
| + size_t const mask = capacity_ - 1;
|
| +
|
| + // Insert the old entries into the new block (skipping dead nodes).
|
| + for (size_t i = 0; i < old_capacity; ++i) {
|
| + Node* const old_entry = old_entries[i];
|
| + if (!old_entry || old_entry->IsDead()) continue;
|
| + for (size_t j = HashCode(old_entry) & mask;; j = (j + 1) & mask) {
|
| + Node* const entry = entries_[j];
|
| + if (entry == old_entry) {
|
| + // Skip duplicate of the old entry.
|
| + break;
|
| + }
|
| + if (!entry) {
|
| + entries_[j] = old_entry;
|
| + size_++;
|
| + break;
|
| + }
|
| }
|
| }
|
| - *head = new (zone()) Entry(node, *head);
|
| - return NoChange();
|
| }
|
|
|
| } // namespace compiler
|
|
|