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