blob: 42ef3ff4a584c1d762e2dda515f4dc796cc11c26 [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
Mathieu Chartierb666f482015-02-18 14:33:14 -080017#include "base/arena_allocator.h"
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010018#include "builder.h"
19#include "gvn.h"
20#include "nodes.h"
21#include "optimizing_unit_test.h"
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000022#include "side_effects_analysis.h"
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010023
24#include "gtest/gtest.h"
25
26namespace art {
27
28TEST(GVNTest, LocalFieldElimination) {
29 ArenaPool pool;
30 ArenaAllocator allocator(&pool);
31
Nicolas Geoffray0a23d742015-05-07 11:57:35 +010032 HGraph* graph = CreateGraph(&allocator);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010033 HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
34 graph->AddBlock(entry);
35 graph->SetEntryBlock(entry);
36 HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
37 entry->AddInstruction(parameter);
38
39 HBasicBlock* block = new (&allocator) HBasicBlock(graph);
40 graph->AddBlock(block);
41 entry->AddSuccessor(block);
42
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +010043 block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
44 Primitive::kPrimNot,
45 MemberOffset(42),
46 false,
47 kUnknownFieldIndex,
48 graph->GetDexFile()));
49 block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
50 Primitive::kPrimNot,
51 MemberOffset(42),
52 false,
53 kUnknownFieldIndex,
54 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010055 HInstruction* to_remove = block->GetLastInstruction();
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +010056 block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
57 Primitive::kPrimNot,
58 MemberOffset(43),
59 false,
60 kUnknownFieldIndex,
61 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010062 HInstruction* different_offset = block->GetLastInstruction();
63 // Kill the value.
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +010064 block->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
65 parameter,
66 Primitive::kPrimNot,
67 MemberOffset(42),
68 false,
69 kUnknownFieldIndex,
70 graph->GetDexFile()));
71 block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
72 Primitive::kPrimNot,
73 MemberOffset(42),
74 false,
75 kUnknownFieldIndex,
76 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010077 HInstruction* use_after_kill = block->GetLastInstruction();
78 block->AddInstruction(new (&allocator) HExit());
79
80 ASSERT_EQ(to_remove->GetBlock(), block);
81 ASSERT_EQ(different_offset->GetBlock(), block);
82 ASSERT_EQ(use_after_kill->GetBlock(), block);
83
Nicolas Geoffraye53798a2014-12-01 10:31:54 +000084 graph->TryBuildingSsa();
Nicolas Geoffraye6f17152015-01-26 15:13:47 +000085 SideEffectsAnalysis side_effects(graph);
86 side_effects.Run();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +000087 GVNOptimization(graph, side_effects).Run();
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010088
89 ASSERT_TRUE(to_remove->GetBlock() == nullptr);
90 ASSERT_EQ(different_offset->GetBlock(), block);
91 ASSERT_EQ(use_after_kill->GetBlock(), block);
92}
93
94TEST(GVNTest, GlobalFieldElimination) {
95 ArenaPool pool;
96 ArenaAllocator allocator(&pool);
97
Nicolas Geoffray0a23d742015-05-07 11:57:35 +010098 HGraph* graph = CreateGraph(&allocator);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +010099 HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
100 graph->AddBlock(entry);
101 graph->SetEntryBlock(entry);
102 HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
103 entry->AddInstruction(parameter);
104
105 HBasicBlock* block = new (&allocator) HBasicBlock(graph);
106 graph->AddBlock(block);
107 entry->AddSuccessor(block);
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100108 block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
109 Primitive::kPrimBoolean,
110 MemberOffset(42),
111 false,
112 kUnknownFieldIndex,
113 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100114
115 block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
116 HBasicBlock* then = new (&allocator) HBasicBlock(graph);
117 HBasicBlock* else_ = new (&allocator) HBasicBlock(graph);
118 HBasicBlock* join = new (&allocator) HBasicBlock(graph);
119 graph->AddBlock(then);
120 graph->AddBlock(else_);
121 graph->AddBlock(join);
122
123 block->AddSuccessor(then);
124 block->AddSuccessor(else_);
125 then->AddSuccessor(join);
126 else_->AddSuccessor(join);
127
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100128 then->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
129 Primitive::kPrimBoolean,
130 MemberOffset(42),
131 false,
132 kUnknownFieldIndex,
133 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100134 then->AddInstruction(new (&allocator) HGoto());
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100135 else_->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
136 Primitive::kPrimBoolean,
137 MemberOffset(42),
138 false,
139 kUnknownFieldIndex,
140 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100141 else_->AddInstruction(new (&allocator) HGoto());
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100142 join->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
143 Primitive::kPrimBoolean,
144 MemberOffset(42),
145 false,
146 kUnknownFieldIndex,
147 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100148 join->AddInstruction(new (&allocator) HExit());
149
Nicolas Geoffraye53798a2014-12-01 10:31:54 +0000150 graph->TryBuildingSsa();
Nicolas Geoffraye6f17152015-01-26 15:13:47 +0000151 SideEffectsAnalysis side_effects(graph);
152 side_effects.Run();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000153 GVNOptimization(graph, side_effects).Run();
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100154
155 // Check that all field get instructions have been GVN'ed.
156 ASSERT_TRUE(then->GetFirstInstruction()->IsGoto());
157 ASSERT_TRUE(else_->GetFirstInstruction()->IsGoto());
158 ASSERT_TRUE(join->GetFirstInstruction()->IsExit());
159}
160
161TEST(GVNTest, LoopFieldElimination) {
162 ArenaPool pool;
163 ArenaAllocator allocator(&pool);
164
Nicolas Geoffray0a23d742015-05-07 11:57:35 +0100165 HGraph* graph = CreateGraph(&allocator);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100166 HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
167 graph->AddBlock(entry);
168 graph->SetEntryBlock(entry);
169
170 HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
171 entry->AddInstruction(parameter);
172
173 HBasicBlock* block = new (&allocator) HBasicBlock(graph);
174 graph->AddBlock(block);
175 entry->AddSuccessor(block);
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100176 block->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
177 Primitive::kPrimBoolean,
178 MemberOffset(42),
179 false,
180 kUnknownFieldIndex,
181 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100182 block->AddInstruction(new (&allocator) HGoto());
183
184 HBasicBlock* loop_header = new (&allocator) HBasicBlock(graph);
185 HBasicBlock* loop_body = new (&allocator) HBasicBlock(graph);
186 HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
187
188 graph->AddBlock(loop_header);
189 graph->AddBlock(loop_body);
190 graph->AddBlock(exit);
191 block->AddSuccessor(loop_header);
192 loop_header->AddSuccessor(loop_body);
193 loop_header->AddSuccessor(exit);
194 loop_body->AddSuccessor(loop_header);
195
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100196 loop_header->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
197 Primitive::kPrimBoolean,
198 MemberOffset(42),
199 false,
200 kUnknownFieldIndex,
201 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100202 HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
203 loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
204
205 // Kill inside the loop body to prevent field gets inside the loop header
206 // and the body to be GVN'ed.
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100207 loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
208 parameter,
Aart Bik854a02b2015-07-14 16:07:00 -0700209 Primitive::kPrimBoolean,
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100210 MemberOffset(42),
211 false,
212 kUnknownFieldIndex,
213 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100214 HInstruction* field_set = loop_body->GetLastInstruction();
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100215 loop_body->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
216 Primitive::kPrimBoolean,
217 MemberOffset(42),
218 false,
219 kUnknownFieldIndex,
220 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100221 HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
222 loop_body->AddInstruction(new (&allocator) HGoto());
223
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100224 exit->AddInstruction(new (&allocator) HInstanceFieldGet(parameter,
225 Primitive::kPrimBoolean,
226 MemberOffset(42),
227 false,
228 kUnknownFieldIndex,
229 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100230 HInstruction* field_get_in_exit = exit->GetLastInstruction();
231 exit->AddInstruction(new (&allocator) HExit());
232
233 ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
234 ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
235 ASSERT_EQ(field_get_in_exit->GetBlock(), exit);
236
Nicolas Geoffraye53798a2014-12-01 10:31:54 +0000237 graph->TryBuildingSsa();
Nicolas Geoffraye6f17152015-01-26 15:13:47 +0000238 {
239 SideEffectsAnalysis side_effects(graph);
240 side_effects.Run();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000241 GVNOptimization(graph, side_effects).Run();
Nicolas Geoffraye6f17152015-01-26 15:13:47 +0000242 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100243
244 // Check that all field get instructions are still there.
245 ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
246 ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
247 // The exit block is dominated by the loop header, whose field get
248 // does not get killed by the loop flags.
249 ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr);
250
251 // Now remove the field set, and check that all field get instructions have been GVN'ed.
252 loop_body->RemoveInstruction(field_set);
Nicolas Geoffraye6f17152015-01-26 15:13:47 +0000253 {
254 SideEffectsAnalysis side_effects(graph);
255 side_effects.Run();
Nicolas Geoffray827eedb2015-01-26 15:18:36 +0000256 GVNOptimization(graph, side_effects).Run();
Nicolas Geoffraye6f17152015-01-26 15:13:47 +0000257 }
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100258
259 ASSERT_TRUE(field_get_in_loop_header->GetBlock() == nullptr);
260 ASSERT_TRUE(field_get_in_loop_body->GetBlock() == nullptr);
261 ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr);
262}
263
264// Test that inner loops affect the side effects of the outer loop.
265TEST(GVNTest, LoopSideEffects) {
266 ArenaPool pool;
267 ArenaAllocator allocator(&pool);
268
Alexandre Rames78e3ef62015-08-12 13:43:29 +0100269 static const SideEffects kCanTriggerGC = SideEffects::CanTriggerGC();
270
Nicolas Geoffray0a23d742015-05-07 11:57:35 +0100271 HGraph* graph = CreateGraph(&allocator);
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100272 HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
273 graph->AddBlock(entry);
274 graph->SetEntryBlock(entry);
275
276 HBasicBlock* outer_loop_header = new (&allocator) HBasicBlock(graph);
277 HBasicBlock* outer_loop_body = new (&allocator) HBasicBlock(graph);
278 HBasicBlock* outer_loop_exit = new (&allocator) HBasicBlock(graph);
279 HBasicBlock* inner_loop_header = new (&allocator) HBasicBlock(graph);
280 HBasicBlock* inner_loop_body = new (&allocator) HBasicBlock(graph);
281 HBasicBlock* inner_loop_exit = new (&allocator) HBasicBlock(graph);
282
283 graph->AddBlock(outer_loop_header);
284 graph->AddBlock(outer_loop_body);
285 graph->AddBlock(outer_loop_exit);
286 graph->AddBlock(inner_loop_header);
287 graph->AddBlock(inner_loop_body);
288 graph->AddBlock(inner_loop_exit);
289
290 entry->AddSuccessor(outer_loop_header);
291 outer_loop_header->AddSuccessor(outer_loop_body);
292 outer_loop_header->AddSuccessor(outer_loop_exit);
293 outer_loop_body->AddSuccessor(inner_loop_header);
294 inner_loop_header->AddSuccessor(inner_loop_body);
295 inner_loop_header->AddSuccessor(inner_loop_exit);
296 inner_loop_body->AddSuccessor(inner_loop_header);
297 inner_loop_exit->AddSuccessor(outer_loop_header);
298
299 HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimBoolean);
300 entry->AddInstruction(parameter);
301 entry->AddInstruction(new (&allocator) HGoto());
302 outer_loop_header->AddInstruction(new (&allocator) HIf(parameter));
303 outer_loop_body->AddInstruction(new (&allocator) HGoto());
304 inner_loop_header->AddInstruction(new (&allocator) HIf(parameter));
305 inner_loop_body->AddInstruction(new (&allocator) HGoto());
306 inner_loop_exit->AddInstruction(new (&allocator) HGoto());
307 outer_loop_exit->AddInstruction(new (&allocator) HExit());
308
Nicolas Geoffraye53798a2014-12-01 10:31:54 +0000309 graph->TryBuildingSsa();
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100310
311 ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
312 *outer_loop_header->GetLoopInformation()));
313
Alexandre Rames78e3ef62015-08-12 13:43:29 +0100314 // Check that the only side effect of loops is to potentially trigger GC.
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100315 {
316 // Make one block with a side effect.
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100317 entry->AddInstruction(new (&allocator) HInstanceFieldSet(parameter,
318 parameter,
319 Primitive::kPrimNot,
320 MemberOffset(42),
321 false,
322 kUnknownFieldIndex,
323 graph->GetDexFile()));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100324
Nicolas Geoffraye6f17152015-01-26 15:13:47 +0000325 SideEffectsAnalysis side_effects(graph);
326 side_effects.Run();
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100327
Aart Bik854a02b2015-07-14 16:07:00 -0700328 ASSERT_TRUE(side_effects.GetBlockEffects(entry).DoesAnyWrite());
329 ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite());
330 ASSERT_FALSE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite());
331 ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite());
Alexandre Rames78e3ef62015-08-12 13:43:29 +0100332 ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).Equals(kCanTriggerGC));
333 ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).Equals(kCanTriggerGC));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100334 }
335
336 // Check that the side effects of the outer loop does not affect the inner loop.
337 {
338 outer_loop_body->InsertInstructionBefore(
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100339 new (&allocator) HInstanceFieldSet(parameter,
340 parameter,
341 Primitive::kPrimNot,
342 MemberOffset(42),
343 false,
344 kUnknownFieldIndex,
345 graph->GetDexFile()),
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100346 outer_loop_body->GetLastInstruction());
347
Nicolas Geoffraye6f17152015-01-26 15:13:47 +0000348 SideEffectsAnalysis side_effects(graph);
349 side_effects.Run();
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100350
Aart Bik854a02b2015-07-14 16:07:00 -0700351 ASSERT_TRUE(side_effects.GetBlockEffects(entry).DoesAnyWrite());
352 ASSERT_TRUE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite());
353 ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite());
354 ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite());
Alexandre Rames78e3ef62015-08-12 13:43:29 +0100355 ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).Equals(kCanTriggerGC));
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100356 }
357
358 // Check that the side effects of the inner loop affects the outer loop.
359 {
360 outer_loop_body->RemoveInstruction(outer_loop_body->GetFirstInstruction());
361 inner_loop_body->InsertInstructionBefore(
Guillaume "Vermeille" Sanchez104fd8a2015-05-20 17:52:13 +0100362 new (&allocator) HInstanceFieldSet(parameter,
363 parameter,
364 Primitive::kPrimNot,
365 MemberOffset(42),
366 false,
367 kUnknownFieldIndex,
368 graph->GetDexFile()),
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100369 inner_loop_body->GetLastInstruction());
370
Nicolas Geoffraye6f17152015-01-26 15:13:47 +0000371 SideEffectsAnalysis side_effects(graph);
372 side_effects.Run();
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100373
Aart Bik854a02b2015-07-14 16:07:00 -0700374 ASSERT_TRUE(side_effects.GetBlockEffects(entry).DoesAnyWrite());
375 ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite());
376 ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite());
377 ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite());
Nicolas Geoffrayd31cf3d2014-09-08 17:30:24 +0100378 }
379}
380} // namespace art