1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "base/arena_allocator.h"
18 #include "base/malloc_arena_pool.h"
19 #include "nodes.h"
20 #include "parallel_move_resolver.h"
21 
22 #include "gtest/gtest-typed-test.h"
23 #include "gtest/gtest.h"
24 
25 namespace art {
26 
27 constexpr int kScratchRegisterStartIndexForTest = 100;
28 
DumpRegisterForTest(std::ostream & os,int reg)29 static void DumpRegisterForTest(std::ostream& os, int reg) {
30   if (reg >= kScratchRegisterStartIndexForTest) {
31     os << "T" << reg - kScratchRegisterStartIndexForTest;
32   } else {
33     os << reg;
34   }
35 }
36 
DumpLocationForTest(std::ostream & os,Location location)37 static void DumpLocationForTest(std::ostream& os, Location location) {
38   if (location.IsConstant()) {
39     os << "C";
40   } else if (location.IsPair()) {
41     DumpRegisterForTest(os, location.low());
42     os << ",";
43     DumpRegisterForTest(os, location.high());
44   } else if (location.IsRegister()) {
45     DumpRegisterForTest(os, location.reg());
46   } else if (location.IsStackSlot()) {
47     os << location.GetStackIndex() << "(sp)";
48   } else {
49     DCHECK(location.IsDoubleStackSlot())<< location;
50     os << "2x" << location.GetStackIndex() << "(sp)";
51   }
52 }
53 
54 class TestParallelMoveResolverWithSwap : public ParallelMoveResolverWithSwap {
55  public:
TestParallelMoveResolverWithSwap(ArenaAllocator * allocator)56   explicit TestParallelMoveResolverWithSwap(ArenaAllocator* allocator)
57       : ParallelMoveResolverWithSwap(allocator) {}
58 
EmitMove(size_t index)59   void EmitMove(size_t index) override {
60     MoveOperands* move = moves_[index];
61     if (!message_.str().empty()) {
62       message_ << " ";
63     }
64     message_ << "(";
65     DumpLocationForTest(message_, move->GetSource());
66     message_ << " -> ";
67     DumpLocationForTest(message_, move->GetDestination());
68     message_ << ")";
69   }
70 
EmitSwap(size_t index)71   void EmitSwap(size_t index) override {
72     MoveOperands* move = moves_[index];
73     if (!message_.str().empty()) {
74       message_ << " ";
75     }
76     message_ << "(";
77     DumpLocationForTest(message_, move->GetSource());
78     message_ << " <-> ";
79     DumpLocationForTest(message_, move->GetDestination());
80     message_ << ")";
81   }
82 
SpillScratch(int reg ATTRIBUTE_UNUSED)83   void SpillScratch(int reg ATTRIBUTE_UNUSED) override {}
RestoreScratch(int reg ATTRIBUTE_UNUSED)84   void RestoreScratch(int reg ATTRIBUTE_UNUSED) override {}
85 
GetMessage() const86   std::string GetMessage() const {
87     return  message_.str();
88   }
89 
90  private:
91   std::ostringstream message_;
92 
93 
94   DISALLOW_COPY_AND_ASSIGN(TestParallelMoveResolverWithSwap);
95 };
96 
97 class TestParallelMoveResolverNoSwap : public ParallelMoveResolverNoSwap {
98  public:
TestParallelMoveResolverNoSwap(ArenaAllocator * allocator)99   explicit TestParallelMoveResolverNoSwap(ArenaAllocator* allocator)
100       : ParallelMoveResolverNoSwap(allocator), scratch_index_(kScratchRegisterStartIndexForTest) {}
101 
PrepareForEmitNativeCode()102   void PrepareForEmitNativeCode() override {
103     scratch_index_ = kScratchRegisterStartIndexForTest;
104   }
105 
FinishEmitNativeCode()106   void FinishEmitNativeCode() override {}
107 
AllocateScratchLocationFor(Location::Kind kind)108   Location AllocateScratchLocationFor(Location::Kind kind) override {
109     if (kind == Location::kStackSlot || kind == Location::kFpuRegister ||
110         kind == Location::kRegister) {
111       kind = Location::kRegister;
112     } else {
113       // Allocate register pair for double stack slot which simulates 32-bit backend's behavior.
114       kind = Location::kRegisterPair;
115     }
116     Location scratch = GetScratchLocation(kind);
117     if (scratch.Equals(Location::NoLocation())) {
118       AddScratchLocation(Location::RegisterLocation(scratch_index_));
119       AddScratchLocation(Location::RegisterLocation(scratch_index_ + 1));
120       AddScratchLocation(Location::RegisterPairLocation(scratch_index_, scratch_index_ + 1));
121       scratch = (kind == Location::kRegister) ? Location::RegisterLocation(scratch_index_)
122           : Location::RegisterPairLocation(scratch_index_, scratch_index_ + 1);
123       scratch_index_ += 2;
124     }
125     return scratch;
126   }
127 
FreeScratchLocation(Location loc ATTRIBUTE_UNUSED)128   void FreeScratchLocation(Location loc ATTRIBUTE_UNUSED) override {}
129 
EmitMove(size_t index)130   void EmitMove(size_t index) override {
131     MoveOperands* move = moves_[index];
132     if (!message_.str().empty()) {
133       message_ << " ";
134     }
135     message_ << "(";
136     DumpLocationForTest(message_, move->GetSource());
137     message_ << " -> ";
138     DumpLocationForTest(message_, move->GetDestination());
139     message_ << ")";
140   }
141 
GetMessage() const142   std::string GetMessage() const {
143     return  message_.str();
144   }
145 
146  private:
147   std::ostringstream message_;
148 
149   int scratch_index_;
150 
151   DISALLOW_COPY_AND_ASSIGN(TestParallelMoveResolverNoSwap);
152 };
153 
BuildParallelMove(ArenaAllocator * allocator,const size_t operands[][2],size_t number_of_moves)154 static HParallelMove* BuildParallelMove(ArenaAllocator* allocator,
155                                         const size_t operands[][2],
156                                         size_t number_of_moves) {
157   HParallelMove* moves = new (allocator) HParallelMove(allocator);
158   for (size_t i = 0; i < number_of_moves; ++i) {
159     moves->AddMove(
160         Location::RegisterLocation(operands[i][0]),
161         Location::RegisterLocation(operands[i][1]),
162         DataType::Type::kInt32,
163         nullptr);
164   }
165   return moves;
166 }
167 
168 template <typename T>
169 class ParallelMoveTest : public ::testing::Test {
170  public:
171   static const bool has_swap;
172 };
173 
174 template<> const bool ParallelMoveTest<TestParallelMoveResolverWithSwap>::has_swap = true;
175 template<> const bool ParallelMoveTest<TestParallelMoveResolverNoSwap>::has_swap = false;
176 
177 using ParallelMoveResolverTestTypes =
178     ::testing::Types<TestParallelMoveResolverWithSwap, TestParallelMoveResolverNoSwap>;
179 
180 TYPED_TEST_CASE(ParallelMoveTest, ParallelMoveResolverTestTypes);
181 
182 
TYPED_TEST(ParallelMoveTest,Dependency)183 TYPED_TEST(ParallelMoveTest, Dependency) {
184   MallocArenaPool pool;
185   ArenaAllocator allocator(&pool);
186 
187   {
188     TypeParam resolver(&allocator);
189     static constexpr size_t moves[][2] = {{0, 1}, {1, 2}};
190     resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
191     if (TestFixture::has_swap) {
192       ASSERT_STREQ("(1 -> 2) (0 -> 1)", resolver.GetMessage().c_str());
193     } else {
194       ASSERT_STREQ("(1 -> 2) (0 -> 1)", resolver.GetMessage().c_str());
195     }
196   }
197 
198   {
199     TypeParam resolver(&allocator);
200     static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {2, 3}, {1, 4}};
201     resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
202     if (TestFixture::has_swap) {
203       ASSERT_STREQ("(2 -> 3) (1 -> 2) (1 -> 4) (0 -> 1)", resolver.GetMessage().c_str());
204     } else {
205       ASSERT_STREQ("(2 -> 3) (1 -> 2) (0 -> 1) (2 -> 4)", resolver.GetMessage().c_str());
206     }
207   }
208 }
209 
TYPED_TEST(ParallelMoveTest,Cycle)210 TYPED_TEST(ParallelMoveTest, Cycle) {
211   MallocArenaPool pool;
212   ArenaAllocator allocator(&pool);
213 
214   {
215     TypeParam resolver(&allocator);
216     static constexpr size_t moves[][2] = {{0, 1}, {1, 0}};
217     resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
218     if (TestFixture::has_swap) {
219       ASSERT_STREQ("(1 <-> 0)", resolver.GetMessage().c_str());
220     } else {
221       ASSERT_STREQ("(1 -> T0) (0 -> 1) (T0 -> 0)", resolver.GetMessage().c_str());
222     }
223   }
224 
225   {
226     TypeParam resolver(&allocator);
227     static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {1, 0}};
228     resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
229     if (TestFixture::has_swap) {
230       ASSERT_STREQ("(1 -> 2) (1 <-> 0)", resolver.GetMessage().c_str());
231     } else {
232       ASSERT_STREQ("(1 -> 2) (0 -> 1) (2 -> 0)", resolver.GetMessage().c_str());
233     }
234   }
235 
236   {
237     TypeParam resolver(&allocator);
238     static constexpr size_t moves[][2] = {{0, 1}, {1, 0}, {0, 2}};
239     resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
240     if (TestFixture::has_swap) {
241       ASSERT_STREQ("(0 -> 2) (1 <-> 0)", resolver.GetMessage().c_str());
242     } else {
243       ASSERT_STREQ("(0 -> 2) (1 -> 0) (2 -> 1)", resolver.GetMessage().c_str());
244     }
245   }
246 
247   {
248     TypeParam resolver(&allocator);
249     static constexpr size_t moves[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0}};
250     resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
251     if (TestFixture::has_swap) {
252       ASSERT_STREQ("(4 <-> 0) (3 <-> 4) (2 <-> 3) (1 <-> 2)", resolver.GetMessage().c_str());
253     } else {
254       ASSERT_STREQ("(4 -> T0) (3 -> 4) (2 -> 3) (1 -> 2) (0 -> 1) (T0 -> 0)",
255           resolver.GetMessage().c_str());
256     }
257   }
258 }
259 
TYPED_TEST(ParallelMoveTest,ConstantLast)260 TYPED_TEST(ParallelMoveTest, ConstantLast) {
261   MallocArenaPool pool;
262   ArenaAllocator allocator(&pool);
263   TypeParam resolver(&allocator);
264   HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
265   moves->AddMove(
266       Location::ConstantLocation(new (&allocator) HIntConstant(0)),
267       Location::RegisterLocation(0),
268       DataType::Type::kInt32,
269       nullptr);
270   moves->AddMove(
271       Location::RegisterLocation(1),
272       Location::RegisterLocation(2),
273       DataType::Type::kInt32,
274       nullptr);
275   resolver.EmitNativeCode(moves);
276   ASSERT_STREQ("(1 -> 2) (C -> 0)", resolver.GetMessage().c_str());
277 }
278 
TYPED_TEST(ParallelMoveTest,Pairs)279 TYPED_TEST(ParallelMoveTest, Pairs) {
280   MallocArenaPool pool;
281   ArenaAllocator allocator(&pool);
282 
283   {
284     TypeParam resolver(&allocator);
285     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
286     moves->AddMove(
287         Location::RegisterLocation(2),
288         Location::RegisterLocation(4),
289         DataType::Type::kInt32,
290         nullptr);
291     moves->AddMove(
292         Location::RegisterPairLocation(0, 1),
293         Location::RegisterPairLocation(2, 3),
294         DataType::Type::kInt64,
295         nullptr);
296     resolver.EmitNativeCode(moves);
297     ASSERT_STREQ("(2 -> 4) (0,1 -> 2,3)", resolver.GetMessage().c_str());
298   }
299 
300   {
301     TypeParam resolver(&allocator);
302     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
303     moves->AddMove(
304         Location::RegisterPairLocation(0, 1),
305         Location::RegisterPairLocation(2, 3),
306         DataType::Type::kInt64,
307         nullptr);
308     moves->AddMove(
309         Location::RegisterLocation(2),
310         Location::RegisterLocation(4),
311         DataType::Type::kInt32,
312         nullptr);
313     resolver.EmitNativeCode(moves);
314     ASSERT_STREQ("(2 -> 4) (0,1 -> 2,3)", resolver.GetMessage().c_str());
315   }
316 
317   {
318     TypeParam resolver(&allocator);
319     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
320     moves->AddMove(
321         Location::RegisterPairLocation(0, 1),
322         Location::RegisterPairLocation(2, 3),
323         DataType::Type::kInt64,
324         nullptr);
325     moves->AddMove(
326         Location::RegisterLocation(2),
327         Location::RegisterLocation(0),
328         DataType::Type::kInt32,
329         nullptr);
330     resolver.EmitNativeCode(moves);
331     if (TestFixture::has_swap) {
332       ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str());
333     } else {
334       ASSERT_STREQ("(2 -> T0) (0,1 -> 2,3) (T0 -> 0)", resolver.GetMessage().c_str());
335     }
336   }
337   {
338     TypeParam resolver(&allocator);
339     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
340     moves->AddMove(
341         Location::RegisterLocation(2),
342         Location::RegisterLocation(7),
343         DataType::Type::kInt32,
344         nullptr);
345     moves->AddMove(
346         Location::RegisterLocation(7),
347         Location::RegisterLocation(1),
348         DataType::Type::kInt32,
349         nullptr);
350     moves->AddMove(
351         Location::RegisterPairLocation(0, 1),
352         Location::RegisterPairLocation(2, 3),
353         DataType::Type::kInt64,
354         nullptr);
355     resolver.EmitNativeCode(moves);
356     if (TestFixture::has_swap) {
357       ASSERT_STREQ("(0,1 <-> 2,3) (7 -> 1) (0 -> 7)", resolver.GetMessage().c_str());
358     } else {
359       ASSERT_STREQ("(0,1 -> T0,T1) (7 -> 1) (2 -> 7) (T0,T1 -> 2,3)",
360           resolver.GetMessage().c_str());
361     }
362   }
363   {
364     TypeParam resolver(&allocator);
365     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
366     moves->AddMove(
367         Location::RegisterLocation(2),
368         Location::RegisterLocation(7),
369         DataType::Type::kInt32,
370         nullptr);
371     moves->AddMove(
372         Location::RegisterPairLocation(0, 1),
373         Location::RegisterPairLocation(2, 3),
374         DataType::Type::kInt64,
375         nullptr);
376     moves->AddMove(
377         Location::RegisterLocation(7),
378         Location::RegisterLocation(1),
379         DataType::Type::kInt32,
380         nullptr);
381     resolver.EmitNativeCode(moves);
382     if (TestFixture::has_swap) {
383       ASSERT_STREQ("(0,1 <-> 2,3) (7 -> 1) (0 -> 7)", resolver.GetMessage().c_str());
384     } else {
385       ASSERT_STREQ("(0,1 -> T0,T1) (7 -> 1) (2 -> 7) (T0,T1 -> 2,3)",
386           resolver.GetMessage().c_str());
387     }
388   }
389   {
390     TypeParam resolver(&allocator);
391     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
392     moves->AddMove(
393         Location::RegisterPairLocation(0, 1),
394         Location::RegisterPairLocation(2, 3),
395         DataType::Type::kInt64,
396         nullptr);
397     moves->AddMove(
398         Location::RegisterLocation(2),
399         Location::RegisterLocation(7),
400         DataType::Type::kInt32,
401         nullptr);
402     moves->AddMove(
403         Location::RegisterLocation(7),
404         Location::RegisterLocation(1),
405         DataType::Type::kInt32,
406         nullptr);
407     resolver.EmitNativeCode(moves);
408     if (TestFixture::has_swap) {
409       ASSERT_STREQ("(0,1 <-> 2,3) (7 -> 1) (0 -> 7)", resolver.GetMessage().c_str());
410     } else {
411       ASSERT_STREQ("(7 -> T0) (2 -> 7) (0,1 -> 2,3) (T0 -> 1)", resolver.GetMessage().c_str());
412     }
413   }
414   {
415     TypeParam resolver(&allocator);
416     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
417     moves->AddMove(
418         Location::RegisterPairLocation(0, 1),
419         Location::RegisterPairLocation(2, 3),
420         DataType::Type::kInt64,
421         nullptr);
422     moves->AddMove(
423         Location::RegisterPairLocation(2, 3),
424         Location::RegisterPairLocation(0, 1),
425         DataType::Type::kInt64,
426         nullptr);
427     resolver.EmitNativeCode(moves);
428     if (TestFixture::has_swap) {
429       ASSERT_STREQ("(2,3 <-> 0,1)", resolver.GetMessage().c_str());
430     } else {
431       ASSERT_STREQ("(2,3 -> T0,T1) (0,1 -> 2,3) (T0,T1 -> 0,1)", resolver.GetMessage().c_str());
432     }
433   }
434   {
435     TypeParam resolver(&allocator);
436     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
437     moves->AddMove(
438         Location::RegisterPairLocation(2, 3),
439         Location::RegisterPairLocation(0, 1),
440         DataType::Type::kInt64,
441         nullptr);
442     moves->AddMove(
443         Location::RegisterPairLocation(0, 1),
444         Location::RegisterPairLocation(2, 3),
445         DataType::Type::kInt64,
446         nullptr);
447     resolver.EmitNativeCode(moves);
448     if (TestFixture::has_swap) {
449       ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str());
450     } else {
451       ASSERT_STREQ("(0,1 -> T0,T1) (2,3 -> 0,1) (T0,T1 -> 2,3)", resolver.GetMessage().c_str());
452     }
453   }
454 }
455 
TYPED_TEST(ParallelMoveTest,MultiCycles)456 TYPED_TEST(ParallelMoveTest, MultiCycles) {
457   MallocArenaPool pool;
458   ArenaAllocator allocator(&pool);
459 
460   {
461     TypeParam resolver(&allocator);
462     static constexpr size_t moves[][2] = {{0, 1}, {1, 0}, {2, 3}, {3, 2}};
463     resolver.EmitNativeCode(BuildParallelMove(&allocator, moves, arraysize(moves)));
464     if (TestFixture::has_swap) {
465       ASSERT_STREQ("(1 <-> 0) (3 <-> 2)",  resolver.GetMessage().c_str());
466     } else {
467       ASSERT_STREQ("(1 -> T0) (0 -> 1) (T0 -> 0) (3 -> T0) (2 -> 3) (T0 -> 2)",
468           resolver.GetMessage().c_str());
469     }
470   }
471   {
472     TypeParam resolver(&allocator);
473     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
474     moves->AddMove(
475         Location::RegisterPairLocation(0, 1),
476         Location::RegisterPairLocation(2, 3),
477         DataType::Type::kInt64,
478         nullptr);
479     moves->AddMove(
480         Location::RegisterLocation(2),
481         Location::RegisterLocation(0),
482         DataType::Type::kInt32,
483         nullptr);
484     moves->AddMove(
485         Location::RegisterLocation(3),
486         Location::RegisterLocation(1),
487         DataType::Type::kInt32,
488         nullptr);
489     resolver.EmitNativeCode(moves);
490     if (TestFixture::has_swap) {
491       ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str());
492     } else {
493       ASSERT_STREQ("(2 -> T0) (3 -> T1) (0,1 -> 2,3) (T0 -> 0) (T1 -> 1)",
494           resolver.GetMessage().c_str());
495     }
496   }
497   {
498     TypeParam resolver(&allocator);
499     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
500     moves->AddMove(
501         Location::RegisterLocation(2),
502         Location::RegisterLocation(0),
503         DataType::Type::kInt32,
504         nullptr);
505     moves->AddMove(
506         Location::RegisterLocation(3),
507         Location::RegisterLocation(1),
508         DataType::Type::kInt32,
509         nullptr);
510     moves->AddMove(
511         Location::RegisterPairLocation(0, 1),
512         Location::RegisterPairLocation(2, 3),
513         DataType::Type::kInt64,
514         nullptr);
515     resolver.EmitNativeCode(moves);
516     if (TestFixture::has_swap) {
517       ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str());
518     } else {
519       ASSERT_STREQ("(3 -> T0) (0,1 -> T2,T3) (T0 -> 1) (2 -> 0) (T2,T3 -> 2,3)",
520           resolver.GetMessage().c_str());
521     }
522   }
523 
524   {
525     // Test involving registers used in single context and pair context.
526     TypeParam resolver(&allocator);
527     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
528     moves->AddMove(
529         Location::RegisterLocation(10),
530         Location::RegisterLocation(5),
531         DataType::Type::kInt32,
532         nullptr);
533     moves->AddMove(
534         Location::RegisterPairLocation(4, 5),
535         Location::DoubleStackSlot(32),
536         DataType::Type::kInt64,
537         nullptr);
538     moves->AddMove(
539         Location::DoubleStackSlot(32),
540         Location::RegisterPairLocation(10, 11),
541         DataType::Type::kInt64,
542         nullptr);
543     resolver.EmitNativeCode(moves);
544     if (TestFixture::has_swap) {
545       ASSERT_STREQ("(2x32(sp) <-> 10,11) (4,5 <-> 2x32(sp)) (4 -> 5)", resolver.GetMessage().c_str());
546     } else {
547       ASSERT_STREQ("(2x32(sp) -> T0,T1) (4,5 -> 2x32(sp)) (10 -> 5) (T0,T1 -> 10,11)",
548           resolver.GetMessage().c_str());
549     }
550   }
551 }
552 
553 // Test that we do 64bits moves before 32bits moves.
TYPED_TEST(ParallelMoveTest,CyclesWith64BitsMoves)554 TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves) {
555   MallocArenaPool pool;
556   ArenaAllocator allocator(&pool);
557 
558   {
559     TypeParam resolver(&allocator);
560     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
561     moves->AddMove(
562         Location::RegisterLocation(0),
563         Location::RegisterLocation(1),
564         DataType::Type::kInt64,
565         nullptr);
566     moves->AddMove(
567         Location::RegisterLocation(1),
568         Location::StackSlot(48),
569         DataType::Type::kInt32,
570         nullptr);
571     moves->AddMove(
572         Location::StackSlot(48),
573         Location::RegisterLocation(0),
574         DataType::Type::kInt32,
575         nullptr);
576     resolver.EmitNativeCode(moves);
577     if (TestFixture::has_swap) {
578       ASSERT_STREQ("(0 <-> 1) (48(sp) <-> 0)", resolver.GetMessage().c_str());
579     } else {
580       ASSERT_STREQ("(48(sp) -> T0) (1 -> 48(sp)) (0 -> 1) (T0 -> 0)",
581           resolver.GetMessage().c_str());
582     }
583   }
584 
585   {
586     TypeParam resolver(&allocator);
587     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
588     moves->AddMove(
589         Location::RegisterPairLocation(0, 1),
590         Location::RegisterPairLocation(2, 3),
591         DataType::Type::kInt64,
592         nullptr);
593     moves->AddMove(
594         Location::RegisterPairLocation(2, 3),
595         Location::DoubleStackSlot(32),
596         DataType::Type::kInt64,
597         nullptr);
598     moves->AddMove(
599         Location::DoubleStackSlot(32),
600         Location::RegisterPairLocation(0, 1),
601         DataType::Type::kInt64,
602         nullptr);
603     resolver.EmitNativeCode(moves);
604     if (TestFixture::has_swap) {
605       ASSERT_STREQ("(2x32(sp) <-> 0,1) (2,3 <-> 2x32(sp))", resolver.GetMessage().c_str());
606     } else {
607       ASSERT_STREQ("(2x32(sp) -> T0,T1) (2,3 -> 2x32(sp)) (0,1 -> 2,3) (T0,T1 -> 0,1)",
608           resolver.GetMessage().c_str());
609     }
610   }
611 }
612 
TYPED_TEST(ParallelMoveTest,CyclesWith64BitsMoves2)613 TYPED_TEST(ParallelMoveTest, CyclesWith64BitsMoves2) {
614   MallocArenaPool pool;
615   ArenaAllocator allocator(&pool);
616 
617   {
618     TypeParam resolver(&allocator);
619     HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
620     moves->AddMove(
621         Location::RegisterLocation(0),
622         Location::RegisterLocation(3),
623         DataType::Type::kInt32,
624         nullptr);
625     moves->AddMove(
626         Location::RegisterPairLocation(2, 3),
627         Location::RegisterPairLocation(0, 1),
628         DataType::Type::kInt64,
629         nullptr);
630     moves->AddMove(
631         Location::RegisterLocation(7),
632         Location::RegisterLocation(2),
633         DataType::Type::kInt32,
634         nullptr);
635     resolver.EmitNativeCode(moves);
636     if (TestFixture::has_swap) {
637       ASSERT_STREQ("(2,3 <-> 0,1) (2 -> 3) (7 -> 2)", resolver.GetMessage().c_str());
638     } else {
639       ASSERT_STREQ("(2,3 -> T0,T1) (0 -> 3) (T0,T1 -> 0,1) (7 -> 2)",
640           resolver.GetMessage().c_str());
641     }
642   }
643 }
644 
645 }  // namespace art
646