xref: /aosp_15_r20/art/compiler/optimizing/cha_guard_optimization.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2016 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 "cha_guard_optimization.h"
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker namespace art HIDDEN {
20*795d594fSAndroid Build Coastguard Worker 
21*795d594fSAndroid Build Coastguard Worker // Note we can only do CHA guard elimination/motion in a single pass, since
22*795d594fSAndroid Build Coastguard Worker // if a guard is not removed, another guard might be removed due to
23*795d594fSAndroid Build Coastguard Worker // the existence of the first guard. The first guard should not be further
24*795d594fSAndroid Build Coastguard Worker // removed in another pass. For example, due to further optimizations,
25*795d594fSAndroid Build Coastguard Worker // a receiver of a guard might turn out to be a parameter value, or defined at
26*795d594fSAndroid Build Coastguard Worker // a different site, which makes the guard removable as a result. However
27*795d594fSAndroid Build Coastguard Worker // it's not safe to remove the guard in another pass since another guard might
28*795d594fSAndroid Build Coastguard Worker // have been removed due to the existence of this guard.
29*795d594fSAndroid Build Coastguard Worker //
30*795d594fSAndroid Build Coastguard Worker // As a consequence, we decided not to rely on other passes to remove them
31*795d594fSAndroid Build Coastguard Worker // (such as GVN or instruction simplifier).
32*795d594fSAndroid Build Coastguard Worker 
33*795d594fSAndroid Build Coastguard Worker class CHAGuardVisitor final : public HGraphVisitor {
34*795d594fSAndroid Build Coastguard Worker  public:
CHAGuardVisitor(HGraph * graph)35*795d594fSAndroid Build Coastguard Worker   explicit CHAGuardVisitor(HGraph* graph)
36*795d594fSAndroid Build Coastguard Worker       : HGraphVisitor(graph),
37*795d594fSAndroid Build Coastguard Worker         block_has_cha_guard_(GetGraph()->GetBlocks().size(),
38*795d594fSAndroid Build Coastguard Worker                              0,
39*795d594fSAndroid Build Coastguard Worker                              graph->GetAllocator()->Adapter(kArenaAllocCHA)),
40*795d594fSAndroid Build Coastguard Worker         instruction_iterator_(nullptr) {
41*795d594fSAndroid Build Coastguard Worker     number_of_guards_to_visit_ = GetGraph()->GetNumberOfCHAGuards();
42*795d594fSAndroid Build Coastguard Worker     DCHECK_NE(number_of_guards_to_visit_, 0u);
43*795d594fSAndroid Build Coastguard Worker     // Will recount number of guards during guard optimization.
44*795d594fSAndroid Build Coastguard Worker     GetGraph()->SetNumberOfCHAGuards(0);
45*795d594fSAndroid Build Coastguard Worker   }
46*795d594fSAndroid Build Coastguard Worker 
47*795d594fSAndroid Build Coastguard Worker   void VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) override;
48*795d594fSAndroid Build Coastguard Worker 
49*795d594fSAndroid Build Coastguard Worker   void VisitBasicBlock(HBasicBlock* block) override;
50*795d594fSAndroid Build Coastguard Worker 
51*795d594fSAndroid Build Coastguard Worker  private:
52*795d594fSAndroid Build Coastguard Worker   void RemoveGuard(HShouldDeoptimizeFlag* flag);
53*795d594fSAndroid Build Coastguard Worker   // Return true if `flag` is removed.
54*795d594fSAndroid Build Coastguard Worker   bool OptimizeForParameter(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
55*795d594fSAndroid Build Coastguard Worker   // Return true if `flag` is removed.
56*795d594fSAndroid Build Coastguard Worker   bool OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
57*795d594fSAndroid Build Coastguard Worker   // Return true if `flag` is hoisted.
58*795d594fSAndroid Build Coastguard Worker   bool HoistGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
59*795d594fSAndroid Build Coastguard Worker 
60*795d594fSAndroid Build Coastguard Worker   // Record if each block has any CHA guard. It's updated during the
61*795d594fSAndroid Build Coastguard Worker   // reverse post order visit. Use int instead of bool since ArenaVector
62*795d594fSAndroid Build Coastguard Worker   // does not support bool.
63*795d594fSAndroid Build Coastguard Worker   ArenaVector<int> block_has_cha_guard_;
64*795d594fSAndroid Build Coastguard Worker 
65*795d594fSAndroid Build Coastguard Worker   // The iterator that's being used for this visitor. Need it to manually
66*795d594fSAndroid Build Coastguard Worker   // advance the iterator due to removing/moving more than one instruction.
67*795d594fSAndroid Build Coastguard Worker   HInstructionIterator* instruction_iterator_;
68*795d594fSAndroid Build Coastguard Worker 
69*795d594fSAndroid Build Coastguard Worker   // Used to short-circuit the pass when there is no more guards left to visit.
70*795d594fSAndroid Build Coastguard Worker   uint32_t number_of_guards_to_visit_;
71*795d594fSAndroid Build Coastguard Worker 
72*795d594fSAndroid Build Coastguard Worker   DISALLOW_COPY_AND_ASSIGN(CHAGuardVisitor);
73*795d594fSAndroid Build Coastguard Worker };
74*795d594fSAndroid Build Coastguard Worker 
VisitBasicBlock(HBasicBlock * block)75*795d594fSAndroid Build Coastguard Worker void CHAGuardVisitor::VisitBasicBlock(HBasicBlock* block) {
76*795d594fSAndroid Build Coastguard Worker   if (number_of_guards_to_visit_ == 0) {
77*795d594fSAndroid Build Coastguard Worker     return;
78*795d594fSAndroid Build Coastguard Worker   }
79*795d594fSAndroid Build Coastguard Worker   // Skip phis, just iterate through instructions.
80*795d594fSAndroid Build Coastguard Worker   HInstructionIterator it(block->GetInstructions());
81*795d594fSAndroid Build Coastguard Worker   instruction_iterator_ = &it;
82*795d594fSAndroid Build Coastguard Worker   for (; !it.Done(); it.Advance()) {
83*795d594fSAndroid Build Coastguard Worker     DCHECK(it.Current()->IsInBlock());
84*795d594fSAndroid Build Coastguard Worker     it.Current()->Accept(this);
85*795d594fSAndroid Build Coastguard Worker   }
86*795d594fSAndroid Build Coastguard Worker }
87*795d594fSAndroid Build Coastguard Worker 
RemoveGuard(HShouldDeoptimizeFlag * flag)88*795d594fSAndroid Build Coastguard Worker void CHAGuardVisitor::RemoveGuard(HShouldDeoptimizeFlag* flag) {
89*795d594fSAndroid Build Coastguard Worker   HBasicBlock* block = flag->GetBlock();
90*795d594fSAndroid Build Coastguard Worker   HInstruction* compare = flag->GetNext();
91*795d594fSAndroid Build Coastguard Worker   DCHECK(compare->IsNotEqual());
92*795d594fSAndroid Build Coastguard Worker   HInstruction* deopt = compare->GetNext();
93*795d594fSAndroid Build Coastguard Worker   DCHECK(deopt->IsDeoptimize());
94*795d594fSAndroid Build Coastguard Worker 
95*795d594fSAndroid Build Coastguard Worker   // Advance instruction iterator first before we remove the guard.
96*795d594fSAndroid Build Coastguard Worker   // We need to do it twice since we remove three instructions and the
97*795d594fSAndroid Build Coastguard Worker   // visitor is responsible for advancing it once.
98*795d594fSAndroid Build Coastguard Worker   instruction_iterator_->Advance();
99*795d594fSAndroid Build Coastguard Worker   instruction_iterator_->Advance();
100*795d594fSAndroid Build Coastguard Worker   block->RemoveInstruction(deopt);
101*795d594fSAndroid Build Coastguard Worker   block->RemoveInstruction(compare);
102*795d594fSAndroid Build Coastguard Worker   block->RemoveInstruction(flag);
103*795d594fSAndroid Build Coastguard Worker }
104*795d594fSAndroid Build Coastguard Worker 
OptimizeForParameter(HShouldDeoptimizeFlag * flag,HInstruction * receiver)105*795d594fSAndroid Build Coastguard Worker bool CHAGuardVisitor::OptimizeForParameter(HShouldDeoptimizeFlag* flag,
106*795d594fSAndroid Build Coastguard Worker                                            HInstruction* receiver) {
107*795d594fSAndroid Build Coastguard Worker   // If some compiled code is invalidated by CHA due to class loading, the
108*795d594fSAndroid Build Coastguard Worker   // compiled code will not be entered anymore. So the very fact that the
109*795d594fSAndroid Build Coastguard Worker   // compiled code is invoked guarantees that a parameter receiver conforms
110*795d594fSAndroid Build Coastguard Worker   // to all the CHA devirtualization assumptions made by the compiled code,
111*795d594fSAndroid Build Coastguard Worker   // since all parameter receivers pre-exist any (potential) invalidation of
112*795d594fSAndroid Build Coastguard Worker   // the compiled code.
113*795d594fSAndroid Build Coastguard Worker   //
114*795d594fSAndroid Build Coastguard Worker   // TODO: allow more cases such as a phi whose inputs are all parameters.
115*795d594fSAndroid Build Coastguard Worker   if (receiver->IsParameterValue()) {
116*795d594fSAndroid Build Coastguard Worker     RemoveGuard(flag);
117*795d594fSAndroid Build Coastguard Worker     return true;
118*795d594fSAndroid Build Coastguard Worker   }
119*795d594fSAndroid Build Coastguard Worker   return false;
120*795d594fSAndroid Build Coastguard Worker }
121*795d594fSAndroid Build Coastguard Worker 
OptimizeWithDominatingGuard(HShouldDeoptimizeFlag * flag,HInstruction * receiver)122*795d594fSAndroid Build Coastguard Worker bool CHAGuardVisitor::OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag,
123*795d594fSAndroid Build Coastguard Worker                                                   HInstruction* receiver) {
124*795d594fSAndroid Build Coastguard Worker   // If there is another guard that dominates the current guard, and
125*795d594fSAndroid Build Coastguard Worker   // that guard is dominated by receiver's definition, then the current
126*795d594fSAndroid Build Coastguard Worker   // guard can be eliminated, since receiver must pre-exist that other
127*795d594fSAndroid Build Coastguard Worker   // guard, and passing that guard guarantees that receiver conforms to
128*795d594fSAndroid Build Coastguard Worker   // all the CHA devirtualization assumptions.
129*795d594fSAndroid Build Coastguard Worker   HBasicBlock* dominator = flag->GetBlock();
130*795d594fSAndroid Build Coastguard Worker   HBasicBlock* receiver_def_block = receiver->GetBlock();
131*795d594fSAndroid Build Coastguard Worker 
132*795d594fSAndroid Build Coastguard Worker   // Complexity of the following algorithm:
133*795d594fSAndroid Build Coastguard Worker   // We potentially need to traverse the full dominator chain to receiver_def_block,
134*795d594fSAndroid Build Coastguard Worker   // plus a (partial) linear search within one block for each guard.
135*795d594fSAndroid Build Coastguard Worker   // So the worst case for each guard is bounded by the size of the
136*795d594fSAndroid Build Coastguard Worker   // biggest block plus the depth of the dominating tree.
137*795d594fSAndroid Build Coastguard Worker 
138*795d594fSAndroid Build Coastguard Worker   while (dominator != receiver_def_block) {
139*795d594fSAndroid Build Coastguard Worker     if (block_has_cha_guard_[dominator->GetBlockId()] == 1) {
140*795d594fSAndroid Build Coastguard Worker       RemoveGuard(flag);
141*795d594fSAndroid Build Coastguard Worker       return true;
142*795d594fSAndroid Build Coastguard Worker     }
143*795d594fSAndroid Build Coastguard Worker     dominator = dominator->GetDominator();
144*795d594fSAndroid Build Coastguard Worker   }
145*795d594fSAndroid Build Coastguard Worker 
146*795d594fSAndroid Build Coastguard Worker   // At this point dominator is the block where receiver is defined.
147*795d594fSAndroid Build Coastguard Worker   // We do a linear search within dominator to see if there is a guard after
148*795d594fSAndroid Build Coastguard Worker   // receiver's definition.
149*795d594fSAndroid Build Coastguard Worker   HInstruction* instruction;
150*795d594fSAndroid Build Coastguard Worker   if (dominator == flag->GetBlock()) {
151*795d594fSAndroid Build Coastguard Worker     // Flag and receiver are defined in the same block. Search backward from
152*795d594fSAndroid Build Coastguard Worker     // the current guard.
153*795d594fSAndroid Build Coastguard Worker     instruction = flag->GetPrevious();
154*795d594fSAndroid Build Coastguard Worker   } else {
155*795d594fSAndroid Build Coastguard Worker     // Search backward from the last instruction of that dominator.
156*795d594fSAndroid Build Coastguard Worker     instruction = dominator->GetLastInstruction();
157*795d594fSAndroid Build Coastguard Worker   }
158*795d594fSAndroid Build Coastguard Worker   while (instruction != receiver) {
159*795d594fSAndroid Build Coastguard Worker     if (instruction == nullptr) {
160*795d594fSAndroid Build Coastguard Worker       // receiver must be defined in this block, we didn't find it
161*795d594fSAndroid Build Coastguard Worker       // in the instruction list, so it must be a Phi.
162*795d594fSAndroid Build Coastguard Worker       DCHECK(receiver->IsPhi());
163*795d594fSAndroid Build Coastguard Worker       break;
164*795d594fSAndroid Build Coastguard Worker     }
165*795d594fSAndroid Build Coastguard Worker     if (instruction->IsShouldDeoptimizeFlag()) {
166*795d594fSAndroid Build Coastguard Worker       RemoveGuard(flag);
167*795d594fSAndroid Build Coastguard Worker       return true;
168*795d594fSAndroid Build Coastguard Worker     }
169*795d594fSAndroid Build Coastguard Worker     instruction = instruction->GetPrevious();
170*795d594fSAndroid Build Coastguard Worker   }
171*795d594fSAndroid Build Coastguard Worker   return false;
172*795d594fSAndroid Build Coastguard Worker }
173*795d594fSAndroid Build Coastguard Worker 
HoistGuard(HShouldDeoptimizeFlag * flag,HInstruction * receiver)174*795d594fSAndroid Build Coastguard Worker bool CHAGuardVisitor::HoistGuard(HShouldDeoptimizeFlag* flag,
175*795d594fSAndroid Build Coastguard Worker                                  HInstruction* receiver) {
176*795d594fSAndroid Build Coastguard Worker   // If receiver is loop invariant, we can hoist the guard out of the
177*795d594fSAndroid Build Coastguard Worker   // loop since passing a guard before entering the loop guarantees that
178*795d594fSAndroid Build Coastguard Worker   // receiver conforms to all the CHA devirtualization assumptions.
179*795d594fSAndroid Build Coastguard Worker   // We only hoist guards out of the inner loop since that offers most of the
180*795d594fSAndroid Build Coastguard Worker   // benefit and it might help remove other guards in the inner loop.
181*795d594fSAndroid Build Coastguard Worker   HBasicBlock* block = flag->GetBlock();
182*795d594fSAndroid Build Coastguard Worker   HLoopInformation* loop_info = block->GetLoopInformation();
183*795d594fSAndroid Build Coastguard Worker   if (loop_info != nullptr &&
184*795d594fSAndroid Build Coastguard Worker       !loop_info->IsIrreducible() &&
185*795d594fSAndroid Build Coastguard Worker       loop_info->IsDefinedOutOfTheLoop(receiver)) {
186*795d594fSAndroid Build Coastguard Worker     HInstruction* compare = flag->GetNext();
187*795d594fSAndroid Build Coastguard Worker     DCHECK(compare->IsNotEqual());
188*795d594fSAndroid Build Coastguard Worker     HInstruction* deopt = compare->GetNext();
189*795d594fSAndroid Build Coastguard Worker     DCHECK(deopt->IsDeoptimize());
190*795d594fSAndroid Build Coastguard Worker 
191*795d594fSAndroid Build Coastguard Worker     // Advance instruction iterator first before we move the guard.
192*795d594fSAndroid Build Coastguard Worker     // We need to do it twice since we move three instructions and the
193*795d594fSAndroid Build Coastguard Worker     // visitor is responsible for advancing it once.
194*795d594fSAndroid Build Coastguard Worker     instruction_iterator_->Advance();
195*795d594fSAndroid Build Coastguard Worker     instruction_iterator_->Advance();
196*795d594fSAndroid Build Coastguard Worker 
197*795d594fSAndroid Build Coastguard Worker     HBasicBlock* pre_header = loop_info->GetPreHeader();
198*795d594fSAndroid Build Coastguard Worker     flag->MoveBefore(pre_header->GetLastInstruction());
199*795d594fSAndroid Build Coastguard Worker     compare->MoveBefore(pre_header->GetLastInstruction());
200*795d594fSAndroid Build Coastguard Worker 
201*795d594fSAndroid Build Coastguard Worker     block->RemoveInstruction(deopt);
202*795d594fSAndroid Build Coastguard Worker     HInstruction* suspend = loop_info->GetSuspendCheck();
203*795d594fSAndroid Build Coastguard Worker     DCHECK(suspend != nullptr);
204*795d594fSAndroid Build Coastguard Worker     // Need a new deoptimize instruction that copies the environment
205*795d594fSAndroid Build Coastguard Worker     // of the suspend instruction for the loop.
206*795d594fSAndroid Build Coastguard Worker     HDeoptimize* deoptimize = new (GetGraph()->GetAllocator()) HDeoptimize(
207*795d594fSAndroid Build Coastguard Worker         GetGraph()->GetAllocator(), compare, DeoptimizationKind::kCHA, suspend->GetDexPc());
208*795d594fSAndroid Build Coastguard Worker     pre_header->InsertInstructionBefore(deoptimize, pre_header->GetLastInstruction());
209*795d594fSAndroid Build Coastguard Worker     deoptimize->CopyEnvironmentFromWithLoopPhiAdjustment(
210*795d594fSAndroid Build Coastguard Worker         suspend->GetEnvironment(), loop_info->GetHeader());
211*795d594fSAndroid Build Coastguard Worker     block_has_cha_guard_[pre_header->GetBlockId()] = 1;
212*795d594fSAndroid Build Coastguard Worker     GetGraph()->IncrementNumberOfCHAGuards();
213*795d594fSAndroid Build Coastguard Worker     return true;
214*795d594fSAndroid Build Coastguard Worker   }
215*795d594fSAndroid Build Coastguard Worker   return false;
216*795d594fSAndroid Build Coastguard Worker }
217*795d594fSAndroid Build Coastguard Worker 
VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag * flag)218*795d594fSAndroid Build Coastguard Worker void CHAGuardVisitor::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
219*795d594fSAndroid Build Coastguard Worker   number_of_guards_to_visit_--;
220*795d594fSAndroid Build Coastguard Worker   HInstruction* receiver = flag->InputAt(0);
221*795d594fSAndroid Build Coastguard Worker   // Don't need the receiver anymore.
222*795d594fSAndroid Build Coastguard Worker   flag->RemoveInputAt(0);
223*795d594fSAndroid Build Coastguard Worker   if (receiver->IsNullCheck()) {
224*795d594fSAndroid Build Coastguard Worker     receiver = receiver->InputAt(0);
225*795d594fSAndroid Build Coastguard Worker   }
226*795d594fSAndroid Build Coastguard Worker 
227*795d594fSAndroid Build Coastguard Worker   if (OptimizeForParameter(flag, receiver)) {
228*795d594fSAndroid Build Coastguard Worker     DCHECK(!flag->IsInBlock());
229*795d594fSAndroid Build Coastguard Worker     return;
230*795d594fSAndroid Build Coastguard Worker   }
231*795d594fSAndroid Build Coastguard Worker   if (OptimizeWithDominatingGuard(flag, receiver)) {
232*795d594fSAndroid Build Coastguard Worker     DCHECK(!flag->IsInBlock());
233*795d594fSAndroid Build Coastguard Worker     return;
234*795d594fSAndroid Build Coastguard Worker   }
235*795d594fSAndroid Build Coastguard Worker   if (HoistGuard(flag, receiver)) {
236*795d594fSAndroid Build Coastguard Worker     DCHECK(flag->IsInBlock());
237*795d594fSAndroid Build Coastguard Worker     return;
238*795d594fSAndroid Build Coastguard Worker   }
239*795d594fSAndroid Build Coastguard Worker 
240*795d594fSAndroid Build Coastguard Worker   // Need to keep the CHA guard in place.
241*795d594fSAndroid Build Coastguard Worker   block_has_cha_guard_[flag->GetBlock()->GetBlockId()] = 1;
242*795d594fSAndroid Build Coastguard Worker   GetGraph()->IncrementNumberOfCHAGuards();
243*795d594fSAndroid Build Coastguard Worker }
244*795d594fSAndroid Build Coastguard Worker 
Run()245*795d594fSAndroid Build Coastguard Worker bool CHAGuardOptimization::Run() {
246*795d594fSAndroid Build Coastguard Worker   if (graph_->GetNumberOfCHAGuards() == 0) {
247*795d594fSAndroid Build Coastguard Worker     return false;
248*795d594fSAndroid Build Coastguard Worker   }
249*795d594fSAndroid Build Coastguard Worker   CHAGuardVisitor visitor(graph_);
250*795d594fSAndroid Build Coastguard Worker   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
251*795d594fSAndroid Build Coastguard Worker     visitor.VisitBasicBlock(block);
252*795d594fSAndroid Build Coastguard Worker   }
253*795d594fSAndroid Build Coastguard Worker   return true;
254*795d594fSAndroid Build Coastguard Worker }
255*795d594fSAndroid Build Coastguard Worker 
256*795d594fSAndroid Build Coastguard Worker }  // namespace art
257