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 "builder.h"
     18 #include "code_generator.h"
     19 #include "dex_file.h"
     20 #include "dex_instruction.h"
     21 #include "nodes.h"
     22 #include "optimizing_unit_test.h"
     23 #include "register_allocator.h"
     24 #include "ssa_liveness_analysis.h"
     25 #include "utils/arena_allocator.h"
     26 
     27 #include "gtest/gtest.h"
     28 
     29 namespace art {
     30 
     31 // Note: the register allocator tests rely on the fact that constants have live
     32 // intervals and registers get allocated to them.
     33 
     34 static bool Check(const uint16_t* data) {
     35   ArenaPool pool;
     36   ArenaAllocator allocator(&pool);
     37   HGraphBuilder builder(&allocator);
     38   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
     39   HGraph* graph = builder.BuildGraph(*item);
     40   graph->BuildDominatorTree();
     41   graph->TransformToSSA();
     42   graph->FindNaturalLoops();
     43   CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
     44   SsaLivenessAnalysis liveness(*graph, codegen);
     45   liveness.Analyze();
     46   RegisterAllocator register_allocator(&allocator, codegen, liveness);
     47   register_allocator.AllocateRegisters();
     48   return register_allocator.Validate(false);
     49 }
     50 
     51 /**
     52  * Unit testing of RegisterAllocator::ValidateIntervals. Register allocator
     53  * tests are based on this validation method.
     54  */
     55 TEST(RegisterAllocatorTest, ValidateIntervals) {
     56   ArenaPool pool;
     57   ArenaAllocator allocator(&pool);
     58   HGraph* graph = new (&allocator) HGraph(&allocator);
     59   CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
     60   GrowableArray<LiveInterval*> intervals(&allocator, 0);
     61 
     62   // Test with two intervals of the same range.
     63   {
     64     static constexpr size_t ranges[][2] = {{0, 42}};
     65     intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 0));
     66     intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 1));
     67     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
     68         intervals, 0, *codegen, &allocator, true, false));
     69 
     70     intervals.Get(1)->SetRegister(0);
     71     ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
     72         intervals, 0, *codegen, &allocator, true, false));
     73     intervals.Reset();
     74   }
     75 
     76   // Test with two non-intersecting intervals.
     77   {
     78     static constexpr size_t ranges1[][2] = {{0, 42}};
     79     intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
     80     static constexpr size_t ranges2[][2] = {{42, 43}};
     81     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
     82     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
     83         intervals, 0, *codegen, &allocator, true, false));
     84 
     85     intervals.Get(1)->SetRegister(0);
     86     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
     87         intervals, 0, *codegen, &allocator, true, false));
     88     intervals.Reset();
     89   }
     90 
     91   // Test with two non-intersecting intervals, with one with a lifetime hole.
     92   {
     93     static constexpr size_t ranges1[][2] = {{0, 42}, {45, 48}};
     94     intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
     95     static constexpr size_t ranges2[][2] = {{42, 43}};
     96     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
     97     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
     98         intervals, 0, *codegen, &allocator, true, false));
     99 
    100     intervals.Get(1)->SetRegister(0);
    101     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
    102         intervals, 0, *codegen, &allocator, true, false));
    103     intervals.Reset();
    104   }
    105 
    106   // Test with intersecting intervals.
    107   {
    108     static constexpr size_t ranges1[][2] = {{0, 42}, {44, 48}};
    109     intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
    110     static constexpr size_t ranges2[][2] = {{42, 47}};
    111     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
    112     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
    113         intervals, 0, *codegen, &allocator, true, false));
    114 
    115     intervals.Get(1)->SetRegister(0);
    116     ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
    117         intervals, 0, *codegen, &allocator, true, false));
    118     intervals.Reset();
    119   }
    120 
    121   // Test with siblings.
    122   {
    123     static constexpr size_t ranges1[][2] = {{0, 42}, {44, 48}};
    124     intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
    125     intervals.Get(0)->SplitAt(43);
    126     static constexpr size_t ranges2[][2] = {{42, 47}};
    127     intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
    128     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
    129         intervals, 0, *codegen, &allocator, true, false));
    130 
    131     intervals.Get(1)->SetRegister(0);
    132     // Sibling of the first interval has no register allocated to it.
    133     ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
    134         intervals, 0, *codegen, &allocator, true, false));
    135 
    136     intervals.Get(0)->GetNextSibling()->SetRegister(0);
    137     ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
    138         intervals, 0, *codegen, &allocator, true, false));
    139   }
    140 }
    141 
    142 TEST(RegisterAllocatorTest, CFG1) {
    143   /*
    144    * Test the following snippet:
    145    *  return 0;
    146    *
    147    * Which becomes the following graph:
    148    *       constant0
    149    *       goto
    150    *        |
    151    *       return
    152    *        |
    153    *       exit
    154    */
    155   const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
    156     Instruction::CONST_4 | 0 | 0,
    157     Instruction::RETURN);
    158 
    159   ASSERT_TRUE(Check(data));
    160 }
    161 
    162 TEST(RegisterAllocatorTest, Loop1) {
    163   /*
    164    * Test the following snippet:
    165    *  int a = 0;
    166    *  while (a == a) {
    167    *    a = 4;
    168    *  }
    169    *  return 5;
    170    *
    171    * Which becomes the following graph:
    172    *       constant0
    173    *       constant4
    174    *       constant5
    175    *       goto
    176    *        |
    177    *       goto
    178    *        |
    179    *       phi
    180    *       equal
    181    *       if +++++
    182    *        |       \ +
    183    *        |     goto
    184    *        |
    185    *       return
    186    *        |
    187    *       exit
    188    */
    189 
    190   const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
    191     Instruction::CONST_4 | 0 | 0,
    192     Instruction::IF_EQ, 4,
    193     Instruction::CONST_4 | 4 << 12 | 0,
    194     Instruction::GOTO | 0xFD00,
    195     Instruction::CONST_4 | 5 << 12 | 1 << 8,
    196     Instruction::RETURN | 1 << 8);
    197 
    198   ASSERT_TRUE(Check(data));
    199 }
    200 
    201 TEST(RegisterAllocatorTest, Loop2) {
    202   /*
    203    * Test the following snippet:
    204    *  int a = 0;
    205    *  while (a == 8) {
    206    *    a = 4 + 5;
    207    *  }
    208    *  return 6 + 7;
    209    *
    210    * Which becomes the following graph:
    211    *       constant0
    212    *       constant4
    213    *       constant5
    214    *       constant6
    215    *       constant7
    216    *       constant8
    217    *       goto
    218    *        |
    219    *       goto
    220    *        |
    221    *       phi
    222    *       equal
    223    *       if +++++
    224    *        |       \ +
    225    *        |      4 + 5
    226    *        |      goto
    227    *        |
    228    *       6 + 7
    229    *       return
    230    *        |
    231    *       exit
    232    */
    233 
    234   const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
    235     Instruction::CONST_4 | 0 | 0,
    236     Instruction::CONST_4 | 8 << 12 | 1 << 8,
    237     Instruction::IF_EQ | 1 << 8, 7,
    238     Instruction::CONST_4 | 4 << 12 | 0 << 8,
    239     Instruction::CONST_4 | 5 << 12 | 1 << 8,
    240     Instruction::ADD_INT, 1 << 8 | 0,
    241     Instruction::GOTO | 0xFA00,
    242     Instruction::CONST_4 | 6 << 12 | 1 << 8,
    243     Instruction::CONST_4 | 7 << 12 | 1 << 8,
    244     Instruction::ADD_INT, 1 << 8 | 0,
    245     Instruction::RETURN | 1 << 8);
    246 
    247   ASSERT_TRUE(Check(data));
    248 }
    249 
    250 static HGraph* BuildSSAGraph(const uint16_t* data, ArenaAllocator* allocator) {
    251   HGraphBuilder builder(allocator);
    252   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
    253   HGraph* graph = builder.BuildGraph(*item);
    254   graph->BuildDominatorTree();
    255   graph->TransformToSSA();
    256   graph->FindNaturalLoops();
    257   return graph;
    258 }
    259 
    260 TEST(RegisterAllocatorTest, Loop3) {
    261   /*
    262    * Test the following snippet:
    263    *  int a = 0
    264    *  do {
    265    *    b = a;
    266    *    a++;
    267    *  } while (a != 5)
    268    *  return b;
    269    *
    270    * Which becomes the following graph:
    271    *       constant0
    272    *       constant1
    273    *       constant5
    274    *       goto
    275    *        |
    276    *       goto
    277    *        |++++++++++++
    278    *       phi          +
    279    *       a++          +
    280    *       equals       +
    281    *       if           +
    282    *        |++++++++++++
    283    *       return
    284    *        |
    285    *       exit
    286    */
    287 
    288   const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
    289     Instruction::CONST_4 | 0 | 0,
    290     Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8,
    291     Instruction::CONST_4 | 5 << 12 | 2 << 8,
    292     Instruction::IF_NE | 1 << 8 | 2 << 12, 3,
    293     Instruction::RETURN | 0 << 8,
    294     Instruction::MOVE | 1 << 12 | 0 << 8,
    295     Instruction::GOTO | 0xF900);
    296 
    297   ArenaPool pool;
    298   ArenaAllocator allocator(&pool);
    299   HGraph* graph = BuildSSAGraph(data, &allocator);
    300   CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
    301   SsaLivenessAnalysis liveness(*graph, codegen);
    302   liveness.Analyze();
    303   RegisterAllocator register_allocator(&allocator, codegen, liveness);
    304   register_allocator.AllocateRegisters();
    305   ASSERT_TRUE(register_allocator.Validate(false));
    306 
    307   HBasicBlock* loop_header = graph->GetBlocks().Get(2);
    308   HPhi* phi = loop_header->GetFirstPhi()->AsPhi();
    309 
    310   LiveInterval* phi_interval = phi->GetLiveInterval();
    311   LiveInterval* loop_update = phi->InputAt(1)->GetLiveInterval();
    312   ASSERT_TRUE(phi_interval->HasRegister());
    313   ASSERT_TRUE(loop_update->HasRegister());
    314   ASSERT_NE(phi_interval->GetRegister(), loop_update->GetRegister());
    315 
    316   HBasicBlock* return_block = graph->GetBlocks().Get(3);
    317   HReturn* ret = return_block->GetLastInstruction()->AsReturn();
    318   ASSERT_EQ(phi_interval->GetRegister(), ret->InputAt(0)->GetLiveInterval()->GetRegister());
    319 }
    320 
    321 TEST(RegisterAllocatorTest, FirstRegisterUse) {
    322   const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
    323     Instruction::CONST_4 | 0 | 0,
    324     Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8,
    325     Instruction::ADD_INT_LIT8 | 0 << 8, 1 << 8,
    326     Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8 | 1,
    327     Instruction::RETURN_VOID);
    328 
    329   ArenaPool pool;
    330   ArenaAllocator allocator(&pool);
    331   HGraph* graph = BuildSSAGraph(data, &allocator);
    332   CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kArm);
    333   SsaLivenessAnalysis liveness(*graph, codegen);
    334   liveness.Analyze();
    335 
    336   HAdd* first_add = graph->GetBlocks().Get(1)->GetFirstInstruction()->AsAdd();
    337   HAdd* last_add = graph->GetBlocks().Get(1)->GetLastInstruction()->GetPrevious()->AsAdd();
    338   ASSERT_EQ(last_add->InputAt(0), first_add);
    339   LiveInterval* interval = first_add->GetLiveInterval();
    340   ASSERT_EQ(interval->GetEnd(), last_add->GetLifetimePosition() + 1);
    341   ASSERT_TRUE(interval->GetNextSibling() == nullptr);
    342 
    343   // We need a register for the output of the instruction.
    344   ASSERT_EQ(interval->FirstRegisterUse(), first_add->GetLifetimePosition());
    345 
    346   // Split at the next instruction.
    347   interval = interval->SplitAt(first_add->GetLifetimePosition() + 2);
    348   // The user of the split is the last add.
    349   ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition() + 1);
    350 
    351   // Split before the last add.
    352   LiveInterval* new_interval = interval->SplitAt(last_add->GetLifetimePosition() - 1);
    353   // Ensure the current interval has no register use...
    354   ASSERT_EQ(interval->FirstRegisterUse(), kNoLifetime);
    355   // And the new interval has it for the last add.
    356   ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition() + 1);
    357 }
    358 
    359 }  // namespace art
    360