• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/all-nodes.h"
8 #include "src/compiler/js-graph.h"
9 #include "src/counters.h"
10 
11 namespace v8 {
12 namespace internal {
13 namespace compiler {
14 
15 #ifdef DEBUG
16 #define TRACE(...)                                    \
17   do {                                                \
18     if (FLAG_trace_turbo_escape) PrintF(__VA_ARGS__); \
19   } while (false)
20 #else
21 #define TRACE(...)
22 #endif  // DEBUG
23 
EscapeAnalysisReducer(Editor * editor,JSGraph * jsgraph,EscapeAnalysis * escape_analysis,Zone * zone)24 EscapeAnalysisReducer::EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph,
25                                              EscapeAnalysis* escape_analysis,
26                                              Zone* zone)
27     : AdvancedReducer(editor),
28       jsgraph_(jsgraph),
29       escape_analysis_(escape_analysis),
30       zone_(zone),
31       fully_reduced_(static_cast<int>(jsgraph->graph()->NodeCount() * 2), zone),
32       exists_virtual_allocate_(escape_analysis->ExistsVirtualAllocate()) {}
33 
ReduceNode(Node * node)34 Reduction EscapeAnalysisReducer::ReduceNode(Node* node) {
35   if (node->id() < static_cast<NodeId>(fully_reduced_.length()) &&
36       fully_reduced_.Contains(node->id())) {
37     return NoChange();
38   }
39 
40   switch (node->opcode()) {
41     case IrOpcode::kLoadField:
42     case IrOpcode::kLoadElement:
43       return ReduceLoad(node);
44     case IrOpcode::kStoreField:
45     case IrOpcode::kStoreElement:
46       return ReduceStore(node);
47     case IrOpcode::kAllocate:
48       return ReduceAllocate(node);
49     case IrOpcode::kFinishRegion:
50       return ReduceFinishRegion(node);
51     case IrOpcode::kReferenceEqual:
52       return ReduceReferenceEqual(node);
53     case IrOpcode::kObjectIsSmi:
54       return ReduceObjectIsSmi(node);
55     // FrameStates and Value nodes are preprocessed here,
56     // and visited via ReduceFrameStateUses from their user nodes.
57     case IrOpcode::kFrameState:
58     case IrOpcode::kStateValues: {
59       if (node->id() >= static_cast<NodeId>(fully_reduced_.length()) ||
60           fully_reduced_.Contains(node->id())) {
61         break;
62       }
63       bool depends_on_object_state = false;
64       for (Node* input : node->inputs()) {
65         switch (input->opcode()) {
66           case IrOpcode::kAllocate:
67           case IrOpcode::kFinishRegion:
68             depends_on_object_state =
69                 depends_on_object_state || escape_analysis()->IsVirtual(input);
70             break;
71           case IrOpcode::kFrameState:
72           case IrOpcode::kStateValues:
73             depends_on_object_state =
74                 depends_on_object_state ||
75                 input->id() >= static_cast<NodeId>(fully_reduced_.length()) ||
76                 !fully_reduced_.Contains(input->id());
77             break;
78           default:
79             break;
80         }
81       }
82       if (!depends_on_object_state) {
83         fully_reduced_.Add(node->id());
84       }
85       return NoChange();
86     }
87     default:
88       // TODO(sigurds): Change this to GetFrameStateInputCount once
89       // it is working. For now we use EffectInputCount > 0 to determine
90       // whether a node might have a frame state input.
91       if (exists_virtual_allocate_ && node->op()->EffectInputCount() > 0) {
92         return ReduceFrameStateUses(node);
93       }
94       break;
95   }
96   return NoChange();
97 }
98 
Reduce(Node * node)99 Reduction EscapeAnalysisReducer::Reduce(Node* node) {
100   Reduction reduction = ReduceNode(node);
101   if (reduction.Changed() && node != reduction.replacement()) {
102     escape_analysis()->SetReplacement(node, reduction.replacement());
103   }
104   return reduction;
105 }
106 
107 namespace {
108 
MaybeGuard(JSGraph * jsgraph,Zone * zone,Node * original,Node * replacement)109 Node* MaybeGuard(JSGraph* jsgraph, Zone* zone, Node* original,
110                  Node* replacement) {
111   // We might need to guard the replacement if the type of the {replacement}
112   // node is not in a sub-type relation to the type of the the {original} node.
113   Type* const replacement_type = NodeProperties::GetType(replacement);
114   Type* const original_type = NodeProperties::GetType(original);
115   if (!replacement_type->Is(original_type)) {
116     Node* const control = NodeProperties::GetControlInput(original);
117     replacement = jsgraph->graph()->NewNode(
118         jsgraph->common()->TypeGuard(original_type), replacement, control);
119     NodeProperties::SetType(replacement, original_type);
120   }
121   return replacement;
122 }
123 
SkipTypeGuards(Node * node)124 Node* SkipTypeGuards(Node* node) {
125   while (node->opcode() == IrOpcode::kTypeGuard) {
126     node = NodeProperties::GetValueInput(node, 0);
127   }
128   return node;
129 }
130 
131 }  // namespace
132 
ReduceLoad(Node * node)133 Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) {
134   DCHECK(node->opcode() == IrOpcode::kLoadField ||
135          node->opcode() == IrOpcode::kLoadElement);
136   if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
137     fully_reduced_.Add(node->id());
138   }
139   if (escape_analysis()->IsVirtual(
140           SkipTypeGuards(NodeProperties::GetValueInput(node, 0)))) {
141     if (Node* rep = escape_analysis()->GetReplacement(node)) {
142       TRACE("Replaced #%d (%s) with #%d (%s)\n", node->id(),
143             node->op()->mnemonic(), rep->id(), rep->op()->mnemonic());
144       rep = MaybeGuard(jsgraph(), zone(), node, rep);
145       ReplaceWithValue(node, rep);
146       return Replace(rep);
147     }
148   }
149   return NoChange();
150 }
151 
152 
ReduceStore(Node * node)153 Reduction EscapeAnalysisReducer::ReduceStore(Node* node) {
154   DCHECK(node->opcode() == IrOpcode::kStoreField ||
155          node->opcode() == IrOpcode::kStoreElement);
156   if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
157     fully_reduced_.Add(node->id());
158   }
159   if (escape_analysis()->IsVirtual(
160           SkipTypeGuards(NodeProperties::GetValueInput(node, 0)))) {
161     TRACE("Removed #%d (%s) from effect chain\n", node->id(),
162           node->op()->mnemonic());
163     RelaxEffectsAndControls(node);
164     return Changed(node);
165   }
166   return NoChange();
167 }
168 
169 
ReduceAllocate(Node * node)170 Reduction EscapeAnalysisReducer::ReduceAllocate(Node* node) {
171   DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
172   if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
173     fully_reduced_.Add(node->id());
174   }
175   if (escape_analysis()->IsVirtual(node)) {
176     RelaxEffectsAndControls(node);
177     TRACE("Removed allocate #%d from effect chain\n", node->id());
178     return Changed(node);
179   }
180   return NoChange();
181 }
182 
183 
ReduceFinishRegion(Node * node)184 Reduction EscapeAnalysisReducer::ReduceFinishRegion(Node* node) {
185   DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion);
186   Node* effect = NodeProperties::GetEffectInput(node, 0);
187   if (effect->opcode() == IrOpcode::kBeginRegion) {
188     // We only add it now to remove empty Begin/Finish region pairs
189     // in the process.
190     if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
191       fully_reduced_.Add(node->id());
192     }
193     RelaxEffectsAndControls(effect);
194     RelaxEffectsAndControls(node);
195 #ifdef DEBUG
196     if (FLAG_trace_turbo_escape) {
197       PrintF("Removed region #%d / #%d from effect chain,", effect->id(),
198              node->id());
199       PrintF(" %d user(s) of #%d remain(s):", node->UseCount(), node->id());
200       for (Edge edge : node->use_edges()) {
201         PrintF(" #%d", edge.from()->id());
202       }
203       PrintF("\n");
204     }
205 #endif  // DEBUG
206     return Changed(node);
207   }
208   return NoChange();
209 }
210 
211 
ReduceReferenceEqual(Node * node)212 Reduction EscapeAnalysisReducer::ReduceReferenceEqual(Node* node) {
213   DCHECK_EQ(node->opcode(), IrOpcode::kReferenceEqual);
214   Node* left = SkipTypeGuards(NodeProperties::GetValueInput(node, 0));
215   Node* right = SkipTypeGuards(NodeProperties::GetValueInput(node, 1));
216   if (escape_analysis()->IsVirtual(left)) {
217     if (escape_analysis()->IsVirtual(right) &&
218         escape_analysis()->CompareVirtualObjects(left, right)) {
219       ReplaceWithValue(node, jsgraph()->TrueConstant());
220       TRACE("Replaced ref eq #%d with true\n", node->id());
221       return Replace(jsgraph()->TrueConstant());
222     }
223     // Right-hand side is not a virtual object, or a different one.
224     ReplaceWithValue(node, jsgraph()->FalseConstant());
225     TRACE("Replaced ref eq #%d with false\n", node->id());
226     return Replace(jsgraph()->FalseConstant());
227   } else if (escape_analysis()->IsVirtual(right)) {
228     // Left-hand side is not a virtual object.
229     ReplaceWithValue(node, jsgraph()->FalseConstant());
230     TRACE("Replaced ref eq #%d with false\n", node->id());
231     return Replace(jsgraph()->FalseConstant());
232   }
233   return NoChange();
234 }
235 
236 
ReduceObjectIsSmi(Node * node)237 Reduction EscapeAnalysisReducer::ReduceObjectIsSmi(Node* node) {
238   DCHECK_EQ(node->opcode(), IrOpcode::kObjectIsSmi);
239   Node* input = SkipTypeGuards(NodeProperties::GetValueInput(node, 0));
240   if (escape_analysis()->IsVirtual(input)) {
241     ReplaceWithValue(node, jsgraph()->FalseConstant());
242     TRACE("Replaced ObjectIsSmi #%d with false\n", node->id());
243     return Replace(jsgraph()->FalseConstant());
244   }
245   return NoChange();
246 }
247 
248 
ReduceFrameStateUses(Node * node)249 Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) {
250   DCHECK_GE(node->op()->EffectInputCount(), 1);
251   if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
252     fully_reduced_.Add(node->id());
253   }
254   bool changed = false;
255   for (int i = 0; i < node->InputCount(); ++i) {
256     Node* input = node->InputAt(i);
257     if (input->opcode() == IrOpcode::kFrameState) {
258       if (Node* ret = ReduceDeoptState(input, node, false)) {
259         node->ReplaceInput(i, ret);
260         changed = true;
261       }
262     }
263   }
264   if (changed) {
265     return Changed(node);
266   }
267   return NoChange();
268 }
269 
270 
271 // Returns the clone if it duplicated the node, and null otherwise.
ReduceDeoptState(Node * node,Node * effect,bool multiple_users)272 Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect,
273                                               bool multiple_users) {
274   DCHECK(node->opcode() == IrOpcode::kFrameState ||
275          node->opcode() == IrOpcode::kStateValues);
276   if (node->id() < static_cast<NodeId>(fully_reduced_.length()) &&
277       fully_reduced_.Contains(node->id())) {
278     return nullptr;
279   }
280   TRACE("Reducing %s %d\n", node->op()->mnemonic(), node->id());
281   Node* clone = nullptr;
282   bool node_multiused = node->UseCount() > 1;
283   bool multiple_users_rec = multiple_users || node_multiused;
284   for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
285     Node* input = NodeProperties::GetValueInput(node, i);
286     if (input->opcode() == IrOpcode::kStateValues) {
287       if (Node* ret = ReduceDeoptState(input, effect, multiple_users_rec)) {
288         if (node_multiused || (multiple_users && !clone)) {
289           TRACE("  Cloning #%d", node->id());
290           node = clone = jsgraph()->graph()->CloneNode(node);
291           TRACE(" to #%d\n", node->id());
292           node_multiused = false;
293         }
294         NodeProperties::ReplaceValueInput(node, ret, i);
295       }
296     } else {
297       if (Node* ret = ReduceStateValueInput(node, i, effect, node_multiused,
298                                             clone, multiple_users)) {
299         DCHECK_NULL(clone);
300         node_multiused = false;  // Don't clone anymore.
301         node = clone = ret;
302       }
303     }
304   }
305   if (node->opcode() == IrOpcode::kFrameState) {
306     Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
307     if (outer_frame_state->opcode() == IrOpcode::kFrameState) {
308       if (Node* ret =
309               ReduceDeoptState(outer_frame_state, effect, multiple_users_rec)) {
310         if (node_multiused || (multiple_users && !clone)) {
311           TRACE("    Cloning #%d", node->id());
312           node = clone = jsgraph()->graph()->CloneNode(node);
313           TRACE(" to #%d\n", node->id());
314         }
315         NodeProperties::ReplaceFrameStateInput(node, ret);
316       }
317     }
318   }
319   if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
320     fully_reduced_.Add(node->id());
321   }
322   return clone;
323 }
324 
325 
326 // Returns the clone if it duplicated the node, and null otherwise.
ReduceStateValueInput(Node * node,int node_index,Node * effect,bool node_multiused,bool already_cloned,bool multiple_users)327 Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index,
328                                                    Node* effect,
329                                                    bool node_multiused,
330                                                    bool already_cloned,
331                                                    bool multiple_users) {
332   Node* input = SkipTypeGuards(NodeProperties::GetValueInput(node, node_index));
333   if (node->id() < static_cast<NodeId>(fully_reduced_.length()) &&
334       fully_reduced_.Contains(node->id())) {
335     return nullptr;
336   }
337   TRACE("Reducing State Input #%d (%s)\n", input->id(),
338         input->op()->mnemonic());
339   Node* clone = nullptr;
340   if (input->opcode() == IrOpcode::kFinishRegion ||
341       input->opcode() == IrOpcode::kAllocate) {
342     if (escape_analysis()->IsVirtual(input)) {
343       if (escape_analysis()->IsCyclicObjectState(effect, input)) {
344         // TODO(mstarzinger): Represent cyclic object states differently to
345         // ensure the scheduler can properly handle such object states.
346         compilation_failed_ = true;
347         return nullptr;
348       }
349       if (Node* object_state =
350               escape_analysis()->GetOrCreateObjectState(effect, input)) {
351         if (node_multiused || (multiple_users && !already_cloned)) {
352           TRACE("Cloning #%d", node->id());
353           node = clone = jsgraph()->graph()->CloneNode(node);
354           TRACE(" to #%d\n", node->id());
355           node_multiused = false;
356           already_cloned = true;
357         }
358         NodeProperties::ReplaceValueInput(node, object_state, node_index);
359         TRACE("Replaced state #%d input #%d with object state #%d\n",
360               node->id(), input->id(), object_state->id());
361       } else {
362         TRACE("No object state replacement for #%d at effect #%d available.\n",
363               input->id(), effect->id());
364         UNREACHABLE();
365       }
366     }
367   }
368   return clone;
369 }
370 
371 
VerifyReplacement() const372 void EscapeAnalysisReducer::VerifyReplacement() const {
373 #ifdef DEBUG
374   AllNodes all(zone(), jsgraph()->graph());
375   for (Node* node : all.reachable) {
376     if (node->opcode() == IrOpcode::kAllocate) {
377       CHECK(!escape_analysis_->IsVirtual(node));
378     }
379   }
380 #endif  // DEBUG
381 }
382 
383 }  // namespace compiler
384 }  // namespace internal
385 }  // namespace v8
386