blob: f1b7bffdf5f6ea39a7565842287218190528e605 [file] [log] [blame]
Artem Serovcced8ba2017-07-19 18:18:09 +01001/*
2 * Copyright (C) 2017 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 "graph_checker.h"
18#include "nodes.h"
19#include "optimizing_unit_test.h"
Artem Serov7f4aff62017-06-21 17:02:18 +010020#include "superblock_cloner.h"
Artem Serovcced8ba2017-07-19 18:18:09 +010021
22#include "gtest/gtest.h"
23
24namespace art {
25
Artem Serov7f4aff62017-06-21 17:02:18 +010026using HBasicBlockMap = SuperblockCloner::HBasicBlockMap;
27using HInstructionMap = SuperblockCloner::HInstructionMap;
28
Artem Serovcced8ba2017-07-19 18:18:09 +010029// This class provides methods and helpers for testing various cloning and copying routines:
30// individual instruction cloning and cloning of the more coarse-grain structures.
Artem Serov5e399b82017-12-21 14:28:35 +000031class SuperblockClonerTest : public OptimizingUnitTest {
Artem Serovcced8ba2017-07-19 18:18:09 +010032 public:
Artem Serov5e399b82017-12-21 14:28:35 +000033 SuperblockClonerTest()
Artem Serovcced8ba2017-07-19 18:18:09 +010034 : graph_(CreateGraph()), entry_block_(nullptr), exit_block_(nullptr), parameter_(nullptr) {}
35
36 void CreateBasicLoopControlFlow(/* out */ HBasicBlock** header_p,
37 /* out */ HBasicBlock** body_p) {
38 entry_block_ = new (GetAllocator()) HBasicBlock(graph_);
39 graph_->AddBlock(entry_block_);
40 graph_->SetEntryBlock(entry_block_);
41
42 HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_);
43 HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_);
44 HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_);
45 HBasicBlock* loop_exit = new (GetAllocator()) HBasicBlock(graph_);
46
47 graph_->AddBlock(loop_preheader);
48 graph_->AddBlock(loop_header);
49 graph_->AddBlock(loop_body);
50 graph_->AddBlock(loop_exit);
51
52 exit_block_ = new (GetAllocator()) HBasicBlock(graph_);
53 graph_->AddBlock(exit_block_);
54 graph_->SetExitBlock(exit_block_);
55
56 entry_block_->AddSuccessor(loop_preheader);
57 loop_preheader->AddSuccessor(loop_header);
58 // Loop exit first to have a proper exit condition/target for HIf.
59 loop_header->AddSuccessor(loop_exit);
60 loop_header->AddSuccessor(loop_body);
61 loop_body->AddSuccessor(loop_header);
62 loop_exit->AddSuccessor(exit_block_);
63
64 *header_p = loop_header;
65 *body_p = loop_body;
66
67 parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
68 dex::TypeIndex(0),
69 0,
70 DataType::Type::kInt32);
71 entry_block_->AddInstruction(parameter_);
72 loop_exit->AddInstruction(new (GetAllocator()) HReturnVoid());
73 exit_block_->AddInstruction(new (GetAllocator()) HExit());
74 }
75
76 void CreateBasicLoopDataFlow(HBasicBlock* loop_header, HBasicBlock* loop_body) {
77 uint32_t dex_pc = 0;
78
79 // Entry block.
80 HIntConstant* const_0 = graph_->GetIntConstant(0);
81 HIntConstant* const_1 = graph_->GetIntConstant(1);
82 HIntConstant* const_128 = graph_->GetIntConstant(128);
83
84 // Header block.
85 HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
86 HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck();
87
88 loop_header->AddPhi(phi);
89 loop_header->AddInstruction(suspend_check);
90 loop_header->AddInstruction(new (GetAllocator()) HGreaterThanOrEqual(phi, const_128));
91 loop_header->AddInstruction(new (GetAllocator()) HIf(parameter_));
92
93 // Loop body block.
94 HInstruction* null_check = new (GetAllocator()) HNullCheck(parameter_, dex_pc);
95 HInstruction* array_length = new (GetAllocator()) HArrayLength(null_check, dex_pc);
96 HInstruction* bounds_check = new (GetAllocator()) HBoundsCheck(phi, array_length, dex_pc);
97 HInstruction* array_get =
98 new (GetAllocator()) HArrayGet(null_check, bounds_check, DataType::Type::kInt32, dex_pc);
99 HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, array_get, const_1);
100 HInstruction* array_set =
101 new (GetAllocator()) HArraySet(null_check, bounds_check, add, DataType::Type::kInt32, dex_pc);
102 HInstruction* induction_inc = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, const_1);
103
104 loop_body->AddInstruction(null_check);
105 loop_body->AddInstruction(array_length);
106 loop_body->AddInstruction(bounds_check);
107 loop_body->AddInstruction(array_get);
108 loop_body->AddInstruction(add);
109 loop_body->AddInstruction(array_set);
110 loop_body->AddInstruction(induction_inc);
111 loop_body->AddInstruction(new (GetAllocator()) HGoto());
112
113 phi->AddInput(const_0);
114 phi->AddInput(induction_inc);
115
116 graph_->SetHasBoundsChecks(true);
117
118 // Adjust HEnvironment for each instruction which require that.
119 ArenaVector<HInstruction*> current_locals({phi, const_128, parameter_},
120 GetAllocator()->Adapter(kArenaAllocInstruction));
121
122 HEnvironment* env = ManuallyBuildEnvFor(suspend_check, &current_locals);
123 null_check->CopyEnvironmentFrom(env);
124 bounds_check->CopyEnvironmentFrom(env);
125 }
126
127 HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction,
128 ArenaVector<HInstruction*>* current_locals) {
129 HEnvironment* environment = new (GetAllocator()) HEnvironment(
130 (GetAllocator()),
131 current_locals->size(),
132 graph_->GetArtMethod(),
133 instruction->GetDexPc(),
134 instruction);
135
136 environment->CopyFrom(ArrayRef<HInstruction* const>(*current_locals));
137 instruction->SetRawEnvironment(environment);
138 return environment;
139 }
140
141 bool CheckGraph() {
142 GraphChecker checker(graph_);
143 checker.Run();
144 if (!checker.IsValid()) {
145 for (const std::string& error : checker.GetErrors()) {
146 std::cout << error << std::endl;
147 }
148 return false;
149 }
150 return true;
151 }
152
153 HGraph* graph_;
154
155 HBasicBlock* entry_block_;
156 HBasicBlock* exit_block_;
157
158 HInstruction* parameter_;
159};
160
Artem Serov5e399b82017-12-21 14:28:35 +0000161TEST_F(SuperblockClonerTest, IndividualInstrCloner) {
Artem Serovcced8ba2017-07-19 18:18:09 +0100162 HBasicBlock* header = nullptr;
163 HBasicBlock* loop_body = nullptr;
164
165 CreateBasicLoopControlFlow(&header, &loop_body);
166 CreateBasicLoopDataFlow(header, loop_body);
167 graph_->BuildDominatorTree();
168 ASSERT_TRUE(CheckGraph());
169
170 HSuspendCheck* old_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
171 CloneAndReplaceInstructionVisitor visitor(graph_);
172 // Do instruction cloning and replacement twice with different visiting order.
173
174 visitor.VisitInsertionOrder();
175 size_t instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
176 EXPECT_EQ(instr_replaced_by_clones_count, 12u);
177 EXPECT_TRUE(CheckGraph());
178
179 visitor.VisitReversePostOrder();
180 instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
181 EXPECT_EQ(instr_replaced_by_clones_count, 24u);
182 EXPECT_TRUE(CheckGraph());
183
184 HSuspendCheck* new_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
185 EXPECT_NE(new_suspend_check, old_suspend_check);
186 EXPECT_NE(new_suspend_check, nullptr);
187}
188
Artem Serov7f4aff62017-06-21 17:02:18 +0100189// Tests SuperblockCloner::CloneBasicBlocks - check instruction cloning and initial remapping of
190// instructions' inputs.
191TEST_F(SuperblockClonerTest, CloneBasicBlocks) {
192 HBasicBlock* header = nullptr;
193 HBasicBlock* loop_body = nullptr;
194 ArenaAllocator* arena = graph_->GetAllocator();
195
196 CreateBasicLoopControlFlow(&header, &loop_body);
197 CreateBasicLoopDataFlow(header, loop_body);
198 graph_->BuildDominatorTree();
199 ASSERT_TRUE(CheckGraph());
200
201 ArenaBitVector orig_bb_set(
202 arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
203 HBasicBlockMap bb_map(std::less<HBasicBlock*>(), arena->Adapter(kArenaAllocSuperblockCloner));
204 HInstructionMap hir_map(std::less<HInstruction*>(), arena->Adapter(kArenaAllocSuperblockCloner));
205
206 HLoopInformation* loop_info = header->GetLoopInformation();
207 orig_bb_set.Union(&loop_info->GetBlocks());
208
209 SuperblockCloner cloner(graph_,
210 &orig_bb_set,
211 &bb_map,
212 &hir_map);
213 EXPECT_TRUE(cloner.IsSubgraphClonable());
214
215 cloner.CloneBasicBlocks();
216
217 EXPECT_EQ(bb_map.size(), 2u);
218 EXPECT_EQ(hir_map.size(), 12u);
219
220 for (auto it : hir_map) {
221 HInstruction* orig_instr = it.first;
222 HInstruction* copy_instr = it.second;
223
224 EXPECT_EQ(cloner.GetBlockCopy(orig_instr->GetBlock()), copy_instr->GetBlock());
225 EXPECT_EQ(orig_instr->GetKind(), copy_instr->GetKind());
226 EXPECT_EQ(orig_instr->GetType(), copy_instr->GetType());
227
228 if (orig_instr->IsPhi()) {
229 continue;
230 }
231
232 EXPECT_EQ(orig_instr->InputCount(), copy_instr->InputCount());
233
234 // Check that inputs match.
235 for (size_t i = 0, e = orig_instr->InputCount(); i < e; i++) {
236 HInstruction* orig_input = orig_instr->InputAt(i);
237 HInstruction* copy_input = copy_instr->InputAt(i);
238 if (cloner.IsInOrigBBSet(orig_input->GetBlock())) {
239 EXPECT_EQ(cloner.GetInstrCopy(orig_input), copy_input);
240 } else {
241 EXPECT_EQ(orig_input, copy_input);
242 }
243 }
244
245 EXPECT_EQ(orig_instr->HasEnvironment(), copy_instr->HasEnvironment());
246
247 // Check that environments match.
248 if (orig_instr->HasEnvironment()) {
249 HEnvironment* orig_env = orig_instr->GetEnvironment();
250 HEnvironment* copy_env = copy_instr->GetEnvironment();
251
252 EXPECT_EQ(copy_env->GetParent(), nullptr);
253 EXPECT_EQ(orig_env->Size(), copy_env->Size());
254
255 for (size_t i = 0, e = orig_env->Size(); i < e; i++) {
256 HInstruction* orig_input = orig_env->GetInstructionAt(i);
257 HInstruction* copy_input = copy_env->GetInstructionAt(i);
258 if (cloner.IsInOrigBBSet(orig_input->GetBlock())) {
259 EXPECT_EQ(cloner.GetInstrCopy(orig_input), copy_input);
260 } else {
261 EXPECT_EQ(orig_input, copy_input);
262 }
263 }
264 }
265 }
266}
267
268// SuperblockCloner::CleanUpControlFlow - checks algorithms of local adjustments of the control
269// flow.
270TEST_F(SuperblockClonerTest, AdjustControlFlowInfo) {
271 HBasicBlock* header = nullptr;
272 HBasicBlock* loop_body = nullptr;
273 ArenaAllocator* arena = graph_->GetAllocator();
274
275 CreateBasicLoopControlFlow(&header, &loop_body);
276 CreateBasicLoopDataFlow(header, loop_body);
277 graph_->BuildDominatorTree();
278 ASSERT_TRUE(CheckGraph());
279
280 ArenaBitVector orig_bb_set(
281 arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
282
283 HLoopInformation* loop_info = header->GetLoopInformation();
284 orig_bb_set.Union(&loop_info->GetBlocks());
285
286 SuperblockCloner cloner(graph_,
287 &orig_bb_set,
288 nullptr,
289 nullptr);
290 EXPECT_TRUE(cloner.IsSubgraphClonable());
291
292 cloner.FindAndSetLocalAreaForAdjustments();
293 cloner.CleanUpControlFlow();
294
295 EXPECT_TRUE(CheckGraph());
296
297 EXPECT_TRUE(entry_block_->Dominates(header));
298 EXPECT_TRUE(entry_block_->Dominates(exit_block_));
299
300 EXPECT_EQ(header->GetLoopInformation(), loop_info);
301 EXPECT_EQ(loop_info->GetHeader(), header);
302 EXPECT_TRUE(loop_info->Contains(*loop_body));
303 EXPECT_TRUE(loop_info->IsBackEdge(*loop_body));
304}
305
Artem Serovcced8ba2017-07-19 18:18:09 +0100306} // namespace art