blob: ff9b9beefc6c139aae141aab5571bdde3c2a12d9 [file] [log] [blame]
Nicolas Geoffraya7062e02014-05-22 12:50:17 +01001/*
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
29namespace 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
34static 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();
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010043 CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010044 SsaLivenessAnalysis liveness(*graph, codegen);
45 liveness.Analyze();
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010046 RegisterAllocator register_allocator(&allocator, *codegen);
47 register_allocator.AllocateRegisters(liveness);
48 return register_allocator.Validate(liveness, false);
49}
50
51/**
52 * Unit testing of RegisterAllocator::ValidateIntervals. Register allocator
53 * tests are based on this validation method.
54 */
55TEST(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));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010067 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
68 intervals, 0, *codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010069
70 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010071 ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
72 intervals, 0, *codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010073 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));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010082 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
83 intervals, 0, *codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010084
85 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010086 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
87 intervals, 0, *codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010088 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));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010097 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
98 intervals, 0, *codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010099
100 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100101 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
102 intervals, 0, *codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100103 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));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100112 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
113 intervals, 0, *codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100114
115 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100116 ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
117 intervals, 0, *codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100118 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));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100128 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
129 intervals, 0, *codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100130
131 intervals.Get(1)->SetRegister(0);
132 // Sibling of the first interval has no register allocated to it.
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100133 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
134 intervals, 0, *codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100135
136 intervals.Get(0)->GetNextSibling()->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100137 ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
138 intervals, 0, *codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100139 }
140}
141
142TEST(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
162TEST(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
201TEST(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
250static 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
260TEST(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);
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100300 CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100301 SsaLivenessAnalysis liveness(*graph, codegen);
302 liveness.Analyze();
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100303 RegisterAllocator register_allocator(&allocator, *codegen);
304 register_allocator.AllocateRegisters(liveness);
305 ASSERT_TRUE(register_allocator.Validate(liveness, 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->GetFirstInstruction()->AsReturn();
318 ASSERT_EQ(phi_interval->GetRegister(), ret->InputAt(0)->GetLiveInterval()->GetRegister());
319}
320
321} // namespace art