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_ = ⁢
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