OLD | NEW |
| (Empty) |
1 // Copyright 2014 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/v8.h" | |
6 #include "test/cctest/cctest.h" | |
7 | |
8 #include "src/base/bits.h" | |
9 #include "src/compiler/all-nodes.h" | |
10 #include "src/compiler/common-operator.h" | |
11 #include "src/compiler/control-reducer.h" | |
12 #include "src/compiler/graph.h" | |
13 #include "src/compiler/js-graph.h" | |
14 #include "src/compiler/node-properties.h" | |
15 | |
16 using namespace v8::internal; | |
17 using namespace v8::internal::compiler; | |
18 | |
19 static const size_t kNumLeafs = 4; | |
20 | |
21 enum Decision { kFalse, kUnknown, kTrue }; | |
22 | |
23 // TODO(titzer): convert this whole file into unit tests. | |
24 | |
25 static int CheckInputs(Node* node, Node* i0 = NULL, Node* i1 = NULL, | |
26 Node* i2 = NULL) { | |
27 int count = 3; | |
28 if (i2 == NULL) count = 2; | |
29 if (i1 == NULL) count = 1; | |
30 if (i0 == NULL) count = 0; | |
31 CHECK_EQ(count, node->InputCount()); | |
32 if (i0 != NULL) CHECK_EQ(i0, node->InputAt(0)); | |
33 if (i1 != NULL) CHECK_EQ(i1, node->InputAt(1)); | |
34 if (i2 != NULL) CHECK_EQ(i2, node->InputAt(2)); | |
35 return count; | |
36 } | |
37 | |
38 | |
39 static int CheckMerge(Node* node, Node* i0 = NULL, Node* i1 = NULL, | |
40 Node* i2 = NULL) { | |
41 CHECK_EQ(IrOpcode::kMerge, node->opcode()); | |
42 int count = CheckInputs(node, i0, i1, i2); | |
43 CHECK_EQ(count, node->op()->ControlInputCount()); | |
44 return count; | |
45 } | |
46 | |
47 | |
48 static int CheckLoop(Node* node, Node* i0 = NULL, Node* i1 = NULL, | |
49 Node* i2 = NULL) { | |
50 CHECK_EQ(IrOpcode::kLoop, node->opcode()); | |
51 int count = CheckInputs(node, i0, i1, i2); | |
52 CHECK_EQ(count, node->op()->ControlInputCount()); | |
53 return count; | |
54 } | |
55 | |
56 | |
57 bool IsUsedBy(Node* a, Node* b) { | |
58 auto const uses = a->uses(); | |
59 return std::find(uses.begin(), uses.end(), b) != uses.end(); | |
60 } | |
61 | |
62 | |
63 // A helper for all tests dealing with ControlTester. | |
64 class ControlReducerTester : HandleAndZoneScope { | |
65 public: | |
66 ControlReducerTester() | |
67 : isolate(main_isolate()), | |
68 common(main_zone()), | |
69 graph(main_zone()), | |
70 jsgraph(main_isolate(), &graph, &common, NULL, NULL), | |
71 start(graph.NewNode(common.Start(1))), | |
72 end(graph.NewNode(common.End(1), start)), | |
73 p0(graph.NewNode(common.Parameter(0), start)), | |
74 zero(jsgraph.Int32Constant(0)), | |
75 one(jsgraph.OneConstant()), | |
76 half(jsgraph.Constant(0.5)), | |
77 self(graph.NewNode(common.Int32Constant(0xaabbccdd))), | |
78 dead(graph.NewNode(common.DeadControl())) { | |
79 graph.SetEnd(end); | |
80 graph.SetStart(start); | |
81 leaf[0] = zero; | |
82 leaf[1] = one; | |
83 leaf[2] = half; | |
84 leaf[3] = p0; | |
85 } | |
86 | |
87 Isolate* isolate; | |
88 CommonOperatorBuilder common; | |
89 Graph graph; | |
90 JSGraph jsgraph; | |
91 Node* start; | |
92 Node* end; | |
93 Node* p0; | |
94 Node* zero; | |
95 Node* one; | |
96 Node* half; | |
97 Node* self; | |
98 Node* dead; | |
99 Node* leaf[kNumLeafs]; | |
100 | |
101 Node* Phi(Node* a) { | |
102 return SetSelfReferences(graph.NewNode(op(1, false), a, start)); | |
103 } | |
104 | |
105 Node* Phi(Node* a, Node* b) { | |
106 return SetSelfReferences(graph.NewNode(op(2, false), a, b, start)); | |
107 } | |
108 | |
109 Node* Phi(Node* a, Node* b, Node* c) { | |
110 return SetSelfReferences(graph.NewNode(op(3, false), a, b, c, start)); | |
111 } | |
112 | |
113 Node* Phi(Node* a, Node* b, Node* c, Node* d) { | |
114 return SetSelfReferences(graph.NewNode(op(4, false), a, b, c, d, start)); | |
115 } | |
116 | |
117 Node* EffectPhi(Node* a) { | |
118 return SetSelfReferences(graph.NewNode(op(1, true), a, start)); | |
119 } | |
120 | |
121 Node* EffectPhi(Node* a, Node* b) { | |
122 return SetSelfReferences(graph.NewNode(op(2, true), a, b, start)); | |
123 } | |
124 | |
125 Node* EffectPhi(Node* a, Node* b, Node* c) { | |
126 return SetSelfReferences(graph.NewNode(op(3, true), a, b, c, start)); | |
127 } | |
128 | |
129 Node* EffectPhi(Node* a, Node* b, Node* c, Node* d) { | |
130 return SetSelfReferences(graph.NewNode(op(4, true), a, b, c, d, start)); | |
131 } | |
132 | |
133 Node* SetSelfReferences(Node* node) { | |
134 for (Edge edge : node->input_edges()) { | |
135 if (edge.to() == self) node->ReplaceInput(edge.index(), node); | |
136 } | |
137 return node; | |
138 } | |
139 | |
140 const Operator* op(int count, bool effect) { | |
141 return effect ? common.EffectPhi(count) : common.Phi(kMachAnyTagged, count); | |
142 } | |
143 | |
144 void ReduceGraph() { ControlReducer::ReduceGraph(main_zone(), &jsgraph); } | |
145 | |
146 void ReduceMerge(Node* expect, Node* merge) { | |
147 Node* result = ControlReducer::ReduceMerge(&jsgraph, merge); | |
148 CHECK_EQ(expect, result); | |
149 } | |
150 | |
151 void ReduceMergeIterative(Node* expect, Node* merge) { | |
152 p0->ReplaceInput(0, start); // hack: parameters may be trimmed. | |
153 Node* end = graph.NewNode(common.End(1), merge); | |
154 graph.SetEnd(end); | |
155 ReduceGraph(); | |
156 CheckInputs(end, expect); | |
157 } | |
158 | |
159 void ReduceBranch(Decision expected, Node* branch) { | |
160 Node* control = branch->InputAt(1); | |
161 for (Node* use : branch->uses()) { | |
162 if (use->opcode() == IrOpcode::kIfTrue) { | |
163 Node* result = ControlReducer::ReduceIfNodeForTesting(&jsgraph, use); | |
164 if (expected == kTrue) CHECK_EQ(control, result); | |
165 if (expected == kFalse) { | |
166 CHECK_EQ(IrOpcode::kDeadControl, result->opcode()); | |
167 } | |
168 if (expected == kUnknown) CHECK_EQ(use, result); | |
169 } else if (use->opcode() == IrOpcode::kIfFalse) { | |
170 Node* result = ControlReducer::ReduceIfNodeForTesting(&jsgraph, use); | |
171 if (expected == kFalse) CHECK_EQ(control, result); | |
172 if (expected == kTrue) { | |
173 CHECK_EQ(IrOpcode::kDeadControl, result->opcode()); | |
174 } | |
175 if (expected == kUnknown) CHECK_EQ(use, result); | |
176 } else { | |
177 UNREACHABLE(); | |
178 } | |
179 } | |
180 } | |
181 | |
182 Node* Return(Node* val, Node* effect, Node* control) { | |
183 Node* ret = graph.NewNode(common.Return(), val, effect, control); | |
184 end->ReplaceInput(0, ret); | |
185 return ret; | |
186 } | |
187 }; | |
188 | |
189 | |
190 struct Branch { | |
191 Node* branch; | |
192 Node* if_true; | |
193 Node* if_false; | |
194 | |
195 Branch(ControlReducerTester& R, Node* cond, Node* control = NULL) { | |
196 if (control == NULL) control = R.start; | |
197 branch = R.graph.NewNode(R.common.Branch(), cond, control); | |
198 if_true = R.graph.NewNode(R.common.IfTrue(), branch); | |
199 if_false = R.graph.NewNode(R.common.IfFalse(), branch); | |
200 } | |
201 }; | |
202 | |
203 | |
204 // TODO(titzer): use the diamonds from src/compiler/diamond.h here. | |
205 struct Diamond { | |
206 Node* branch; | |
207 Node* if_true; | |
208 Node* if_false; | |
209 Node* merge; | |
210 Node* phi; | |
211 | |
212 Diamond(ControlReducerTester& R, Node* cond) { | |
213 branch = R.graph.NewNode(R.common.Branch(), cond, R.start); | |
214 if_true = R.graph.NewNode(R.common.IfTrue(), branch); | |
215 if_false = R.graph.NewNode(R.common.IfFalse(), branch); | |
216 merge = R.graph.NewNode(R.common.Merge(2), if_true, if_false); | |
217 phi = NULL; | |
218 } | |
219 | |
220 Diamond(ControlReducerTester& R, Node* cond, Node* tv, Node* fv) { | |
221 branch = R.graph.NewNode(R.common.Branch(), cond, R.start); | |
222 if_true = R.graph.NewNode(R.common.IfTrue(), branch); | |
223 if_false = R.graph.NewNode(R.common.IfFalse(), branch); | |
224 merge = R.graph.NewNode(R.common.Merge(2), if_true, if_false); | |
225 phi = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 2), tv, fv, merge); | |
226 } | |
227 | |
228 void chain(Diamond& that) { branch->ReplaceInput(1, that.merge); } | |
229 | |
230 // Nest {this} into either the if_true or if_false branch of {that}. | |
231 void nest(Diamond& that, bool if_true) { | |
232 if (if_true) { | |
233 branch->ReplaceInput(1, that.if_true); | |
234 that.merge->ReplaceInput(0, merge); | |
235 } else { | |
236 branch->ReplaceInput(1, that.if_false); | |
237 that.merge->ReplaceInput(1, merge); | |
238 } | |
239 } | |
240 }; | |
241 | |
242 | |
243 struct While { | |
244 Node* branch; | |
245 Node* if_true; | |
246 Node* exit; | |
247 Node* loop; | |
248 | |
249 While(ControlReducerTester& R, Node* cond) { | |
250 loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start); | |
251 branch = R.graph.NewNode(R.common.Branch(), cond, loop); | |
252 if_true = R.graph.NewNode(R.common.IfTrue(), branch); | |
253 exit = R.graph.NewNode(R.common.IfFalse(), branch); | |
254 loop->ReplaceInput(1, if_true); | |
255 } | |
256 | |
257 void chain(Node* control) { loop->ReplaceInput(0, control); } | |
258 }; | |
259 | |
260 | |
261 // Helper for checking that nodes are *not* reachable from end. | |
262 struct DeadChecker { | |
263 Zone zone; | |
264 AllNodes nodes; | |
265 explicit DeadChecker(Graph* graph) : zone(), nodes(&zone, graph) {} | |
266 | |
267 void Check(Node* node) { CHECK(!nodes.IsLive(node)); } | |
268 | |
269 void Check(Diamond& d) { | |
270 Check(d.branch); | |
271 Check(d.if_true); | |
272 Check(d.if_false); | |
273 Check(d.merge); | |
274 if (d.phi != NULL) Check(d.phi); | |
275 } | |
276 | |
277 void CheckLive(Diamond& d, bool live_phi = true) { | |
278 CheckInputs(d.merge, d.if_true, d.if_false); | |
279 CheckInputs(d.if_true, d.branch); | |
280 CheckInputs(d.if_false, d.branch); | |
281 if (d.phi != NULL) { | |
282 if (live_phi) { | |
283 CHECK_EQ(3, d.phi->InputCount()); | |
284 CHECK_EQ(d.merge, d.phi->InputAt(2)); | |
285 } else { | |
286 Check(d.phi); | |
287 } | |
288 } | |
289 } | |
290 }; | |
291 | |
292 | |
293 TEST(CMergeReduce_simple1) { | |
294 ControlReducerTester R; | |
295 | |
296 Node* merge = R.graph.NewNode(R.common.Merge(1), R.start); | |
297 R.ReduceMerge(R.start, merge); | |
298 } | |
299 | |
300 | |
301 TEST(CMergeReduce_simple2) { | |
302 ControlReducerTester R; | |
303 | |
304 Node* merge1 = R.graph.NewNode(R.common.Merge(1), R.start); | |
305 Node* merge2 = R.graph.NewNode(R.common.Merge(1), merge1); | |
306 R.ReduceMerge(merge1, merge2); | |
307 R.ReduceMergeIterative(R.start, merge2); | |
308 } | |
309 | |
310 | |
311 TEST(CMergeReduce_none1) { | |
312 ControlReducerTester R; | |
313 | |
314 Node* merge = R.graph.NewNode(R.common.Merge(2), R.start, R.start); | |
315 R.ReduceMerge(merge, merge); | |
316 } | |
317 | |
318 | |
319 TEST(CMergeReduce_none2) { | |
320 ControlReducerTester R; | |
321 | |
322 Node* t1 = R.graph.NewNode(R.common.IfTrue(), R.start); | |
323 Node* t2 = R.graph.NewNode(R.common.IfTrue(), R.start); | |
324 Node* merge = R.graph.NewNode(R.common.Merge(2), t1, t2); | |
325 R.ReduceMerge(merge, merge); | |
326 } | |
327 | |
328 | |
329 TEST(CMergeReduce_self3) { | |
330 ControlReducerTester R; | |
331 | |
332 Node* merge = | |
333 R.SetSelfReferences(R.graph.NewNode(R.common.Merge(2), R.start, R.self)); | |
334 R.ReduceMerge(merge, merge); | |
335 } | |
336 | |
337 | |
338 TEST(CMergeReduce_dead1) { | |
339 ControlReducerTester R; | |
340 | |
341 Node* merge = R.graph.NewNode(R.common.Merge(2), R.start, R.dead); | |
342 R.ReduceMerge(R.start, merge); | |
343 } | |
344 | |
345 | |
346 TEST(CMergeReduce_dead2) { | |
347 ControlReducerTester R; | |
348 | |
349 Node* merge1 = R.graph.NewNode(R.common.Merge(1), R.start); | |
350 Node* merge2 = R.graph.NewNode(R.common.Merge(2), merge1, R.dead); | |
351 R.ReduceMerge(merge1, merge2); | |
352 R.ReduceMergeIterative(R.start, merge2); | |
353 } | |
354 | |
355 | |
356 TEST(CMergeReduce_dead_rm1a) { | |
357 ControlReducerTester R; | |
358 | |
359 for (int i = 0; i < 3; i++) { | |
360 Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start); | |
361 merge->ReplaceInput(i, R.dead); | |
362 R.ReduceMerge(merge, merge); | |
363 CheckMerge(merge, R.start, R.start); | |
364 } | |
365 } | |
366 | |
367 | |
368 TEST(CMergeReduce_dead_rm1b) { | |
369 ControlReducerTester R; | |
370 | |
371 Node* t = R.graph.NewNode(R.common.IfTrue(), R.start); | |
372 Node* f = R.graph.NewNode(R.common.IfTrue(), R.start); | |
373 for (int i = 0; i < 2; i++) { | |
374 for (int j = i + 1; j < 3; j++) { | |
375 Node* merge = R.graph.NewNode(R.common.Merge(3), R.dead, R.dead, R.dead); | |
376 merge->ReplaceInput(i, t); | |
377 merge->ReplaceInput(j, f); | |
378 R.ReduceMerge(merge, merge); | |
379 CheckMerge(merge, t, f); | |
380 } | |
381 } | |
382 } | |
383 | |
384 | |
385 TEST(CMergeReduce_dead_rm2) { | |
386 ControlReducerTester R; | |
387 | |
388 for (int i = 0; i < 3; i++) { | |
389 Node* merge = R.graph.NewNode(R.common.Merge(3), R.dead, R.dead, R.dead); | |
390 merge->ReplaceInput(i, R.start); | |
391 R.ReduceMerge(R.start, merge); | |
392 } | |
393 } | |
394 | |
395 | |
396 TEST(CLoopReduce_dead_rm1) { | |
397 ControlReducerTester R; | |
398 | |
399 for (int i = 0; i < 3; i++) { | |
400 Node* loop = R.graph.NewNode(R.common.Loop(3), R.dead, R.start, R.start); | |
401 R.ReduceMerge(loop, loop); | |
402 CheckLoop(loop, R.start, R.start); | |
403 } | |
404 } | |
405 | |
406 | |
407 TEST(CMergeReduce_edit_phi1) { | |
408 ControlReducerTester R; | |
409 | |
410 for (int i = 0; i < 3; i++) { | |
411 Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start); | |
412 merge->ReplaceInput(i, R.dead); | |
413 Node* phi = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 3), R.leaf[0], | |
414 R.leaf[1], R.leaf[2], merge); | |
415 R.ReduceMerge(merge, merge); | |
416 CHECK_EQ(IrOpcode::kPhi, phi->opcode()); | |
417 CHECK_EQ(2, phi->op()->ValueInputCount()); | |
418 CHECK_EQ(3, phi->InputCount()); | |
419 CHECK_EQ(R.leaf[i < 1 ? 1 : 0], phi->InputAt(0)); | |
420 CHECK_EQ(R.leaf[i < 2 ? 2 : 1], phi->InputAt(1)); | |
421 CHECK_EQ(merge, phi->InputAt(2)); | |
422 } | |
423 } | |
424 | |
425 | |
426 TEST(CMergeReduce_edit_effect_phi1) { | |
427 ControlReducerTester R; | |
428 | |
429 for (int i = 0; i < 3; i++) { | |
430 Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start); | |
431 merge->ReplaceInput(i, R.dead); | |
432 Node* phi = R.graph.NewNode(R.common.EffectPhi(3), R.leaf[0], R.leaf[1], | |
433 R.leaf[2], merge); | |
434 R.ReduceMerge(merge, merge); | |
435 CHECK_EQ(IrOpcode::kEffectPhi, phi->opcode()); | |
436 CHECK_EQ(0, phi->op()->ValueInputCount()); | |
437 CHECK_EQ(2, phi->op()->EffectInputCount()); | |
438 CHECK_EQ(3, phi->InputCount()); | |
439 CHECK_EQ(R.leaf[i < 1 ? 1 : 0], phi->InputAt(0)); | |
440 CHECK_EQ(R.leaf[i < 2 ? 2 : 1], phi->InputAt(1)); | |
441 CHECK_EQ(merge, phi->InputAt(2)); | |
442 } | |
443 } | |
444 | |
445 | |
446 static const int kSelectorSize = 4; | |
447 | |
448 // Helper to select K of N nodes according to a mask, useful for the test below. | |
449 struct Selector { | |
450 int mask; | |
451 int count; | |
452 explicit Selector(int m) { | |
453 mask = m; | |
454 count = v8::base::bits::CountPopulation32(m); | |
455 } | |
456 bool is_selected(int i) { return (mask & (1 << i)) != 0; } | |
457 void CheckNode(Node* node, IrOpcode::Value opcode, Node** inputs, | |
458 Node* control) { | |
459 CHECK_EQ(opcode, node->opcode()); | |
460 CHECK_EQ(count + (control != NULL ? 1 : 0), node->InputCount()); | |
461 int index = 0; | |
462 for (int i = 0; i < kSelectorSize; i++) { | |
463 if (mask & (1 << i)) { | |
464 CHECK_EQ(inputs[i], node->InputAt(index++)); | |
465 } | |
466 } | |
467 CHECK_EQ(count, index); | |
468 if (control != NULL) CHECK_EQ(control, node->InputAt(index++)); | |
469 } | |
470 int single_index() { | |
471 CHECK_EQ(1, count); | |
472 return WhichPowerOf2(mask); | |
473 } | |
474 }; | |
475 | |
476 | |
477 TEST(CMergeReduce_exhaustive_4) { | |
478 ControlReducerTester R; | |
479 Node* controls[] = { | |
480 R.graph.NewNode(R.common.Start(1)), R.graph.NewNode(R.common.Start(2)), | |
481 R.graph.NewNode(R.common.Start(3)), R.graph.NewNode(R.common.Start(4))}; | |
482 Node* values[] = {R.jsgraph.Int32Constant(11), R.jsgraph.Int32Constant(22), | |
483 R.jsgraph.Int32Constant(33), R.jsgraph.Int32Constant(44)}; | |
484 Node* effects[] = { | |
485 R.jsgraph.Float64Constant(123.4), R.jsgraph.Float64Constant(223.4), | |
486 R.jsgraph.Float64Constant(323.4), R.jsgraph.Float64Constant(423.4)}; | |
487 | |
488 for (int mask = 0; mask < (1 << (kSelectorSize - 1)); mask++) { | |
489 // Reduce a single merge with a given mask. | |
490 Node* merge = R.graph.NewNode(R.common.Merge(4), controls[0], controls[1], | |
491 controls[2], controls[3]); | |
492 Node* phi = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 4), values[0], | |
493 values[1], values[2], values[3], merge); | |
494 Node* ephi = R.graph.NewNode(R.common.EffectPhi(4), effects[0], effects[1], | |
495 effects[2], effects[3], merge); | |
496 | |
497 Node* phi_use = | |
498 R.graph.NewNode(R.common.Phi(kMachAnyTagged, 1), phi, R.start); | |
499 Node* ephi_use = R.graph.NewNode(R.common.EffectPhi(1), ephi, R.start); | |
500 | |
501 Selector selector(mask); | |
502 | |
503 for (int i = 0; i < kSelectorSize; i++) { // set up dead merge inputs. | |
504 if (!selector.is_selected(i)) merge->ReplaceInput(i, R.dead); | |
505 } | |
506 | |
507 Node* result = ControlReducer::ReduceMerge(&R.jsgraph, merge); | |
508 | |
509 int count = selector.count; | |
510 if (count == 0) { | |
511 // result should be dead. | |
512 CHECK_EQ(IrOpcode::kDeadControl, result->opcode()); | |
513 } else if (count == 1) { | |
514 // merge should be replaced with one of the controls. | |
515 CHECK_EQ(controls[selector.single_index()], result); | |
516 // Phis should have been directly replaced. | |
517 CHECK_EQ(values[selector.single_index()], phi_use->InputAt(0)); | |
518 CHECK_EQ(effects[selector.single_index()], ephi_use->InputAt(0)); | |
519 } else { | |
520 // Otherwise, nodes should be edited in place. | |
521 CHECK_EQ(merge, result); | |
522 selector.CheckNode(merge, IrOpcode::kMerge, controls, NULL); | |
523 selector.CheckNode(phi, IrOpcode::kPhi, values, merge); | |
524 selector.CheckNode(ephi, IrOpcode::kEffectPhi, effects, merge); | |
525 CHECK_EQ(phi, phi_use->InputAt(0)); | |
526 CHECK_EQ(ephi, ephi_use->InputAt(0)); | |
527 CHECK_EQ(count, phi->op()->ValueInputCount()); | |
528 CHECK_EQ(count + 1, phi->InputCount()); | |
529 CHECK_EQ(count, ephi->op()->EffectInputCount()); | |
530 CHECK_EQ(count + 1, ephi->InputCount()); | |
531 } | |
532 } | |
533 } | |
534 | |
535 | |
536 TEST(CMergeReduce_edit_many_phis1) { | |
537 ControlReducerTester R; | |
538 | |
539 const int kPhiCount = 10; | |
540 Node* phis[kPhiCount]; | |
541 | |
542 for (int i = 0; i < 3; i++) { | |
543 Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start); | |
544 merge->ReplaceInput(i, R.dead); | |
545 for (int j = 0; j < kPhiCount; j++) { | |
546 phis[j] = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 3), R.leaf[0], | |
547 R.leaf[1], R.leaf[2], merge); | |
548 } | |
549 R.ReduceMerge(merge, merge); | |
550 for (int j = 0; j < kPhiCount; j++) { | |
551 Node* phi = phis[j]; | |
552 CHECK_EQ(IrOpcode::kPhi, phi->opcode()); | |
553 CHECK_EQ(2, phi->op()->ValueInputCount()); | |
554 CHECK_EQ(3, phi->InputCount()); | |
555 CHECK_EQ(R.leaf[i < 1 ? 1 : 0], phi->InputAt(0)); | |
556 CHECK_EQ(R.leaf[i < 2 ? 2 : 1], phi->InputAt(1)); | |
557 CHECK_EQ(merge, phi->InputAt(2)); | |
558 } | |
559 } | |
560 } | |
561 | |
562 | |
563 TEST(CMergeReduce_simple_chain1) { | |
564 ControlReducerTester R; | |
565 for (int i = 0; i < 5; i++) { | |
566 Node* merge = R.graph.NewNode(R.common.Merge(1), R.start); | |
567 for (int j = 0; j < i; j++) { | |
568 merge = R.graph.NewNode(R.common.Merge(1), merge); | |
569 } | |
570 R.ReduceMergeIterative(R.start, merge); | |
571 } | |
572 } | |
573 | |
574 | |
575 TEST(CMergeReduce_dead_chain1) { | |
576 ControlReducerTester R; | |
577 for (int i = 0; i < 5; i++) { | |
578 Node* merge = R.graph.NewNode(R.common.Merge(1), R.dead); | |
579 for (int j = 0; j < i; j++) { | |
580 merge = R.graph.NewNode(R.common.Merge(1), merge); | |
581 } | |
582 Node* end = R.graph.NewNode(R.common.End(1), merge); | |
583 R.graph.SetEnd(end); | |
584 R.ReduceGraph(); | |
585 CHECK(merge->IsDead()); | |
586 CHECK(!end->InputAt(0)); // end dies. | |
587 } | |
588 } | |
589 | |
590 | |
591 TEST(CMergeReduce_dead_chain2) { | |
592 ControlReducerTester R; | |
593 for (int i = 0; i < 5; i++) { | |
594 Node* merge = R.graph.NewNode(R.common.Merge(1), R.start); | |
595 for (int j = 0; j < i; j++) { | |
596 merge = R.graph.NewNode(R.common.Merge(2), merge, R.dead); | |
597 } | |
598 R.ReduceMergeIterative(R.start, merge); | |
599 } | |
600 } | |
601 | |
602 | |
603 TEST(CBranchReduce_none1) { | |
604 ControlReducerTester R; | |
605 Diamond d(R, R.p0); | |
606 R.ReduceBranch(kUnknown, d.branch); | |
607 } | |
608 | |
609 | |
610 TEST(CBranchReduce_none2) { | |
611 ControlReducerTester R; | |
612 Diamond d1(R, R.p0); | |
613 Diamond d2(R, R.p0); | |
614 d2.chain(d1); | |
615 R.ReduceBranch(kUnknown, d2.branch); | |
616 } | |
617 | |
618 | |
619 TEST(CBranchReduce_true) { | |
620 ControlReducerTester R; | |
621 Node* true_values[] = {R.jsgraph.Int32Constant(2), | |
622 R.jsgraph.Int64Constant(0x7fffffff), | |
623 R.jsgraph.TrueConstant()}; | |
624 for (size_t i = 0; i < arraysize(true_values); i++) { | |
625 Diamond d(R, true_values[i]); | |
626 R.ReduceBranch(kTrue, d.branch); | |
627 } | |
628 } | |
629 | |
630 | |
631 TEST(CBranchReduce_false) { | |
632 ControlReducerTester R; | |
633 Node* false_values[] = {R.jsgraph.Int32Constant(0), | |
634 R.jsgraph.Int64Constant(0), | |
635 R.jsgraph.FalseConstant()}; | |
636 for (size_t i = 0; i < arraysize(false_values); i++) { | |
637 Diamond d(R, false_values[i]); | |
638 R.ReduceBranch(kFalse, d.branch); | |
639 } | |
640 } | |
641 | |
642 | |
643 TEST(CDiamondReduce_true) { | |
644 ControlReducerTester R; | |
645 Diamond d1(R, R.jsgraph.TrueConstant()); | |
646 R.ReduceMergeIterative(R.start, d1.merge); | |
647 } | |
648 | |
649 | |
650 TEST(CDiamondReduce_false) { | |
651 ControlReducerTester R; | |
652 Diamond d2(R, R.jsgraph.FalseConstant()); | |
653 R.ReduceMergeIterative(R.start, d2.merge); | |
654 } | |
655 | |
656 | |
657 TEST(CChainedDiamondsReduce_true_false) { | |
658 ControlReducerTester R; | |
659 Diamond d1(R, R.jsgraph.TrueConstant()); | |
660 Diamond d2(R, R.jsgraph.FalseConstant()); | |
661 d2.chain(d1); | |
662 | |
663 R.ReduceMergeIterative(R.start, d2.merge); | |
664 } | |
665 | |
666 | |
667 TEST(CChainedDiamondsReduce_x_false) { | |
668 ControlReducerTester R; | |
669 Diamond d1(R, R.p0); | |
670 Diamond d2(R, R.jsgraph.FalseConstant()); | |
671 d2.chain(d1); | |
672 | |
673 R.ReduceMergeIterative(R.start, d2.merge); | |
674 } | |
675 | |
676 | |
677 TEST(CChainedDiamondsReduce_false_x) { | |
678 ControlReducerTester R; | |
679 Diamond d1(R, R.zero); | |
680 Diamond d2(R, R.p0); | |
681 d2.chain(d1); | |
682 | |
683 R.ReduceMergeIterative(R.start, d2.merge); | |
684 } | |
685 | |
686 | |
687 TEST(CChainedDiamondsReduce_phi1) { | |
688 ControlReducerTester R; | |
689 Diamond d1(R, R.zero, R.one, R.zero); // foldable branch, phi. | |
690 Diamond d2(R, d1.phi); | |
691 d2.chain(d1); | |
692 | |
693 R.ReduceMergeIterative(R.start, d2.merge); | |
694 } | |
695 | |
696 | |
697 TEST(CNestedDiamondsReduce_true_true_false) { | |
698 ControlReducerTester R; | |
699 Diamond d1(R, R.jsgraph.TrueConstant()); | |
700 Diamond d2(R, R.jsgraph.FalseConstant()); | |
701 d2.nest(d1, true); | |
702 | |
703 R.ReduceMergeIterative(R.start, d1.merge); | |
704 } | |
705 | |
706 | |
707 TEST(CNestedDiamondsReduce_false_true_false) { | |
708 ControlReducerTester R; | |
709 Diamond d1(R, R.jsgraph.TrueConstant()); | |
710 Diamond d2(R, R.jsgraph.FalseConstant()); | |
711 d2.nest(d1, false); | |
712 | |
713 R.ReduceMergeIterative(R.start, d1.merge); | |
714 } | |
715 | |
716 | |
717 TEST(CNestedDiamonds_xyz) { | |
718 ControlReducerTester R; | |
719 | |
720 for (int a = 0; a < 2; a++) { | |
721 for (int b = 0; b < 2; b++) { | |
722 for (int c = 0; c < 2; c++) { | |
723 Diamond d1(R, R.jsgraph.Int32Constant(a)); | |
724 Diamond d2(R, R.jsgraph.Int32Constant(b)); | |
725 d2.nest(d1, c); | |
726 | |
727 R.ReduceMergeIterative(R.start, d1.merge); | |
728 } | |
729 } | |
730 } | |
731 } | |
732 | |
733 | |
734 TEST(CUnusedDiamond1) { | |
735 ControlReducerTester R; | |
736 // if (p0) { } else { } | |
737 Node* branch = R.graph.NewNode(R.common.Branch(), R.p0, R.start); | |
738 Node* if_true = R.graph.NewNode(R.common.IfTrue(), branch); | |
739 Node* if_false = R.graph.NewNode(R.common.IfFalse(), branch); | |
740 Node* merge = R.graph.NewNode(R.common.Merge(2), if_true, if_false); | |
741 R.ReduceMergeIterative(R.start, merge); | |
742 } | |
743 | |
744 | |
745 TEST(CUnusedDiamond2) { | |
746 ControlReducerTester R; | |
747 // if (p0) { } else { } | |
748 Node* branch = R.graph.NewNode(R.common.Branch(), R.p0, R.start); | |
749 Node* if_true = R.graph.NewNode(R.common.IfTrue(), branch); | |
750 Node* if_false = R.graph.NewNode(R.common.IfFalse(), branch); | |
751 Node* merge = R.graph.NewNode(R.common.Merge(2), if_false, if_true); | |
752 R.ReduceMergeIterative(R.start, merge); | |
753 } | |
754 | |
755 | |
756 TEST(CDeadLoop1) { | |
757 ControlReducerTester R; | |
758 | |
759 While w(R, R.p0); | |
760 Diamond d(R, R.zero); | |
761 // if (0) { while (p0) ; } else { } | |
762 w.branch->ReplaceInput(1, d.if_true); | |
763 d.merge->ReplaceInput(0, w.exit); | |
764 | |
765 R.ReduceMergeIterative(R.start, d.merge); | |
766 | |
767 DeadChecker dead(&R.graph); | |
768 dead.Check(d); | |
769 dead.Check(w.loop); | |
770 } | |
771 | |
772 | |
773 TEST(Return1) { | |
774 ControlReducerTester R; | |
775 Node* ret = R.Return(R.one, R.start, R.start); | |
776 R.ReduceGraph(); | |
777 CheckInputs(R.graph.end(), ret); | |
778 CheckInputs(ret, R.one, R.start, R.start); | |
779 } | |
780 | |
781 | |
782 TEST(Return2) { | |
783 ControlReducerTester R; | |
784 Diamond d(R, R.jsgraph.TrueConstant()); | |
785 Node* ret = R.Return(R.half, R.start, d.merge); | |
786 R.ReduceGraph(); | |
787 | |
788 DeadChecker dead(&R.graph); | |
789 dead.Check(d.branch); | |
790 dead.Check(d.if_true); | |
791 dead.Check(d.if_false); | |
792 dead.Check(d.merge); | |
793 | |
794 CheckInputs(R.graph.end(), ret); | |
795 CheckInputs(ret, R.half, R.start, R.start); | |
796 } | |
797 | |
798 | |
799 TEST(Return_true1) { | |
800 ControlReducerTester R; | |
801 Diamond d(R, R.jsgraph.TrueConstant(), R.half, R.zero); | |
802 Node* ret = R.Return(d.phi, R.start, d.merge); | |
803 R.ReduceGraph(); | |
804 | |
805 DeadChecker dead(&R.graph); | |
806 dead.Check(d.branch); | |
807 dead.Check(d.if_true); | |
808 dead.Check(d.if_false); | |
809 dead.Check(d.merge); | |
810 dead.Check(d.phi); | |
811 | |
812 CheckInputs(R.graph.end(), ret); | |
813 CheckInputs(ret, R.half, R.start, R.start); | |
814 } | |
815 | |
816 | |
817 TEST(Return_false1) { | |
818 ControlReducerTester R; | |
819 Diamond d(R, R.jsgraph.FalseConstant(), R.one, R.half); | |
820 Node* ret = R.Return(d.phi, R.start, d.merge); | |
821 R.ReduceGraph(); | |
822 | |
823 DeadChecker dead(&R.graph); | |
824 dead.Check(d.branch); | |
825 dead.Check(d.if_true); | |
826 dead.Check(d.if_false); | |
827 dead.Check(d.merge); | |
828 dead.Check(d.phi); | |
829 | |
830 CheckInputs(R.graph.end(), ret); | |
831 CheckInputs(ret, R.half, R.start, R.start); | |
832 } | |
833 | |
834 | |
835 TEST(Return_effect1) { | |
836 ControlReducerTester R; | |
837 Diamond d(R, R.jsgraph.TrueConstant()); | |
838 Node* e1 = R.jsgraph.Float64Constant(-100.1); | |
839 Node* e2 = R.jsgraph.Float64Constant(+100.1); | |
840 Node* effect = R.graph.NewNode(R.common.EffectPhi(2), e1, e2, d.merge); | |
841 Node* ret = R.Return(R.p0, effect, d.merge); | |
842 R.ReduceGraph(); | |
843 | |
844 DeadChecker dead(&R.graph); | |
845 dead.Check(d); | |
846 dead.Check(effect); | |
847 | |
848 CheckInputs(R.graph.end(), ret); | |
849 CheckInputs(ret, R.p0, e1, R.start); | |
850 } | |
851 | |
852 | |
853 TEST(Return_nested_diamonds1) { | |
854 ControlReducerTester R; | |
855 Diamond d2(R, R.p0, R.one, R.zero); | |
856 Diamond d3(R, R.p0, R.one, R.zero); | |
857 Diamond d1(R, R.p0, d2.phi, d3.phi); | |
858 | |
859 d2.nest(d1, true); | |
860 d3.nest(d1, false); | |
861 | |
862 Node* ret = R.Return(d1.phi, R.start, d1.merge); | |
863 | |
864 R.ReduceGraph(); // nothing should happen. | |
865 | |
866 CheckInputs(ret, d1.phi, R.start, d1.merge); | |
867 CheckInputs(d1.phi, d2.phi, d3.phi, d1.merge); | |
868 CheckInputs(d1.merge, d2.merge, d3.merge); | |
869 | |
870 DeadChecker dead(&R.graph); | |
871 dead.CheckLive(d2); | |
872 dead.CheckLive(d3); | |
873 } | |
874 | |
875 | |
876 TEST(Return_nested_diamonds1_dead1) { | |
877 ControlReducerTester R; | |
878 Diamond d2(R, R.p0); // dead diamond | |
879 Diamond d3(R, R.p0, R.one, R.zero); | |
880 Diamond d1(R, R.p0, R.one, d3.phi); | |
881 | |
882 d2.nest(d1, true); | |
883 d3.nest(d1, false); | |
884 | |
885 Node* ret = R.Return(d1.phi, R.start, d1.merge); | |
886 | |
887 R.ReduceGraph(); | |
888 | |
889 CheckInputs(ret, d1.phi, R.start, d1.merge); | |
890 CheckInputs(d1.phi, R.one, d3.phi, d1.merge); | |
891 CheckInputs(d1.merge, d1.if_true, d3.merge); | |
892 | |
893 DeadChecker dead(&R.graph); | |
894 dead.Check(d2); // d2 was a dead diamond. | |
895 dead.CheckLive(d3); | |
896 } | |
897 | |
898 | |
899 TEST(Return_nested_diamonds1_dead2) { | |
900 ControlReducerTester R; | |
901 Diamond d2(R, R.p0); // dead diamond | |
902 Diamond d3(R, R.p0); // dead diamond | |
903 Diamond d1(R, R.p0, R.one, R.zero); | |
904 | |
905 d2.nest(d1, true); | |
906 d3.nest(d1, false); | |
907 | |
908 Node* ret = R.Return(d1.phi, R.start, d1.merge); | |
909 | |
910 R.ReduceGraph(); | |
911 | |
912 CheckInputs(ret, d1.phi, R.start, d1.merge); | |
913 CheckInputs(d1.phi, R.one, R.zero, d1.merge); | |
914 CheckInputs(d1.merge, d1.if_true, d1.if_false); | |
915 | |
916 DeadChecker dead(&R.graph); | |
917 dead.Check(d2); | |
918 dead.Check(d3); | |
919 } | |
920 | |
921 | |
922 TEST(Return_nested_diamonds_true1) { | |
923 ControlReducerTester R; | |
924 Diamond d2(R, R.p0, R.one, R.zero); | |
925 Diamond d1(R, R.jsgraph.TrueConstant(), d2.phi, R.zero); | |
926 Diamond d3(R, R.p0); | |
927 | |
928 d2.nest(d1, true); | |
929 d3.nest(d1, false); | |
930 | |
931 Node* ret = R.Return(d1.phi, R.start, d1.merge); | |
932 | |
933 R.ReduceGraph(); // d1 gets folded true. | |
934 | |
935 CheckInputs(ret, d2.phi, R.start, d2.merge); | |
936 CheckInputs(d2.branch, R.p0, R.start); | |
937 | |
938 DeadChecker dead(&R.graph); | |
939 dead.Check(d1); | |
940 dead.CheckLive(d2); | |
941 dead.Check(d3); | |
942 } | |
943 | |
944 | |
945 TEST(Return_nested_diamonds_false1) { | |
946 ControlReducerTester R; | |
947 Diamond d3(R, R.p0, R.one, R.zero); | |
948 Diamond d1(R, R.zero, R.one, d3.phi); | |
949 Diamond d2(R, R.p0); | |
950 | |
951 d2.nest(d1, true); | |
952 d3.nest(d1, false); | |
953 | |
954 Node* ret = R.Return(d1.phi, R.start, d1.merge); | |
955 | |
956 R.ReduceGraph(); // d1 gets folded false. | |
957 | |
958 CheckInputs(ret, d3.phi, R.start, d3.merge); | |
959 CheckInputs(d3.branch, R.p0, R.start); | |
960 | |
961 DeadChecker dead(&R.graph); | |
962 dead.Check(d1); | |
963 dead.Check(d2); | |
964 dead.CheckLive(d3); | |
965 } | |
966 | |
967 | |
968 TEST(Return_nested_diamonds_true_true1) { | |
969 ControlReducerTester R; | |
970 Diamond d1(R, R.jsgraph.TrueConstant(), R.one, R.zero); | |
971 Diamond d2(R, R.jsgraph.TrueConstant()); | |
972 Diamond d3(R, R.p0); | |
973 | |
974 d2.nest(d1, true); | |
975 d3.nest(d1, false); | |
976 | |
977 Node* ret = R.Return(d1.phi, R.start, d1.merge); | |
978 | |
979 R.ReduceGraph(); // d1 and d2 both get folded true. | |
980 | |
981 CheckInputs(ret, R.one, R.start, R.start); | |
982 | |
983 DeadChecker dead(&R.graph); | |
984 dead.Check(d1); | |
985 dead.Check(d2); | |
986 dead.Check(d3); | |
987 } | |
988 | |
989 | |
990 TEST(Return_nested_diamonds_true_false1) { | |
991 ControlReducerTester R; | |
992 Diamond d1(R, R.jsgraph.TrueConstant(), R.one, R.zero); | |
993 Diamond d2(R, R.jsgraph.FalseConstant()); | |
994 Diamond d3(R, R.p0); | |
995 | |
996 d2.nest(d1, true); | |
997 d3.nest(d1, false); | |
998 | |
999 Node* ret = R.Return(d1.phi, R.start, d1.merge); | |
1000 | |
1001 R.ReduceGraph(); // d1 gets folded true and d2 gets folded false. | |
1002 | |
1003 CheckInputs(ret, R.one, R.start, R.start); | |
1004 | |
1005 DeadChecker dead(&R.graph); | |
1006 dead.Check(d1); | |
1007 dead.Check(d2); | |
1008 dead.Check(d3); | |
1009 } | |
1010 | |
1011 | |
1012 TEST(Return_nested_diamonds2) { | |
1013 ControlReducerTester R; | |
1014 Node* x2 = R.jsgraph.Float64Constant(11.1); | |
1015 Node* y2 = R.jsgraph.Float64Constant(22.2); | |
1016 Node* x3 = R.jsgraph.Float64Constant(33.3); | |
1017 Node* y3 = R.jsgraph.Float64Constant(44.4); | |
1018 | |
1019 Diamond d2(R, R.p0, x2, y2); | |
1020 Diamond d3(R, R.p0, x3, y3); | |
1021 Diamond d1(R, R.p0, d2.phi, d3.phi); | |
1022 | |
1023 d2.nest(d1, true); | |
1024 d3.nest(d1, false); | |
1025 | |
1026 Node* ret = R.Return(d1.phi, R.start, d1.merge); | |
1027 | |
1028 R.ReduceGraph(); // nothing should happen. | |
1029 | |
1030 CheckInputs(ret, d1.phi, R.start, d1.merge); | |
1031 CheckInputs(d1.phi, d2.phi, d3.phi, d1.merge); | |
1032 CheckInputs(d1.merge, d2.merge, d3.merge); | |
1033 | |
1034 DeadChecker dead(&R.graph); | |
1035 dead.CheckLive(d2); | |
1036 dead.CheckLive(d3); | |
1037 } | |
1038 | |
1039 | |
1040 TEST(Return_nested_diamonds_true2) { | |
1041 ControlReducerTester R; | |
1042 Node* x2 = R.jsgraph.Float64Constant(11.1); | |
1043 Node* y2 = R.jsgraph.Float64Constant(22.2); | |
1044 Node* x3 = R.jsgraph.Float64Constant(33.3); | |
1045 Node* y3 = R.jsgraph.Float64Constant(44.4); | |
1046 | |
1047 Diamond d2(R, R.p0, x2, y2); | |
1048 Diamond d3(R, R.p0, x3, y3); | |
1049 Diamond d1(R, R.jsgraph.TrueConstant(), d2.phi, d3.phi); | |
1050 | |
1051 d2.nest(d1, true); | |
1052 d3.nest(d1, false); | |
1053 | |
1054 Node* ret = R.Return(d1.phi, R.start, d1.merge); | |
1055 | |
1056 R.ReduceGraph(); // d1 gets folded true. | |
1057 | |
1058 CheckInputs(ret, d2.phi, R.start, d2.merge); | |
1059 CheckInputs(d2.branch, R.p0, R.start); | |
1060 | |
1061 DeadChecker dead(&R.graph); | |
1062 dead.Check(d1); | |
1063 dead.CheckLive(d2); | |
1064 dead.Check(d3); | |
1065 } | |
1066 | |
1067 | |
1068 TEST(Return_nested_diamonds_true_true2) { | |
1069 ControlReducerTester R; | |
1070 Node* x2 = R.jsgraph.Float64Constant(11.1); | |
1071 Node* y2 = R.jsgraph.Float64Constant(22.2); | |
1072 Node* x3 = R.jsgraph.Float64Constant(33.3); | |
1073 Node* y3 = R.jsgraph.Float64Constant(44.4); | |
1074 | |
1075 Diamond d2(R, R.jsgraph.TrueConstant(), x2, y2); | |
1076 Diamond d3(R, R.p0, x3, y3); | |
1077 Diamond d1(R, R.jsgraph.TrueConstant(), d2.phi, d3.phi); | |
1078 | |
1079 d2.nest(d1, true); | |
1080 d3.nest(d1, false); | |
1081 | |
1082 Node* ret = R.Return(d1.phi, R.start, d1.merge); | |
1083 | |
1084 R.ReduceGraph(); // d1 gets folded true. | |
1085 | |
1086 CheckInputs(ret, x2, R.start, R.start); | |
1087 | |
1088 DeadChecker dead(&R.graph); | |
1089 dead.Check(d1); | |
1090 dead.Check(d2); | |
1091 dead.Check(d3); | |
1092 } | |
1093 | |
1094 | |
1095 TEST(Return_nested_diamonds_true_false2) { | |
1096 ControlReducerTester R; | |
1097 Node* x2 = R.jsgraph.Float64Constant(11.1); | |
1098 Node* y2 = R.jsgraph.Float64Constant(22.2); | |
1099 Node* x3 = R.jsgraph.Float64Constant(33.3); | |
1100 Node* y3 = R.jsgraph.Float64Constant(44.4); | |
1101 | |
1102 Diamond d2(R, R.jsgraph.FalseConstant(), x2, y2); | |
1103 Diamond d3(R, R.p0, x3, y3); | |
1104 Diamond d1(R, R.jsgraph.TrueConstant(), d2.phi, d3.phi); | |
1105 | |
1106 d2.nest(d1, true); | |
1107 d3.nest(d1, false); | |
1108 | |
1109 Node* ret = R.Return(d1.phi, R.start, d1.merge); | |
1110 | |
1111 R.ReduceGraph(); // d1 gets folded true. | |
1112 | |
1113 CheckInputs(ret, y2, R.start, R.start); | |
1114 | |
1115 DeadChecker dead(&R.graph); | |
1116 dead.Check(d1); | |
1117 dead.Check(d2); | |
1118 dead.Check(d3); | |
1119 } | |
OLD | NEW |