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-reducer.h"
6 
7 #include "src/compiler/js-graph.h"
8 #include "src/counters.h"
9 
10 namespace v8 {
11 namespace internal {
12 namespace compiler {
13 
EscapeAnalysisReducer(Editor * editor,JSGraph * jsgraph,EscapeAnalysis * escape_analysis,Zone * zone)14 EscapeAnalysisReducer::EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph,
15                                              EscapeAnalysis* escape_analysis,
16                                              Zone* zone)
17     : AdvancedReducer(editor),
18       jsgraph_(jsgraph),
19       escape_analysis_(escape_analysis),
20       zone_(zone),
21       visited_(static_cast<int>(jsgraph->graph()->NodeCount()), zone) {}
22 
23 
Reduce(Node * node)24 Reduction EscapeAnalysisReducer::Reduce(Node* node) {
25   switch (node->opcode()) {
26     case IrOpcode::kLoadField:
27     case IrOpcode::kLoadElement:
28       return ReduceLoad(node);
29     case IrOpcode::kStoreField:
30     case IrOpcode::kStoreElement:
31       return ReduceStore(node);
32     case IrOpcode::kAllocate:
33       return ReduceAllocate(node);
34     case IrOpcode::kFinishRegion:
35       return ReduceFinishRegion(node);
36     case IrOpcode::kReferenceEqual:
37       return ReduceReferenceEqual(node);
38     case IrOpcode::kObjectIsSmi:
39       return ReduceObjectIsSmi(node);
40     default:
41       // TODO(sigurds): Change this to GetFrameStateInputCount once
42       // it is working. For now we use EffectInputCount > 0 to determine
43       // whether a node might have a frame state input.
44       if (node->op()->EffectInputCount() > 0) {
45         return ReduceFrameStateUses(node);
46       }
47       break;
48   }
49   return NoChange();
50 }
51 
52 
ReduceLoad(Node * node)53 Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) {
54   DCHECK(node->opcode() == IrOpcode::kLoadField ||
55          node->opcode() == IrOpcode::kLoadElement);
56   if (visited_.Contains(node->id())) return NoChange();
57   visited_.Add(node->id());
58   if (Node* rep = escape_analysis()->GetReplacement(node)) {
59     visited_.Add(node->id());
60     counters()->turbo_escape_loads_replaced()->Increment();
61     if (FLAG_trace_turbo_escape) {
62       PrintF("Replaced #%d (%s) with #%d (%s)\n", node->id(),
63              node->op()->mnemonic(), rep->id(), rep->op()->mnemonic());
64     }
65     ReplaceWithValue(node, rep);
66     return Changed(rep);
67   }
68   return NoChange();
69 }
70 
71 
ReduceStore(Node * node)72 Reduction EscapeAnalysisReducer::ReduceStore(Node* node) {
73   DCHECK(node->opcode() == IrOpcode::kStoreField ||
74          node->opcode() == IrOpcode::kStoreElement);
75   if (visited_.Contains(node->id())) return NoChange();
76   visited_.Add(node->id());
77   if (escape_analysis()->IsVirtual(NodeProperties::GetValueInput(node, 0))) {
78     if (FLAG_trace_turbo_escape) {
79       PrintF("Removed #%d (%s) from effect chain\n", node->id(),
80              node->op()->mnemonic());
81     }
82     RelaxEffectsAndControls(node);
83     return Changed(node);
84   }
85   return NoChange();
86 }
87 
88 
ReduceAllocate(Node * node)89 Reduction EscapeAnalysisReducer::ReduceAllocate(Node* node) {
90   DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
91   if (visited_.Contains(node->id())) return NoChange();
92   visited_.Add(node->id());
93   if (escape_analysis()->IsVirtual(node)) {
94     RelaxEffectsAndControls(node);
95     counters()->turbo_escape_allocs_replaced()->Increment();
96     if (FLAG_trace_turbo_escape) {
97       PrintF("Removed allocate #%d from effect chain\n", node->id());
98     }
99     return Changed(node);
100   }
101   return NoChange();
102 }
103 
104 
ReduceFinishRegion(Node * node)105 Reduction EscapeAnalysisReducer::ReduceFinishRegion(Node* node) {
106   DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion);
107   Node* effect = NodeProperties::GetEffectInput(node, 0);
108   if (effect->opcode() == IrOpcode::kBeginRegion) {
109     RelaxEffectsAndControls(effect);
110     RelaxEffectsAndControls(node);
111     if (FLAG_trace_turbo_escape) {
112       PrintF("Removed region #%d / #%d from effect chain,", effect->id(),
113              node->id());
114       PrintF(" %d user(s) of #%d remain(s):", node->UseCount(), node->id());
115       for (Edge edge : node->use_edges()) {
116         PrintF(" #%d", edge.from()->id());
117       }
118       PrintF("\n");
119     }
120     return Changed(node);
121   }
122   return NoChange();
123 }
124 
125 
ReduceReferenceEqual(Node * node)126 Reduction EscapeAnalysisReducer::ReduceReferenceEqual(Node* node) {
127   DCHECK_EQ(node->opcode(), IrOpcode::kReferenceEqual);
128   Node* left = NodeProperties::GetValueInput(node, 0);
129   Node* right = NodeProperties::GetValueInput(node, 1);
130   if (escape_analysis()->IsVirtual(left)) {
131     if (escape_analysis()->IsVirtual(right) &&
132         escape_analysis()->CompareVirtualObjects(left, right)) {
133       ReplaceWithValue(node, jsgraph()->TrueConstant());
134       if (FLAG_trace_turbo_escape) {
135         PrintF("Replaced ref eq #%d with true\n", node->id());
136       }
137     }
138     // Right-hand side is not a virtual object, or a different one.
139     ReplaceWithValue(node, jsgraph()->FalseConstant());
140     if (FLAG_trace_turbo_escape) {
141       PrintF("Replaced ref eq #%d with false\n", node->id());
142     }
143     return Replace(node);
144   } else if (escape_analysis()->IsVirtual(right)) {
145     // Left-hand side is not a virtual object.
146     ReplaceWithValue(node, jsgraph()->FalseConstant());
147     if (FLAG_trace_turbo_escape) {
148       PrintF("Replaced ref eq #%d with false\n", node->id());
149     }
150   }
151   return NoChange();
152 }
153 
154 
ReduceObjectIsSmi(Node * node)155 Reduction EscapeAnalysisReducer::ReduceObjectIsSmi(Node* node) {
156   DCHECK_EQ(node->opcode(), IrOpcode::kObjectIsSmi);
157   Node* input = NodeProperties::GetValueInput(node, 0);
158   if (escape_analysis()->IsVirtual(input)) {
159     ReplaceWithValue(node, jsgraph()->FalseConstant());
160     if (FLAG_trace_turbo_escape) {
161       PrintF("Replaced ObjectIsSmi #%d with false\n", node->id());
162     }
163     return Replace(node);
164   }
165   return NoChange();
166 }
167 
168 
ReduceFrameStateUses(Node * node)169 Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) {
170   if (visited_.Contains(node->id())) return NoChange();
171   visited_.Add(node->id());
172   DCHECK_GE(node->op()->EffectInputCount(), 1);
173   bool changed = false;
174   for (int i = 0; i < node->InputCount(); ++i) {
175     Node* input = node->InputAt(i);
176     if (input->opcode() == IrOpcode::kFrameState) {
177       if (Node* ret = ReduceFrameState(input, node, false)) {
178         node->ReplaceInput(i, ret);
179         changed = true;
180       }
181     }
182   }
183   if (changed) {
184     return Changed(node);
185   }
186   return NoChange();
187 }
188 
189 
190 // Returns the clone if it duplicated the node, and null otherwise.
ReduceFrameState(Node * node,Node * effect,bool multiple_users)191 Node* EscapeAnalysisReducer::ReduceFrameState(Node* node, Node* effect,
192                                               bool multiple_users) {
193   DCHECK(node->opcode() == IrOpcode::kFrameState);
194   if (FLAG_trace_turbo_escape) {
195     PrintF("Reducing FrameState %d\n", node->id());
196   }
197   Node* clone = nullptr;
198   for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
199     Node* input = NodeProperties::GetValueInput(node, i);
200     Node* ret =
201         input->opcode() == IrOpcode::kStateValues
202             ? ReduceStateValueInputs(input, effect, node->UseCount() > 1)
203             : ReduceStateValueInput(node, i, effect, node->UseCount() > 1);
204     if (ret) {
205       if (node->UseCount() > 1 || multiple_users) {
206         if (FLAG_trace_turbo_escape) {
207           PrintF("  Cloning #%d", node->id());
208         }
209         node = clone = jsgraph()->graph()->CloneNode(node);
210         if (FLAG_trace_turbo_escape) {
211           PrintF(" to #%d\n", node->id());
212         }
213         multiple_users = false;  // Don't clone anymore.
214       }
215       NodeProperties::ReplaceValueInput(node, ret, i);
216     }
217   }
218   Node* outer_frame_state = NodeProperties::GetFrameStateInput(node, 0);
219   if (outer_frame_state->opcode() == IrOpcode::kFrameState) {
220     if (Node* ret =
221             ReduceFrameState(outer_frame_state, effect, node->UseCount() > 1)) {
222       if (node->UseCount() > 1 || multiple_users) {
223         if (FLAG_trace_turbo_escape) {
224           PrintF("  Cloning #%d", node->id());
225         }
226         node = clone = jsgraph()->graph()->CloneNode(node);
227         if (FLAG_trace_turbo_escape) {
228           PrintF(" to #%d\n", node->id());
229         }
230         multiple_users = false;
231       }
232       NodeProperties::ReplaceFrameStateInput(node, 0, ret);
233     }
234   }
235   return clone;
236 }
237 
238 
239 // Returns the clone if it duplicated the node, and null otherwise.
ReduceStateValueInputs(Node * node,Node * effect,bool multiple_users)240 Node* EscapeAnalysisReducer::ReduceStateValueInputs(Node* node, Node* effect,
241                                                     bool multiple_users) {
242   if (FLAG_trace_turbo_escape) {
243     PrintF("Reducing StateValue #%d\n", node->id());
244   }
245   DCHECK(node->opcode() == IrOpcode::kStateValues);
246   DCHECK_NOT_NULL(effect);
247   Node* clone = nullptr;
248   for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
249     Node* input = NodeProperties::GetValueInput(node, i);
250     Node* ret = nullptr;
251     if (input->opcode() == IrOpcode::kStateValues) {
252       ret = ReduceStateValueInputs(input, effect, multiple_users);
253     } else {
254       ret = ReduceStateValueInput(node, i, effect, multiple_users);
255     }
256     if (ret) {
257       node = ret;
258       DCHECK_NULL(clone);
259       clone = ret;
260       multiple_users = false;
261     }
262   }
263   return clone;
264 }
265 
266 
267 // Returns the clone if it duplicated the node, and null otherwise.
ReduceStateValueInput(Node * node,int node_index,Node * effect,bool multiple_users)268 Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index,
269                                                    Node* effect,
270                                                    bool multiple_users) {
271   Node* input = NodeProperties::GetValueInput(node, node_index);
272   if (FLAG_trace_turbo_escape) {
273     PrintF("Reducing State Input #%d (%s)\n", input->id(),
274            input->op()->mnemonic());
275   }
276   Node* clone = nullptr;
277   if (input->opcode() == IrOpcode::kFinishRegion ||
278       input->opcode() == IrOpcode::kAllocate) {
279     if (escape_analysis()->IsVirtual(input)) {
280       if (Node* object_state =
281               escape_analysis()->GetOrCreateObjectState(effect, input)) {
282         if (node->UseCount() > 1 || multiple_users) {
283           if (FLAG_trace_turbo_escape) {
284             PrintF("Cloning #%d", node->id());
285           }
286           node = clone = jsgraph()->graph()->CloneNode(node);
287           if (FLAG_trace_turbo_escape) {
288             PrintF(" to #%d\n", node->id());
289           }
290         }
291         NodeProperties::ReplaceValueInput(node, object_state, node_index);
292         if (FLAG_trace_turbo_escape) {
293           PrintF("Replaced state #%d input #%d with object state #%d\n",
294                  node->id(), input->id(), object_state->id());
295         }
296       } else {
297         if (FLAG_trace_turbo_escape) {
298           PrintF("No object state replacement available.\n");
299         }
300       }
301     }
302   }
303   return clone;
304 }
305 
306 
counters() const307 Counters* EscapeAnalysisReducer::counters() const {
308   return jsgraph_->isolate()->counters();
309 }
310 
311 }  // namespace compiler
312 }  // namespace internal
313 }  // namespace v8
314