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