blob: fd77eb81fc899e44ab7526df7df493ea688bcd7b [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"
20
21#include "gtest/gtest.h"
22
23namespace art {
24
25// This class provides methods and helpers for testing various cloning and copying routines:
26// individual instruction cloning and cloning of the more coarse-grain structures.
Artem Serov5e399b82017-12-21 14:28:35 +000027class SuperblockClonerTest : public OptimizingUnitTest {
Artem Serovcced8ba2017-07-19 18:18:09 +010028 public:
Artem Serov5e399b82017-12-21 14:28:35 +000029 SuperblockClonerTest()
Artem Serovcced8ba2017-07-19 18:18:09 +010030 : graph_(CreateGraph()), entry_block_(nullptr), exit_block_(nullptr), parameter_(nullptr) {}
31
32 void CreateBasicLoopControlFlow(/* out */ HBasicBlock** header_p,
33 /* out */ HBasicBlock** body_p) {
34 entry_block_ = new (GetAllocator()) HBasicBlock(graph_);
35 graph_->AddBlock(entry_block_);
36 graph_->SetEntryBlock(entry_block_);
37
38 HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_);
39 HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_);
40 HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_);
41 HBasicBlock* loop_exit = new (GetAllocator()) HBasicBlock(graph_);
42
43 graph_->AddBlock(loop_preheader);
44 graph_->AddBlock(loop_header);
45 graph_->AddBlock(loop_body);
46 graph_->AddBlock(loop_exit);
47
48 exit_block_ = new (GetAllocator()) HBasicBlock(graph_);
49 graph_->AddBlock(exit_block_);
50 graph_->SetExitBlock(exit_block_);
51
52 entry_block_->AddSuccessor(loop_preheader);
53 loop_preheader->AddSuccessor(loop_header);
54 // Loop exit first to have a proper exit condition/target for HIf.
55 loop_header->AddSuccessor(loop_exit);
56 loop_header->AddSuccessor(loop_body);
57 loop_body->AddSuccessor(loop_header);
58 loop_exit->AddSuccessor(exit_block_);
59
60 *header_p = loop_header;
61 *body_p = loop_body;
62
63 parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
64 dex::TypeIndex(0),
65 0,
66 DataType::Type::kInt32);
67 entry_block_->AddInstruction(parameter_);
68 loop_exit->AddInstruction(new (GetAllocator()) HReturnVoid());
69 exit_block_->AddInstruction(new (GetAllocator()) HExit());
70 }
71
72 void CreateBasicLoopDataFlow(HBasicBlock* loop_header, HBasicBlock* loop_body) {
73 uint32_t dex_pc = 0;
74
75 // Entry block.
76 HIntConstant* const_0 = graph_->GetIntConstant(0);
77 HIntConstant* const_1 = graph_->GetIntConstant(1);
78 HIntConstant* const_128 = graph_->GetIntConstant(128);
79
80 // Header block.
81 HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
82 HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck();
83
84 loop_header->AddPhi(phi);
85 loop_header->AddInstruction(suspend_check);
86 loop_header->AddInstruction(new (GetAllocator()) HGreaterThanOrEqual(phi, const_128));
87 loop_header->AddInstruction(new (GetAllocator()) HIf(parameter_));
88
89 // Loop body block.
90 HInstruction* null_check = new (GetAllocator()) HNullCheck(parameter_, dex_pc);
91 HInstruction* array_length = new (GetAllocator()) HArrayLength(null_check, dex_pc);
92 HInstruction* bounds_check = new (GetAllocator()) HBoundsCheck(phi, array_length, dex_pc);
93 HInstruction* array_get =
94 new (GetAllocator()) HArrayGet(null_check, bounds_check, DataType::Type::kInt32, dex_pc);
95 HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, array_get, const_1);
96 HInstruction* array_set =
97 new (GetAllocator()) HArraySet(null_check, bounds_check, add, DataType::Type::kInt32, dex_pc);
98 HInstruction* induction_inc = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, const_1);
99
100 loop_body->AddInstruction(null_check);
101 loop_body->AddInstruction(array_length);
102 loop_body->AddInstruction(bounds_check);
103 loop_body->AddInstruction(array_get);
104 loop_body->AddInstruction(add);
105 loop_body->AddInstruction(array_set);
106 loop_body->AddInstruction(induction_inc);
107 loop_body->AddInstruction(new (GetAllocator()) HGoto());
108
109 phi->AddInput(const_0);
110 phi->AddInput(induction_inc);
111
112 graph_->SetHasBoundsChecks(true);
113
114 // Adjust HEnvironment for each instruction which require that.
115 ArenaVector<HInstruction*> current_locals({phi, const_128, parameter_},
116 GetAllocator()->Adapter(kArenaAllocInstruction));
117
118 HEnvironment* env = ManuallyBuildEnvFor(suspend_check, &current_locals);
119 null_check->CopyEnvironmentFrom(env);
120 bounds_check->CopyEnvironmentFrom(env);
121 }
122
123 HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction,
124 ArenaVector<HInstruction*>* current_locals) {
125 HEnvironment* environment = new (GetAllocator()) HEnvironment(
126 (GetAllocator()),
127 current_locals->size(),
128 graph_->GetArtMethod(),
129 instruction->GetDexPc(),
130 instruction);
131
132 environment->CopyFrom(ArrayRef<HInstruction* const>(*current_locals));
133 instruction->SetRawEnvironment(environment);
134 return environment;
135 }
136
137 bool CheckGraph() {
138 GraphChecker checker(graph_);
139 checker.Run();
140 if (!checker.IsValid()) {
141 for (const std::string& error : checker.GetErrors()) {
142 std::cout << error << std::endl;
143 }
144 return false;
145 }
146 return true;
147 }
148
149 HGraph* graph_;
150
151 HBasicBlock* entry_block_;
152 HBasicBlock* exit_block_;
153
154 HInstruction* parameter_;
155};
156
Artem Serov5e399b82017-12-21 14:28:35 +0000157TEST_F(SuperblockClonerTest, IndividualInstrCloner) {
Artem Serovcced8ba2017-07-19 18:18:09 +0100158 HBasicBlock* header = nullptr;
159 HBasicBlock* loop_body = nullptr;
160
161 CreateBasicLoopControlFlow(&header, &loop_body);
162 CreateBasicLoopDataFlow(header, loop_body);
163 graph_->BuildDominatorTree();
164 ASSERT_TRUE(CheckGraph());
165
166 HSuspendCheck* old_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
167 CloneAndReplaceInstructionVisitor visitor(graph_);
168 // Do instruction cloning and replacement twice with different visiting order.
169
170 visitor.VisitInsertionOrder();
171 size_t instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
172 EXPECT_EQ(instr_replaced_by_clones_count, 12u);
173 EXPECT_TRUE(CheckGraph());
174
175 visitor.VisitReversePostOrder();
176 instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
177 EXPECT_EQ(instr_replaced_by_clones_count, 24u);
178 EXPECT_TRUE(CheckGraph());
179
180 HSuspendCheck* new_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
181 EXPECT_NE(new_suspend_check, old_suspend_check);
182 EXPECT_NE(new_suspend_check, nullptr);
183}
184
185} // namespace art