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