blob: e4aafb7af3db16baa86a5f05ca0787623eb6615f [file] [log] [blame]
Nicolas Geoffrayc32e7702014-04-24 12:43:16 +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 "base/stringprintf.h"
18#include "builder.h"
19#include "dex_file.h"
20#include "dex_instruction.h"
21#include "nodes.h"
22#include "optimizing_unit_test.h"
23#include "pretty_printer.h"
24#include "ssa_builder.h"
25#include "utils/arena_allocator.h"
26
27#include "gtest/gtest.h"
28
29namespace art {
30
31class StringPrettyPrinter : public HPrettyPrinter {
32 public:
33 explicit StringPrettyPrinter(HGraph* graph) : HPrettyPrinter(graph), str_("") {}
34
35 virtual void PrintInt(int value) {
36 str_ += StringPrintf("%d", value);
37 }
38
39 virtual void PrintString(const char* value) {
40 str_ += value;
41 }
42
43 virtual void PrintNewLine() {
44 str_ += '\n';
45 }
46
47 void Clear() { str_.clear(); }
48
49 std::string str() const { return str_; }
50
51 virtual void VisitIntConstant(HIntConstant* constant) {
52 PrintPreInstruction(constant);
53 str_ += constant->DebugName();
54 str_ += " ";
55 PrintInt(constant->GetValue());
56 PrintPostInstruction(constant);
57 }
58
59 private:
60 std::string str_;
61
62 DISALLOW_COPY_AND_ASSIGN(StringPrettyPrinter);
63};
64
65static void ReNumberInstructions(HGraph* graph) {
66 int id = 0;
Nicolas Geoffray804d0932014-05-02 08:46:00 +010067 for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
68 HBasicBlock* block = graph->GetBlocks().Get(i);
Nicolas Geoffrayc32e7702014-04-24 12:43:16 +010069 for (HInstructionIterator it(*block->GetPhis()); !it.Done(); it.Advance()) {
70 it.Current()->SetId(id++);
71 }
72 for (HInstructionIterator it(*block->GetInstructions()); !it.Done(); it.Advance()) {
73 it.Current()->SetId(id++);
74 }
75 }
76}
77
78static void TestCode(const uint16_t* data, const char* expected) {
79 ArenaPool pool;
80 ArenaAllocator allocator(&pool);
81 HGraphBuilder builder(&allocator);
82 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
83 HGraph* graph = builder.BuildGraph(*item);
84 ASSERT_NE(graph, nullptr);
85 graph->BuildDominatorTree();
86 graph->TransformToSSA();
87 ReNumberInstructions(graph);
88
89 StringPrettyPrinter printer(graph);
90 printer.VisitInsertionOrder();
91
92 ASSERT_STREQ(expected, printer.str().c_str());
93}
94
95TEST(SsaTest, CFG1) {
96 // Test that we get rid of loads and stores.
97 const char* expected =
98 "BasicBlock 0, succ: 1\n"
99 " 0: IntConstant 0 [2, 2]\n"
100 " 1: Goto\n"
101 "BasicBlock 1, pred: 0, succ: 3, 2\n"
102 " 2: Equal(0, 0) [3]\n"
103 " 3: If(2)\n"
104 "BasicBlock 2, pred: 1, succ: 3\n"
105 " 4: Goto\n"
106 "BasicBlock 3, pred: 1, 2, succ: 4\n"
107 " 5: ReturnVoid\n"
108 "BasicBlock 4, pred: 3\n"
109 " 6: Exit\n";
110
111 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
112 Instruction::CONST_4 | 0 | 0,
113 Instruction::IF_EQ, 3,
114 Instruction::GOTO | 0x100,
115 Instruction::RETURN_VOID);
116
117 TestCode(data, expected);
118}
119
120TEST(SsaTest, CFG2) {
121 // Test that we create a phi for the join block of an if control flow instruction
122 // when there is only code in the else branch.
123 const char* expected =
124 "BasicBlock 0, succ: 1\n"
125 " 0: IntConstant 0 [6, 3, 3]\n"
126 " 1: IntConstant 4 [6]\n"
127 " 2: Goto\n"
128 "BasicBlock 1, pred: 0, succ: 3, 2\n"
129 " 3: Equal(0, 0) [4]\n"
130 " 4: If(3)\n"
131 "BasicBlock 2, pred: 1, succ: 3\n"
132 " 5: Goto\n"
133 "BasicBlock 3, pred: 1, 2, succ: 4\n"
134 " 6: Phi(0, 1) [7]\n"
135 " 7: Return(6)\n"
136 "BasicBlock 4, pred: 3\n"
137 " 8: Exit\n";
138
139 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
140 Instruction::CONST_4 | 0 | 0,
141 Instruction::IF_EQ, 3,
142 Instruction::CONST_4 | 4 << 12 | 0,
143 Instruction::RETURN | 0 << 8);
144
145 TestCode(data, expected);
146}
147
148TEST(SsaTest, CFG3) {
149 // Test that we create a phi for the join block of an if control flow instruction
Nicolas Geoffray804d0932014-05-02 08:46:00 +0100150 // when both branches update a local.
Nicolas Geoffrayc32e7702014-04-24 12:43:16 +0100151 const char* expected =
152 "BasicBlock 0, succ: 1\n"
153 " 0: IntConstant 0 [4, 4]\n"
154 " 1: IntConstant 4 [8]\n"
155 " 2: IntConstant 5 [8]\n"
156 " 3: Goto\n"
157 "BasicBlock 1, pred: 0, succ: 3, 2\n"
158 " 4: Equal(0, 0) [5]\n"
159 " 5: If(4)\n"
160 "BasicBlock 2, pred: 1, succ: 4\n"
161 " 6: Goto\n"
162 "BasicBlock 3, pred: 1, succ: 4\n"
163 " 7: Goto\n"
164 "BasicBlock 4, pred: 2, 3, succ: 5\n"
165 " 8: Phi(1, 2) [9]\n"
166 " 9: Return(8)\n"
167 "BasicBlock 5, pred: 4\n"
168 " 10: Exit\n";
169
170 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
171 Instruction::CONST_4 | 0 | 0,
172 Instruction::IF_EQ, 4,
173 Instruction::CONST_4 | 4 << 12 | 0,
174 Instruction::GOTO | 0x200,
175 Instruction::CONST_4 | 5 << 12 | 0,
176 Instruction::RETURN | 0 << 8);
177
178 TestCode(data, expected);
179}
180
181TEST(SsaTest, Loop1) {
182 // Test that we create a phi for an initialized local at entry of a loop.
183 const char* expected =
184 "BasicBlock 0, succ: 1\n"
185 " 0: IntConstant 0 [6, 4, 2, 2]\n"
186 " 1: Goto\n"
187 "BasicBlock 1, pred: 0, succ: 3, 2\n"
188 " 2: Equal(0, 0) [3]\n"
189 " 3: If(2)\n"
190 "BasicBlock 2, pred: 1, 3, succ: 3\n"
191 " 4: Phi(0, 6) [6]\n"
192 " 5: Goto\n"
193 "BasicBlock 3, pred: 1, 2, succ: 2\n"
194 " 6: Phi(0, 4) [4]\n"
195 " 7: Goto\n"
196 "BasicBlock 4\n";
197
198 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
199 Instruction::CONST_4 | 0 | 0,
200 Instruction::IF_EQ, 3,
201 Instruction::GOTO | 0x100,
202 Instruction::GOTO | 0xFF00);
203
204 TestCode(data, expected);
205}
206
207TEST(SsaTest, Loop2) {
208 // Simple loop with one preheader and one back edge.
209 const char* expected =
210 "BasicBlock 0, succ: 1\n"
211 " 0: IntConstant 0 [4]\n"
212 " 1: IntConstant 4 [4]\n"
213 " 2: Goto\n"
214 "BasicBlock 1, pred: 0, succ: 2\n"
215 " 3: Goto\n"
216 "BasicBlock 2, pred: 1, 3, succ: 4, 3\n"
217 " 4: Phi(0, 1) [5, 5]\n"
218 " 5: Equal(4, 4) [6]\n"
219 " 6: If(5)\n"
220 "BasicBlock 3, pred: 2, succ: 2\n"
221 " 7: Goto\n"
222 "BasicBlock 4, pred: 2, succ: 5\n"
223 " 8: ReturnVoid\n"
224 "BasicBlock 5, pred: 4\n"
225 " 9: Exit\n";
226
227 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
228 Instruction::CONST_4 | 0 | 0,
229 Instruction::IF_EQ, 4,
230 Instruction::CONST_4 | 4 << 12 | 0,
231 Instruction::GOTO | 0xFD00,
232 Instruction::RETURN_VOID);
233
234 TestCode(data, expected);
235}
236
237TEST(SsaTest, Loop3) {
238 // Test that a local not yet defined at the entry of a loop is handled properly.
239 const char* expected =
240 "BasicBlock 0, succ: 1\n"
241 " 0: IntConstant 0 [5]\n"
242 " 1: IntConstant 4 [5]\n"
243 " 2: IntConstant 5 [9]\n"
244 " 3: Goto\n"
245 "BasicBlock 1, pred: 0, succ: 2\n"
246 " 4: Goto\n"
247 "BasicBlock 2, pred: 1, 3, succ: 4, 3\n"
248 " 5: Phi(0, 1) [6, 6]\n"
249 " 6: Equal(5, 5) [7]\n"
250 " 7: If(6)\n"
251 "BasicBlock 3, pred: 2, succ: 2\n"
252 " 8: Goto\n"
253 "BasicBlock 4, pred: 2, succ: 5\n"
254 " 9: Return(2)\n"
255 "BasicBlock 5, pred: 4\n"
256 " 10: Exit\n";
257
258 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
259 Instruction::CONST_4 | 0 | 0,
260 Instruction::IF_EQ, 4,
261 Instruction::CONST_4 | 4 << 12 | 0,
262 Instruction::GOTO | 0xFD00,
263 Instruction::CONST_4 | 5 << 12 | 1 << 8,
264 Instruction::RETURN | 1 << 8);
265
266 TestCode(data, expected);
267}
268
269TEST(SsaTest, Loop4) {
270 // Make sure we support a preheader of a loop not being the first predecessor
271 // in the predecessor list of the header.
272 const char* expected =
273 "BasicBlock 0, succ: 1\n"
274 " 0: IntConstant 0 [4]\n"
275 " 1: IntConstant 4 [4]\n"
276 " 2: Goto\n"
277 "BasicBlock 1, pred: 0, succ: 4\n"
278 " 3: Goto\n"
279 "BasicBlock 2, pred: 3, 4, succ: 5, 3\n"
280 " 4: Phi(1, 0) [9, 5, 5]\n"
281 " 5: Equal(4, 4) [6]\n"
282 " 6: If(5)\n"
283 "BasicBlock 3, pred: 2, succ: 2\n"
284 " 7: Goto\n"
285 "BasicBlock 4, pred: 1, succ: 2\n"
286 " 8: Goto\n"
287 "BasicBlock 5, pred: 2, succ: 6\n"
288 " 9: Return(4)\n"
289 "BasicBlock 6, pred: 5\n"
290 " 10: Exit\n";
291
292 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
293 Instruction::CONST_4 | 0 | 0,
294 Instruction::GOTO | 0x500,
295 Instruction::IF_EQ, 5,
296 Instruction::CONST_4 | 4 << 12 | 0,
297 Instruction::GOTO | 0xFD00,
298 Instruction::GOTO | 0xFC00,
299 Instruction::RETURN | 0 << 8);
300
301 TestCode(data, expected);
302}
303
304TEST(SsaTest, Loop5) {
305 // Make sure we create a preheader of a loop when a header originally has two
306 // incoming blocks and one back edge.
307 const char* expected =
308 "BasicBlock 0, succ: 1\n"
309 " 0: IntConstant 0 [4, 4]\n"
310 " 1: IntConstant 4 [14]\n"
311 " 2: IntConstant 5 [14]\n"
312 " 3: Goto\n"
313 "BasicBlock 1, pred: 0, succ: 3, 2\n"
314 " 4: Equal(0, 0) [5]\n"
315 " 5: If(4)\n"
316 "BasicBlock 2, pred: 1, succ: 8\n"
317 " 6: Goto\n"
318 "BasicBlock 3, pred: 1, succ: 8\n"
319 " 7: Goto\n"
320 "BasicBlock 4, pred: 5, 8, succ: 6, 5\n"
321 " 8: Phi(8, 14) [8, 12, 9, 9]\n"
322 " 9: Equal(8, 8) [10]\n"
323 " 10: If(9)\n"
324 "BasicBlock 5, pred: 4, succ: 4\n"
325 " 11: Goto\n"
326 "BasicBlock 6, pred: 4, succ: 7\n"
327 " 12: Return(8)\n"
328 "BasicBlock 7, pred: 6\n"
329 " 13: Exit\n"
330 "BasicBlock 8, pred: 2, 3, succ: 4\n"
331 " 14: Phi(1, 2) [8]\n"
332 " 15: Goto\n";
333
334 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
335 Instruction::CONST_4 | 0 | 0,
336 Instruction::IF_EQ, 4,
337 Instruction::CONST_4 | 4 << 12 | 0,
338 Instruction::GOTO | 0x200,
339 Instruction::CONST_4 | 5 << 12 | 0,
340 Instruction::IF_EQ, 3,
341 Instruction::GOTO | 0xFE00,
342 Instruction::RETURN | 0 << 8);
343
344 TestCode(data, expected);
345}
346
347TEST(SsaTest, Loop6) {
348 // Test a loop with one preheader and two back edges (e.g. continue).
349 const char* expected =
350 "BasicBlock 0, succ: 1\n"
351 " 0: IntConstant 0 [5]\n"
352 " 1: IntConstant 4 [5, 8, 8]\n"
353 " 2: IntConstant 5 [5]\n"
354 " 3: Goto\n"
355 "BasicBlock 1, pred: 0, succ: 2\n"
356 " 4: Goto\n"
357 "BasicBlock 2, pred: 1, 4, 5, succ: 6, 3\n"
358 " 5: Phi(0, 2, 1) [12, 6, 6]\n"
359 " 6: Equal(5, 5) [7]\n"
360 " 7: If(6)\n"
361 "BasicBlock 3, pred: 2, succ: 5, 4\n"
362 " 8: Equal(1, 1) [9]\n"
363 " 9: If(8)\n"
364 "BasicBlock 4, pred: 3, succ: 2\n"
365 " 10: Goto\n"
366 "BasicBlock 5, pred: 3, succ: 2\n"
367 " 11: Goto\n"
368 "BasicBlock 6, pred: 2, succ: 7\n"
369 " 12: Return(5)\n"
370 "BasicBlock 7, pred: 6\n"
371 " 13: Exit\n";
372
373 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
374 Instruction::CONST_4 | 0 | 0,
375 Instruction::IF_EQ, 8,
376 Instruction::CONST_4 | 4 << 12 | 0,
377 Instruction::IF_EQ, 4,
378 Instruction::CONST_4 | 5 << 12 | 0,
379 Instruction::GOTO | 0xFA00,
380 Instruction::GOTO | 0xF900,
381 Instruction::RETURN | 0 << 8);
382
383 TestCode(data, expected);
384}
385
386TEST(SsaTest, Loop7) {
387 // Test a loop with one preheader, one back edge, and two exit edges (e.g. break).
388 const char* expected =
389 "BasicBlock 0, succ: 1\n"
390 " 0: IntConstant 0 [5]\n"
391 " 1: IntConstant 4 [5, 8, 8]\n"
392 " 2: IntConstant 5 [12]\n"
393 " 3: Goto\n"
394 "BasicBlock 1, pred: 0, succ: 2\n"
395 " 4: Goto\n"
396 "BasicBlock 2, pred: 1, 5, succ: 6, 3\n"
397 " 5: Phi(0, 1) [12, 6, 6]\n"
398 " 6: Equal(5, 5) [7]\n"
399 " 7: If(6)\n"
400 "BasicBlock 3, pred: 2, succ: 5, 4\n"
401 " 8: Equal(1, 1) [9]\n"
402 " 9: If(8)\n"
403 "BasicBlock 4, pred: 3, succ: 6\n"
404 " 10: Goto\n"
405 "BasicBlock 5, pred: 3, succ: 2\n"
406 " 11: Goto\n"
407 "BasicBlock 6, pred: 2, 4, succ: 7\n"
408 " 12: Phi(5, 2) [13]\n"
409 " 13: Return(12)\n"
410 "BasicBlock 7, pred: 6\n"
411 " 14: Exit\n";
412
413 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
414 Instruction::CONST_4 | 0 | 0,
415 Instruction::IF_EQ, 8,
416 Instruction::CONST_4 | 4 << 12 | 0,
417 Instruction::IF_EQ, 4,
418 Instruction::CONST_4 | 5 << 12 | 0,
419 Instruction::GOTO | 0x0200,
420 Instruction::GOTO | 0xF900,
421 Instruction::RETURN | 0 << 8);
422
423 TestCode(data, expected);
424}
425
426TEST(SsaTest, DeadLocal) {
427 // Test that we correctly handle a local not being used.
428 const char* expected =
429 "BasicBlock 0, succ: 1\n"
430 " 0: IntConstant 0\n"
431 " 1: Goto\n"
432 "BasicBlock 1, pred: 0, succ: 2\n"
433 " 2: ReturnVoid\n"
434 "BasicBlock 2, pred: 1\n"
435 " 3: Exit\n";
436
437 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
438 Instruction::CONST_4 | 0 | 0,
439 Instruction::RETURN_VOID);
440
441 TestCode(data, expected);
442}
443
444} // namespace art