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
7#include "graph-tester.h"
8#include "src/compiler/generic-node-inl.h"
9#include "src/compiler/graph-reducer.h"
10
11using namespace v8::internal;
12using namespace v8::internal::compiler;
13
14const uint8_t OPCODE_A0 = 10;
15const uint8_t OPCODE_A1 = 11;
16const uint8_t OPCODE_A2 = 12;
17const uint8_t OPCODE_B0 = 20;
18const uint8_t OPCODE_B1 = 21;
19const uint8_t OPCODE_B2 = 22;
20const uint8_t OPCODE_C0 = 30;
21const uint8_t OPCODE_C1 = 31;
22const uint8_t OPCODE_C2 = 32;
23
24static SimpleOperator OPA0(OPCODE_A0, Operator::kNoWrite, 0, 0, "opa0");
25static SimpleOperator OPA1(OPCODE_A1, Operator::kNoWrite, 1, 0, "opa1");
26static SimpleOperator OPA2(OPCODE_A2, Operator::kNoWrite, 2, 0, "opa2");
27static SimpleOperator OPB0(OPCODE_B0, Operator::kNoWrite, 0, 0, "opa0");
28static SimpleOperator OPB1(OPCODE_B1, Operator::kNoWrite, 1, 0, "opa1");
29static SimpleOperator OPB2(OPCODE_B2, Operator::kNoWrite, 2, 0, "opa2");
30static SimpleOperator OPC0(OPCODE_C0, Operator::kNoWrite, 0, 0, "opc0");
31static SimpleOperator OPC1(OPCODE_C1, Operator::kNoWrite, 1, 0, "opc1");
32static SimpleOperator OPC2(OPCODE_C2, Operator::kNoWrite, 2, 0, "opc2");
33
34
35// Replaces all "A" operators with "B" operators without creating new nodes.
36class InPlaceABReducer : public Reducer {
37 public:
38  virtual Reduction Reduce(Node* node) {
39    switch (node->op()->opcode()) {
40      case OPCODE_A0:
41        CHECK_EQ(0, node->InputCount());
42        node->set_op(&OPB0);
43        return Replace(node);
44      case OPCODE_A1:
45        CHECK_EQ(1, node->InputCount());
46        node->set_op(&OPB1);
47        return Replace(node);
48      case OPCODE_A2:
49        CHECK_EQ(2, node->InputCount());
50        node->set_op(&OPB2);
51        return Replace(node);
52    }
53    return NoChange();
54  }
55};
56
57
58// Replaces all "A" operators with "B" operators by allocating new nodes.
59class NewABReducer : public Reducer {
60 public:
61  explicit NewABReducer(Graph* graph) : graph_(graph) {}
62  virtual Reduction Reduce(Node* node) {
63    switch (node->op()->opcode()) {
64      case OPCODE_A0:
65        CHECK_EQ(0, node->InputCount());
66        return Replace(graph_->NewNode(&OPB0));
67      case OPCODE_A1:
68        CHECK_EQ(1, node->InputCount());
69        return Replace(graph_->NewNode(&OPB1, node->InputAt(0)));
70      case OPCODE_A2:
71        CHECK_EQ(2, node->InputCount());
72        return Replace(
73            graph_->NewNode(&OPB2, node->InputAt(0), node->InputAt(1)));
74    }
75    return NoChange();
76  }
77  Graph* graph_;
78};
79
80
81// Replaces all "B" operators with "C" operators without creating new nodes.
82class InPlaceBCReducer : public Reducer {
83 public:
84  virtual Reduction Reduce(Node* node) {
85    switch (node->op()->opcode()) {
86      case OPCODE_B0:
87        CHECK_EQ(0, node->InputCount());
88        node->set_op(&OPC0);
89        return Replace(node);
90      case OPCODE_B1:
91        CHECK_EQ(1, node->InputCount());
92        node->set_op(&OPC1);
93        return Replace(node);
94      case OPCODE_B2:
95        CHECK_EQ(2, node->InputCount());
96        node->set_op(&OPC2);
97        return Replace(node);
98    }
99    return NoChange();
100  }
101};
102
103
104// Wraps all "OPA0" nodes in "OPB1" operators by allocating new nodes.
105class A0Wrapper FINAL : public Reducer {
106 public:
107  explicit A0Wrapper(Graph* graph) : graph_(graph) {}
108  virtual Reduction Reduce(Node* node) OVERRIDE {
109    switch (node->op()->opcode()) {
110      case OPCODE_A0:
111        CHECK_EQ(0, node->InputCount());
112        return Replace(graph_->NewNode(&OPB1, node));
113    }
114    return NoChange();
115  }
116  Graph* graph_;
117};
118
119
120// Wraps all "OPB0" nodes in two "OPC1" operators by allocating new nodes.
121class B0Wrapper FINAL : public Reducer {
122 public:
123  explicit B0Wrapper(Graph* graph) : graph_(graph) {}
124  virtual Reduction Reduce(Node* node) OVERRIDE {
125    switch (node->op()->opcode()) {
126      case OPCODE_B0:
127        CHECK_EQ(0, node->InputCount());
128        return Replace(graph_->NewNode(&OPC1, graph_->NewNode(&OPC1, node)));
129    }
130    return NoChange();
131  }
132  Graph* graph_;
133};
134
135
136// Replaces all "OPA1" nodes with the first input.
137class A1Forwarder : public Reducer {
138  virtual Reduction Reduce(Node* node) {
139    switch (node->op()->opcode()) {
140      case OPCODE_A1:
141        CHECK_EQ(1, node->InputCount());
142        return Replace(node->InputAt(0));
143    }
144    return NoChange();
145  }
146};
147
148
149// Replaces all "OPB1" nodes with the first input.
150class B1Forwarder : public Reducer {
151  virtual Reduction Reduce(Node* node) {
152    switch (node->op()->opcode()) {
153      case OPCODE_B1:
154        CHECK_EQ(1, node->InputCount());
155        return Replace(node->InputAt(0));
156    }
157    return NoChange();
158  }
159};
160
161
162// Swaps the inputs to "OP2A" and "OP2B" nodes based on ids.
163class AB2Sorter : public Reducer {
164  virtual Reduction Reduce(Node* node) {
165    switch (node->op()->opcode()) {
166      case OPCODE_A2:
167      case OPCODE_B2:
168        CHECK_EQ(2, node->InputCount());
169        Node* x = node->InputAt(0);
170        Node* y = node->InputAt(1);
171        if (x->id() > y->id()) {
172          node->ReplaceInput(0, y);
173          node->ReplaceInput(1, x);
174          return Replace(node);
175        }
176    }
177    return NoChange();
178  }
179};
180
181
182// Simply records the nodes visited.
183class ReducerRecorder : public Reducer {
184 public:
185  explicit ReducerRecorder(Zone* zone)
186      : set(NodeSet::key_compare(), NodeSet::allocator_type(zone)) {}
187  virtual Reduction Reduce(Node* node) {
188    set.insert(node);
189    return NoChange();
190  }
191  void CheckContains(Node* node) {
192    CHECK_EQ(1, static_cast<int>(set.count(node)));
193  }
194  NodeSet set;
195};
196
197
198TEST(ReduceGraphFromEnd1) {
199  GraphTester graph;
200
201  Node* n1 = graph.NewNode(&OPA0);
202  Node* end = graph.NewNode(&OPA1, n1);
203  graph.SetEnd(end);
204
205  GraphReducer reducer(&graph);
206  ReducerRecorder recorder(graph.zone());
207  reducer.AddReducer(&recorder);
208  reducer.ReduceGraph();
209  recorder.CheckContains(n1);
210  recorder.CheckContains(end);
211}
212
213
214TEST(ReduceGraphFromEnd2) {
215  GraphTester graph;
216
217  Node* n1 = graph.NewNode(&OPA0);
218  Node* n2 = graph.NewNode(&OPA1, n1);
219  Node* n3 = graph.NewNode(&OPA1, n1);
220  Node* end = graph.NewNode(&OPA2, n2, n3);
221  graph.SetEnd(end);
222
223  GraphReducer reducer(&graph);
224  ReducerRecorder recorder(graph.zone());
225  reducer.AddReducer(&recorder);
226  reducer.ReduceGraph();
227  recorder.CheckContains(n1);
228  recorder.CheckContains(n2);
229  recorder.CheckContains(n3);
230  recorder.CheckContains(end);
231}
232
233
234TEST(ReduceInPlace1) {
235  GraphTester graph;
236
237  Node* n1 = graph.NewNode(&OPA0);
238  Node* end = graph.NewNode(&OPA1, n1);
239  graph.SetEnd(end);
240
241  GraphReducer reducer(&graph);
242  InPlaceABReducer r;
243  reducer.AddReducer(&r);
244
245  // Tests A* => B* with in-place updates.
246  for (int i = 0; i < 3; i++) {
247    int before = graph.NodeCount();
248    reducer.ReduceGraph();
249    CHECK_EQ(before, graph.NodeCount());
250    CHECK_EQ(&OPB0, n1->op());
251    CHECK_EQ(&OPB1, end->op());
252    CHECK_EQ(n1, end->InputAt(0));
253  }
254}
255
256
257TEST(ReduceInPlace2) {
258  GraphTester graph;
259
260  Node* n1 = graph.NewNode(&OPA0);
261  Node* n2 = graph.NewNode(&OPA1, n1);
262  Node* n3 = graph.NewNode(&OPA1, n1);
263  Node* end = graph.NewNode(&OPA2, n2, n3);
264  graph.SetEnd(end);
265
266  GraphReducer reducer(&graph);
267  InPlaceABReducer r;
268  reducer.AddReducer(&r);
269
270  // Tests A* => B* with in-place updates.
271  for (int i = 0; i < 3; i++) {
272    int before = graph.NodeCount();
273    reducer.ReduceGraph();
274    CHECK_EQ(before, graph.NodeCount());
275    CHECK_EQ(&OPB0, n1->op());
276    CHECK_EQ(&OPB1, n2->op());
277    CHECK_EQ(n1, n2->InputAt(0));
278    CHECK_EQ(&OPB1, n3->op());
279    CHECK_EQ(n1, n3->InputAt(0));
280    CHECK_EQ(&OPB2, end->op());
281    CHECK_EQ(n2, end->InputAt(0));
282    CHECK_EQ(n3, end->InputAt(1));
283  }
284}
285
286
287TEST(ReduceNew1) {
288  GraphTester graph;
289
290  Node* n1 = graph.NewNode(&OPA0);
291  Node* n2 = graph.NewNode(&OPA1, n1);
292  Node* n3 = graph.NewNode(&OPA1, n1);
293  Node* end = graph.NewNode(&OPA2, n2, n3);
294  graph.SetEnd(end);
295
296  GraphReducer reducer(&graph);
297  NewABReducer r(&graph);
298  reducer.AddReducer(&r);
299
300  // Tests A* => B* while creating new nodes.
301  for (int i = 0; i < 3; i++) {
302    int before = graph.NodeCount();
303    reducer.ReduceGraph();
304    if (i == 0) {
305      CHECK_NE(before, graph.NodeCount());
306    } else {
307      CHECK_EQ(before, graph.NodeCount());
308    }
309    Node* nend = graph.end();
310    CHECK_NE(end, nend);  // end() should be updated too.
311
312    Node* nn2 = nend->InputAt(0);
313    Node* nn3 = nend->InputAt(1);
314    Node* nn1 = nn2->InputAt(0);
315
316    CHECK_EQ(nn1, nn3->InputAt(0));
317
318    CHECK_EQ(&OPB0, nn1->op());
319    CHECK_EQ(&OPB1, nn2->op());
320    CHECK_EQ(&OPB1, nn3->op());
321    CHECK_EQ(&OPB2, nend->op());
322  }
323}
324
325
326TEST(Wrapping1) {
327  GraphTester graph;
328
329  Node* end = graph.NewNode(&OPA0);
330  graph.SetEnd(end);
331  CHECK_EQ(1, graph.NodeCount());
332
333  GraphReducer reducer(&graph);
334  A0Wrapper r(&graph);
335  reducer.AddReducer(&r);
336
337  reducer.ReduceGraph();
338  CHECK_EQ(2, graph.NodeCount());
339
340  Node* nend = graph.end();
341  CHECK_NE(end, nend);
342  CHECK_EQ(&OPB1, nend->op());
343  CHECK_EQ(1, nend->InputCount());
344  CHECK_EQ(end, nend->InputAt(0));
345}
346
347
348TEST(Wrapping2) {
349  GraphTester graph;
350
351  Node* end = graph.NewNode(&OPB0);
352  graph.SetEnd(end);
353  CHECK_EQ(1, graph.NodeCount());
354
355  GraphReducer reducer(&graph);
356  B0Wrapper r(&graph);
357  reducer.AddReducer(&r);
358
359  reducer.ReduceGraph();
360  CHECK_EQ(3, graph.NodeCount());
361
362  Node* nend = graph.end();
363  CHECK_NE(end, nend);
364  CHECK_EQ(&OPC1, nend->op());
365  CHECK_EQ(1, nend->InputCount());
366
367  Node* n1 = nend->InputAt(0);
368  CHECK_NE(end, n1);
369  CHECK_EQ(&OPC1, n1->op());
370  CHECK_EQ(1, n1->InputCount());
371  CHECK_EQ(end, n1->InputAt(0));
372}
373
374
375TEST(Forwarding1) {
376  GraphTester graph;
377
378  Node* n1 = graph.NewNode(&OPA0);
379  Node* end = graph.NewNode(&OPA1, n1);
380  graph.SetEnd(end);
381
382  GraphReducer reducer(&graph);
383  A1Forwarder r;
384  reducer.AddReducer(&r);
385
386  // Tests A1(x) => x
387  for (int i = 0; i < 3; i++) {
388    int before = graph.NodeCount();
389    reducer.ReduceGraph();
390    CHECK_EQ(before, graph.NodeCount());
391    CHECK_EQ(&OPA0, n1->op());
392    CHECK_EQ(n1, graph.end());
393  }
394}
395
396
397TEST(Forwarding2) {
398  GraphTester graph;
399
400  Node* n1 = graph.NewNode(&OPA0);
401  Node* n2 = graph.NewNode(&OPA1, n1);
402  Node* n3 = graph.NewNode(&OPA1, n1);
403  Node* end = graph.NewNode(&OPA2, n2, n3);
404  graph.SetEnd(end);
405
406  GraphReducer reducer(&graph);
407  A1Forwarder r;
408  reducer.AddReducer(&r);
409
410  // Tests reducing A2(A1(x), A1(y)) => A2(x, y).
411  for (int i = 0; i < 3; i++) {
412    int before = graph.NodeCount();
413    reducer.ReduceGraph();
414    CHECK_EQ(before, graph.NodeCount());
415    CHECK_EQ(&OPA0, n1->op());
416    CHECK_EQ(n1, end->InputAt(0));
417    CHECK_EQ(n1, end->InputAt(1));
418    CHECK_EQ(&OPA2, end->op());
419    CHECK_EQ(0, n2->UseCount());
420    CHECK_EQ(0, n3->UseCount());
421  }
422}
423
424
425TEST(Forwarding3) {
426  // Tests reducing a chain of A1(A1(A1(A1(x)))) => x.
427  for (int i = 0; i < 8; i++) {
428    GraphTester graph;
429
430    Node* n1 = graph.NewNode(&OPA0);
431    Node* end = n1;
432    for (int j = 0; j < i; j++) {
433      end = graph.NewNode(&OPA1, end);
434    }
435    graph.SetEnd(end);
436
437    GraphReducer reducer(&graph);
438    A1Forwarder r;
439    reducer.AddReducer(&r);
440
441    for (int i = 0; i < 3; i++) {
442      int before = graph.NodeCount();
443      reducer.ReduceGraph();
444      CHECK_EQ(before, graph.NodeCount());
445      CHECK_EQ(&OPA0, n1->op());
446      CHECK_EQ(n1, graph.end());
447    }
448  }
449}
450
451
452TEST(ReduceForward1) {
453  GraphTester graph;
454
455  Node* n1 = graph.NewNode(&OPA0);
456  Node* n2 = graph.NewNode(&OPA1, n1);
457  Node* n3 = graph.NewNode(&OPA1, n1);
458  Node* end = graph.NewNode(&OPA2, n2, n3);
459  graph.SetEnd(end);
460
461  GraphReducer reducer(&graph);
462  InPlaceABReducer r;
463  B1Forwarder f;
464  reducer.AddReducer(&r);
465  reducer.AddReducer(&f);
466
467  // Tests first reducing A => B, then B1(x) => x.
468  for (int i = 0; i < 3; i++) {
469    int before = graph.NodeCount();
470    reducer.ReduceGraph();
471    CHECK_EQ(before, graph.NodeCount());
472    CHECK_EQ(&OPB0, n1->op());
473    CHECK(n2->IsDead());
474    CHECK_EQ(n1, end->InputAt(0));
475    CHECK(n3->IsDead());
476    CHECK_EQ(n1, end->InputAt(0));
477    CHECK_EQ(&OPB2, end->op());
478    CHECK_EQ(0, n2->UseCount());
479    CHECK_EQ(0, n3->UseCount());
480  }
481}
482
483
484TEST(Sorter1) {
485  HandleAndZoneScope scope;
486  AB2Sorter r;
487  for (int i = 0; i < 6; i++) {
488    GraphTester graph;
489
490    Node* n1 = graph.NewNode(&OPA0);
491    Node* n2 = graph.NewNode(&OPA1, n1);
492    Node* n3 = graph.NewNode(&OPA1, n1);
493    Node* end = NULL;  // Initialize to please the compiler.
494
495    if (i == 0) end = graph.NewNode(&OPA2, n2, n3);
496    if (i == 1) end = graph.NewNode(&OPA2, n3, n2);
497    if (i == 2) end = graph.NewNode(&OPA2, n2, n1);
498    if (i == 3) end = graph.NewNode(&OPA2, n1, n2);
499    if (i == 4) end = graph.NewNode(&OPA2, n3, n1);
500    if (i == 5) end = graph.NewNode(&OPA2, n1, n3);
501
502    graph.SetEnd(end);
503
504    GraphReducer reducer(&graph);
505    reducer.AddReducer(&r);
506
507    int before = graph.NodeCount();
508    reducer.ReduceGraph();
509    CHECK_EQ(before, graph.NodeCount());
510    CHECK_EQ(&OPA0, n1->op());
511    CHECK_EQ(&OPA1, n2->op());
512    CHECK_EQ(&OPA1, n3->op());
513    CHECK_EQ(&OPA2, end->op());
514    CHECK_EQ(end, graph.end());
515    CHECK(end->InputAt(0)->id() <= end->InputAt(1)->id());
516  }
517}
518
519
520// Generate a node graph with the given permutations.
521void GenDAG(Graph* graph, int* p3, int* p2, int* p1) {
522  Node* level4 = graph->NewNode(&OPA0);
523  Node* level3[] = {graph->NewNode(&OPA1, level4),
524                    graph->NewNode(&OPA1, level4)};
525
526  Node* level2[] = {graph->NewNode(&OPA1, level3[p3[0]]),
527                    graph->NewNode(&OPA1, level3[p3[1]]),
528                    graph->NewNode(&OPA1, level3[p3[0]]),
529                    graph->NewNode(&OPA1, level3[p3[1]])};
530
531  Node* level1[] = {graph->NewNode(&OPA2, level2[p2[0]], level2[p2[1]]),
532                    graph->NewNode(&OPA2, level2[p2[2]], level2[p2[3]])};
533
534  Node* end = graph->NewNode(&OPA2, level1[p1[0]], level1[p1[1]]);
535  graph->SetEnd(end);
536}
537
538
539TEST(SortForwardReduce) {
540  GraphTester graph;
541
542  // Tests combined reductions on a series of DAGs.
543  for (int j = 0; j < 2; j++) {
544    int p3[] = {j, 1 - j};
545    for (int m = 0; m < 2; m++) {
546      int p1[] = {m, 1 - m};
547      for (int k = 0; k < 24; k++) {  // All permutations of 0, 1, 2, 3
548        int p2[] = {-1, -1, -1, -1};
549        int n = k;
550        for (int d = 4; d >= 1; d--) {  // Construct permutation.
551          int p = n % d;
552          for (int z = 0; z < 4; z++) {
553            if (p2[z] == -1) {
554              if (p == 0) p2[z] = d - 1;
555              p--;
556            }
557          }
558          n = n / d;
559        }
560
561        GenDAG(&graph, p3, p2, p1);
562
563        GraphReducer reducer(&graph);
564        AB2Sorter r1;
565        A1Forwarder r2;
566        InPlaceABReducer r3;
567        reducer.AddReducer(&r1);
568        reducer.AddReducer(&r2);
569        reducer.AddReducer(&r3);
570
571        reducer.ReduceGraph();
572
573        Node* end = graph.end();
574        CHECK_EQ(&OPB2, end->op());
575        Node* n1 = end->InputAt(0);
576        Node* n2 = end->InputAt(1);
577        CHECK_NE(n1, n2);
578        CHECK(n1->id() < n2->id());
579        CHECK_EQ(&OPB2, n1->op());
580        CHECK_EQ(&OPB2, n2->op());
581        Node* n4 = n1->InputAt(0);
582        CHECK_EQ(&OPB0, n4->op());
583        CHECK_EQ(n4, n1->InputAt(1));
584        CHECK_EQ(n4, n2->InputAt(0));
585        CHECK_EQ(n4, n2->InputAt(1));
586      }
587    }
588  }
589}
590
591
592TEST(Order) {
593  // Test that the order of reducers doesn't matter, as they should be
594  // rerun for changed nodes.
595  for (int i = 0; i < 2; i++) {
596    GraphTester graph;
597
598    Node* n1 = graph.NewNode(&OPA0);
599    Node* end = graph.NewNode(&OPA1, n1);
600    graph.SetEnd(end);
601
602    GraphReducer reducer(&graph);
603    InPlaceABReducer abr;
604    InPlaceBCReducer bcr;
605    if (i == 0) {
606      reducer.AddReducer(&abr);
607      reducer.AddReducer(&bcr);
608    } else {
609      reducer.AddReducer(&bcr);
610      reducer.AddReducer(&abr);
611    }
612
613    // Tests A* => C* with in-place updates.
614    for (int i = 0; i < 3; i++) {
615      int before = graph.NodeCount();
616      reducer.ReduceGraph();
617      CHECK_EQ(before, graph.NodeCount());
618      CHECK_EQ(&OPC0, n1->op());
619      CHECK_EQ(&OPC1, end->op());
620      CHECK_EQ(n1, end->InputAt(0));
621    }
622  }
623}
624
625
626// Tests that a reducer is only applied once.
627class OneTimeReducer : public Reducer {
628 public:
629  OneTimeReducer(Reducer* reducer, Zone* zone)
630      : reducer_(reducer),
631        nodes_(NodeSet::key_compare(), NodeSet::allocator_type(zone)) {}
632  virtual Reduction Reduce(Node* node) {
633    CHECK_EQ(0, static_cast<int>(nodes_.count(node)));
634    nodes_.insert(node);
635    return reducer_->Reduce(node);
636  }
637  Reducer* reducer_;
638  NodeSet nodes_;
639};
640
641
642TEST(OneTimeReduce1) {
643  GraphTester graph;
644
645  Node* n1 = graph.NewNode(&OPA0);
646  Node* end = graph.NewNode(&OPA1, n1);
647  graph.SetEnd(end);
648
649  GraphReducer reducer(&graph);
650  InPlaceABReducer r;
651  OneTimeReducer once(&r, graph.zone());
652  reducer.AddReducer(&once);
653
654  // Tests A* => B* with in-place updates. Should only be applied once.
655  int before = graph.NodeCount();
656  reducer.ReduceGraph();
657  CHECK_EQ(before, graph.NodeCount());
658  CHECK_EQ(&OPB0, n1->op());
659  CHECK_EQ(&OPB1, end->op());
660  CHECK_EQ(n1, end->InputAt(0));
661}
662