Home | History | Annotate | Download | only in optimizing
      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-typed-test.h"
     22 #include "gtest/gtest.h"
     23 
     24 namespace art {
     25 
     26 constexpr int kScratchRegisterStartIndexForTest = 100;
     27 
     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 
     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:
     55   explicit TestParallelMoveResolverWithSwap(ArenaAllocator* allocator)
     56       : ParallelMoveResolverWithSwap(allocator) {}
     57 
     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 
     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 
     82   void SpillScratch(int reg ATTRIBUTE_UNUSED) OVERRIDE {}
     83   void RestoreScratch(int reg ATTRIBUTE_UNUSED) OVERRIDE {}
     84 
     85   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:
     98   explicit TestParallelMoveResolverNoSwap(ArenaAllocator* allocator)
     99       : ParallelMoveResolverNoSwap(allocator), scratch_index_(kScratchRegisterStartIndexForTest) {}
    100 
    101   void PrepareForEmitNativeCode() OVERRIDE {
    102     scratch_index_ = kScratchRegisterStartIndexForTest;
    103   }
    104 
    105   void FinishEmitNativeCode() OVERRIDE {}
    106 
    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 
    127   void FreeScratchLocation(Location loc ATTRIBUTE_UNUSED) OVERRIDE {}
    128 
    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 
    141   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 
    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         DataType::Type::kInt32,
    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 
    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 
    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 
    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       DataType::Type::kInt32,
    268       nullptr);
    269   moves->AddMove(
    270       Location::RegisterLocation(1),
    271       Location::RegisterLocation(2),
    272       DataType::Type::kInt32,
    273       nullptr);
    274   resolver.EmitNativeCode(moves);
    275   ASSERT_STREQ("(1 -> 2) (C -> 0)", resolver.GetMessage().c_str());
    276 }
    277 
    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         DataType::Type::kInt32,
    289         nullptr);
    290     moves->AddMove(
    291         Location::RegisterPairLocation(0, 1),
    292         Location::RegisterPairLocation(2, 3),
    293         DataType::Type::kInt64,
    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         DataType::Type::kInt64,
    306         nullptr);
    307     moves->AddMove(
    308         Location::RegisterLocation(2),
    309         Location::RegisterLocation(4),
    310         DataType::Type::kInt32,
    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         DataType::Type::kInt64,
    323         nullptr);
    324     moves->AddMove(
    325         Location::RegisterLocation(2),
    326         Location::RegisterLocation(0),
    327         DataType::Type::kInt32,
    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         DataType::Type::kInt32,
    343         nullptr);
    344     moves->AddMove(
    345         Location::RegisterLocation(7),
    346         Location::RegisterLocation(1),
    347         DataType::Type::kInt32,
    348         nullptr);
    349     moves->AddMove(
    350         Location::RegisterPairLocation(0, 1),
    351         Location::RegisterPairLocation(2, 3),
    352         DataType::Type::kInt64,
    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         DataType::Type::kInt32,
    369         nullptr);
    370     moves->AddMove(
    371         Location::RegisterPairLocation(0, 1),
    372         Location::RegisterPairLocation(2, 3),
    373         DataType::Type::kInt64,
    374         nullptr);
    375     moves->AddMove(
    376         Location::RegisterLocation(7),
    377         Location::RegisterLocation(1),
    378         DataType::Type::kInt32,
    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         DataType::Type::kInt64,
    395         nullptr);
    396     moves->AddMove(
    397         Location::RegisterLocation(2),
    398         Location::RegisterLocation(7),
    399         DataType::Type::kInt32,
    400         nullptr);
    401     moves->AddMove(
    402         Location::RegisterLocation(7),
    403         Location::RegisterLocation(1),
    404         DataType::Type::kInt32,
    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         DataType::Type::kInt64,
    420         nullptr);
    421     moves->AddMove(
    422         Location::RegisterPairLocation(2, 3),
    423         Location::RegisterPairLocation(0, 1),
    424         DataType::Type::kInt64,
    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         DataType::Type::kInt64,
    440         nullptr);
    441     moves->AddMove(
    442         Location::RegisterPairLocation(0, 1),
    443         Location::RegisterPairLocation(2, 3),
    444         DataType::Type::kInt64,
    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 
    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         DataType::Type::kInt64,
    477         nullptr);
    478     moves->AddMove(
    479         Location::RegisterLocation(2),
    480         Location::RegisterLocation(0),
    481         DataType::Type::kInt32,
    482         nullptr);
    483     moves->AddMove(
    484         Location::RegisterLocation(3),
    485         Location::RegisterLocation(1),
    486         DataType::Type::kInt32,
    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         DataType::Type::kInt32,
    503         nullptr);
    504     moves->AddMove(
    505         Location::RegisterLocation(3),
    506         Location::RegisterLocation(1),
    507         DataType::Type::kInt32,
    508         nullptr);
    509     moves->AddMove(
    510         Location::RegisterPairLocation(0, 1),
    511         Location::RegisterPairLocation(2, 3),
    512         DataType::Type::kInt64,
    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         DataType::Type::kInt32,
    531         nullptr);
    532     moves->AddMove(
    533         Location::RegisterPairLocation(4, 5),
    534         Location::DoubleStackSlot(32),
    535         DataType::Type::kInt64,
    536         nullptr);
    537     moves->AddMove(
    538         Location::DoubleStackSlot(32),
    539         Location::RegisterPairLocation(10, 11),
    540         DataType::Type::kInt64,
    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.
    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         DataType::Type::kInt64,
    564         nullptr);
    565     moves->AddMove(
    566         Location::RegisterLocation(1),
    567         Location::StackSlot(48),
    568         DataType::Type::kInt32,
    569         nullptr);
    570     moves->AddMove(
    571         Location::StackSlot(48),
    572         Location::RegisterLocation(0),
    573         DataType::Type::kInt32,
    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         DataType::Type::kInt64,
    591         nullptr);
    592     moves->AddMove(
    593         Location::RegisterPairLocation(2, 3),
    594         Location::DoubleStackSlot(32),
    595         DataType::Type::kInt64,
    596         nullptr);
    597     moves->AddMove(
    598         Location::DoubleStackSlot(32),
    599         Location::RegisterPairLocation(0, 1),
    600         DataType::Type::kInt64,
    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 
    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         DataType::Type::kInt32,
    623         nullptr);
    624     moves->AddMove(
    625         Location::RegisterPairLocation(2, 3),
    626         Location::RegisterPairLocation(0, 1),
    627         DataType::Type::kInt64,
    628         nullptr);
    629     moves->AddMove(
    630         Location::RegisterLocation(7),
    631         Location::RegisterLocation(2),
    632         DataType::Type::kInt32,
    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