Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(31)

Side by Side Diff: src/compiler/state-values-utils.cc

Issue 2509623002: [turbofan] Sparse representation for state values (Closed)
Patch Set: Large reformatting, addressing Jaro's comments Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 the V8 project authors. All rights reserved. 1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/compiler/state-values-utils.h" 5 #include "src/compiler/state-values-utils.h"
6 6
7 #include "src/bit-vector.h"
8
7 namespace v8 { 9 namespace v8 {
8 namespace internal { 10 namespace internal {
9 namespace compiler { 11 namespace compiler {
10 12
11 StateValuesCache::StateValuesCache(JSGraph* js_graph) 13 StateValuesCache::StateValuesCache(JSGraph* js_graph)
12 : js_graph_(js_graph), 14 : js_graph_(js_graph),
13 hash_map_(AreKeysEqual, ZoneHashMap::kDefaultHashMapCapacity, 15 hash_map_(AreKeysEqual, ZoneHashMap::kDefaultHashMapCapacity,
14 ZoneAllocationPolicy(zone())), 16 ZoneAllocationPolicy(zone())),
15 working_space_(zone()), 17 working_space_(zone()),
16 empty_state_values_(nullptr) {} 18 empty_state_values_(nullptr) {}
(...skipping 23 matching lines...) Expand all
40 } 42 }
41 UNREACHABLE(); 43 UNREACHABLE();
42 } 44 }
43 45
44 46
45 // static 47 // static
46 bool StateValuesCache::IsKeysEqualToNode(StateValuesKey* key, Node* node) { 48 bool StateValuesCache::IsKeysEqualToNode(StateValuesKey* key, Node* node) {
47 if (key->count != static_cast<size_t>(node->InputCount())) { 49 if (key->count != static_cast<size_t>(node->InputCount())) {
48 return false; 50 return false;
49 } 51 }
52
53 DCHECK(node->opcode() == IrOpcode::kStateValues);
54 SparseInputMask node_mask = SparseInputMaskOf(node->op());
55
56 if (node_mask != key->mask) {
57 return false;
58 }
59
60 // Comparing real inputs rather than sparse inputs, since we already know the
61 // sparse input masks are the same.
50 for (size_t i = 0; i < key->count; i++) { 62 for (size_t i = 0; i < key->count; i++) {
51 if (key->values[i] != node->InputAt(static_cast<int>(i))) { 63 if (key->values[i] != node->InputAt(static_cast<int>(i))) {
52 return false; 64 return false;
53 } 65 }
54 } 66 }
55 return true; 67 return true;
56 } 68 }
57 69
58 70
59 // static 71 // static
60 bool StateValuesCache::AreValueKeysEqual(StateValuesKey* key1, 72 bool StateValuesCache::AreValueKeysEqual(StateValuesKey* key1,
61 StateValuesKey* key2) { 73 StateValuesKey* key2) {
62 if (key1->count != key2->count) { 74 if (key1->count != key2->count) {
63 return false; 75 return false;
64 } 76 }
77 if (key1->mask != key2->mask) {
78 return false;
79 }
65 for (size_t i = 0; i < key1->count; i++) { 80 for (size_t i = 0; i < key1->count; i++) {
66 if (key1->values[i] != key2->values[i]) { 81 if (key1->values[i] != key2->values[i]) {
67 return false; 82 return false;
68 } 83 }
69 } 84 }
70 return true; 85 return true;
71 } 86 }
72 87
73 88
74 Node* StateValuesCache::GetEmptyStateValues() { 89 Node* StateValuesCache::GetEmptyStateValues() {
75 if (empty_state_values_ == nullptr) { 90 if (empty_state_values_ == nullptr) {
76 empty_state_values_ = graph()->NewNode(common()->StateValues(0)); 91 empty_state_values_ =
92 graph()->NewNode(common()->StateValues(0, SparseInputMask::Dense()));
77 } 93 }
78 return empty_state_values_; 94 return empty_state_values_;
79 } 95 }
80 96
81 97 StateValuesCache::WorkingBuffer& StateValuesCache::GetWorkingSpace(
82 NodeVector* StateValuesCache::GetWorkingSpace(size_t level) { 98 size_t level) {
83 while (working_space_.size() <= level) { 99 if (working_space_.size() <= level) {
84 void* space = zone()->New(sizeof(NodeVector)); 100 working_space_.resize(level + 1);
85 working_space_.push_back(new (space)
86 NodeVector(kMaxInputCount, nullptr, zone()));
87 } 101 }
88 return working_space_[level]; 102 return working_space_[level];
89 } 103 }
90 104
91 namespace { 105 namespace {
92 106
93 int StateValuesHashKey(Node** nodes, size_t count) { 107 int StateValuesHashKey(Node** nodes, size_t count) {
94 size_t hash = count; 108 size_t hash = count;
95 for (size_t i = 0; i < count; i++) { 109 for (size_t i = 0; i < count; i++) {
96 hash = hash * 23 + nodes[i]->id(); 110 hash = hash * 23 + (nodes[i] == nullptr ? 0 : nodes[i]->id());
97 } 111 }
98 return static_cast<int>(hash & 0x7fffffff); 112 return static_cast<int>(hash & 0x7fffffff);
99 } 113 }
100 114
101 } // namespace 115 } // namespace
102 116
103 117 Node* StateValuesCache::GetValuesNodeFromCache(Node** nodes, size_t count,
104 Node* StateValuesCache::GetValuesNodeFromCache(Node** nodes, size_t count) { 118 SparseInputMask mask) {
105 StateValuesKey key(count, nodes); 119 StateValuesKey key(count, mask, nodes);
106 int hash = StateValuesHashKey(nodes, count); 120 int hash = StateValuesHashKey(nodes, count);
107 ZoneHashMap::Entry* lookup = 121 ZoneHashMap::Entry* lookup =
108 hash_map_.LookupOrInsert(&key, hash, ZoneAllocationPolicy(zone())); 122 hash_map_.LookupOrInsert(&key, hash, ZoneAllocationPolicy(zone()));
109 DCHECK_NOT_NULL(lookup); 123 DCHECK_NOT_NULL(lookup);
110 Node* node; 124 Node* node;
111 if (lookup->value == nullptr) { 125 if (lookup->value == nullptr) {
112 int input_count = static_cast<int>(count); 126 int input_count = static_cast<int>(count);
113 node = graph()->NewNode(common()->StateValues(input_count), input_count, 127 node = graph()->NewNode(common()->StateValues(input_count, mask),
114 nodes); 128 input_count, nodes);
115 NodeKey* new_key = new (zone()->New(sizeof(NodeKey))) NodeKey(node); 129 NodeKey* new_key = new (zone()->New(sizeof(NodeKey))) NodeKey(node);
116 lookup->key = new_key; 130 lookup->key = new_key;
117 lookup->value = node; 131 lookup->value = node;
118 } else { 132 } else {
119 node = reinterpret_cast<Node*>(lookup->value); 133 node = reinterpret_cast<Node*>(lookup->value);
120 } 134 }
121 return node; 135 return node;
122 } 136 }
123 137
138 SparseInputMask::BitMaskType StateValuesCache::FillBufferWithValues(
139 WorkingBuffer& input_buffer, size_t& input_count, size_t& idx,
Jarin 2016/12/10 10:15:06 The arguments to this function are really confusin
Leszek Swirski 2016/12/13 14:35:16 Yeah, this is tough because of the difference betw
140 Node** values, size_t count, const BitVector* liveness) {
141 SparseInputMask::BitMaskType input_mask = 0;
124 142
125 class StateValuesCache::ValueArrayIterator { 143 // Virtual inputs are the live inputs plus the implicit optimized out inputs,
126 public: 144 // which are implied by the liveness mask.
127 ValueArrayIterator(Node** values, size_t count) 145 size_t virtual_input_count = input_count;
128 : values_(values), count_(count), current_(0) {}
129 146
130 void Advance() { 147 while (idx < count && input_count < kMaxInputCount &&
131 if (!done()) { 148 virtual_input_count < SparseInputMask::kMaxSparseInputs) {
132 current_++; 149 DCHECK_LE(idx, static_cast<size_t>(INT_MAX));
150
151 if (liveness == nullptr || liveness->Contains(static_cast<int>(idx))) {
152 input_mask |= 1 << (virtual_input_count);
153 input_buffer[input_count++] = values[idx];
154 }
155 virtual_input_count++;
156
157 idx++;
158 }
159
160 DCHECK(input_count <= StateValuesCache::kMaxInputCount);
161 DCHECK(virtual_input_count <= SparseInputMask::kMaxSparseInputs);
162
163 // Add the end marker at the end of the mask.
164 input_mask |= SparseInputMask::kEndMarker << virtual_input_count;
165
166 return input_mask;
167 }
168
169 Node* StateValuesCache::BuildTree(size_t& idx, Node** values, size_t count,
170 const BitVector* liveness, size_t level) {
171 WorkingBuffer& input_buffer = GetWorkingSpace(level);
172 size_t input_count = 0;
173 SparseInputMask::BitMaskType input_mask = SparseInputMask::kDenseBitMask;
174
175 if (level == 0) {
176 input_mask = FillBufferWithValues(input_buffer, input_count, idx, values,
177 count, liveness);
178 // Make sure we returned a sparse input mask.
179 DCHECK_NE(input_mask, SparseInputMask::kDenseBitMask);
180 } else {
181 while (idx < count && input_count < kMaxInputCount) {
182 if (count - idx < kMaxInputCount - input_count) {
183 // If we have fewer values remaining than inputs remaining, dump the
184 // remaining values into this node.
185 // TODO(leszeks): We could optimise this further by only counting
186 // remaining live nodes.
187
188 size_t previous_input_count = input_count;
189 input_mask = FillBufferWithValues(input_buffer, input_count, idx,
190 values, count, liveness);
191 // Make sure we have exhausted our values.
192 DCHECK_EQ(idx, count);
193 // Make sure we returned a sparse input mask.
194 DCHECK_NE(input_mask, SparseInputMask::kDenseBitMask);
195
196 // Make sure we haven't touched inputs below previous_input_count in the
197 // mask.
198 DCHECK_EQ(input_mask & ((1 << previous_input_count) - 1), 0u);
199 // Mark all previous inputs as live.
200 input_mask |= ((1 << previous_input_count) - 1);
201
202 break;
203
204 } else {
205 // Otherwise, add the values to a subtree and add that as an input.
206 Node* subtree = BuildTree(idx, values, count, liveness, level - 1);
207 input_buffer[input_count++] = subtree;
208 // Don't touch the bitmask, so that it stays dense.
209 }
133 } 210 }
134 } 211 }
135 212
136 bool done() { return current_ >= count_; } 213 if (input_count == 1 && input_mask == SparseInputMask::kDenseBitMask) {
137 214 // Elide the StateValue node if there is only one, dense input. This will
138 Node* node() { 215 // only happen if we built a single subtree (as nodes with values are always
139 DCHECK(!done()); 216 // sparse), and so we can replace ourselves with it.
140 return values_[current_]; 217 DCHECK_EQ(input_buffer[0]->opcode(), IrOpcode::kStateValues);
141 } 218 return input_buffer[0];
142
143 private:
144 Node** values_;
145 size_t count_;
146 size_t current_;
147 };
148
149
150 Node* StateValuesCache::BuildTree(ValueArrayIterator* it, size_t max_height) {
151 if (max_height == 0) {
152 Node* node = it->node();
153 it->Advance();
154 return node;
155 }
156 DCHECK(!it->done());
157
158 NodeVector* buffer = GetWorkingSpace(max_height);
159 size_t count = 0;
160 for (; count < kMaxInputCount; count++) {
161 if (it->done()) break;
162 (*buffer)[count] = BuildTree(it, max_height - 1);
163 }
164 if (count == 1) {
165 return (*buffer)[0];
166 } else { 219 } else {
167 return GetValuesNodeFromCache(&(buffer->front()), count); 220 return GetValuesNodeFromCache(input_buffer.data(), input_count,
221 SparseInputMask(input_mask));
168 } 222 }
169 } 223 }
170 224
225 #if DEBUG
226 namespace {
171 227
172 Node* StateValuesCache::GetNodeForValues(Node** values, size_t count) { 228 void CheckTreeContainsValues(Node* tree, Node** values, size_t count,
229 const BitVector* liveness) {
230 CHECK_EQ(count, StateValuesAccess(tree).size());
231
232 int i;
233 auto access = StateValuesAccess(tree);
234 auto it = access.begin();
235 auto itend = access.end();
236 for (i = 0; it != itend; ++it, ++i) {
237 if (liveness == nullptr || liveness->Contains(i)) {
238 CHECK((*it).node == values[i]);
239 } else {
240 CHECK((*it).node == nullptr);
241 }
242 }
243 CHECK_EQ(static_cast<size_t>(i), count);
244 }
245
246 } // namespace
247 #endif
248
249 Node* StateValuesCache::GetNodeForValues(Node** values, size_t count,
250 const BitVector* liveness) {
173 #if DEBUG 251 #if DEBUG
252 // Check that the values represent actual values, and not a tree of values.
174 for (size_t i = 0; i < count; i++) { 253 for (size_t i = 0; i < count; i++) {
175 DCHECK_NE(values[i]->opcode(), IrOpcode::kStateValues); 254 if (values[i] != nullptr) {
176 DCHECK_NE(values[i]->opcode(), IrOpcode::kTypedStateValues); 255 DCHECK_NE(values[i]->opcode(), IrOpcode::kStateValues);
256 DCHECK_NE(values[i]->opcode(), IrOpcode::kTypedStateValues);
257 }
258 }
259 if (liveness != nullptr) {
260 // Liveness can have extra bits for the stack or accumulator, which we
261 // ignore here.
262 DCHECK_LE(count, static_cast<size_t>(liveness->length()));
263
264 for (size_t i = 0; i < count; i++) {
265 if (liveness->Contains(static_cast<int>(i))) {
266 DCHECK_NOT_NULL(values[i]);
267 }
268 }
177 } 269 }
178 #endif 270 #endif
271
179 if (count == 0) { 272 if (count == 0) {
180 return GetEmptyStateValues(); 273 return GetEmptyStateValues();
181 } 274 }
275
276 // This is a worst-case tree height estimate, assuming that all values are
277 // live. We could get a better estimate by counting zeroes in the liveness
278 // vector, but there's no point -- any excess height in the tree will be
279 // collapsed by the single-input elision at the end of BuildTree.
182 size_t height = 0; 280 size_t height = 0;
183 size_t max_nodes = 1; 281 size_t max_inputs = kMaxInputCount;
184 while (count > max_nodes) { 282 while (count > max_inputs) {
185 height++; 283 height++;
186 max_nodes *= kMaxInputCount; 284 max_inputs *= kMaxInputCount;
187 } 285 }
188 286
189 ValueArrayIterator it(values, count); 287 size_t idx = 0;
288 Node* tree = BuildTree(idx, values, count, liveness, height);
190 289
191 Node* tree = BuildTree(&it, height); 290 // The 'tree' must be rooted with a state value node.
291 DCHECK_EQ(tree->opcode(), IrOpcode::kStateValues);
192 292
193 // If the 'tree' is a single node, equip it with a StateValues wrapper. 293 #if DEBUG
194 if (tree->opcode() != IrOpcode::kStateValues && 294 CheckTreeContainsValues(tree, values, count, liveness);
195 tree->opcode() != IrOpcode::kTypedStateValues) { 295 #endif
196 tree = GetValuesNodeFromCache(&tree, 1);
197 }
198 296
199 return tree; 297 return tree;
200 } 298 }
201 299
202
203 StateValuesAccess::iterator::iterator(Node* node) : current_depth_(0) { 300 StateValuesAccess::iterator::iterator(Node* node) : current_depth_(0) {
204 // A hacky way initialize - just set the index before the node we want 301 stack_[current_depth_] =
205 // to process and then advance to it. 302 SparseInputMaskOf(node->op()).IterateOverInputs(node);
206 stack_[current_depth_].node = node; 303 EnsureValid();
207 stack_[current_depth_].index = -1;
208 Advance();
209 } 304 }
210 305
211 306 SparseInputMask::InputIterator* StateValuesAccess::iterator::Top() {
212 StateValuesAccess::iterator::StatePos* StateValuesAccess::iterator::Top() {
213 DCHECK(current_depth_ >= 0); 307 DCHECK(current_depth_ >= 0);
214 DCHECK(current_depth_ < kMaxInlineDepth); 308 DCHECK(current_depth_ < kMaxInlineDepth);
215 return &(stack_[current_depth_]); 309 return &(stack_[current_depth_]);
216 } 310 }
217 311
218
219 void StateValuesAccess::iterator::Push(Node* node) { 312 void StateValuesAccess::iterator::Push(Node* node) {
220 current_depth_++; 313 current_depth_++;
221 CHECK(current_depth_ < kMaxInlineDepth); 314 CHECK(current_depth_ < kMaxInlineDepth);
222 stack_[current_depth_].node = node; 315 stack_[current_depth_] =
223 stack_[current_depth_].index = 0; 316 SparseInputMaskOf(node->op()).IterateOverInputs(node);
224 } 317 }
225 318
226 319
227 void StateValuesAccess::iterator::Pop() { 320 void StateValuesAccess::iterator::Pop() {
228 DCHECK(current_depth_ >= 0); 321 DCHECK(current_depth_ >= 0);
229 current_depth_--; 322 current_depth_--;
230 } 323 }
231 324
232 325
233 bool StateValuesAccess::iterator::done() { return current_depth_ < 0; } 326 bool StateValuesAccess::iterator::done() { return current_depth_ < 0; }
234 327
235 328
236 void StateValuesAccess::iterator::Advance() { 329 void StateValuesAccess::iterator::Advance() {
237 // Advance the current index. 330 Top()->Advance();
238 Top()->index++; 331 EnsureValid();
332 }
239 333
240 // Fix up the position to point to a valid node. 334 void StateValuesAccess::iterator::EnsureValid() {
241 while (true) { 335 while (true) {
242 // TODO(jarin): Factor to a separate method. 336 SparseInputMask::InputIterator* top = Top();
243 Node* node = Top()->node;
244 int index = Top()->index;
245 337
246 if (index >= node->InputCount()) { 338 if (top->IsEmpty()) {
247 // Pop stack and move to the next sibling. 339 // We are on a valid (albeit optimized out) node.
340 return;
341 }
342
343 if (top->IsEnd()) {
344 // We have hit the end of this iterator. Pop the stack and move to the
345 // next sibling iterator.
248 Pop(); 346 Pop();
249 if (done()) { 347 if (done()) {
250 // Stack is exhausted, we have reached the end. 348 // Stack is exhausted, we have reached the end.
251 return; 349 return;
252 } 350 }
253 Top()->index++; 351 Top()->Advance();
254 } else if (node->InputAt(index)->opcode() == IrOpcode::kStateValues || 352 continue;
255 node->InputAt(index)->opcode() == IrOpcode::kTypedStateValues) { 353 }
354
355 // At this point the value is known to be live and within our input nodes.
356 Node* value_node = top->GetReal();
357
358 if (value_node->opcode() == IrOpcode::kStateValues ||
359 value_node->opcode() == IrOpcode::kTypedStateValues) {
256 // Nested state, we need to push to the stack. 360 // Nested state, we need to push to the stack.
257 Push(node->InputAt(index)); 361 Push(value_node);
362 continue;
363 }
364
365 // We are on a valid node, we can stop the iteration.
366 return;
367 }
368 }
369
370 Node* StateValuesAccess::iterator::node() { return Top()->Get(nullptr); }
371
372 MachineType StateValuesAccess::iterator::type() {
373 Node* parent = Top()->parent();
374 if (parent->opcode() == IrOpcode::kStateValues) {
375 return MachineType::AnyTagged();
376 } else {
377 DCHECK_EQ(IrOpcode::kTypedStateValues, parent->opcode());
378
379 if (Top()->IsEmpty()) {
380 return MachineType::None();
258 } else { 381 } else {
259 // We are on a valid node, we can stop the iteration. 382 ZoneVector<MachineType> const* types = MachineTypesOf(parent->op());
260 return; 383 return (*types)[Top()->real_index()];
261 } 384 }
262 } 385 }
263 } 386 }
264 387
265
266 Node* StateValuesAccess::iterator::node() {
267 return Top()->node->InputAt(Top()->index);
268 }
269
270
271 MachineType StateValuesAccess::iterator::type() {
272 Node* state = Top()->node;
273 if (state->opcode() == IrOpcode::kStateValues) {
274 return MachineType::AnyTagged();
275 } else {
276 DCHECK_EQ(IrOpcode::kTypedStateValues, state->opcode());
277 ZoneVector<MachineType> const* types = MachineTypesOf(state->op());
278 return (*types)[Top()->index];
279 }
280 }
281
282 388
283 bool StateValuesAccess::iterator::operator!=(iterator& other) { 389 bool StateValuesAccess::iterator::operator!=(iterator& other) {
284 // We only allow comparison with end(). 390 // We only allow comparison with end().
285 CHECK(other.done()); 391 CHECK(other.done());
286 return !done(); 392 return !done();
287 } 393 }
288 394
289 395
290 StateValuesAccess::iterator& StateValuesAccess::iterator::operator++() { 396 StateValuesAccess::iterator& StateValuesAccess::iterator::operator++() {
291 Advance(); 397 Advance();
292 return *this; 398 return *this;
293 } 399 }
294 400
295 401
296 StateValuesAccess::TypedNode StateValuesAccess::iterator::operator*() { 402 StateValuesAccess::TypedNode StateValuesAccess::iterator::operator*() {
297 return TypedNode(node(), type()); 403 return TypedNode(node(), type());
298 } 404 }
299 405
300 406
301 size_t StateValuesAccess::size() { 407 size_t StateValuesAccess::size() {
302 size_t count = 0; 408 size_t count = 0;
303 for (int i = 0; i < node_->InputCount(); i++) { 409 SparseInputMask mask = SparseInputMaskOf(node_->op());
304 if (node_->InputAt(i)->opcode() == IrOpcode::kStateValues || 410
305 node_->InputAt(i)->opcode() == IrOpcode::kTypedStateValues) { 411 SparseInputMask::InputIterator iterator = mask.IterateOverInputs(node_);
306 count += StateValuesAccess(node_->InputAt(i)).size(); 412
413 for (; !iterator.IsEnd(); iterator.Advance()) {
414 if (iterator.IsEmpty()) {
415 count++;
307 } else { 416 } else {
308 count++; 417 Node* value = iterator.GetReal();
418 if (value->opcode() == IrOpcode::kStateValues ||
419 value->opcode() == IrOpcode::kTypedStateValues) {
420 count += StateValuesAccess(value).size();
421 } else {
422 count++;
423 }
309 } 424 }
310 } 425 }
426
311 return count; 427 return count;
312 } 428 }
313 429
314 } // namespace compiler 430 } // namespace compiler
315 } // namespace internal 431 } // namespace internal
316 } // namespace v8 432 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698