blob: 8b4d58eaaedbbe3007514cdd3ee0e95f3b55ae0d [file] [log] [blame]
Aart Bik281c6812016-08-26 11:31:48 -07001/*
2 * Copyright (C) 2016 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
Artem Serovc8150b52019-07-31 18:28:00 +010017#include "code_generator.h"
Aart Bik281c6812016-08-26 11:31:48 -070018#include "loop_optimization.h"
19#include "optimizing_unit_test.h"
20
Vladimir Marko0a516052019-10-14 13:00:44 +000021namespace art {
Aart Bik281c6812016-08-26 11:31:48 -070022
23/**
24 * Fixture class for the loop optimization tests. These unit tests focus
25 * constructing the loop hierarchy. Actual optimizations are tested
26 * through the checker tests.
27 */
Vladimir Markoca6fff82017-10-03 14:49:14 +010028class LoopOptimizationTest : public OptimizingUnitTest {
Artem Serovc8150b52019-07-31 18:28:00 +010029 protected:
30 void SetUp() override {
31 OverrideInstructionSetFeatures(instruction_set_, "default");
32 OptimizingUnitTest::SetUp();
33
34 graph_ = CreateGraph();
Aart Bik281c6812016-08-26 11:31:48 -070035 BuildGraph();
Artem Serovc8150b52019-07-31 18:28:00 +010036 iva_ = new (GetAllocator()) HInductionVarAnalysis(graph_);
37 DCHECK(compiler_options_ != nullptr);
38 codegen_ = CodeGenerator::Create(graph_, *compiler_options_);
39 DCHECK(codegen_.get() != nullptr);
40 loop_opt_ = new (GetAllocator()) HLoopOptimization(
41 graph_, *codegen_.get(), iva_, /* stats= */ nullptr);
Aart Bik281c6812016-08-26 11:31:48 -070042 }
43
Artem Serovc8150b52019-07-31 18:28:00 +010044 void TearDown() override {
45 codegen_.reset();
46 graph_ = nullptr;
47 ResetPoolAndAllocator();
48 OptimizingUnitTest::TearDown();
49 }
50
51 virtual ~LoopOptimizationTest() {}
Aart Bik281c6812016-08-26 11:31:48 -070052
53 /** Constructs bare minimum graph. */
54 void BuildGraph() {
55 graph_->SetNumberOfVRegs(1);
Vladimir Markoca6fff82017-10-03 14:49:14 +010056 entry_block_ = new (GetAllocator()) HBasicBlock(graph_);
57 return_block_ = new (GetAllocator()) HBasicBlock(graph_);
58 exit_block_ = new (GetAllocator()) HBasicBlock(graph_);
Aart Bik281c6812016-08-26 11:31:48 -070059 graph_->AddBlock(entry_block_);
60 graph_->AddBlock(return_block_);
61 graph_->AddBlock(exit_block_);
62 graph_->SetEntryBlock(entry_block_);
63 graph_->SetExitBlock(exit_block_);
Vladimir Markoca6fff82017-10-03 14:49:14 +010064 parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
65 dex::TypeIndex(0),
66 0,
67 DataType::Type::kInt32);
Aart Bik281c6812016-08-26 11:31:48 -070068 entry_block_->AddInstruction(parameter_);
Vladimir Markoca6fff82017-10-03 14:49:14 +010069 return_block_->AddInstruction(new (GetAllocator()) HReturnVoid());
70 exit_block_->AddInstruction(new (GetAllocator()) HExit());
Aart Bik281c6812016-08-26 11:31:48 -070071 entry_block_->AddSuccessor(return_block_);
72 return_block_->AddSuccessor(exit_block_);
73 }
74
75 /** Adds a loop nest at given position before successor. */
76 HBasicBlock* AddLoop(HBasicBlock* position, HBasicBlock* successor) {
Vladimir Markoca6fff82017-10-03 14:49:14 +010077 HBasicBlock* header = new (GetAllocator()) HBasicBlock(graph_);
78 HBasicBlock* body = new (GetAllocator()) HBasicBlock(graph_);
Aart Bik281c6812016-08-26 11:31:48 -070079 graph_->AddBlock(header);
80 graph_->AddBlock(body);
81 // Control flow.
82 position->ReplaceSuccessor(successor, header);
83 header->AddSuccessor(body);
84 header->AddSuccessor(successor);
Vladimir Markoca6fff82017-10-03 14:49:14 +010085 header->AddInstruction(new (GetAllocator()) HIf(parameter_));
Aart Bik281c6812016-08-26 11:31:48 -070086 body->AddSuccessor(header);
Vladimir Markoca6fff82017-10-03 14:49:14 +010087 body->AddInstruction(new (GetAllocator()) HGoto());
Aart Bik281c6812016-08-26 11:31:48 -070088 return header;
89 }
90
91 /** Performs analysis. */
92 void PerformAnalysis() {
93 graph_->BuildDominatorTree();
94 iva_->Run();
Aart Bik96202302016-10-04 17:33:56 -070095 // Do not release the loop hierarchy.
Vladimir Markoca6fff82017-10-03 14:49:14 +010096 ScopedArenaAllocator loop_allocator(GetArenaStack());
97 loop_opt_->loop_allocator_ = &loop_allocator;
Aart Bik96202302016-10-04 17:33:56 -070098 loop_opt_->LocalRun();
Aart Bik281c6812016-08-26 11:31:48 -070099 }
100
101 /** Constructs string representation of computed loop hierarchy. */
102 std::string LoopStructure() {
103 return LoopStructureRecurse(loop_opt_->top_loop_);
104 }
105
106 // Helper method
107 std::string LoopStructureRecurse(HLoopOptimization::LoopNode* node) {
108 std::string s;
109 for ( ; node != nullptr; node = node->next) {
110 s.append("[");
111 s.append(LoopStructureRecurse(node->inner));
112 s.append("]");
113 }
114 return s;
115 }
116
117 // General building fields.
Aart Bik281c6812016-08-26 11:31:48 -0700118 HGraph* graph_;
Artem Serovc8150b52019-07-31 18:28:00 +0100119
120 std::unique_ptr<CodeGenerator> codegen_;
Aart Bik281c6812016-08-26 11:31:48 -0700121 HInductionVarAnalysis* iva_;
122 HLoopOptimization* loop_opt_;
123
124 HBasicBlock* entry_block_;
125 HBasicBlock* return_block_;
126 HBasicBlock* exit_block_;
127
128 HInstruction* parameter_;
129};
130
131//
132// The actual tests.
133//
134
135TEST_F(LoopOptimizationTest, NoLoops) {
136 PerformAnalysis();
137 EXPECT_EQ("", LoopStructure());
138}
139
140TEST_F(LoopOptimizationTest, SingleLoop) {
141 AddLoop(entry_block_, return_block_);
142 PerformAnalysis();
143 EXPECT_EQ("[]", LoopStructure());
144}
145
146TEST_F(LoopOptimizationTest, LoopNest10) {
147 HBasicBlock* b = entry_block_;
148 HBasicBlock* s = return_block_;
149 for (int i = 0; i < 10; i++) {
150 s = AddLoop(b, s);
151 b = s->GetSuccessors()[0];
152 }
153 PerformAnalysis();
154 EXPECT_EQ("[[[[[[[[[[]]]]]]]]]]", LoopStructure());
155}
156
157TEST_F(LoopOptimizationTest, LoopSequence10) {
158 HBasicBlock* b = entry_block_;
159 HBasicBlock* s = return_block_;
160 for (int i = 0; i < 10; i++) {
161 b = AddLoop(b, s);
162 s = b->GetSuccessors()[1];
163 }
164 PerformAnalysis();
165 EXPECT_EQ("[][][][][][][][][][]", LoopStructure());
166}
167
168TEST_F(LoopOptimizationTest, LoopSequenceOfNests) {
169 HBasicBlock* b = entry_block_;
170 HBasicBlock* s = return_block_;
171 for (int i = 0; i < 10; i++) {
172 b = AddLoop(b, s);
173 s = b->GetSuccessors()[1];
174 HBasicBlock* bi = b->GetSuccessors()[0];
175 HBasicBlock* si = b;
176 for (int j = 0; j < i; j++) {
177 si = AddLoop(bi, si);
178 bi = si->GetSuccessors()[0];
179 }
180 }
181 PerformAnalysis();
182 EXPECT_EQ("[]"
183 "[[]]"
184 "[[[]]]"
185 "[[[[]]]]"
186 "[[[[[]]]]]"
187 "[[[[[[]]]]]]"
188 "[[[[[[[]]]]]]]"
189 "[[[[[[[[]]]]]]]]"
190 "[[[[[[[[[]]]]]]]]]"
191 "[[[[[[[[[[]]]]]]]]]]",
192 LoopStructure());
193}
194
195TEST_F(LoopOptimizationTest, LoopNestWithSequence) {
196 HBasicBlock* b = entry_block_;
197 HBasicBlock* s = return_block_;
198 for (int i = 0; i < 10; i++) {
199 s = AddLoop(b, s);
200 b = s->GetSuccessors()[0];
201 }
202 b = s;
203 s = b->GetSuccessors()[1];
204 for (int i = 0; i < 9; i++) {
205 b = AddLoop(b, s);
206 s = b->GetSuccessors()[1];
207 }
208 PerformAnalysis();
209 EXPECT_EQ("[[[[[[[[[[][][][][][][][][][]]]]]]]]]]", LoopStructure());
210}
211
Artem Serovc73ee372017-07-31 15:08:40 +0100212// Check that SimplifyLoop() doesn't invalidate data flow when ordering loop headers'
213// predecessors.
Artem Serov09faaea2017-12-07 14:36:01 +0000214//
215// This is a test for nodes.cc functionality - HGraph::SimplifyLoop.
216TEST_F(LoopOptimizationTest, SimplifyLoopReoderPredecessors) {
Artem Serovc73ee372017-07-31 15:08:40 +0100217 // Can't use AddLoop as we want special order for blocks predecessors.
Vladimir Markoca6fff82017-10-03 14:49:14 +0100218 HBasicBlock* header = new (GetAllocator()) HBasicBlock(graph_);
219 HBasicBlock* body = new (GetAllocator()) HBasicBlock(graph_);
Artem Serovc73ee372017-07-31 15:08:40 +0100220 graph_->AddBlock(header);
221 graph_->AddBlock(body);
222
223 // Control flow: make a loop back edge first in the list of predecessors.
224 entry_block_->RemoveSuccessor(return_block_);
225 body->AddSuccessor(header);
226 entry_block_->AddSuccessor(header);
227 header->AddSuccessor(body);
228 header->AddSuccessor(return_block_);
229 DCHECK(header->GetSuccessors()[1] == return_block_);
230
231 // Data flow.
Vladimir Markoca6fff82017-10-03 14:49:14 +0100232 header->AddInstruction(new (GetAllocator()) HIf(parameter_));
233 body->AddInstruction(new (GetAllocator()) HGoto());
Artem Serovc73ee372017-07-31 15:08:40 +0100234
Vladimir Markoca6fff82017-10-03 14:49:14 +0100235 HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
236 HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, parameter_);
Artem Serovc73ee372017-07-31 15:08:40 +0100237 header->AddPhi(phi);
238 body->AddInstruction(add);
239
240 phi->AddInput(add);
241 phi->AddInput(parameter_);
242
243 graph_->ClearLoopInformation();
244 graph_->ClearDominanceInformation();
245 graph_->BuildDominatorTree();
246
Artem Serov02eebcf2017-12-13 19:48:31 +0000247 // BuildDominatorTree inserts a block beetween loop header and entry block.
248 EXPECT_EQ(header->GetPredecessors()[0]->GetSinglePredecessor(), entry_block_);
249
Artem Serovc73ee372017-07-31 15:08:40 +0100250 // Check that after optimizations in BuildDominatorTree()/SimplifyCFG() phi inputs
251 // are still mapped correctly to the block predecessors.
252 for (size_t i = 0, e = phi->InputCount(); i < e; i++) {
253 HInstruction* input = phi->InputAt(i);
Artem Serov02eebcf2017-12-13 19:48:31 +0000254 EXPECT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i]));
Artem Serovc73ee372017-07-31 15:08:40 +0100255 }
256}
Artem Serov09faaea2017-12-07 14:36:01 +0000257
258// Test that SimplifyLoop() processes the multiple-preheaders loops correctly.
259//
260// This is a test for nodes.cc functionality - HGraph::SimplifyLoop.
261TEST_F(LoopOptimizationTest, SimplifyLoopSinglePreheader) {
262 HBasicBlock* header = AddLoop(entry_block_, return_block_);
263
264 header->InsertInstructionBefore(
265 new (GetAllocator()) HSuspendCheck(), header->GetLastInstruction());
266
267 // Insert an if construct before the loop so it will have two preheaders.
268 HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_);
269 HBasicBlock* preheader0 = new (GetAllocator()) HBasicBlock(graph_);
270 HBasicBlock* preheader1 = new (GetAllocator()) HBasicBlock(graph_);
271
272 graph_->AddBlock(if_block);
273 graph_->AddBlock(preheader0);
274 graph_->AddBlock(preheader1);
275
276 // Fix successors/predecessors.
277 entry_block_->ReplaceSuccessor(header, if_block);
278 if_block->AddSuccessor(preheader0);
279 if_block->AddSuccessor(preheader1);
280 preheader0->AddSuccessor(header);
281 preheader1->AddSuccessor(header);
282
283 if_block->AddInstruction(new (GetAllocator()) HIf(parameter_));
284 preheader0->AddInstruction(new (GetAllocator()) HGoto());
285 preheader1->AddInstruction(new (GetAllocator()) HGoto());
286
287 HBasicBlock* body = header->GetSuccessors()[0];
288 DCHECK(body != return_block_);
289
290 // Add some data flow.
291 HIntConstant* const_0 = graph_->GetIntConstant(0);
292 HIntConstant* const_1 = graph_->GetIntConstant(1);
293 HIntConstant* const_2 = graph_->GetIntConstant(2);
294
295 HAdd* preheader0_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_0);
296 preheader0->AddInstruction(preheader0_add);
297 HAdd* preheader1_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_1);
298 preheader1->AddInstruction(preheader1_add);
299
300 HPhi* header_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
301 header->AddPhi(header_phi);
302
303 HAdd* body_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_2);
304 body->AddInstruction(body_add);
305
306 DCHECK(header->GetPredecessors()[0] == body);
307 DCHECK(header->GetPredecessors()[1] == preheader0);
308 DCHECK(header->GetPredecessors()[2] == preheader1);
309
310 header_phi->AddInput(body_add);
311 header_phi->AddInput(preheader0_add);
312 header_phi->AddInput(preheader1_add);
313
314 graph_->ClearLoopInformation();
315 graph_->ClearDominanceInformation();
316 graph_->BuildDominatorTree();
317
318 EXPECT_EQ(header->GetPredecessors().size(), 2u);
319 EXPECT_EQ(header->GetPredecessors()[1], body);
320
321 HBasicBlock* new_preheader = header->GetLoopInformation()->GetPreHeader();
322 EXPECT_EQ(preheader0->GetSingleSuccessor(), new_preheader);
323 EXPECT_EQ(preheader1->GetSingleSuccessor(), new_preheader);
324
325 EXPECT_EQ(new_preheader->GetPhis().CountSize(), 1u);
326 HPhi* new_preheader_phi = new_preheader->GetFirstPhi()->AsPhi();
327 EXPECT_EQ(new_preheader_phi->InputCount(), 2u);
328 EXPECT_EQ(new_preheader_phi->InputAt(0), preheader0_add);
329 EXPECT_EQ(new_preheader_phi->InputAt(1), preheader1_add);
330
331 EXPECT_EQ(header_phi->InputCount(), 2u);
332 EXPECT_EQ(header_phi->InputAt(0), new_preheader_phi);
333 EXPECT_EQ(header_phi->InputAt(1), body_add);
334}
335
Aart Bik281c6812016-08-26 11:31:48 -0700336} // namespace art