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