blob: 98493880e14f63ca5ac9cefe72daa74d75c53dd7 [file] [log] [blame]
Nicolas Geoffrayddb311f2014-05-16 09:28:54 +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 "dex_file.h"
19#include "dex_instruction.h"
20#include "nodes.h"
21#include "optimizing_unit_test.h"
22#include "ssa_liveness_analysis.h"
23#include "utils/arena_allocator.h"
24
25#include "gtest/gtest.h"
26
27namespace art {
28
29static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) {
30 HGraphBuilder builder(allocator);
31 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
32 HGraph* graph = builder.BuildGraph(*item);
33 graph->BuildDominatorTree();
34 graph->TransformToSSA();
35 graph->FindNaturalLoops();
36 return graph;
37}
38
39TEST(LiveRangesTest, CFG1) {
40 /*
41 * Test the following snippet:
42 * return 0;
43 *
44 * Which becomes the following graph (numbered by lifetime position):
45 * 2: constant0
46 * 3: goto
47 * |
48 * 6: return
49 * |
50 * 9: exit
51 */
52 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
53 Instruction::CONST_4 | 0 | 0,
54 Instruction::RETURN);
55
56 ArenaPool pool;
57 ArenaAllocator allocator(&pool);
58 HGraph* graph = BuildGraph(data, &allocator);
59 SsaLivenessAnalysis liveness(*graph);
60 liveness.Analyze();
61
62 LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
63 ASSERT_EQ(1u, interval->GetRanges().Size());
64 LiveRange range = interval->GetRanges().Get(0);
65 ASSERT_EQ(2u, range.GetStart());
66 // Last use is the return instruction.
67 ASSERT_EQ(6u, range.GetEnd());
68 HBasicBlock* block = graph->GetBlocks().Get(1);
69 ASSERT_TRUE(block->GetLastInstruction()->AsReturn() != nullptr);
70 ASSERT_EQ(6u, block->GetLastInstruction()->GetLifetimePosition());
71}
72
73TEST(LiveRangesTest, CFG2) {
74 /*
75 * Test the following snippet:
76 * var a = 0;
77 * if (0 == 0) {
78 * } else {
79 * }
80 * return a;
81 *
82 * Which becomes the following graph (numbered by lifetime position):
83 * 2: constant0
84 * 3: goto
85 * |
86 * 6: equal
87 * 7: if
88 * / \
89 * 10: goto 13: goto
90 * \ /
91 * 16: return
92 * |
93 * 19: exit
94 */
95 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
96 Instruction::CONST_4 | 0 | 0,
97 Instruction::IF_EQ, 3,
98 Instruction::GOTO | 0x100,
99 Instruction::RETURN | 0 << 8);
100
101 ArenaPool pool;
102 ArenaAllocator allocator(&pool);
103 HGraph* graph = BuildGraph(data, &allocator);
104 SsaLivenessAnalysis liveness(*graph);
105 liveness.Analyze();
106
107 LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
108 ASSERT_EQ(1u, interval->GetRanges().Size());
109 LiveRange range = interval->GetRanges().Get(0);
110 ASSERT_EQ(2u, range.GetStart());
111 // Last use is the return instruction.
112 ASSERT_EQ(16u, range.GetEnd());
113 HBasicBlock* block = graph->GetBlocks().Get(3);
114 ASSERT_TRUE(block->GetLastInstruction()->AsReturn() != nullptr);
115 ASSERT_EQ(16u, block->GetLastInstruction()->GetLifetimePosition());
116}
117
118TEST(LiveRangesTest, CFG3) {
119 /*
120 * Test the following snippet:
121 * var a = 0;
122 * if (0 == 0) {
123 * } else {
124 * a = 4;
125 * }
126 * return a;
127 *
128 * Which becomes the following graph (numbered by lifetime position):
129 * 2: constant0
130 * 3: constant4
131 * 4: goto
132 * |
133 * 7: equal
134 * 8: if
135 * / \
136 * 11: goto 14: goto
137 * \ /
138 * 16: phi
139 * 17: return
140 * |
141 * 20: exit
142 */
143 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
144 Instruction::CONST_4 | 0 | 0,
145 Instruction::IF_EQ, 3,
146 Instruction::CONST_4 | 4 << 12 | 0,
147 Instruction::RETURN | 0 << 8);
148
149 ArenaPool pool;
150 ArenaAllocator allocator(&pool);
151 HGraph* graph = BuildGraph(data, &allocator);
152 SsaLivenessAnalysis liveness(*graph);
153 liveness.Analyze();
154
155 // Test for the 0 constant.
156 LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
157 ASSERT_EQ(1u, interval->GetRanges().Size());
158 LiveRange range = interval->GetRanges().Get(0);
159 ASSERT_EQ(2u, range.GetStart());
160 // Last use is the phi at the return block so instruction is live until
161 // the end of the then block.
162 ASSERT_EQ(12u, range.GetEnd());
163
164 // Test for the 4 constant.
165 interval = liveness.GetInstructionFromSsaIndex(1)->GetLiveInterval();
166 // The then branch is a hole for this constant, therefore its interval has 2 ranges.
167 ASSERT_EQ(2u, interval->GetRanges().Size());
168 // First range is the else block.
169 range = interval->GetRanges().Get(0);
170 ASSERT_EQ(13u, range.GetStart());
171 // Last use is the phi at the return block.
172 ASSERT_EQ(15u, range.GetEnd());
173 // Second range starts from the definition and ends at the if block.
174 range = interval->GetRanges().Get(1);
175 ASSERT_EQ(3u, range.GetStart());
176 // 9 is the end of the if block.
177 ASSERT_EQ(9u, range.GetEnd());
178
179 // Test for the phi.
180 interval = liveness.GetInstructionFromSsaIndex(3)->GetLiveInterval();
181 ASSERT_EQ(1u, interval->GetRanges().Size());
182 range = interval->GetRanges().Get(0);
183 ASSERT_EQ(16u, range.GetStart());
184 ASSERT_EQ(17u, range.GetEnd());
185}
186
187TEST(LiveRangesTest, Loop) {
188 /*
189 * Test the following snippet:
190 * var a = 0;
191 * while (a == a) {
192 * a = 4;
193 * }
194 * return 5;
195 *
196 * Which becomes the following graph (numbered by lifetime position):
197 * 2: constant0
198 * 3: constant4
199 * 4: constant5
200 * 5: goto
201 * |
202 * 8: goto
203 * |
204 * 10: phi
205 * 11: equal
206 * 12: if +++++
207 * | \ +
208 * | 15: goto
209 * |
210 * 18: return
211 * |
212 * 21: exit
213 */
214
215 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
216 Instruction::CONST_4 | 0 | 0,
217 Instruction::IF_EQ, 4,
218 Instruction::CONST_4 | 4 << 12 | 0,
219 Instruction::GOTO | 0xFD00,
220 Instruction::CONST_4 | 5 << 12 | 1 << 8,
221 Instruction::RETURN | 1 << 8);
222
223 ArenaPool pool;
224 ArenaAllocator allocator(&pool);
225 HGraph* graph = BuildGraph(data, &allocator);
226 SsaLivenessAnalysis liveness(*graph);
227 liveness.Analyze();
228
229 // Test for the 0 constant.
230 LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
231 ASSERT_EQ(1u, interval->GetRanges().Size());
232 LiveRange range = interval->GetRanges().Get(0);
233 ASSERT_EQ(2u, range.GetStart());
234 // Last use is the loop phi so instruction is live until
235 // the end of the pre loop header.
236 ASSERT_EQ(9u, range.GetEnd());
237
238 // Test for the 4 constant.
239 interval = liveness.GetInstructionFromSsaIndex(1)->GetLiveInterval();
240 // The instruction is live until the end of the loop.
241 ASSERT_EQ(1u, interval->GetRanges().Size());
242 range = interval->GetRanges().Get(0);
243 ASSERT_EQ(3u, range.GetStart());
244 ASSERT_EQ(16u, range.GetEnd());
245
246 // Test for the 5 constant.
247 interval = liveness.GetInstructionFromSsaIndex(2)->GetLiveInterval();
248 // The instruction is live until the return instruction of the loop.
249 ASSERT_EQ(1u, interval->GetRanges().Size());
250 range = interval->GetRanges().Get(0);
251 ASSERT_EQ(4u, range.GetStart());
252 ASSERT_EQ(18u, range.GetEnd());
253
254 // Test for the phi.
255 interval = liveness.GetInstructionFromSsaIndex(3)->GetLiveInterval();
256 ASSERT_EQ(1u, interval->GetRanges().Size());
257 range = interval->GetRanges().Get(0);
258 // Instruction is consumed by the if.
259 ASSERT_EQ(10u, range.GetStart());
260 ASSERT_EQ(11u, range.GetEnd());
261}
262
263} // namespace art