v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
value-numbering-reducer.cc
Go to the documentation of this file.
1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <cstring>
8
10#include "src/compiler/node.h"
11
12namespace v8 {
13namespace internal {
14namespace compiler {
15
17 : entries_(nullptr),
18 capacity_(0),
19 size_(0),
20 temp_zone_(temp_zone),
21 graph_zone_(graph_zone) {}
22
24
25
27 if (!node->op()->HasProperty(Operator::kIdempotent)) return NoChange();
28
29 const size_t hash = NodeProperties::HashCode(node);
30 if (!entries_) {
31 DCHECK_EQ(0, size_);
32 DCHECK_EQ(0, capacity_);
33 // Allocate the initial entries and insert the first entry.
34 capacity_ = kInitialCapacity;
35 entries_ = temp_zone()->AllocateArray<Node*>(kInitialCapacity);
36 memset(entries_, 0, sizeof(*entries_) * kInitialCapacity);
37 entries_[hash & (kInitialCapacity - 1)] = node;
38 size_ = 1;
39 return NoChange();
40 }
41
42 DCHECK(size_ < capacity_);
43 DCHECK(size_ + size_ / 4 < capacity_);
44
45 const size_t mask = capacity_ - 1;
46 size_t dead = capacity_;
47
48 for (size_t i = hash & mask;; i = (i + 1) & mask) {
49 Node* entry = entries_[i];
50 if (!entry) {
51 if (dead != capacity_) {
52 // Reuse dead entry that we discovered on the way.
53 entries_[dead] = node;
54 } else {
55 // Have to insert a new entry.
56 entries_[i] = node;
57 size_++;
58
59 // Resize to keep load factor below 80%
60 if (size_ + size_ / 4 >= capacity_) Grow();
61 }
62 DCHECK(size_ + size_ / 4 < capacity_);
63 return NoChange();
64 }
65
66 if (entry == node) {
67 // We need to check for a certain class of collisions here. Imagine the
68 // following scenario:
69 //
70 // 1. We insert node1 with op1 and certain inputs at index i.
71 // 2. We insert node2 with op2 and certain inputs at index i+1.
72 // 3. Some other reducer changes node1 to op2 and the inputs from node2.
73 //
74 // Now we are called again to reduce node1, and we would return NoChange
75 // in this case because we find node1 first, but what we should actually
76 // do is return Replace(node2) instead.
77 for (size_t j = (i + 1) & mask;; j = (j + 1) & mask) {
78 Node* other_entry = entries_[j];
79 if (!other_entry) {
80 // No collision, {node} is fine.
81 return NoChange();
82 }
83 if (other_entry->IsDead()) {
84 continue;
85 }
86 if (other_entry == node) {
87 // Collision with ourselves, doesn't count as a real collision.
88 // Opportunistically clean-up the duplicate entry if we're at the end
89 // of a bucket.
90 if (!entries_[(j + 1) & mask]) {
91 entries_[j] = nullptr;
92 size_--;
93 return NoChange();
94 }
95 // Otherwise, keep searching for another collision.
96 continue;
97 }
98 if (NodeProperties::Equals(other_entry, node)) {
99 Reduction reduction = ReplaceIfTypesMatch(node, other_entry);
100 if (reduction.Changed()) {
101 // Overwrite the colliding entry with the actual entry.
102 entries_[i] = other_entry;
103 // Opportunistically clean-up the duplicate entry if we're at the
104 // end of a bucket.
105 if (!entries_[(j + 1) & mask]) {
106 entries_[j] = nullptr;
107 size_--;
108 }
109 }
110 return reduction;
111 }
112 }
113 }
114
115 // Skip dead entries, but remember their indices so we can reuse them.
116 if (entry->IsDead()) {
117 dead = i;
118 continue;
119 }
120 if (NodeProperties::Equals(entry, node)) {
121 return ReplaceIfTypesMatch(node, entry);
122 }
123 }
124}
125
127 Node* replacement) {
128 // Make sure the replacement has at least as good type as the original node.
129 if (NodeProperties::IsTyped(replacement) && NodeProperties::IsTyped(node)) {
130 Type replacement_type = NodeProperties::GetType(replacement);
131 Type node_type = NodeProperties::GetType(node);
132 if (!replacement_type.Is(node_type)) {
133 // Ideally, we would set an intersection of {replacement_type} and
134 // {node_type} here. However, typing of NumberConstants assigns different
135 // types to constants with the same value (it creates a fresh heap
136 // number), which would make the intersection empty. To be safe, we use
137 // the smaller type if the types are comparable.
138 if (node_type.Is(replacement_type)) {
139 NodeProperties::SetType(replacement, node_type);
140 } else {
141 // Types are not comparable => do not replace.
142 return NoChange();
143 }
144 }
145 }
146 return Replace(replacement);
147}
148
149
151 // Allocate a new block of entries double the previous capacity.
152 Node** const old_entries = entries_;
153 size_t const old_capacity = capacity_;
154 capacity_ *= 2;
155 entries_ = temp_zone()->AllocateArray<Node*>(capacity_);
156 memset(entries_, 0, sizeof(*entries_) * capacity_);
157 size_ = 0;
158 size_t const mask = capacity_ - 1;
159
160 // Insert the old entries into the new block (skipping dead nodes).
161 for (size_t i = 0; i < old_capacity; ++i) {
162 Node* const old_entry = old_entries[i];
163 if (!old_entry || old_entry->IsDead()) continue;
164 for (size_t j = NodeProperties::HashCode(old_entry) & mask;;
165 j = (j + 1) & mask) {
166 Node* const entry = entries_[j];
167 if (entry == old_entry) {
168 // Skip duplicate of the old entry.
169 break;
170 }
171 if (!entry) {
172 entries_[j] = old_entry;
173 size_++;
174 break;
175 }
176 }
177 }
178}
179
180} // namespace compiler
181} // namespace internal
182} // namespace v8
static Type GetType(const Node *node)
static bool IsTyped(const Node *node)
static void SetType(Node *node, Type type)
static bool Equals(Node *a, Node *b)
bool Is(Type that) const
Reduction ReplaceIfTypesMatch(Node *node, Node *replacement)
ValueNumberingReducer(Zone *temp_zone, Zone *graph_zone)
const int size_
Definition assembler.cc:132
Zone * graph_zone
Node * node
std::vector< EntryBuilder > entries_
uint32_t const mask
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485