blob: ad6e3382bcd30676c9aaa11157ad8d6db7e843d6 [file] [log] [blame]
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +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 "gvn.h"
19#include "nodes.h"
20#include "optimizing_unit_test.h"
21#include "utils/arena_allocator.h"
22
23#include "gtest/gtest.h"
24
25namespace art {
26
27TEST(GVNTest, LocalFieldElimination) {
28 ArenaPool pool;
29 ArenaAllocator allocator(&pool);
30
31 HGraph* graph = new (&allocator) HGraph(&allocator);
32 HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
33 graph->AddBlock(entry);
34 graph->SetEntryBlock(entry);
35 HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
36 entry->AddInstruction(parameter);
37
38 HBasicBlock* block = new (&allocator) HBasicBlock(graph);
39 graph->AddBlock(block);
40 entry->AddSuccessor(block);
41
42 block->AddInstruction(
43 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
44 block->AddInstruction(
45 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
46 HInstruction* to_remove = block->GetLastInstruction();
47 block->AddInstruction(
48 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(43)));
49 HInstruction* different_offset = block->GetLastInstruction();
50 // Kill the value.
51 block->AddInstruction(new (&allocator) HInstanceFieldSet(
52 parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
53 block->AddInstruction(
54 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
55 HInstruction* use_after_kill = block->GetLastInstruction();
56 block->AddInstruction(new (&allocator) HExit());
57
58 ASSERT_EQ(to_remove->GetBlock(), block);
59 ASSERT_EQ(different_offset->GetBlock(), block);
60 ASSERT_EQ(use_after_kill->GetBlock(), block);
61
62 graph->BuildDominatorTree();
63 graph->TransformToSSA();
64 GlobalValueNumberer(&allocator, graph).Run();
65
66 ASSERT_TRUE(to_remove->GetBlock() == nullptr);
67 ASSERT_EQ(different_offset->GetBlock(), block);
68 ASSERT_EQ(use_after_kill->GetBlock(), block);
69}
70
71TEST(GVNTest, GlobalFieldElimination) {
72 ArenaPool pool;
73 ArenaAllocator allocator(&pool);
74
75 HGraph* graph = new (&allocator) HGraph(&allocator);
76 HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
77 graph->AddBlock(entry);
78 graph->SetEntryBlock(entry);
79 HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
80 entry->AddInstruction(parameter);
81
82 HBasicBlock* block = new (&allocator) HBasicBlock(graph);
83 graph->AddBlock(block);
84 entry->AddSuccessor(block);
85 block->AddInstruction(
86 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
87
88 block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
89 HBasicBlock* then = new (&allocator) HBasicBlock(graph);
90 HBasicBlock* else_ = new (&allocator) HBasicBlock(graph);
91 HBasicBlock* join = new (&allocator) HBasicBlock(graph);
92 graph->AddBlock(then);
93 graph->AddBlock(else_);
94 graph->AddBlock(join);
95
96 block->AddSuccessor(then);
97 block->AddSuccessor(else_);
98 then->AddSuccessor(join);
99 else_->AddSuccessor(join);
100
101 then->AddInstruction(
102 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
103 then->AddInstruction(new (&allocator) HGoto());
104 else_->AddInstruction(
105 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
106 else_->AddInstruction(new (&allocator) HGoto());
107 join->AddInstruction(
108 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
109 join->AddInstruction(new (&allocator) HExit());
110
111 graph->BuildDominatorTree();
112 graph->TransformToSSA();
113 GlobalValueNumberer(&allocator, graph).Run();
114
115 // Check that all field get instructions have been GVN'ed.
116 ASSERT_TRUE(then->GetFirstInstruction()->IsGoto());
117 ASSERT_TRUE(else_->GetFirstInstruction()->IsGoto());
118 ASSERT_TRUE(join->GetFirstInstruction()->IsExit());
119}
120
121TEST(GVNTest, LoopFieldElimination) {
122 ArenaPool pool;
123 ArenaAllocator allocator(&pool);
124
125 HGraph* graph = new (&allocator) HGraph(&allocator);
126 HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
127 graph->AddBlock(entry);
128 graph->SetEntryBlock(entry);
129
130 HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
131 entry->AddInstruction(parameter);
132
133 HBasicBlock* block = new (&allocator) HBasicBlock(graph);
134 graph->AddBlock(block);
135 entry->AddSuccessor(block);
136 block->AddInstruction(
137 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
138 block->AddInstruction(new (&allocator) HGoto());
139
140 HBasicBlock* loop_header = new (&allocator) HBasicBlock(graph);
141 HBasicBlock* loop_body = new (&allocator) HBasicBlock(graph);
142 HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
143
144 graph->AddBlock(loop_header);
145 graph->AddBlock(loop_body);
146 graph->AddBlock(exit);
147 block->AddSuccessor(loop_header);
148 loop_header->AddSuccessor(loop_body);
149 loop_header->AddSuccessor(exit);
150 loop_body->AddSuccessor(loop_header);
151
152 loop_header->AddInstruction(
153 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
154 HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
155 loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
156
157 // Kill inside the loop body to prevent field gets inside the loop header
158 // and the body to be GVN'ed.
159 loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(
160 parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
161 HInstruction* field_set = loop_body->GetLastInstruction();
162 loop_body->AddInstruction(
163 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
164 HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
165 loop_body->AddInstruction(new (&allocator) HGoto());
166
167 exit->AddInstruction(
168 new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
169 HInstruction* field_get_in_exit = exit->GetLastInstruction();
170 exit->AddInstruction(new (&allocator) HExit());
171
172 ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
173 ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
174 ASSERT_EQ(field_get_in_exit->GetBlock(), exit);
175
176 graph->BuildDominatorTree();
177 graph->TransformToSSA();
178 graph->FindNaturalLoops();
179 GlobalValueNumberer(&allocator, graph).Run();
180
181 // Check that all field get instructions are still there.
182 ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
183 ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
184 // The exit block is dominated by the loop header, whose field get
185 // does not get killed by the loop flags.
186 ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr);
187
188 // Now remove the field set, and check that all field get instructions have been GVN'ed.
189 loop_body->RemoveInstruction(field_set);
190 GlobalValueNumberer(&allocator, graph).Run();
191
192 ASSERT_TRUE(field_get_in_loop_header->GetBlock() == nullptr);
193 ASSERT_TRUE(field_get_in_loop_body->GetBlock() == nullptr);
194 ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr);
195}
196
197// Test that inner loops affect the side effects of the outer loop.
198TEST(GVNTest, LoopSideEffects) {
199 ArenaPool pool;
200 ArenaAllocator allocator(&pool);
201
202 HGraph* graph = new (&allocator) HGraph(&allocator);
203 HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
204 graph->AddBlock(entry);
205 graph->SetEntryBlock(entry);
206
207 HBasicBlock* outer_loop_header = new (&allocator) HBasicBlock(graph);
208 HBasicBlock* outer_loop_body = new (&allocator) HBasicBlock(graph);
209 HBasicBlock* outer_loop_exit = new (&allocator) HBasicBlock(graph);
210 HBasicBlock* inner_loop_header = new (&allocator) HBasicBlock(graph);
211 HBasicBlock* inner_loop_body = new (&allocator) HBasicBlock(graph);
212 HBasicBlock* inner_loop_exit = new (&allocator) HBasicBlock(graph);
213
214 graph->AddBlock(outer_loop_header);
215 graph->AddBlock(outer_loop_body);
216 graph->AddBlock(outer_loop_exit);
217 graph->AddBlock(inner_loop_header);
218 graph->AddBlock(inner_loop_body);
219 graph->AddBlock(inner_loop_exit);
220
221 entry->AddSuccessor(outer_loop_header);
222 outer_loop_header->AddSuccessor(outer_loop_body);
223 outer_loop_header->AddSuccessor(outer_loop_exit);
224 outer_loop_body->AddSuccessor(inner_loop_header);
225 inner_loop_header->AddSuccessor(inner_loop_body);
226 inner_loop_header->AddSuccessor(inner_loop_exit);
227 inner_loop_body->AddSuccessor(inner_loop_header);
228 inner_loop_exit->AddSuccessor(outer_loop_header);
229
230 HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimBoolean);
231 entry->AddInstruction(parameter);
232 entry->AddInstruction(new (&allocator) HGoto());
233 outer_loop_header->AddInstruction(new (&allocator) HIf(parameter));
234 outer_loop_body->AddInstruction(new (&allocator) HGoto());
235 inner_loop_header->AddInstruction(new (&allocator) HIf(parameter));
236 inner_loop_body->AddInstruction(new (&allocator) HGoto());
237 inner_loop_exit->AddInstruction(new (&allocator) HGoto());
238 outer_loop_exit->AddInstruction(new (&allocator) HExit());
239
240 graph->BuildDominatorTree();
241 graph->TransformToSSA();
242 graph->FindNaturalLoops();
243
244 ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
245 *outer_loop_header->GetLoopInformation()));
246
247 // Check that the loops don't have side effects.
248 {
249 // Make one block with a side effect.
250 entry->AddInstruction(new (&allocator) HInstanceFieldSet(
251 parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
252
253 GlobalValueNumberer gvn(&allocator, graph);
254 gvn.Run();
255
256 ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
257 ASSERT_FALSE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
258 ASSERT_FALSE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
259 }
260
261 // Check that the side effects of the outer loop does not affect the inner loop.
262 {
263 outer_loop_body->InsertInstructionBefore(
264 new (&allocator) HInstanceFieldSet(
265 parameter, parameter, Primitive::kPrimNot, MemberOffset(42)),
266 outer_loop_body->GetLastInstruction());
267
268 GlobalValueNumberer gvn(&allocator, graph);
269 gvn.Run();
270
271 ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
272 ASSERT_TRUE(gvn.GetBlockEffects(outer_loop_body).HasSideEffects());
273 ASSERT_TRUE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
274 ASSERT_FALSE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
275 }
276
277 // Check that the side effects of the inner loop affects the outer loop.
278 {
279 outer_loop_body->RemoveInstruction(outer_loop_body->GetFirstInstruction());
280 inner_loop_body->InsertInstructionBefore(
281 new (&allocator) HInstanceFieldSet(
282 parameter, parameter, Primitive::kPrimNot, MemberOffset(42)),
283 inner_loop_body->GetLastInstruction());
284
285 GlobalValueNumberer gvn(&allocator, graph);
286 gvn.Run();
287
288 ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
289 ASSERT_FALSE(gvn.GetBlockEffects(outer_loop_body).HasSideEffects());
290 ASSERT_TRUE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
291 ASSERT_TRUE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
292 }
293}
294} // namespace art