Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(179)

Side by Side Diff: test/cctest/compiler/test-graph-reducer.cc

Issue 426233002: Land the Fan (disabled) (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
7 #include "graph-tester.h"
8 #include "src/compiler/generic-node-inl.h"
9 #include "src/compiler/graph-reducer.h"
10
11 using namespace v8::internal;
12 using namespace v8::internal::compiler;
13
14 const uint8_t OPCODE_A0 = 10;
15 const uint8_t OPCODE_A1 = 11;
16 const uint8_t OPCODE_A2 = 12;
17 const uint8_t OPCODE_B0 = 20;
18 const uint8_t OPCODE_B1 = 21;
19 const uint8_t OPCODE_B2 = 22;
20 const uint8_t OPCODE_C0 = 30;
21 const uint8_t OPCODE_C1 = 31;
22 const uint8_t OPCODE_C2 = 32;
23
24 static SimpleOperator OPA0(OPCODE_A0, Operator::kNoWrite, 0, 0, "opa0");
25 static SimpleOperator OPA1(OPCODE_A1, Operator::kNoWrite, 1, 0, "opa1");
26 static SimpleOperator OPA2(OPCODE_A2, Operator::kNoWrite, 2, 0, "opa2");
27 static SimpleOperator OPB0(OPCODE_B0, Operator::kNoWrite, 0, 0, "opa0");
28 static SimpleOperator OPB1(OPCODE_B1, Operator::kNoWrite, 1, 0, "opa1");
29 static SimpleOperator OPB2(OPCODE_B2, Operator::kNoWrite, 2, 0, "opa2");
30 static SimpleOperator OPC0(OPCODE_C0, Operator::kNoWrite, 0, 0, "opc0");
31 static SimpleOperator OPC1(OPCODE_C1, Operator::kNoWrite, 1, 0, "opc1");
32 static SimpleOperator OPC2(OPCODE_C2, Operator::kNoWrite, 2, 0, "opc2");
33
34
35 // Replaces all "A" operators with "B" operators without creating new nodes.
36 class 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.
59 class 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(graph_->NewNode(&OPB2, node->InputAt(0),
73 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.
82 class 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.
105 class A0Wrapper V8_FINAL : public Reducer {
106 public:
107 explicit A0Wrapper(Graph* graph) : graph_(graph) {}
108 virtual Reduction Reduce(Node* node) V8_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.
121 class B0Wrapper V8_FINAL : public Reducer {
122 public:
123 explicit B0Wrapper(Graph* graph) : graph_(graph) {}
124 virtual Reduction Reduce(Node* node) V8_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.
137 class 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.
150 class 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.
163 class 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.
183 class ReducerRecorder : public Reducer {
184 public:
185 explicit ReducerRecorder(Zone* zone)
186 : set(NodeSet::key_compare(), NodeSet::allocator_type(zone)) {
187 }
188 virtual Reduction Reduce(Node* node) {
189 set.insert(node);
190 return NoChange();
191 }
192 void CheckContains(Node* node) {
193 CHECK_EQ(1, set.count(node));
194 }
195 NodeSet set;
196 };
197
198
199 TEST(ReduceGraphFromEnd1) {
200 GraphTester graph;
201
202 Node* n1 = graph.NewNode(&OPA0);
203 Node* end = graph.NewNode(&OPA1, n1);
204 graph.SetEnd(end);
205
206 GraphReducer reducer(&graph);
207 ReducerRecorder recorder(graph.zone());
208 reducer.AddReducer(&recorder);
209 reducer.ReduceGraph();
210 recorder.CheckContains(n1);
211 recorder.CheckContains(end);
212 }
213
214
215 TEST(ReduceGraphFromEnd2) {
216 GraphTester graph;
217
218 Node* n1 = graph.NewNode(&OPA0);
219 Node* n2 = graph.NewNode(&OPA1, n1);
220 Node* n3 = graph.NewNode(&OPA1, n1);
221 Node* end = graph.NewNode(&OPA2, n2, n3);
222 graph.SetEnd(end);
223
224 GraphReducer reducer(&graph);
225 ReducerRecorder recorder(graph.zone());
226 reducer.AddReducer(&recorder);
227 reducer.ReduceGraph();
228 recorder.CheckContains(n1);
229 recorder.CheckContains(n2);
230 recorder.CheckContains(n3);
231 recorder.CheckContains(end);
232 }
233
234
235 TEST(ReduceInPlace1) {
236 GraphTester graph;
237
238 Node* n1 = graph.NewNode(&OPA0);
239 Node* end = graph.NewNode(&OPA1, n1);
240 graph.SetEnd(end);
241
242 GraphReducer reducer(&graph);
243 InPlaceABReducer r;
244 reducer.AddReducer(&r);
245
246 // Tests A* => B* with in-place updates.
247 for (int i = 0; i < 3; i++) {
248 int before = graph.NodeCount();
249 reducer.ReduceGraph();
250 CHECK_EQ(before, graph.NodeCount());
251 CHECK_EQ(&OPB0, n1->op());
252 CHECK_EQ(&OPB1, end->op());
253 CHECK_EQ(n1, end->InputAt(0));
254 }
255 }
256
257
258 TEST(ReduceInPlace2) {
259 GraphTester graph;
260
261 Node* n1 = graph.NewNode(&OPA0);
262 Node* n2 = graph.NewNode(&OPA1, n1);
263 Node* n3 = graph.NewNode(&OPA1, n1);
264 Node* end = graph.NewNode(&OPA2, n2, n3);
265 graph.SetEnd(end);
266
267 GraphReducer reducer(&graph);
268 InPlaceABReducer r;
269 reducer.AddReducer(&r);
270
271 // Tests A* => B* with in-place updates.
272 for (int i = 0; i < 3; i++) {
273 int before = graph.NodeCount();
274 reducer.ReduceGraph();
275 CHECK_EQ(before, graph.NodeCount());
276 CHECK_EQ(&OPB0, n1->op());
277 CHECK_EQ(&OPB1, n2->op());
278 CHECK_EQ(n1, n2->InputAt(0));
279 CHECK_EQ(&OPB1, n3->op());
280 CHECK_EQ(n1, n3->InputAt(0));
281 CHECK_EQ(&OPB2, end->op());
282 CHECK_EQ(n2, end->InputAt(0));
283 CHECK_EQ(n3, end->InputAt(1));
284 }
285 }
286
287
288 TEST(ReduceNew1) {
289 GraphTester graph;
290
291 Node* n1 = graph.NewNode(&OPA0);
292 Node* n2 = graph.NewNode(&OPA1, n1);
293 Node* n3 = graph.NewNode(&OPA1, n1);
294 Node* end = graph.NewNode(&OPA2, n2, n3);
295 graph.SetEnd(end);
296
297 GraphReducer reducer(&graph);
298 NewABReducer r(&graph);
299 reducer.AddReducer(&r);
300
301 // Tests A* => B* while creating new nodes.
302 for (int i = 0; i < 3; i++) {
303 int before = graph.NodeCount();
304 reducer.ReduceGraph();
305 if (i == 0) {
306 CHECK_NE(before, graph.NodeCount());
307 } else {
308 CHECK_EQ(before, graph.NodeCount());
309 }
310 Node* nend = graph.end();
311 CHECK_NE(end, nend); // end() should be updated too.
312
313 Node* nn2 = nend->InputAt(0);
314 Node* nn3 = nend->InputAt(1);
315 Node* nn1 = nn2->InputAt(0);
316
317 CHECK_EQ(nn1, nn3->InputAt(0));
318
319 CHECK_EQ(&OPB0, nn1->op());
320 CHECK_EQ(&OPB1, nn2->op());
321 CHECK_EQ(&OPB1, nn3->op());
322 CHECK_EQ(&OPB2, nend->op());
323 }
324 }
325
326
327 TEST(Wrapping1) {
328 GraphTester graph;
329
330 Node* end = graph.NewNode(&OPA0);
331 graph.SetEnd(end);
332 CHECK_EQ(1, graph.NodeCount());
333
334 GraphReducer reducer(&graph);
335 A0Wrapper r(&graph);
336 reducer.AddReducer(&r);
337
338 reducer.ReduceGraph();
339 CHECK_EQ(2, graph.NodeCount());
340
341 Node* nend = graph.end();
342 CHECK_NE(end, nend);
343 CHECK_EQ(&OPB1, nend->op());
344 CHECK_EQ(1, nend->InputCount());
345 CHECK_EQ(end, nend->InputAt(0));
346 }
347
348
349 TEST(Wrapping2) {
350 GraphTester graph;
351
352 Node* end = graph.NewNode(&OPB0);
353 graph.SetEnd(end);
354 CHECK_EQ(1, graph.NodeCount());
355
356 GraphReducer reducer(&graph);
357 B0Wrapper r(&graph);
358 reducer.AddReducer(&r);
359
360 reducer.ReduceGraph();
361 CHECK_EQ(3, graph.NodeCount());
362
363 Node* nend = graph.end();
364 CHECK_NE(end, nend);
365 CHECK_EQ(&OPC1, nend->op());
366 CHECK_EQ(1, nend->InputCount());
367
368 Node* n1 = nend->InputAt(0);
369 CHECK_NE(end, n1);
370 CHECK_EQ(&OPC1, n1->op());
371 CHECK_EQ(1, n1->InputCount());
372 CHECK_EQ(end, n1->InputAt(0));
373 }
374
375
376 TEST(Forwarding1) {
377 GraphTester graph;
378
379 Node* n1 = graph.NewNode(&OPA0);
380 Node* end = graph.NewNode(&OPA1, n1);
381 graph.SetEnd(end);
382
383 GraphReducer reducer(&graph);
384 A1Forwarder r;
385 reducer.AddReducer(&r);
386
387 // Tests A1(x) => x
388 for (int i = 0; i < 3; i++) {
389 int before = graph.NodeCount();
390 reducer.ReduceGraph();
391 CHECK_EQ(before, graph.NodeCount());
392 CHECK_EQ(&OPA0, n1->op());
393 CHECK_EQ(n1, graph.end());
394 }
395 }
396
397
398 TEST(Forwarding2) {
399 GraphTester graph;
400
401 Node* n1 = graph.NewNode(&OPA0);
402 Node* n2 = graph.NewNode(&OPA1, n1);
403 Node* n3 = graph.NewNode(&OPA1, n1);
404 Node* end = graph.NewNode(&OPA2, n2, n3);
405 graph.SetEnd(end);
406
407 GraphReducer reducer(&graph);
408 A1Forwarder r;
409 reducer.AddReducer(&r);
410
411 // Tests reducing A2(A1(x), A1(y)) => A2(x, y).
412 for (int i = 0; i < 3; i++) {
413 int before = graph.NodeCount();
414 reducer.ReduceGraph();
415 CHECK_EQ(before, graph.NodeCount());
416 CHECK_EQ(&OPA0, n1->op());
417 CHECK_EQ(n1, end->InputAt(0));
418 CHECK_EQ(n1, end->InputAt(1));
419 CHECK_EQ(&OPA2, end->op());
420 CHECK_EQ(0, n2->UseCount());
421 CHECK_EQ(0, n3->UseCount());
422 }
423 }
424
425
426 TEST(Forwarding3) {
427 // Tests reducing a chain of A1(A1(A1(A1(x)))) => x.
428 for (int i = 0; i < 8; i++) {
429 GraphTester graph;
430
431 Node* n1 = graph.NewNode(&OPA0);
432 Node* end = n1;
433 for (int j = 0; j < i; j++) {
434 end = graph.NewNode(&OPA1, end);
435 }
436 graph.SetEnd(end);
437
438 GraphReducer reducer(&graph);
439 A1Forwarder r;
440 reducer.AddReducer(&r);
441
442 for (int i = 0; i < 3; i++) {
443 int before = graph.NodeCount();
444 reducer.ReduceGraph();
445 CHECK_EQ(before, graph.NodeCount());
446 CHECK_EQ(&OPA0, n1->op());
447 CHECK_EQ(n1, graph.end());
448 }
449 }
450 }
451
452
453 TEST(ReduceForward1) {
454 GraphTester graph;
455
456 Node* n1 = graph.NewNode(&OPA0);
457 Node* n2 = graph.NewNode(&OPA1, n1);
458 Node* n3 = graph.NewNode(&OPA1, n1);
459 Node* end = graph.NewNode(&OPA2, n2, n3);
460 graph.SetEnd(end);
461
462 GraphReducer reducer(&graph);
463 InPlaceABReducer r;
464 B1Forwarder f;
465 reducer.AddReducer(&r);
466 reducer.AddReducer(&f);
467
468 // Tests first reducing A => B, then B1(x) => x.
469 for (int i = 0; i < 3; i++) {
470 int before = graph.NodeCount();
471 reducer.ReduceGraph();
472 CHECK_EQ(before, graph.NodeCount());
473 CHECK_EQ(&OPB0, n1->op());
474 CHECK_EQ(&OPB1, n2->op());
475 CHECK_EQ(n1, end->InputAt(0));
476 CHECK_EQ(&OPB1, n3->op());
477 CHECK_EQ(n1, end->InputAt(0));
478 CHECK_EQ(&OPB2, end->op());
479 CHECK_EQ(0, n2->UseCount());
480 CHECK_EQ(0, n3->UseCount());
481 }
482 }
483
484
485 TEST(Sorter1) {
486 HandleAndZoneScope scope;
487 AB2Sorter r;
488 for (int i = 0; i < 6; i++) {
489 GraphTester graph;
490
491 Node* n1 = graph.NewNode(&OPA0);
492 Node* n2 = graph.NewNode(&OPA1, n1);
493 Node* n3 = graph.NewNode(&OPA1, n1);
494 Node* end;
495
496 if (i == 0) end = graph.NewNode(&OPA2, n2, n3);
497 if (i == 1) end = graph.NewNode(&OPA2, n3, n2);
498 if (i == 2) end = graph.NewNode(&OPA2, n2, n1);
499 if (i == 3) end = graph.NewNode(&OPA2, n1, n2);
500 if (i == 4) end = graph.NewNode(&OPA2, n3, n1);
501 if (i == 5) end = graph.NewNode(&OPA2, n1, n3);
502
503 graph.SetEnd(end);
504
505 GraphReducer reducer(&graph);
506 reducer.AddReducer(&r);
507
508 int before = graph.NodeCount();
509 reducer.ReduceGraph();
510 CHECK_EQ(before, graph.NodeCount());
511 CHECK_EQ(&OPA0, n1->op());
512 CHECK_EQ(&OPA1, n2->op());
513 CHECK_EQ(&OPA1, n3->op());
514 CHECK_EQ(&OPA2, end->op());
515 CHECK_EQ(end, graph.end());
516 CHECK(end->InputAt(0)->id() <= end->InputAt(1)->id());
517 }
518 }
519
520
521 // Generate a node graph with the given permutations.
522 void GenDAG(Graph* graph, int* p3, int* p2, int* p1) {
523 Node* level4 = graph->NewNode(&OPA0);
524 Node* level3[] = {
525 graph->NewNode(&OPA1, level4),
526 graph->NewNode(&OPA1, level4)
527 };
528
529 Node* level2[] = {
530 graph->NewNode(&OPA1, level3[p3[0]]),
531 graph->NewNode(&OPA1, level3[p3[1]]),
532 graph->NewNode(&OPA1, level3[p3[0]]),
533 graph->NewNode(&OPA1, level3[p3[1]])
534 };
535
536 Node* level1[] = {
537 graph->NewNode(&OPA2, level2[p2[0]], level2[p2[1]]),
538 graph->NewNode(&OPA2, level2[p2[2]], level2[p2[3]])
539 };
540
541 Node* end = graph->NewNode(&OPA2, level1[p1[0]], level1[p1[1]]);
542 graph->SetEnd(end);
543 }
544
545
546 TEST(SortForwardReduce) {
547 GraphTester graph;
548
549 // Tests combined reductions on a series of DAGs.
550 for (int j = 0; j < 2; j++) {
551 int p3[] = { j, 1 - j };
552 for (int m = 0; m < 2; m++) {
553 int p1[] = { m, 1 - m };
554 for (int k = 0; k < 24; k++) { // All permutations of 0, 1, 2, 3
555 int p2[] = { -1, -1, -1, -1 };
556 int n = k;
557 for (int d = 4; d >= 1; d--) { // Construct permutation.
558 int p = n % d;
559 for (int z = 0; z < 4; z++) {
560 if (p2[z] == -1) {
561 if (p == 0) p2[z] = d - 1;
562 p--;
563 }
564 }
565 n = n / d;
566 }
567
568 GenDAG(&graph, p3, p2, p1);
569
570 GraphReducer reducer(&graph);
571 AB2Sorter r1;
572 A1Forwarder r2;
573 InPlaceABReducer r3;
574 reducer.AddReducer(&r1);
575 reducer.AddReducer(&r2);
576 reducer.AddReducer(&r3);
577
578 reducer.ReduceGraph();
579
580 Node* end = graph.end();
581 CHECK_EQ(&OPB2, end->op());
582 Node* n1 = end->InputAt(0);
583 Node* n2 = end->InputAt(1);
584 CHECK_NE(n1, n2);
585 CHECK(n1->id() < n2->id());
586 CHECK_EQ(&OPB2, n1->op());
587 CHECK_EQ(&OPB2, n2->op());
588 Node* n4 = n1->InputAt(0);
589 CHECK_EQ(&OPB0, n4->op());
590 CHECK_EQ(n4, n1->InputAt(1));
591 CHECK_EQ(n4, n2->InputAt(0));
592 CHECK_EQ(n4, n2->InputAt(1));
593 }
594 }
595 }
596 }
597
598
599 TEST(Order) {
600 // Test that the order of reducers doesn't matter, as they should be
601 // rerun for changed nodes.
602 for (int i = 0; i < 2; i++) {
603 GraphTester graph;
604
605 Node* n1 = graph.NewNode(&OPA0);
606 Node* end = graph.NewNode(&OPA1, n1);
607 graph.SetEnd(end);
608
609 GraphReducer reducer(&graph);
610 InPlaceABReducer abr;
611 InPlaceBCReducer bcr;
612 if (i == 0) {
613 reducer.AddReducer(&abr);
614 reducer.AddReducer(&bcr);
615 } else {
616 reducer.AddReducer(&bcr);
617 reducer.AddReducer(&abr);
618 }
619
620 // Tests A* => C* with in-place updates.
621 for (int i = 0; i < 3; i++) {
622 int before = graph.NodeCount();
623 reducer.ReduceGraph();
624 CHECK_EQ(before, graph.NodeCount());
625 CHECK_EQ(&OPC0, n1->op());
626 CHECK_EQ(&OPC1, end->op());
627 CHECK_EQ(n1, end->InputAt(0));
628 }
629 }
630 }
631
632
633 // Tests that a reducer is only applied once.
634 class OneTimeReducer : public Reducer {
635 public:
636 OneTimeReducer(Reducer* reducer, Zone* zone)
637 : reducer_(reducer),
638 nodes_(NodeSet::key_compare(), NodeSet::allocator_type(zone)) { }
639 virtual Reduction Reduce(Node* node) {
640 CHECK_EQ(0, nodes_.count(node));
641 nodes_.insert(node);
642 return reducer_->Reduce(node);
643 }
644 Reducer* reducer_;
645 NodeSet nodes_;
646 };
647
648
649 TEST(OneTimeReduce1) {
650 GraphTester graph;
651
652 Node* n1 = graph.NewNode(&OPA0);
653 Node* end = graph.NewNode(&OPA1, n1);
654 graph.SetEnd(end);
655
656 GraphReducer reducer(&graph);
657 InPlaceABReducer r;
658 OneTimeReducer once(&r, graph.zone());
659 reducer.AddReducer(&once);
660
661 // Tests A* => B* with in-place updates. Should only be applied once.
662 int before = graph.NodeCount();
663 reducer.ReduceGraph();
664 CHECK_EQ(before, graph.NodeCount());
665 CHECK_EQ(&OPB0, n1->op());
666 CHECK_EQ(&OPB1, end->op());
667 CHECK_EQ(n1, end->InputAt(0));
668 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698