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( | |
215 EscapeObjectAnalysis* object_analysis, Graph* graph, Zone* zone) | |
216 : object_analysis_(object_analysis), | |
217 graph_(graph), | |
218 zone_(zone), | |
219 info_(zone), | |
220 queue_(zone) {} | |
221 | |
222 | |
223 EscapeStatusAnalysis::~EscapeStatusAnalysis() {} | |
224 | |
225 | |
226 bool EscapeStatusAnalysis::HasEntry(Node* node) { | |
227 return info_[node->id()] != kUnknown; | |
228 } | |
229 | |
230 | |
231 bool EscapeStatusAnalysis::IsVirtual(Node* node) { | |
232 if (node->id() >= info_.size()) { | |
233 return false; | |
234 } | |
235 return info_[node->id()] == kVirtual; | |
236 } | |
237 | |
238 | |
239 bool EscapeStatusAnalysis::IsEscaped(Node* node) { | |
240 return info_[node->id()] == kEscaped; | |
241 } | |
242 | |
243 | |
244 bool EscapeStatusAnalysis::SetEscaped(Node* node) { | |
245 bool changed = info_[node->id()] != kEscaped; | |
246 info_[node->id()] = kEscaped; | |
247 return changed; | |
248 } | |
249 | |
250 | |
251 void EscapeStatusAnalysis::Run() { | |
252 info_.resize(graph()->NodeCount()); | |
253 ZoneVector<bool> visited(zone()); | |
254 visited.resize(graph()->NodeCount()); | |
255 queue_.push_back(graph()->end()); | |
256 while (!queue_.empty()) { | |
257 Node* node = queue_.front(); | |
258 queue_.pop_front(); | |
259 Process(node); | |
260 if (!visited[node->id()]) { | |
261 RevisitInputs(node); | |
262 } | |
263 visited[node->id()] = true; | |
264 } | |
265 if (FLAG_trace_turbo_escape) { | |
266 DebugPrint(); | |
267 } | |
268 } | |
269 | |
270 | |
271 void EscapeStatusAnalysis::RevisitInputs(Node* node) { | |
272 for (Edge edge : node->input_edges()) { | |
273 Node* input = edge.to(); | |
274 queue_.push_back(input); | |
275 } | |
276 } | |
277 | |
278 | |
279 void EscapeStatusAnalysis::RevisitUses(Node* node) { | |
280 for (Edge edge : node->use_edges()) { | |
281 Node* use = edge.from(); | |
282 queue_.push_back(use); | |
283 } | |
284 } | |
285 | |
286 | |
287 void EscapeStatusAnalysis::Process(Node* node) { | |
288 switch (node->opcode()) { | |
289 case IrOpcode::kAllocate: | |
290 ProcessAllocate(node); | |
291 break; | |
292 case IrOpcode::kFinishRegion: | |
293 ProcessFinishRegion(node); | |
294 break; | |
295 case IrOpcode::kStoreField: | |
296 ProcessStoreField(node); | |
297 break; | |
298 case IrOpcode::kLoadField: { | |
sigurds
2015/12/01 15:10:07
This is the bugfix.
| |
299 if (Node* rep = object_analysis_->GetReplacement(node, node->id())) { | |
300 if (rep->opcode() == IrOpcode::kAllocate || | |
301 rep->opcode() == IrOpcode::kFinishRegion) { | |
302 if (CheckUsesForEscape(node, rep)) { | |
303 RevisitInputs(rep); | |
304 RevisitUses(rep); | |
305 } | |
306 } | |
307 } | |
308 break; | |
309 } | |
310 case IrOpcode::kPhi: | |
311 if (!HasEntry(node)) { | |
312 info_[node->id()] = kVirtual; | |
313 } | |
314 CheckUsesForEscape(node); | |
315 default: | |
316 break; | |
317 } | |
318 } | |
319 | |
320 | |
321 void EscapeStatusAnalysis::ProcessStoreField(Node* node) { | |
322 DCHECK_EQ(node->opcode(), IrOpcode::kStoreField); | |
323 Node* to = NodeProperties::GetValueInput(node, 0); | |
324 Node* val = NodeProperties::GetValueInput(node, 1); | |
325 if (IsEscaped(to) && SetEscaped(val)) { | |
326 RevisitUses(val); | |
327 if (FLAG_trace_turbo_escape) { | |
328 PrintF("Setting #%d (%s) to escaped because of store to field of #%d\n", | |
329 val->id(), val->op()->mnemonic(), to->id()); | |
330 } | |
331 } | |
332 } | |
333 | |
334 | |
335 void EscapeStatusAnalysis::ProcessAllocate(Node* node) { | |
336 DCHECK_EQ(node->opcode(), IrOpcode::kAllocate); | |
337 if (!HasEntry(node)) { | |
338 info_[node->id()] = kVirtual; | |
339 if (FLAG_trace_turbo_escape) { | |
340 PrintF("Created status entry for node #%d (%s)\n", node->id(), | |
341 node->op()->mnemonic()); | |
342 } | |
343 NumberMatcher size(node->InputAt(0)); | |
344 if (!size.HasValue() && SetEscaped(node)) { | |
345 RevisitUses(node); | |
346 if (FLAG_trace_turbo_escape) { | |
347 PrintF("Setting #%d to escaped because of non-const alloc\n", | |
348 node->id()); | |
349 } | |
350 // This node is known to escape, uses do not have to be checked. | |
351 return; | |
352 } | |
353 } | |
354 if (CheckUsesForEscape(node)) { | |
355 RevisitUses(node); | |
356 } | |
357 } | |
358 | |
359 | |
360 bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep) { | |
361 for (Edge edge : uses->use_edges()) { | |
362 Node* use = edge.from(); | |
363 if (!NodeProperties::IsValueEdge(edge)) continue; | |
364 switch (use->opcode()) { | |
365 case IrOpcode::kStoreField: | |
366 case IrOpcode::kLoadField: | |
367 case IrOpcode::kFrameState: | |
368 case IrOpcode::kStateValues: | |
369 case IrOpcode::kReferenceEqual: | |
370 case IrOpcode::kFinishRegion: | |
371 case IrOpcode::kPhi: | |
372 if (HasEntry(use) && IsEscaped(use) && SetEscaped(rep)) { | |
373 if (FLAG_trace_turbo_escape) { | |
374 PrintF( | |
375 "Setting #%d (%s) to escaped because of use by escaping node " | |
376 "#%d (%s)\n", | |
377 rep->id(), rep->op()->mnemonic(), use->id(), | |
378 use->op()->mnemonic()); | |
379 } | |
380 return true; | |
381 } | |
382 break; | |
383 default: | |
384 if (SetEscaped(rep)) { | |
385 if (FLAG_trace_turbo_escape) { | |
386 PrintF("Setting #%d (%s) to escaped because of use by #%d (%s)\n", | |
387 rep->id(), rep->op()->mnemonic(), use->id(), | |
388 use->op()->mnemonic()); | |
389 } | |
390 return true; | |
391 } | |
392 if (use->op()->EffectInputCount() == 0 && | |
393 uses->op()->EffectInputCount() > 0 && | |
394 uses->opcode() != IrOpcode::kLoadField) { | |
395 if (FLAG_trace_turbo_escape) { | |
396 PrintF("Encountered unaccounted use by #%d (%s)\n", use->id(), | |
397 use->op()->mnemonic()); | |
398 } | |
399 UNREACHABLE(); | |
400 } | |
401 } | |
402 } | |
403 return false; | |
404 } | |
405 | |
406 | |
407 void EscapeStatusAnalysis::ProcessFinishRegion(Node* node) { | |
408 DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion); | |
409 if (!HasEntry(node)) { | |
410 info_[node->id()] = kVirtual; | |
411 RevisitUses(node); | |
412 } | |
413 if (CheckUsesForEscape(node)) { | |
414 RevisitInputs(node); | |
415 } | |
416 } | |
417 | |
418 | |
419 void EscapeStatusAnalysis::DebugPrint() { | |
420 for (NodeId id = 0; id < info_.size(); id++) { | |
421 if (info_[id] != kUnknown) { | |
422 PrintF("Node #%d is %s\n", id, | |
423 info_[id] == kEscaped ? "escaping" : "virtual"); | |
424 } | |
425 } | |
426 } | |
427 | |
428 | |
429 // -----------------------------EscapeObjectAnalysis--------------------------- | |
430 | |
431 | |
432 EscapeObjectAnalysis::EscapeObjectAnalysis(Graph* graph, | |
433 CommonOperatorBuilder* common, | |
434 Zone* zone) | |
435 : graph_(graph), common_(common), zone_(zone), virtual_states_(zone) {} | |
436 | |
437 | |
438 EscapeObjectAnalysis::~EscapeObjectAnalysis() {} | |
439 | |
440 | |
441 void EscapeObjectAnalysis::Run() { | |
442 virtual_states_.resize(graph()->NodeCount()); | |
443 ZoneVector<Node*> queue(zone()); | |
444 queue.push_back(graph()->start()); | |
445 while (!queue.empty()) { | |
446 Node* node = queue.back(); | |
447 queue.pop_back(); | |
448 if (Process(node)) { | |
449 for (Edge edge : node->use_edges()) { | |
450 if (NodeProperties::IsEffectEdge(edge)) { | |
451 Node* use = edge.from(); | |
452 if (use->opcode() != IrOpcode::kLoadField || | |
453 !IsDanglingEffectNode(use)) { | |
454 queue.push_back(use); | |
455 } | |
456 } | |
457 } | |
458 // First process loads: dangling loads are a problem otherwise. | |
459 for (Edge edge : node->use_edges()) { | |
460 if (NodeProperties::IsEffectEdge(edge)) { | |
461 Node* use = edge.from(); | |
462 if (use->opcode() == IrOpcode::kLoadField && | |
463 IsDanglingEffectNode(use)) { | |
464 queue.push_back(use); | |
465 } | |
466 } | |
467 } | |
468 } | |
469 } | |
470 if (FLAG_trace_turbo_escape) { | |
471 DebugPrint(); | |
472 } | |
473 } | |
474 | |
475 | |
476 bool EscapeObjectAnalysis::IsDanglingEffectNode(Node* node) { | |
477 if (node->op()->EffectInputCount() == 0) return false; | |
478 if (node->op()->EffectOutputCount() == 0) return false; | |
479 for (Edge edge : node->use_edges()) { | |
480 if (NodeProperties::IsEffectEdge(edge)) { | |
481 return false; | |
482 } | |
483 } | |
484 return true; | |
485 } | |
486 | |
487 | |
488 bool EscapeObjectAnalysis::Process(Node* node) { | |
489 switch (node->opcode()) { | |
490 case IrOpcode::kAllocate: | |
491 ProcessAllocation(node); | |
492 break; | |
493 case IrOpcode::kBeginRegion: | |
494 ForwardVirtualState(node); | |
495 break; | |
496 case IrOpcode::kFinishRegion: | |
497 ProcessFinishRegion(node); | |
498 break; | |
499 case IrOpcode::kStoreField: | |
500 ProcessStoreField(node); | |
501 break; | |
502 case IrOpcode::kLoadField: | |
503 ProcessLoadField(node); | |
504 break; | |
505 case IrOpcode::kStart: | |
506 ProcessStart(node); | |
507 break; | |
508 case IrOpcode::kEffectPhi: | |
509 return ProcessEffectPhi(node); | |
510 break; | |
511 default: | |
512 if (node->op()->EffectInputCount() > 0) { | |
513 ForwardVirtualState(node); | |
514 } | |
515 break; | |
516 } | |
517 return true; | |
518 } | |
519 | |
520 | |
521 bool EscapeObjectAnalysis::IsEffectBranchPoint(Node* node) { | |
522 int count = 0; | |
523 for (Edge edge : node->use_edges()) { | |
524 Node* use = edge.from(); | |
525 if (NodeProperties::IsEffectEdge(edge) && | |
526 use->opcode() != IrOpcode::kLoadField) { | |
527 if (++count > 1) { | |
528 return true; | |
529 } | |
530 } | |
531 } | |
532 return false; | |
533 } | |
534 | |
535 | |
536 void EscapeObjectAnalysis::ForwardVirtualState(Node* node) { | |
537 DCHECK_EQ(node->op()->EffectInputCount(), 1); | |
538 if (node->opcode() != IrOpcode::kLoadField && IsDanglingEffectNode(node)) { | |
539 PrintF("Dangeling effect node: #%d (%s)\n", node->id(), | |
540 node->op()->mnemonic()); | |
541 DCHECK(false); | |
542 } | |
543 Node* effect = NodeProperties::GetEffectInput(node); | |
544 // Break the cycle for effect phis. | |
545 if (effect->opcode() == IrOpcode::kEffectPhi) { | |
546 if (virtual_states_[effect->id()] == nullptr) { | |
547 virtual_states_[effect->id()] = | |
548 new (zone()) VirtualState(zone(), graph()->NodeCount()); | |
549 } | |
550 } | |
551 DCHECK_NOT_NULL(virtual_states_[effect->id()]); | |
552 if (IsEffectBranchPoint(effect)) { | |
553 if (virtual_states_[node->id()]) return; | |
554 virtual_states_[node->id()] = | |
555 new (zone()) VirtualState(*virtual_states_[effect->id()]); | |
556 if (FLAG_trace_turbo_escape) { | |
557 PrintF("Copying object state %p from #%d (%s) to #%d (%s)\n", | |
558 static_cast<void*>(virtual_states_[effect->id()]), effect->id(), | |
559 effect->op()->mnemonic(), node->id(), node->op()->mnemonic()); | |
560 } | |
561 } else { | |
562 virtual_states_[node->id()] = virtual_states_[effect->id()]; | |
563 if (FLAG_trace_turbo_escape) { | |
564 PrintF("Forwarding object state %p from #%d (%s) to #%d (%s)\n", | |
565 static_cast<void*>(virtual_states_[effect->id()]), effect->id(), | |
566 effect->op()->mnemonic(), node->id(), node->op()->mnemonic()); | |
567 } | |
568 } | |
569 } | |
570 | |
571 | |
572 void EscapeObjectAnalysis::ProcessStart(Node* node) { | |
573 DCHECK_EQ(node->opcode(), IrOpcode::kStart); | |
574 virtual_states_[node->id()] = | |
575 new (zone()) VirtualState(zone(), graph()->NodeCount()); | |
576 } | |
577 | |
578 | |
579 bool EscapeObjectAnalysis::ProcessEffectPhi(Node* node) { | |
580 DCHECK_EQ(node->opcode(), IrOpcode::kEffectPhi); | |
581 // For now only support binary phis. | |
582 DCHECK_EQ(node->op()->EffectInputCount(), 2); | |
583 Node* left = NodeProperties::GetEffectInput(node, 0); | |
584 Node* right = NodeProperties::GetEffectInput(node, 1); | |
585 bool changed = false; | |
586 | |
587 VirtualState* mergeState = virtual_states_[node->id()]; | |
588 if (!mergeState) { | |
589 mergeState = new (zone()) VirtualState(zone(), graph()->NodeCount()); | |
590 virtual_states_[node->id()] = mergeState; | |
591 changed = true; | |
592 if (FLAG_trace_turbo_escape) { | |
593 PrintF("Phi #%d got new states map %p.\n", node->id(), | |
594 static_cast<void*>(mergeState)); | |
595 } | |
596 } else if (mergeState->GetLastChanged() != node) { | |
597 changed = true; | |
598 } | |
599 | |
600 VirtualState* l = virtual_states_[left->id()]; | |
601 VirtualState* r = virtual_states_[right->id()]; | |
602 | |
603 if (l == nullptr && r == nullptr) { | |
604 return changed; | |
605 } | |
606 | |
607 if (FLAG_trace_turbo_escape) { | |
608 PrintF("At Phi #%d, merging states %p (from #%d) and %p (from #%d)\n", | |
609 node->id(), static_cast<void*>(l), left->id(), static_cast<void*>(r), | |
610 right->id()); | |
611 } | |
612 | |
613 if (r && l == nullptr) { | |
614 changed = mergeState->UpdateFrom(r, zone()) || changed; | |
615 } else if (l && r == nullptr) { | |
616 changed = mergeState->UpdateFrom(l, zone()) || changed; | |
617 } else { | |
618 changed = mergeState->MergeFrom(l, r, zone(), graph(), common(), | |
619 NodeProperties::GetControlInput(node)) || | |
620 changed; | |
621 } | |
622 if (FLAG_trace_turbo_escape) { | |
623 PrintF("Merge %s the node.\n", changed ? "changed" : "did not change"); | |
624 } | |
625 if (changed) { | |
626 mergeState->LastChangedAt(node); | |
627 } | |
628 return changed; | |
629 } | |
630 | |
631 | |
632 void EscapeObjectAnalysis::ProcessAllocation(Node* node) { | |
633 DCHECK_EQ(node->opcode(), IrOpcode::kAllocate); | |
634 ForwardVirtualState(node); | |
635 | |
636 // Check if we have already processed this node. | |
637 if (virtual_states_[node->id()]->GetVirtualObject(node)) return; | |
638 | |
639 NumberMatcher size(node->InputAt(0)); | |
640 if (size.HasValue()) { | |
641 virtual_states_[node->id()]->SetVirtualObject( | |
642 node->id(), new (zone()) VirtualObject(node->id(), zone(), | |
643 size.Value() / kPointerSize)); | |
644 } else { | |
645 virtual_states_[node->id()]->SetVirtualObject( | |
646 node->id(), new (zone()) VirtualObject(node->id(), zone())); | |
647 } | |
648 virtual_states_[node->id()]->LastChangedAt(node); | |
649 } | |
650 | |
651 | |
652 void EscapeObjectAnalysis::ProcessFinishRegion(Node* node) { | |
653 DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion); | |
654 ForwardVirtualState(node); | |
655 Node* allocation = NodeProperties::GetValueInput(node, 0); | |
656 if (allocation->opcode() == IrOpcode::kAllocate) { | |
657 VirtualState* states = virtual_states_[node->id()]; | |
658 DCHECK_NOT_NULL(states->GetVirtualObject(allocation)); | |
659 if (!states->GetVirtualObject(node->id())) { | |
660 states->SetVirtualObject(node->id(), | |
661 states->GetVirtualObject(allocation)); | |
662 if (FLAG_trace_turbo_escape) { | |
663 PrintF("Linked finish region node #%d to node #%d\n", node->id(), | |
664 allocation->id()); | |
665 } | |
666 states->LastChangedAt(node); | |
667 } | |
668 } | |
669 } | |
670 | |
671 | |
672 Node* EscapeObjectAnalysis::GetReplacement(Node* at, NodeId id) { | |
673 VirtualState* states = virtual_states_[at->id()]; | |
674 if (VirtualObject* obj = states->GetVirtualObject(id)) { | |
675 return obj->GetReplacement(); | |
676 } | |
677 return nullptr; | |
678 } | |
679 | |
680 | |
681 int EscapeObjectAnalysis::OffsetFromAccess(Node* node) { | |
682 DCHECK(OpParameter<FieldAccess>(node).offset % kPointerSize == 0); | |
683 return OpParameter<FieldAccess>(node).offset / kPointerSize; | |
684 } | |
685 | |
686 | |
687 void EscapeObjectAnalysis::ProcessLoadField(Node* node) { | |
688 DCHECK_EQ(node->opcode(), IrOpcode::kLoadField); | |
689 ForwardVirtualState(node); | |
690 Node* from = NodeProperties::GetValueInput(node, 0); | |
691 int offset = OffsetFromAccess(node); | |
692 VirtualState* states = virtual_states_[node->id()]; | |
693 if (VirtualObject* state = states->GetVirtualObject(from)) { | |
694 if (!state->IsTracked()) return; | |
695 Node* value = state->GetField(offset); | |
696 if (value) { | |
697 // Record that the load has this alias. | |
698 states->UpdateReplacement(node, value, zone()); | |
699 } else if (FLAG_trace_turbo_escape) { | |
700 PrintF("No field %d on record for #%d\n", offset, from->id()); | |
701 } | |
702 } else { | |
703 if (from->opcode() == IrOpcode::kPhi) { | |
704 // Only binary phis are supported for now. | |
705 CHECK_EQ(from->op()->ValueInputCount(), 2); | |
706 if (FLAG_trace_turbo_escape) { | |
707 PrintF("Load #%d from phi #%d", node->id(), from->id()); | |
708 } | |
709 Node* left = NodeProperties::GetValueInput(from, 0); | |
710 Node* right = NodeProperties::GetValueInput(from, 1); | |
711 VirtualObject* l = states->GetVirtualObject(left); | |
712 VirtualObject* r = states->GetVirtualObject(right); | |
713 if (l && r) { | |
714 Node* lv = l->GetField(offset); | |
715 Node* rv = r->GetField(offset); | |
716 if (lv && rv) { | |
717 if (!states->GetVirtualObject(node)) { | |
718 states->SetVirtualObject( | |
719 node->id(), new (zone()) VirtualObject(node->id(), zone())); | |
720 } | |
721 Node* rep = states->GetVirtualObject(node)->GetReplacement(); | |
722 if (!rep || rep->opcode() != IrOpcode::kPhi || | |
723 NodeProperties::GetValueInput(rep, 0) != lv || | |
724 NodeProperties::GetValueInput(rep, 1) != rv) { | |
725 Node* phi = | |
726 graph()->NewNode(common()->Phi(kMachAnyTagged, 2), lv, rv, | |
727 NodeProperties::GetControlInput(from)); | |
728 states->GetVirtualObject(node)->SetReplacement(phi); | |
729 states->LastChangedAt(node); | |
730 if (FLAG_trace_turbo_escape) { | |
731 PrintF(" got phi of #%d is #%d created.\n", lv->id(), rv->id()); | |
732 } | |
733 } else if (FLAG_trace_turbo_escape) { | |
734 PrintF(" has already the right phi representation.\n"); | |
735 } | |
736 } else if (FLAG_trace_turbo_escape) { | |
737 PrintF(" has incomplete field info: %p %p\n", static_cast<void*>(lv), | |
738 static_cast<void*>(rv)); | |
739 } | |
740 } else if (FLAG_trace_turbo_escape) { | |
741 PrintF(" has incomplete virtual object info: %p %p\n", | |
742 static_cast<void*>(l), static_cast<void*>(r)); | |
743 } | |
744 } | |
745 } | |
746 } | |
747 | |
748 | |
749 void EscapeObjectAnalysis::ProcessStoreField(Node* node) { | |
750 DCHECK_EQ(node->opcode(), IrOpcode::kStoreField); | |
751 ForwardVirtualState(node); | |
752 Node* to = NodeProperties::GetValueInput(node, 0); | |
753 Node* val = NodeProperties::GetValueInput(node, 1); | |
754 int offset = OffsetFromAccess(node); | |
755 VirtualState* states = virtual_states_[node->id()]; | |
756 if (VirtualObject* obj = states->GetVirtualObject(to)) { | |
757 if (!obj->IsTracked()) return; | |
758 if (obj->SetField(offset, states->ResolveReplacement(val))) { | |
759 states->LastChangedAt(node); | |
760 } | |
761 } | |
762 } | |
763 | |
764 | |
765 void EscapeObjectAnalysis::DebugPrint() { | |
766 ZoneVector<VirtualState*> object_states(zone()); | |
767 for (NodeId id = 0; id < virtual_states_.size(); id++) { | |
768 if (VirtualState* states = virtual_states_[id]) { | |
769 if (std::find(object_states.begin(), object_states.end(), states) == | |
770 object_states.end()) { | |
771 object_states.push_back(states); | |
772 } | |
773 } | |
774 } | |
775 for (size_t n = 0; n < object_states.size(); n++) { | |
776 PrintF("Dumping object state %p\n", static_cast<void*>(object_states[n])); | |
777 for (size_t id = 0; id < object_states[n]->size(); id++) { | |
778 if (VirtualObject* obj = object_states[n]->GetVirtualObject(id)) { | |
779 if (obj->id() == id) { | |
780 PrintF(" Object #%zu with %zu fields", id, obj->fields()); | |
781 if (Node* rep = obj->GetReplacement()) { | |
782 PrintF(", rep = #%d (%s)", rep->id(), rep->op()->mnemonic()); | |
783 } | |
784 PrintF("\n"); | |
785 for (size_t i = 0; i < obj->fields(); ++i) { | |
786 if (Node* f = obj->GetField(i)) { | |
787 PrintF(" Field %zu = #%d (%s)\n", i, f->id(), | |
788 f->op()->mnemonic()); | |
789 } | |
790 } | |
791 } else { | |
792 PrintF(" Object #%zu links to object #%d\n", id, obj->id()); | |
793 } | |
794 } | |
795 } | |
796 } | |
797 } | |
798 | |
799 } // namespace compiler | |
800 } // namespace internal | |
801 } // namespace v8 | |
OLD | NEW |