OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "src/compiler/escape-analysis.h" | |
6 | |
7 #include "src/base/flags.h" | |
8 #include "src/bootstrapper.h" | |
9 #include "src/compilation-dependencies.h" | |
10 #include "src/compiler/common-operator.h" | |
11 #include "src/compiler/graph-reducer.h" | |
12 #include "src/compiler/js-operator.h" | |
13 #include "src/compiler/node.h" | |
14 #include "src/compiler/node-matchers.h" | |
15 #include "src/compiler/node-properties.h" | |
16 #include "src/compiler/simplified-operator.h" | |
17 #include "src/objects-inl.h" | |
18 #include "src/type-cache.h" | |
19 | |
20 namespace v8 { | |
21 namespace internal { | |
22 namespace compiler { | |
23 | |
24 bool VirtualObject::UpdateFrom(const VirtualObject& other) { | |
25 bool changed = status_ != other.status_; | |
26 status_ = other.status_; | |
27 changed = replacement_ != other.replacement_ || changed; | |
28 replacement_ = other.replacement_; | |
29 if (fields_.size() != other.fields_.size()) { | |
30 fields_ = other.fields_; | |
31 return true; | |
32 } | |
33 for (size_t i = 0; i < fields_.size(); ++i) { | |
34 if (fields_[i] != other.fields_[i]) { | |
35 changed = true; | |
36 fields_[i] = other.fields_[i]; | |
37 } | |
38 } | |
39 return changed; | |
40 } | |
41 | |
42 | |
43 VirtualState::VirtualState(Zone* zone, size_t size) | |
44 : info_(zone), last_changed_(nullptr) { | |
45 info_.resize(size); | |
46 } | |
47 | |
48 | |
49 VirtualState::VirtualState(const VirtualState& state) | |
50 : info_(state.info_.get_allocator().zone()), | |
51 last_changed_(state.last_changed_) { | |
52 info_.resize(state.info_.size()); | |
53 for (size_t i = 0; i < state.info_.size(); ++i) { | |
54 if (state.info_[i] && state.info_[i]->id() == i) { | |
55 info_[i] = new (state.info_.get_allocator().zone()) | |
56 VirtualObject(*state.info_[i]); | |
57 } | |
58 } | |
59 for (size_t i = 0; i < state.info_.size(); ++i) { | |
60 if (state.info_[i] && state.info_[i]->id() != i) { | |
61 info_[i] = info_[state.info_[i]->id()]; | |
62 } | |
63 } | |
64 } | |
65 | |
66 | |
67 VirtualObject* VirtualState::GetVirtualObject(size_t id) { return info_[id]; } | |
68 | |
69 | |
70 VirtualObject* VirtualState::GetVirtualObject(Node* node) { | |
71 return GetVirtualObject(node->id()); | |
72 } | |
73 | |
74 | |
75 VirtualObject* VirtualState::GetOrCreateTrackedVirtualObject(NodeId id, | |
76 Zone* zone) { | |
77 if (VirtualObject* obj = GetVirtualObject(id)) { | |
78 return obj; | |
79 } | |
80 VirtualObject* obj = new (zone) VirtualObject(id, zone, 0); | |
81 SetVirtualObject(id, obj); | |
82 return obj; | |
83 } | |
84 | |
85 | |
86 void VirtualState::SetVirtualObject(NodeId id, VirtualObject* obj) { | |
87 info_[id] = obj; | |
88 } | |
89 | |
90 | |
91 bool VirtualState::UpdateFrom(NodeId id, VirtualObject* fromObj, Zone* zone) { | |
92 VirtualObject* obj = GetVirtualObject(id); | |
93 if (!obj) { | |
94 obj = new (zone) VirtualObject(*fromObj); | |
95 SetVirtualObject(id, obj); | |
96 if (FLAG_trace_turbo_escape) { | |
97 PrintF(" Taking field for #%d from %p\n", id, | |
98 static_cast<void*>(fromObj)); | |
99 } | |
100 return true; | |
101 } | |
102 | |
103 if (obj->UpdateFrom(*fromObj)) { | |
104 if (FLAG_trace_turbo_escape) { | |
105 PrintF(" Updating field for #%d from %p\n", id, | |
106 static_cast<void*>(fromObj)); | |
107 } | |
108 return true; | |
109 } | |
110 | |
111 return false; | |
112 } | |
113 | |
114 | |
115 bool VirtualState::UpdateFrom(VirtualState* from, Zone* zone) { | |
116 DCHECK_EQ(size(), from->size()); | |
117 bool changed = false; | |
118 for (NodeId id = 0; id < size(); ++id) { | |
119 VirtualObject* ls = GetVirtualObject(id); | |
120 VirtualObject* rs = from->GetVirtualObject(id); | |
121 | |
122 if (rs == nullptr) { | |
123 continue; | |
124 } | |
125 | |
126 if (ls == nullptr) { | |
127 ls = new (zone) VirtualObject(*rs); | |
128 SetVirtualObject(id, ls); | |
129 changed = true; | |
130 continue; | |
131 } | |
132 | |
133 if (FLAG_trace_turbo_escape) { | |
134 PrintF(" Updating fields of #%d\n", id); | |
135 } | |
136 | |
137 changed = ls->UpdateFrom(*rs) || changed; | |
138 } | |
139 return false; | |
140 } | |
141 | |
142 | |
143 bool VirtualState::MergeFrom(VirtualState* left, VirtualState* right, | |
144 Zone* zone, Graph* graph, | |
145 CommonOperatorBuilder* common, Node* control) { | |
146 bool changed = false; | |
147 for (NodeId id = 0; id < std::min(left->size(), right->size()); ++id) { | |
148 VirtualObject* ls = left->GetVirtualObject(id); | |
149 VirtualObject* rs = right->GetVirtualObject(id); | |
150 | |
151 if (ls != nullptr && rs != nullptr) { | |
152 if (FLAG_trace_turbo_escape) { | |
153 PrintF(" Merging fields of #%d\n", id); | |
154 } | |
155 VirtualObject* mergeObject = GetOrCreateTrackedVirtualObject(id, zone); | |
156 size_t fields = std::max(ls->fields(), rs->fields()); | |
157 changed = mergeObject->ResizeFields(fields) || changed; | |
158 for (size_t i = 0; i < fields; ++i) { | |
159 if (ls->GetField(i) == rs->GetField(i)) { | |
160 changed = mergeObject->SetField(i, ls->GetField(i)) || changed; | |
161 if (FLAG_trace_turbo_escape && ls->GetField(i)) { | |
162 PrintF(" Field %zu agree on rep #%d\n", i, | |
163 ls->GetField(i)->id()); | |
164 } | |
165 } else if (ls->GetField(i) != nullptr && rs->GetField(i) != nullptr) { | |
166 Node* phi = graph->NewNode(common->Phi(kMachAnyTagged, 2), | |
167 ls->GetField(i), rs->GetField(i), control); | |
168 if (mergeObject->SetField(i, phi)) { | |
169 if (FLAG_trace_turbo_escape) { | |
170 PrintF(" Creating Phi #%d as merge of #%d and #%d\n", | |
171 phi->id(), ls->GetField(i)->id(), rs->GetField(i)->id()); | |
172 } | |
173 changed = true; | |
174 } | |
175 } else { | |
176 changed = mergeObject->SetField(i, nullptr) || changed; | |
177 } | |
178 } | |
179 } | |
180 } | |
181 return changed; | |
182 } | |
183 | |
184 | |
185 Node* VirtualState::ResolveReplacement(Node* node) { | |
186 if (VirtualObject* obj = GetVirtualObject(node)) { | |
187 if (Node* rep = obj->GetReplacement()) { | |
188 return rep; | |
189 } | |
190 } | |
191 return node; | |
192 } | |
193 | |
194 | |
195 bool VirtualState::UpdateReplacement(Node* node, Node* rep, Zone* zone) { | |
196 if (!GetVirtualObject(node)) { | |
197 SetVirtualObject(node->id(), new (zone) VirtualObject(node->id(), zone)); | |
198 } | |
199 if (GetVirtualObject(node)->SetReplacement(rep)) { | |
200 LastChangedAt(node); | |
201 if (FLAG_trace_turbo_escape) { | |
202 PrintF("Representation of #%d is #%d (%s)\n", node->id(), rep->id(), | |
203 rep->op()->mnemonic()); | |
204 } | |
205 return true; | |
206 } | |
207 return false; | |
208 } | |
209 | |
210 | |
211 // ------------------------------EscapeStatusAnalysis--------------------------- | |
212 | |
213 | |
214 EscapeStatusAnalysis::EscapeStatusAnalysis(Graph* graph, Zone* zone) | |
215 : graph_(graph), zone_(zone), info_(zone), queue_(zone) {} | |
216 | |
217 | |
218 EscapeStatusAnalysis::~EscapeStatusAnalysis() {} | |
219 | |
220 | |
221 bool EscapeStatusAnalysis::HasEntry(Node* node) { | |
222 return info_[node->id()] != kUnknown; | |
223 } | |
224 | |
225 | |
226 bool EscapeStatusAnalysis::IsVirtual(Node* node) { | |
227 if (node->id() >= info_.size()) { | |
228 return false; | |
229 } | |
230 return info_[node->id()] == kVirtual; | |
231 } | |
232 | |
233 | |
234 bool EscapeStatusAnalysis::IsEscaped(Node* node) { | |
235 return info_[node->id()] == kEscaped; | |
236 } | |
237 | |
238 | |
239 bool EscapeStatusAnalysis::SetEscaped(Node* node) { | |
240 bool changed = info_[node->id()] != kEscaped; | |
241 info_[node->id()] = kEscaped; | |
242 return changed; | |
243 } | |
244 | |
245 | |
246 void EscapeStatusAnalysis::Run() { | |
247 info_.resize(graph()->NodeCount()); | |
248 ZoneVector<bool> visited(zone()); | |
249 visited.resize(graph()->NodeCount()); | |
250 queue_.push_back(graph()->end()); | |
251 while (!queue_.empty()) { | |
252 Node* node = queue_.front(); | |
253 queue_.pop_front(); | |
254 Process(node); | |
255 if (!visited[node->id()]) { | |
256 RevisitInputs(node); | |
257 } | |
258 visited[node->id()] = true; | |
259 } | |
260 if (FLAG_trace_turbo_escape) { | |
261 DebugPrint(); | |
262 } | |
263 } | |
264 | |
265 | |
266 void EscapeStatusAnalysis::RevisitInputs(Node* node) { | |
267 for (Edge edge : node->input_edges()) { | |
268 Node* input = edge.to(); | |
269 queue_.push_back(input); | |
270 } | |
271 } | |
272 | |
273 | |
274 void EscapeStatusAnalysis::RevisitUses(Node* node) { | |
275 for (Edge edge : node->use_edges()) { | |
276 Node* use = edge.from(); | |
277 queue_.push_back(use); | |
278 } | |
279 } | |
280 | |
281 | |
282 void EscapeStatusAnalysis::Process(Node* node) { | |
283 switch (node->opcode()) { | |
284 case IrOpcode::kAllocate: | |
285 ProcessAllocate(node); | |
286 break; | |
287 case IrOpcode::kFinishRegion: | |
288 ProcessFinishRegion(node); | |
289 break; | |
290 case IrOpcode::kStoreField: | |
291 ProcessStoreField(node); | |
292 break; | |
293 case IrOpcode::kPhi: | |
294 if (!HasEntry(node)) { | |
295 info_[node->id()] = kVirtual; | |
296 } | |
297 CheckUsesForEscape(node); | |
298 default: | |
299 break; | |
300 } | |
301 } | |
302 | |
303 | |
304 void EscapeStatusAnalysis::ProcessStoreField(Node* node) { | |
305 DCHECK_EQ(node->opcode(), IrOpcode::kStoreField); | |
306 Node* to = NodeProperties::GetValueInput(node, 0); | |
307 Node* val = NodeProperties::GetValueInput(node, 1); | |
308 if (IsEscaped(to) && SetEscaped(val)) { | |
309 RevisitUses(val); | |
310 if (FLAG_trace_turbo_escape) { | |
311 PrintF("Setting #%d (%s) to escaped because of store to field of #%d\n", | |
312 val->id(), val->op()->mnemonic(), to->id()); | |
313 } | |
314 } | |
315 } | |
316 | |
317 | |
318 void EscapeStatusAnalysis::ProcessAllocate(Node* node) { | |
319 DCHECK_EQ(node->opcode(), IrOpcode::kAllocate); | |
320 if (!HasEntry(node)) { | |
321 info_[node->id()] = kVirtual; | |
322 if (FLAG_trace_turbo_escape) { | |
323 PrintF("Created status entry for node #%d (%s)\n", node->id(), | |
324 node->op()->mnemonic()); | |
325 } | |
326 NumberMatcher size(node->InputAt(0)); | |
327 if (!size.HasValue() && SetEscaped(node)) { | |
328 RevisitUses(node); | |
329 if (FLAG_trace_turbo_escape) { | |
330 PrintF("Setting #%d to escaped because of non-const alloc\n", | |
331 node->id()); | |
332 } | |
333 // This node is known to escape, uses do not have to be checked. | |
334 return; | |
335 } | |
336 } | |
337 if (CheckUsesForEscape(node)) { | |
338 RevisitUses(node); | |
339 } | |
340 } | |
341 | |
342 | |
343 bool EscapeStatusAnalysis::CheckUsesForEscape(Node* node) { | |
344 DCHECK(HasEntry(node)); | |
345 | |
346 for (Edge edge : node->use_edges()) { | |
347 Node* use = edge.from(); | |
348 if (!NodeProperties::IsValueEdge(edge)) continue; | |
349 switch (use->opcode()) { | |
350 case IrOpcode::kStoreField: | |
351 case IrOpcode::kLoadField: | |
352 case IrOpcode::kFrameState: | |
353 case IrOpcode::kStateValues: | |
354 case IrOpcode::kReferenceEqual: | |
355 case IrOpcode::kFinishRegion: | |
356 case IrOpcode::kPhi: | |
357 if (HasEntry(use) && IsEscaped(use) && SetEscaped(node)) { | |
358 if (FLAG_trace_turbo_escape) { | |
359 PrintF( | |
360 "Setting #%d (%s) to escaped because of use by escaping node " | |
361 "#%d (%s)\n", | |
362 node->id(), node->op()->mnemonic(), use->id(), | |
363 use->op()->mnemonic()); | |
364 } | |
365 return true; | |
366 } | |
367 break; | |
368 default: | |
369 if (SetEscaped(node)) { | |
370 if (FLAG_trace_turbo_escape) { | |
371 PrintF("Setting #%d (%s) to escaped because of use by #%d (%s)\n", | |
372 node->id(), node->op()->mnemonic(), use->id(), | |
373 use->op()->mnemonic()); | |
374 } | |
375 return true; | |
376 } | |
377 if (use->op()->EffectInputCount() == 0 && | |
378 node->op()->EffectInputCount() > 0) { | |
379 if (FLAG_trace_turbo_escape) { | |
380 PrintF("Encountered unaccounted use by #%d (%s)\n", use->id(), | |
381 use->op()->mnemonic()); | |
382 } | |
383 UNREACHABLE(); | |
384 } | |
385 } | |
386 } | |
387 return false; | |
388 } | |
389 | |
390 | |
391 void EscapeStatusAnalysis::ProcessFinishRegion(Node* node) { | |
392 DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion); | |
393 if (!HasEntry(node)) { | |
394 info_[node->id()] = kVirtual; | |
395 RevisitUses(node); | |
396 } | |
397 if (CheckUsesForEscape(node)) { | |
398 RevisitInputs(node); | |
399 } | |
400 } | |
401 | |
402 | |
403 void EscapeStatusAnalysis::DebugPrint() { | |
404 for (NodeId id = 0; id < info_.size(); id++) { | |
405 if (info_[id] != kUnknown) { | |
406 PrintF("Node #%d is %s\n", id, | |
407 info_[id] == kEscaped ? "escaping" : "virtual"); | |
408 } | |
409 } | |
410 } | |
411 | |
412 | |
413 // -----------------------------EscapeObjectAnalysis--------------------------- | |
414 | |
415 | |
416 EscapeObjectAnalysis::EscapeObjectAnalysis(Graph* graph, | |
417 CommonOperatorBuilder* common, | |
418 Zone* zone) | |
419 : graph_(graph), common_(common), zone_(zone), virtual_states_(zone) {} | |
420 | |
421 | |
422 EscapeObjectAnalysis::~EscapeObjectAnalysis() {} | |
423 | |
424 | |
425 void EscapeObjectAnalysis::Run() { | |
426 virtual_states_.resize(graph()->NodeCount()); | |
427 ZoneVector<Node*> queue_(zone()); | |
428 queue_.push_back(graph()->start()); | |
429 while (!queue_.empty()) { | |
430 Node* node = queue_.back(); | |
431 queue_.pop_back(); | |
432 if (Process(node)) { | |
433 for (Edge edge : node->use_edges()) { | |
434 if (NodeProperties::IsEffectEdge(edge)) { | |
435 Node* use = edge.from(); | |
436 if (use->opcode() != IrOpcode::kLoadField || | |
437 !IsDanglingEffectNode(use)) { | |
438 queue_.push_back(use); | |
439 } | |
440 } | |
441 } | |
442 // First process loads: dangling loads are a problem otherwise. | |
443 for (Edge edge : node->use_edges()) { | |
444 if (NodeProperties::IsEffectEdge(edge)) { | |
445 Node* use = edge.from(); | |
446 if (use->opcode() == IrOpcode::kLoadField && | |
447 IsDanglingEffectNode(use)) { | |
448 queue_.push_back(use); | |
449 } | |
450 } | |
451 } | |
452 } | |
453 } | |
454 if (FLAG_trace_turbo_escape) { | |
455 DebugPrint(); | |
456 } | |
457 } | |
458 | |
459 | |
460 bool EscapeObjectAnalysis::IsDanglingEffectNode(Node* node) { | |
461 if (node->op()->EffectInputCount() == 0) return false; | |
462 if (node->op()->EffectOutputCount() == 0) return false; | |
463 for (Edge edge : node->use_edges()) { | |
464 if (NodeProperties::IsEffectEdge(edge)) { | |
465 return false; | |
466 } | |
467 } | |
468 return true; | |
469 } | |
470 | |
471 | |
472 bool EscapeObjectAnalysis::Process(Node* node) { | |
473 switch (node->opcode()) { | |
474 case IrOpcode::kAllocate: | |
475 ProcessAllocation(node); | |
476 break; | |
477 case IrOpcode::kBeginRegion: | |
478 ForwardVirtualState(node); | |
479 break; | |
480 case IrOpcode::kFinishRegion: | |
481 ProcessFinishRegion(node); | |
482 break; | |
483 case IrOpcode::kStoreField: | |
484 ProcessStoreField(node); | |
485 break; | |
486 case IrOpcode::kLoadField: | |
487 ProcessLoadField(node); | |
488 break; | |
489 case IrOpcode::kStart: | |
490 ProcessStart(node); | |
491 break; | |
492 case IrOpcode::kEffectPhi: | |
493 return ProcessEffectPhi(node); | |
494 break; | |
495 default: | |
496 if (node->op()->EffectInputCount() > 0) { | |
497 ForwardVirtualState(node); | |
498 } | |
499 break; | |
500 } | |
501 return true; | |
502 } | |
503 | |
504 | |
505 bool EscapeObjectAnalysis::IsEffectBranchPoint(Node* node) { | |
506 int count = 0; | |
507 for (Edge edge : node->use_edges()) { | |
508 Node* use = edge.from(); | |
509 if (NodeProperties::IsEffectEdge(edge) && | |
510 use->opcode() != IrOpcode::kLoadField) { | |
511 ++count; | |
Benedikt Meurer
2015/11/30 17:21:16
Nit: if (++count > 1) return true;
and then return
sigurds
2015/12/01 10:13:16
Done.
| |
512 } | |
513 } | |
514 return count > 1; | |
515 } | |
516 | |
517 | |
518 void EscapeObjectAnalysis::ForwardVirtualState(Node* node) { | |
519 DCHECK_EQ(node->op()->EffectInputCount(), 1); | |
520 if (node->opcode() != IrOpcode::kLoadField && IsDanglingEffectNode(node)) { | |
521 PrintF("Dangeling effect node: #%d (%s)\n", node->id(), | |
522 node->op()->mnemonic()); | |
523 DCHECK(false); | |
524 } | |
525 Node* effect = NodeProperties::GetEffectInput(node); | |
526 // Break the cycle for effect phis. | |
527 if (effect->opcode() == IrOpcode::kEffectPhi) { | |
528 if (virtual_states_[effect->id()] == nullptr) { | |
529 virtual_states_[effect->id()] = | |
530 new (zone()) VirtualState(zone(), graph()->NodeCount()); | |
531 } | |
532 } | |
533 DCHECK_NOT_NULL(virtual_states_[effect->id()]); | |
534 if (IsEffectBranchPoint(effect)) { | |
535 if (virtual_states_[node->id()]) return; | |
536 virtual_states_[node->id()] = | |
537 new (zone()) VirtualState(*virtual_states_[effect->id()]); | |
538 if (FLAG_trace_turbo_escape) { | |
539 PrintF("Copying object state %p from #%d (%s) to #%d (%s)\n", | |
540 static_cast<void*>(virtual_states_[effect->id()]), effect->id(), | |
541 effect->op()->mnemonic(), node->id(), node->op()->mnemonic()); | |
542 } | |
543 } else { | |
544 virtual_states_[node->id()] = virtual_states_[effect->id()]; | |
545 if (FLAG_trace_turbo_escape) { | |
546 PrintF("Forwarding object state %p from #%d (%s) to #%d (%s)\n", | |
547 static_cast<void*>(virtual_states_[effect->id()]), effect->id(), | |
548 effect->op()->mnemonic(), node->id(), node->op()->mnemonic()); | |
549 } | |
550 } | |
551 } | |
552 | |
553 | |
554 void EscapeObjectAnalysis::ProcessStart(Node* node) { | |
555 DCHECK_EQ(node->opcode(), IrOpcode::kStart); | |
556 virtual_states_[node->id()] = | |
557 new (zone()) VirtualState(zone(), graph()->NodeCount()); | |
558 } | |
559 | |
560 | |
561 bool EscapeObjectAnalysis::ProcessEffectPhi(Node* node) { | |
562 DCHECK_EQ(node->opcode(), IrOpcode::kEffectPhi); | |
563 // For now only support binary phis. | |
564 DCHECK_EQ(node->op()->EffectInputCount(), 2); | |
565 Node* left = NodeProperties::GetEffectInput(node, 0); | |
566 Node* right = NodeProperties::GetEffectInput(node, 1); | |
567 bool changed = false; | |
568 | |
569 VirtualState* mergeState = virtual_states_[node->id()]; | |
570 if (!mergeState) { | |
571 mergeState = new (zone()) VirtualState(zone(), graph()->NodeCount()); | |
572 virtual_states_[node->id()] = mergeState; | |
573 changed = true; | |
574 if (FLAG_trace_turbo_escape) { | |
575 PrintF("Phi #%d got new states map %p.\n", node->id(), | |
576 static_cast<void*>(mergeState)); | |
577 } | |
578 } else if (mergeState->GetLastChanged() != node) { | |
579 changed = true; | |
580 } | |
581 | |
582 VirtualState* l = virtual_states_[left->id()]; | |
583 VirtualState* r = virtual_states_[right->id()]; | |
584 | |
585 if (l == nullptr && r == nullptr) { | |
586 return changed; | |
587 } | |
588 | |
589 if (FLAG_trace_turbo_escape) { | |
590 PrintF("At Phi #%d, merging states %p and %p\n", node->id(), | |
591 static_cast<void*>(l), static_cast<void*>(r)); | |
592 } | |
593 | |
594 if (r && l == nullptr) { | |
595 changed = mergeState->UpdateFrom(r, zone()) || changed; | |
596 } else if (l && r == nullptr) { | |
597 changed = mergeState->UpdateFrom(l, zone()) || changed; | |
598 } else { | |
599 changed = mergeState->MergeFrom(l, r, zone(), graph(), common(), | |
600 NodeProperties::GetControlInput(node)) || | |
601 changed; | |
602 } | |
603 if (FLAG_trace_turbo_escape) { | |
604 PrintF("Merge %s the node.\n", changed ? "changed" : "did not change"); | |
605 } | |
606 if (changed) { | |
607 mergeState->LastChangedAt(node); | |
608 } | |
609 return changed; | |
610 } | |
611 | |
612 | |
613 void EscapeObjectAnalysis::ProcessAllocation(Node* node) { | |
614 DCHECK_EQ(node->opcode(), IrOpcode::kAllocate); | |
615 ForwardVirtualState(node); | |
616 | |
617 // Check if we have already processed this node. | |
618 if (virtual_states_[node->id()]->GetVirtualObject(node)) return; | |
619 | |
620 NumberMatcher size(node->InputAt(0)); | |
621 if (size.HasValue()) { | |
622 virtual_states_[node->id()]->SetVirtualObject( | |
623 node->id(), | |
624 new (zone()) VirtualObject(node->id(), zone(), size.Value())); | |
625 } else { | |
626 virtual_states_[node->id()]->SetVirtualObject( | |
627 node->id(), new (zone()) VirtualObject(node->id(), zone())); | |
628 } | |
629 virtual_states_[node->id()]->LastChangedAt(node); | |
630 } | |
631 | |
632 | |
633 void EscapeObjectAnalysis::ProcessFinishRegion(Node* node) { | |
634 DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion); | |
635 ForwardVirtualState(node); | |
636 Node* allocation = NodeProperties::GetValueInput(node, 0); | |
637 if (allocation->opcode() == IrOpcode::kAllocate) { | |
638 VirtualState* states = virtual_states_[node->id()]; | |
639 DCHECK_NOT_NULL(states->GetVirtualObject(allocation)); | |
640 if (!states->GetVirtualObject(node->id())) { | |
641 states->SetVirtualObject(node->id(), | |
642 states->GetVirtualObject(allocation)); | |
643 if (FLAG_trace_turbo_escape) { | |
644 PrintF("Linked finish region node #%d to node #%d\n", node->id(), | |
645 allocation->id()); | |
646 } | |
647 states->LastChangedAt(node); | |
648 } | |
649 } | |
650 } | |
651 | |
652 | |
653 Node* EscapeObjectAnalysis::GetReplacement(Node* at, NodeId id) { | |
654 VirtualState* states = virtual_states_[at->id()]; | |
655 if (VirtualObject* obj = states->GetVirtualObject(id)) { | |
656 return obj->GetReplacement(); | |
657 } | |
658 return nullptr; | |
659 } | |
660 | |
661 | |
662 void EscapeObjectAnalysis::ProcessLoadField(Node* node) { | |
663 DCHECK_EQ(node->opcode(), IrOpcode::kLoadField); | |
664 ForwardVirtualState(node); | |
665 Node* from = NodeProperties::GetValueInput(node, 0); | |
666 int offset = OpParameter<FieldAccess>(node).offset; | |
667 VirtualState* states = virtual_states_[node->id()]; | |
668 if (VirtualObject* state = states->GetVirtualObject(from)) { | |
669 if (!state->IsTracked()) return; | |
670 Node* value = state->GetField(offset); | |
671 if (value) { | |
672 // Record that the load has this alias. | |
673 states->UpdateReplacement(node, value, zone()); | |
674 } else if (FLAG_trace_turbo_escape) { | |
675 PrintF("No field %d on record for #%d\n", offset, from->id()); | |
676 } | |
677 } else { | |
678 if (from->opcode() == IrOpcode::kPhi) { | |
679 // Only binary phis are supported for now. | |
680 CHECK_EQ(from->op()->ValueInputCount(), 2); | |
681 if (FLAG_trace_turbo_escape) { | |
682 PrintF("Load #%d from phi #%d", node->id(), from->id()); | |
683 } | |
684 Node* left = NodeProperties::GetValueInput(from, 0); | |
685 Node* right = NodeProperties::GetValueInput(from, 1); | |
686 VirtualObject* l = states->GetVirtualObject(left); | |
687 VirtualObject* r = states->GetVirtualObject(right); | |
688 if (l && r) { | |
689 Node* lv = l->GetField(offset); | |
690 Node* rv = r->GetField(offset); | |
691 if (lv && rv) { | |
692 if (!states->GetVirtualObject(node)) { | |
693 states->SetVirtualObject( | |
694 node->id(), new (zone()) VirtualObject(node->id(), zone())); | |
695 } | |
696 Node* rep = states->GetVirtualObject(node)->GetReplacement(); | |
697 if (!rep || rep->opcode() != IrOpcode::kPhi || | |
698 NodeProperties::GetValueInput(rep, 0) != lv || | |
699 NodeProperties::GetValueInput(rep, 1) != rv) { | |
700 Node* phi = | |
701 graph()->NewNode(common()->Phi(kMachAnyTagged, 2), lv, rv, | |
702 NodeProperties::GetControlInput(from)); | |
703 states->GetVirtualObject(node)->SetReplacement(phi); | |
704 states->LastChangedAt(node); | |
705 if (FLAG_trace_turbo_escape) { | |
706 PrintF(" got phi of #%d is #%d created.\n", lv->id(), rv->id()); | |
707 } | |
708 } else if (FLAG_trace_turbo_escape) { | |
709 PrintF(" has already the right phi representation.\n"); | |
710 } | |
711 } else if (FLAG_trace_turbo_escape) { | |
712 PrintF(" has incomplete field info: %p %p\n", static_cast<void*>(lv), | |
713 static_cast<void*>(rv)); | |
714 } | |
715 } else if (FLAG_trace_turbo_escape) { | |
716 PrintF(" has incomplete virtual object info: %p %p\n", | |
717 static_cast<void*>(l), static_cast<void*>(r)); | |
718 } | |
719 } | |
720 } | |
721 } | |
722 | |
723 | |
724 void EscapeObjectAnalysis::ProcessStoreField(Node* node) { | |
725 DCHECK_EQ(node->opcode(), IrOpcode::kStoreField); | |
726 ForwardVirtualState(node); | |
727 Node* to = NodeProperties::GetValueInput(node, 0); | |
728 Node* val = NodeProperties::GetValueInput(node, 1); | |
729 int offset = OpParameter<FieldAccess>(node).offset; | |
730 VirtualState* states = virtual_states_[node->id()]; | |
731 if (VirtualObject* obj = states->GetVirtualObject(to)) { | |
732 if (!obj->IsTracked()) return; | |
733 if (obj->SetField(offset, states->ResolveReplacement(val))) { | |
734 states->LastChangedAt(node); | |
735 } | |
736 } | |
737 } | |
738 | |
739 | |
740 void EscapeObjectAnalysis::DebugPrint() { | |
741 ZoneVector<VirtualState*> object_states(zone()); | |
742 for (NodeId id = 0; id < virtual_states_.size(); id++) { | |
743 if (VirtualState* states = virtual_states_[id]) { | |
744 if (std::find(object_states.begin(), object_states.end(), states) == | |
745 object_states.end()) { | |
746 object_states.push_back(states); | |
747 } | |
748 } | |
749 } | |
750 for (size_t n = 0; n < object_states.size(); n++) { | |
751 PrintF("Dumping object state %p\n", static_cast<void*>(object_states[n])); | |
752 for (size_t id = 0; id < object_states[n]->size(); id++) { | |
753 if (VirtualObject* obj = object_states[n]->GetVirtualObject(id)) { | |
754 if (obj->id() == id) { | |
755 PrintF(" Object #%zu with %zu fields", id, obj->fields()); | |
756 if (Node* rep = obj->GetReplacement()) { | |
757 PrintF(", rep = #%d (%s)", rep->id(), rep->op()->mnemonic()); | |
758 } | |
759 PrintF("\n"); | |
760 for (size_t i = 0; i < obj->fields(); ++i) { | |
761 if (Node* f = obj->GetField(i)) { | |
762 PrintF(" Field %zu = #%d (%s)\n", i, f->id(), | |
763 f->op()->mnemonic()); | |
764 } | |
765 } | |
766 } else { | |
767 PrintF(" Object #%zu links to object #%d\n", id, obj->id()); | |
768 } | |
769 } | |
770 } | |
771 } | |
772 } | |
773 | |
774 } // namespace compiler | |
775 } // namespace internal | |
776 } // namespace v8 | |
OLD | NEW |