xref: /aosp_15_r20/art/compiler/optimizing/gvn_test.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2014 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include "gvn.h"
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker #include "base/arena_allocator.h"
20*795d594fSAndroid Build Coastguard Worker #include "base/macros.h"
21*795d594fSAndroid Build Coastguard Worker #include "builder.h"
22*795d594fSAndroid Build Coastguard Worker #include "nodes.h"
23*795d594fSAndroid Build Coastguard Worker #include "optimizing_unit_test.h"
24*795d594fSAndroid Build Coastguard Worker #include "side_effects_analysis.h"
25*795d594fSAndroid Build Coastguard Worker 
26*795d594fSAndroid Build Coastguard Worker namespace art HIDDEN {
27*795d594fSAndroid Build Coastguard Worker 
28*795d594fSAndroid Build Coastguard Worker class GVNTest : public OptimizingUnitTest {};
29*795d594fSAndroid Build Coastguard Worker 
TEST_F(GVNTest,LocalFieldElimination)30*795d594fSAndroid Build Coastguard Worker TEST_F(GVNTest, LocalFieldElimination) {
31*795d594fSAndroid Build Coastguard Worker   HBasicBlock* block = InitEntryMainExitGraphWithReturnVoid();
32*795d594fSAndroid Build Coastguard Worker 
33*795d594fSAndroid Build Coastguard Worker   HInstruction* parameter = MakeParam(DataType::Type::kReference);
34*795d594fSAndroid Build Coastguard Worker 
35*795d594fSAndroid Build Coastguard Worker   MakeIFieldGet(block, parameter, DataType::Type::kReference, MemberOffset(42));
36*795d594fSAndroid Build Coastguard Worker   HInstruction* to_remove =
37*795d594fSAndroid Build Coastguard Worker       MakeIFieldGet(block, parameter, DataType::Type::kReference, MemberOffset(42));
38*795d594fSAndroid Build Coastguard Worker   HInstruction* different_offset =
39*795d594fSAndroid Build Coastguard Worker       MakeIFieldGet(block, parameter, DataType::Type::kReference, MemberOffset(43));
40*795d594fSAndroid Build Coastguard Worker   // Kill the value.
41*795d594fSAndroid Build Coastguard Worker   MakeIFieldSet(block, parameter, parameter, MemberOffset(42));
42*795d594fSAndroid Build Coastguard Worker   HInstruction* use_after_kill =
43*795d594fSAndroid Build Coastguard Worker       MakeIFieldGet(block, parameter, DataType::Type::kReference, MemberOffset(42));
44*795d594fSAndroid Build Coastguard Worker 
45*795d594fSAndroid Build Coastguard Worker   ASSERT_EQ(to_remove->GetBlock(), block);
46*795d594fSAndroid Build Coastguard Worker   ASSERT_EQ(different_offset->GetBlock(), block);
47*795d594fSAndroid Build Coastguard Worker   ASSERT_EQ(use_after_kill->GetBlock(), block);
48*795d594fSAndroid Build Coastguard Worker 
49*795d594fSAndroid Build Coastguard Worker   graph_->BuildDominatorTree();
50*795d594fSAndroid Build Coastguard Worker   SideEffectsAnalysis side_effects(graph_);
51*795d594fSAndroid Build Coastguard Worker   side_effects.Run();
52*795d594fSAndroid Build Coastguard Worker   GVNOptimization(graph_, side_effects).Run();
53*795d594fSAndroid Build Coastguard Worker 
54*795d594fSAndroid Build Coastguard Worker   ASSERT_TRUE(to_remove->GetBlock() == nullptr);
55*795d594fSAndroid Build Coastguard Worker   ASSERT_EQ(different_offset->GetBlock(), block);
56*795d594fSAndroid Build Coastguard Worker   ASSERT_EQ(use_after_kill->GetBlock(), block);
57*795d594fSAndroid Build Coastguard Worker }
58*795d594fSAndroid Build Coastguard Worker 
TEST_F(GVNTest,GlobalFieldElimination)59*795d594fSAndroid Build Coastguard Worker TEST_F(GVNTest, GlobalFieldElimination) {
60*795d594fSAndroid Build Coastguard Worker   HBasicBlock* join = InitEntryMainExitGraphWithReturnVoid();
61*795d594fSAndroid Build Coastguard Worker   auto [block, then, else_] = CreateDiamondPattern(join);
62*795d594fSAndroid Build Coastguard Worker 
63*795d594fSAndroid Build Coastguard Worker   HInstruction* parameter = MakeParam(DataType::Type::kReference);
64*795d594fSAndroid Build Coastguard Worker 
65*795d594fSAndroid Build Coastguard Worker   HInstruction* field_get =
66*795d594fSAndroid Build Coastguard Worker       MakeIFieldGet(block, parameter, DataType::Type::kBool, MemberOffset(42));
67*795d594fSAndroid Build Coastguard Worker   MakeIf(block, field_get);
68*795d594fSAndroid Build Coastguard Worker 
69*795d594fSAndroid Build Coastguard Worker   MakeIFieldGet(then, parameter, DataType::Type::kBool, MemberOffset(42));
70*795d594fSAndroid Build Coastguard Worker   MakeIFieldGet(else_, parameter, DataType::Type::kBool, MemberOffset(42));
71*795d594fSAndroid Build Coastguard Worker   MakeIFieldGet(join, parameter, DataType::Type::kBool, MemberOffset(42));
72*795d594fSAndroid Build Coastguard Worker 
73*795d594fSAndroid Build Coastguard Worker   graph_->BuildDominatorTree();
74*795d594fSAndroid Build Coastguard Worker   SideEffectsAnalysis side_effects(graph_);
75*795d594fSAndroid Build Coastguard Worker   side_effects.Run();
76*795d594fSAndroid Build Coastguard Worker   GVNOptimization(graph_, side_effects).Run();
77*795d594fSAndroid Build Coastguard Worker 
78*795d594fSAndroid Build Coastguard Worker   // Check that all field get instructions have been GVN'ed.
79*795d594fSAndroid Build Coastguard Worker   ASSERT_TRUE(then->GetFirstInstruction()->IsGoto());
80*795d594fSAndroid Build Coastguard Worker   ASSERT_TRUE(else_->GetFirstInstruction()->IsGoto());
81*795d594fSAndroid Build Coastguard Worker   ASSERT_TRUE(join->GetFirstInstruction()->IsReturnVoid());
82*795d594fSAndroid Build Coastguard Worker }
83*795d594fSAndroid Build Coastguard Worker 
TEST_F(GVNTest,LoopFieldElimination)84*795d594fSAndroid Build Coastguard Worker TEST_F(GVNTest, LoopFieldElimination) {
85*795d594fSAndroid Build Coastguard Worker   HBasicBlock* return_block = InitEntryMainExitGraphWithReturnVoid();
86*795d594fSAndroid Build Coastguard Worker   auto [pre_header, loop_header, loop_body] = CreateWhileLoop(return_block);
87*795d594fSAndroid Build Coastguard Worker   loop_header->SwapSuccessors();  // Move the loop exit to the "else" successor.
88*795d594fSAndroid Build Coastguard Worker 
89*795d594fSAndroid Build Coastguard Worker   HInstruction* parameter = MakeParam(DataType::Type::kReference);
90*795d594fSAndroid Build Coastguard Worker 
91*795d594fSAndroid Build Coastguard Worker   MakeIFieldGet(pre_header, parameter, DataType::Type::kBool, MemberOffset(42));
92*795d594fSAndroid Build Coastguard Worker 
93*795d594fSAndroid Build Coastguard Worker   HInstruction* field_get_in_loop_header =
94*795d594fSAndroid Build Coastguard Worker       MakeIFieldGet(loop_header, parameter, DataType::Type::kBool, MemberOffset(42));
95*795d594fSAndroid Build Coastguard Worker   MakeIf(loop_header, field_get_in_loop_header);
96*795d594fSAndroid Build Coastguard Worker 
97*795d594fSAndroid Build Coastguard Worker   // Kill inside the loop body to prevent field gets inside the loop header
98*795d594fSAndroid Build Coastguard Worker   // and the body to be GVN'ed.
99*795d594fSAndroid Build Coastguard Worker   HInstruction* field_set =
100*795d594fSAndroid Build Coastguard Worker       MakeIFieldSet(loop_body, parameter, parameter, DataType::Type::kBool, MemberOffset(42));
101*795d594fSAndroid Build Coastguard Worker   HInstruction* field_get_in_loop_body =
102*795d594fSAndroid Build Coastguard Worker       MakeIFieldGet(loop_body, parameter, DataType::Type::kBool, MemberOffset(42));
103*795d594fSAndroid Build Coastguard Worker 
104*795d594fSAndroid Build Coastguard Worker   HInstruction* field_get_in_return_block =
105*795d594fSAndroid Build Coastguard Worker       MakeIFieldGet(return_block, parameter, DataType::Type::kBool, MemberOffset(42));
106*795d594fSAndroid Build Coastguard Worker 
107*795d594fSAndroid Build Coastguard Worker   ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
108*795d594fSAndroid Build Coastguard Worker   ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
109*795d594fSAndroid Build Coastguard Worker   ASSERT_EQ(field_get_in_return_block->GetBlock(), return_block);
110*795d594fSAndroid Build Coastguard Worker 
111*795d594fSAndroid Build Coastguard Worker   graph_->BuildDominatorTree();
112*795d594fSAndroid Build Coastguard Worker   {
113*795d594fSAndroid Build Coastguard Worker     SideEffectsAnalysis side_effects(graph_);
114*795d594fSAndroid Build Coastguard Worker     side_effects.Run();
115*795d594fSAndroid Build Coastguard Worker     GVNOptimization(graph_, side_effects).Run();
116*795d594fSAndroid Build Coastguard Worker   }
117*795d594fSAndroid Build Coastguard Worker 
118*795d594fSAndroid Build Coastguard Worker   // Check that all field get instructions are still there.
119*795d594fSAndroid Build Coastguard Worker   ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
120*795d594fSAndroid Build Coastguard Worker   ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
121*795d594fSAndroid Build Coastguard Worker   // The `return_block` is dominated by the `loop_header`, whose field get
122*795d594fSAndroid Build Coastguard Worker   // does not get killed by the loop flags.
123*795d594fSAndroid Build Coastguard Worker   ASSERT_TRUE(field_get_in_return_block->GetBlock() == nullptr);
124*795d594fSAndroid Build Coastguard Worker 
125*795d594fSAndroid Build Coastguard Worker   // Now remove the field set, and check that all field get instructions have been GVN'ed.
126*795d594fSAndroid Build Coastguard Worker   loop_body->RemoveInstruction(field_set);
127*795d594fSAndroid Build Coastguard Worker   {
128*795d594fSAndroid Build Coastguard Worker     SideEffectsAnalysis side_effects(graph_);
129*795d594fSAndroid Build Coastguard Worker     side_effects.Run();
130*795d594fSAndroid Build Coastguard Worker     GVNOptimization(graph_, side_effects).Run();
131*795d594fSAndroid Build Coastguard Worker   }
132*795d594fSAndroid Build Coastguard Worker 
133*795d594fSAndroid Build Coastguard Worker   ASSERT_TRUE(field_get_in_loop_header->GetBlock() == nullptr);
134*795d594fSAndroid Build Coastguard Worker   ASSERT_TRUE(field_get_in_loop_body->GetBlock() == nullptr);
135*795d594fSAndroid Build Coastguard Worker   ASSERT_TRUE(field_get_in_return_block->GetBlock() == nullptr);
136*795d594fSAndroid Build Coastguard Worker }
137*795d594fSAndroid Build Coastguard Worker 
138*795d594fSAndroid Build Coastguard Worker // Test that inner loops affect the side effects of the outer loop.
TEST_F(GVNTest,LoopSideEffects)139*795d594fSAndroid Build Coastguard Worker TEST_F(GVNTest, LoopSideEffects) {
140*795d594fSAndroid Build Coastguard Worker   static const SideEffects kCanTriggerGC = SideEffects::CanTriggerGC();
141*795d594fSAndroid Build Coastguard Worker 
142*795d594fSAndroid Build Coastguard Worker   HBasicBlock* outer_loop_exit = InitEntryMainExitGraphWithReturnVoid();
143*795d594fSAndroid Build Coastguard Worker   auto [outer_preheader, outer_loop_header, inner_loop_exit] = CreateWhileLoop(outer_loop_exit);
144*795d594fSAndroid Build Coastguard Worker   outer_loop_header->SwapSuccessors();  // Move the loop exit to the "else" successor.
145*795d594fSAndroid Build Coastguard Worker   auto [outer_loop_body, inner_loop_header, inner_loop_body] = CreateWhileLoop(inner_loop_exit);
146*795d594fSAndroid Build Coastguard Worker   inner_loop_header->SwapSuccessors();  // Move the loop exit to the "else" successor.
147*795d594fSAndroid Build Coastguard Worker 
148*795d594fSAndroid Build Coastguard Worker   HInstruction* parameter = MakeParam(DataType::Type::kBool);
149*795d594fSAndroid Build Coastguard Worker   MakeSuspendCheck(outer_loop_header);
150*795d594fSAndroid Build Coastguard Worker   MakeIf(outer_loop_header, parameter);
151*795d594fSAndroid Build Coastguard Worker   MakeSuspendCheck(inner_loop_header);
152*795d594fSAndroid Build Coastguard Worker   MakeIf(inner_loop_header, parameter);
153*795d594fSAndroid Build Coastguard Worker 
154*795d594fSAndroid Build Coastguard Worker   graph_->BuildDominatorTree();
155*795d594fSAndroid Build Coastguard Worker 
156*795d594fSAndroid Build Coastguard Worker   ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
157*795d594fSAndroid Build Coastguard Worker       *outer_loop_header->GetLoopInformation()));
158*795d594fSAndroid Build Coastguard Worker 
159*795d594fSAndroid Build Coastguard Worker   // Check that the only side effect of loops is to potentially trigger GC.
160*795d594fSAndroid Build Coastguard Worker   {
161*795d594fSAndroid Build Coastguard Worker     // Make one block with a side effect.
162*795d594fSAndroid Build Coastguard Worker     MakeIFieldSet(entry_block_, parameter, parameter, DataType::Type::kReference, MemberOffset(42));
163*795d594fSAndroid Build Coastguard Worker 
164*795d594fSAndroid Build Coastguard Worker     SideEffectsAnalysis side_effects(graph_);
165*795d594fSAndroid Build Coastguard Worker     side_effects.Run();
166*795d594fSAndroid Build Coastguard Worker 
167*795d594fSAndroid Build Coastguard Worker     ASSERT_TRUE(side_effects.GetBlockEffects(entry_block_).DoesAnyWrite());
168*795d594fSAndroid Build Coastguard Worker     ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite());
169*795d594fSAndroid Build Coastguard Worker     ASSERT_FALSE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite());
170*795d594fSAndroid Build Coastguard Worker     ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite());
171*795d594fSAndroid Build Coastguard Worker     ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).Equals(kCanTriggerGC));
172*795d594fSAndroid Build Coastguard Worker     ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).Equals(kCanTriggerGC));
173*795d594fSAndroid Build Coastguard Worker   }
174*795d594fSAndroid Build Coastguard Worker 
175*795d594fSAndroid Build Coastguard Worker   // Check that the side effects of the outer loop does not affect the inner loop.
176*795d594fSAndroid Build Coastguard Worker   {
177*795d594fSAndroid Build Coastguard Worker     MakeIFieldSet(
178*795d594fSAndroid Build Coastguard Worker         outer_loop_body, parameter, parameter, DataType::Type::kReference, MemberOffset(42));
179*795d594fSAndroid Build Coastguard Worker 
180*795d594fSAndroid Build Coastguard Worker     SideEffectsAnalysis side_effects(graph_);
181*795d594fSAndroid Build Coastguard Worker     side_effects.Run();
182*795d594fSAndroid Build Coastguard Worker 
183*795d594fSAndroid Build Coastguard Worker     ASSERT_TRUE(side_effects.GetBlockEffects(entry_block_).DoesAnyWrite());
184*795d594fSAndroid Build Coastguard Worker     ASSERT_TRUE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite());
185*795d594fSAndroid Build Coastguard Worker     ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite());
186*795d594fSAndroid Build Coastguard Worker     ASSERT_FALSE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite());
187*795d594fSAndroid Build Coastguard Worker     ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).Equals(kCanTriggerGC));
188*795d594fSAndroid Build Coastguard Worker   }
189*795d594fSAndroid Build Coastguard Worker 
190*795d594fSAndroid Build Coastguard Worker   // Check that the side effects of the inner loop affects the outer loop.
191*795d594fSAndroid Build Coastguard Worker   {
192*795d594fSAndroid Build Coastguard Worker     outer_loop_body->RemoveInstruction(outer_loop_body->GetFirstInstruction());
193*795d594fSAndroid Build Coastguard Worker     MakeIFieldSet(
194*795d594fSAndroid Build Coastguard Worker         inner_loop_body, parameter, parameter, DataType::Type::kReference, MemberOffset(42));
195*795d594fSAndroid Build Coastguard Worker 
196*795d594fSAndroid Build Coastguard Worker     SideEffectsAnalysis side_effects(graph_);
197*795d594fSAndroid Build Coastguard Worker     side_effects.Run();
198*795d594fSAndroid Build Coastguard Worker 
199*795d594fSAndroid Build Coastguard Worker     ASSERT_TRUE(side_effects.GetBlockEffects(entry_block_).DoesAnyWrite());
200*795d594fSAndroid Build Coastguard Worker     ASSERT_FALSE(side_effects.GetBlockEffects(outer_loop_body).DoesAnyWrite());
201*795d594fSAndroid Build Coastguard Worker     ASSERT_TRUE(side_effects.GetLoopEffects(outer_loop_header).DoesAnyWrite());
202*795d594fSAndroid Build Coastguard Worker     ASSERT_TRUE(side_effects.GetLoopEffects(inner_loop_header).DoesAnyWrite());
203*795d594fSAndroid Build Coastguard Worker   }
204*795d594fSAndroid Build Coastguard Worker }
205*795d594fSAndroid Build Coastguard Worker }  // namespace art
206