xref: /aosp_15_r20/art/compiler/optimizing/prepare_for_register_allocation.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1 /*
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 "prepare_for_register_allocation.h"
18 
19 #include "dex/dex_file_types.h"
20 #include "driver/compiler_options.h"
21 #include "jni/jni_internal.h"
22 #include "nodes.h"
23 #include "optimizing_compiler_stats.h"
24 #include "well_known_classes.h"
25 
26 namespace art HIDDEN {
27 
28 class PrepareForRegisterAllocationVisitor final : public HGraphDelegateVisitor {
29  public:
PrepareForRegisterAllocationVisitor(HGraph * graph,const CompilerOptions & compiler_options,OptimizingCompilerStats * stats)30   PrepareForRegisterAllocationVisitor(HGraph* graph,
31                                       const CompilerOptions& compiler_options,
32                                       OptimizingCompilerStats* stats)
33       : HGraphDelegateVisitor(graph, stats),
34         compiler_options_(compiler_options) {}
35 
36  private:
37   void VisitCheckCast(HCheckCast* check_cast) override;
38   void VisitInstanceOf(HInstanceOf* instance_of) override;
39   void VisitNullCheck(HNullCheck* check) override;
40   void VisitDivZeroCheck(HDivZeroCheck* check) override;
41   void VisitBoundsCheck(HBoundsCheck* check) override;
42   void VisitBoundType(HBoundType* bound_type) override;
43   void VisitArraySet(HArraySet* instruction) override;
44   void VisitClinitCheck(HClinitCheck* check) override;
45   void VisitCondition(HCondition* condition) override;
46   void VisitConstructorFence(HConstructorFence* constructor_fence) override;
47   void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) override;
48   void VisitDeoptimize(HDeoptimize* deoptimize) override;
49   void VisitTypeConversion(HTypeConversion* instruction) override;
50 
51   bool CanMoveClinitCheck(HInstruction* input, HInstruction* user) const;
52   bool CanEmitConditionAt(HCondition* condition, HInstruction* user) const;
53 
54   const CompilerOptions& compiler_options_;
55 };
56 
Run()57 bool PrepareForRegisterAllocation::Run() {
58   PrepareForRegisterAllocationVisitor visitor(graph_, compiler_options_, stats_);
59   // Order does not matter.
60   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
61     // No need to visit the phis.
62     for (HInstructionIteratorHandleChanges inst_it(block->GetInstructions()); !inst_it.Done();
63          inst_it.Advance()) {
64       inst_it.Current()->Accept(&visitor);
65     }
66   }
67   return true;
68 }
69 
VisitCheckCast(HCheckCast * check_cast)70 void PrepareForRegisterAllocationVisitor::VisitCheckCast(HCheckCast* check_cast) {
71   // Record only those bitstring type checks that make it to the codegen stage.
72   if (check_cast->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
73     MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck);
74   }
75 }
76 
VisitInstanceOf(HInstanceOf * instance_of)77 void PrepareForRegisterAllocationVisitor::VisitInstanceOf(HInstanceOf* instance_of) {
78   // Record only those bitstring type checks that make it to the codegen stage.
79   if (instance_of->GetTypeCheckKind() == TypeCheckKind::kBitstringCheck) {
80     MaybeRecordStat(stats_, MethodCompilationStat::kBitstringTypeCheck);
81   }
82 }
83 
VisitNullCheck(HNullCheck * check)84 void PrepareForRegisterAllocationVisitor::VisitNullCheck(HNullCheck* check) {
85   check->ReplaceWith(check->InputAt(0));
86   if (compiler_options_.GetImplicitNullChecks()) {
87     HInstruction* next = check->GetNext();
88 
89     // The `PrepareForRegisterAllocation` pass removes `HBoundType` from the graph,
90     // so do it ourselves now to not prevent optimizations.
91     while (next->IsBoundType()) {
92       next = next->GetNext();
93       VisitBoundType(next->GetPrevious()->AsBoundType());
94     }
95     if (next->CanDoImplicitNullCheckOn(check->InputAt(0))) {
96       check->MarkEmittedAtUseSite();
97     }
98   }
99 }
100 
VisitDivZeroCheck(HDivZeroCheck * check)101 void PrepareForRegisterAllocationVisitor::VisitDivZeroCheck(HDivZeroCheck* check) {
102   check->ReplaceWith(check->InputAt(0));
103 }
104 
VisitDeoptimize(HDeoptimize * deoptimize)105 void PrepareForRegisterAllocationVisitor::VisitDeoptimize(HDeoptimize* deoptimize) {
106   if (deoptimize->GuardsAnInput()) {
107     // Replace the uses with the actual guarded instruction.
108     deoptimize->ReplaceWith(deoptimize->GuardedInput());
109     deoptimize->RemoveGuard();
110   }
111 }
112 
VisitBoundsCheck(HBoundsCheck * check)113 void PrepareForRegisterAllocationVisitor::VisitBoundsCheck(HBoundsCheck* check) {
114   check->ReplaceWith(check->InputAt(0));
115   if (check->IsStringCharAt()) {
116     // Add a fake environment for String.charAt() inline info as we want the exception
117     // to appear as being thrown from there. Skip if we're compiling String.charAt() itself.
118     ArtMethod* char_at_method = WellKnownClasses::java_lang_String_charAt;
119     if (GetGraph()->GetArtMethod() != char_at_method) {
120       ArenaAllocator* allocator = GetGraph()->GetAllocator();
121       HEnvironment* environment = HEnvironment::Create(allocator,
122                                                        /* number_of_vregs= */ 0u,
123                                                        char_at_method,
124                                                        /* dex_pc= */ dex::kDexNoIndex,
125                                                        check);
126       check->InsertRawEnvironment(environment);
127     }
128   }
129 }
130 
VisitBoundType(HBoundType * bound_type)131 void PrepareForRegisterAllocationVisitor::VisitBoundType(HBoundType* bound_type) {
132   bound_type->ReplaceWith(bound_type->InputAt(0));
133   bound_type->GetBlock()->RemoveInstruction(bound_type);
134 }
135 
VisitArraySet(HArraySet * instruction)136 void PrepareForRegisterAllocationVisitor::VisitArraySet(HArraySet* instruction) {
137   HInstruction* value = instruction->GetValue();
138   // PrepareForRegisterAllocationVisitor::VisitBoundType may have replaced a
139   // BoundType (as value input of this ArraySet) with a NullConstant.
140   // If so, this ArraySet no longer needs a type check.
141   if (value->IsNullConstant()) {
142     DCHECK_EQ(value->GetType(), DataType::Type::kReference);
143     if (instruction->NeedsTypeCheck()) {
144       instruction->ClearTypeCheck();
145     }
146   }
147 }
148 
VisitClinitCheck(HClinitCheck * check)149 void PrepareForRegisterAllocationVisitor::VisitClinitCheck(HClinitCheck* check) {
150   // Try to find a static invoke or a new-instance from which this check originated.
151   HInstruction* implicit_clinit = nullptr;
152   for (const HUseListNode<HInstruction*>& use : check->GetUses()) {
153     HInstruction* user = use.GetUser();
154     if ((user->IsInvokeStaticOrDirect() || user->IsNewInstance()) &&
155         CanMoveClinitCheck(check, user)) {
156       implicit_clinit = user;
157       if (user->IsInvokeStaticOrDirect()) {
158         DCHECK(user->AsInvokeStaticOrDirect()->IsStaticWithExplicitClinitCheck());
159         user->AsInvokeStaticOrDirect()->RemoveExplicitClinitCheck(
160             HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
161       } else {
162         DCHECK(user->IsNewInstance());
163         // We delegate the initialization duty to the allocation.
164         if (user->AsNewInstance()->GetEntrypoint() == kQuickAllocObjectInitialized) {
165           user->AsNewInstance()->SetEntrypoint(kQuickAllocObjectResolved);
166         }
167       }
168       break;
169     }
170   }
171   // If we found a static invoke or new-instance for merging, remove the check
172   // from dominated static invokes.
173   if (implicit_clinit != nullptr) {
174     const HUseList<HInstruction*>& uses = check->GetUses();
175     for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
176       HInstruction* user = it->GetUser();
177       // All other uses must be dominated.
178       DCHECK(implicit_clinit->StrictlyDominates(user) || (implicit_clinit == user));
179       ++it;  // Advance before we remove the node, reference to the next node is preserved.
180       if (user->IsInvokeStaticOrDirect()) {
181         user->AsInvokeStaticOrDirect()->RemoveExplicitClinitCheck(
182             HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
183       }
184     }
185   }
186 
187   HLoadClass* load_class = check->GetLoadClass();
188   bool can_merge_with_load_class = CanMoveClinitCheck(load_class, check);
189 
190   check->ReplaceWith(load_class);
191 
192   if (implicit_clinit != nullptr) {
193     // Remove the check from the graph. It has been merged into the invoke or new-instance.
194     check->GetBlock()->RemoveInstruction(check);
195     // Check if we can merge the load class as well, or whether the LoadClass is now dead.
196     if ((can_merge_with_load_class || !load_class->CanThrow()) && !load_class->HasUses()) {
197       load_class->GetBlock()->RemoveInstruction(load_class);
198     }
199   } else if (can_merge_with_load_class &&
200              load_class->GetLoadKind() != HLoadClass::LoadKind::kRuntimeCall) {
201     // Pass the initialization duty to the `HLoadClass` instruction,
202     // and remove the instruction from the graph.
203     DCHECK(load_class->HasEnvironment());
204     load_class->SetMustGenerateClinitCheck(true);
205     check->GetBlock()->RemoveInstruction(check);
206   }
207 }
208 
CanEmitConditionAt(HCondition * condition,HInstruction * user) const209 bool PrepareForRegisterAllocationVisitor::CanEmitConditionAt(HCondition* condition,
210                                                              HInstruction* user) const {
211   if (condition->GetNext() != user) {
212     return false;
213   }
214 
215   if (GetGraph()->IsCompilingBaseline() && compiler_options_.ProfileBranches()) {
216     // To do branch profiling, we cannot emit conditions at use site.
217     return false;
218   }
219 
220   if (user->IsIf() || user->IsDeoptimize()) {
221     return true;
222   }
223 
224   if (user->IsSelect() && user->AsSelect()->GetCondition() == condition) {
225     return true;
226   }
227 
228   return false;
229 }
230 
VisitCondition(HCondition * condition)231 void PrepareForRegisterAllocationVisitor::VisitCondition(HCondition* condition) {
232   if (condition->HasOnlyOneNonEnvironmentUse()) {
233     HInstruction* user = condition->GetUses().front().GetUser();
234     if (CanEmitConditionAt(condition, user)) {
235       condition->MarkEmittedAtUseSite();
236     }
237   }
238 }
239 
VisitConstructorFence(HConstructorFence * constructor_fence)240 void PrepareForRegisterAllocationVisitor::VisitConstructorFence(
241     HConstructorFence* constructor_fence) {
242   // Trivially remove redundant HConstructorFence when it immediately follows an HNewInstance
243   // to an uninitialized class. In this special case, the art_quick_alloc_object_resolved
244   // will already have the 'dmb' which is strictly stronger than an HConstructorFence.
245   //
246   // The instruction builder always emits "x = HNewInstance; HConstructorFence(x)" so this
247   // is effectively pattern-matching that particular case and undoing the redundancy the builder
248   // had introduced.
249   //
250   // TODO: Move this to a separate pass.
251   HInstruction* allocation_inst = constructor_fence->GetAssociatedAllocation();
252   if (allocation_inst != nullptr && allocation_inst->IsNewInstance()) {
253     HNewInstance* new_inst = allocation_inst->AsNewInstance();
254     // This relies on the entrypoint already being set to the more optimized version;
255     // as that happens in this pass, this redundancy removal also cannot happen any earlier.
256     if (new_inst != nullptr && new_inst->GetEntrypoint() == kQuickAllocObjectResolved) {
257       // If this was done in an earlier pass, we would want to match that `previous` was an input
258       // to the `constructor_fence`. However, since this pass removes the inputs to the fence,
259       // we can ignore the inputs and just remove the instruction from its block.
260       DCHECK_EQ(1u, constructor_fence->InputCount());
261       // TODO: GetAssociatedAllocation should not care about multiple inputs
262       // if we are in prepare_for_register_allocation pass only.
263       constructor_fence->GetBlock()->RemoveInstruction(constructor_fence);
264       MaybeRecordStat(stats_,
265                       MethodCompilationStat::kConstructorFenceRemovedPFRA);
266       return;
267     }
268 
269     // HNewArray does not need this check because the art_quick_alloc_array does not itself
270     // have a dmb in any normal situation (i.e. the array class is never exactly in the
271     // "resolved" state). If the array class is not yet loaded, it will always go from
272     // Unloaded->Initialized state.
273   }
274 
275   // Remove all the inputs to the constructor fence;
276   // they aren't used by the InstructionCodeGenerator and this lets us avoid creating a
277   // LocationSummary in the LocationsBuilder.
278   constructor_fence->RemoveAllInputs();
279 }
280 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)281 void PrepareForRegisterAllocationVisitor::VisitInvokeStaticOrDirect(
282     HInvokeStaticOrDirect* invoke) {
283   if (invoke->IsStaticWithExplicitClinitCheck()) {
284     HInstruction* last_input = invoke->GetInputs().back();
285     DCHECK(last_input->IsLoadClass())
286         << "Last input is not HLoadClass. It is " << last_input->DebugName();
287 
288     // Detach the explicit class initialization check from the invoke.
289     // Keeping track of the initializing instruction is no longer required
290     // at this stage (i.e., after inlining has been performed).
291     invoke->RemoveExplicitClinitCheck(HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
292 
293     // Merging with load class should have happened in VisitClinitCheck().
294     DCHECK(!CanMoveClinitCheck(last_input, invoke));
295   }
296 }
297 
CanMoveClinitCheck(HInstruction * input,HInstruction * user) const298 bool PrepareForRegisterAllocationVisitor::CanMoveClinitCheck(HInstruction* input,
299                                                              HInstruction* user) const {
300   // Determine if input and user come from the same dex instruction, so that we can move
301   // the clinit check responsibility from one to the other, i.e. from HClinitCheck (user)
302   // to HLoadClass (input), or from HClinitCheck (input) to HInvokeStaticOrDirect (user),
303   // or from HLoadClass (input) to HNewInstance (user).
304 
305   // Start with a quick dex pc check.
306   if (user->GetDexPc() != input->GetDexPc()) {
307     return false;
308   }
309 
310   if (user->IsNewInstance() && user->AsNewInstance()->IsPartialMaterialization()) {
311     return false;
312   }
313 
314   // Now do a thorough environment check that this is really coming from the same instruction in
315   // the same inlined graph. Unfortunately, we have to go through the whole environment chain.
316   HEnvironment* user_environment = user->GetEnvironment();
317   HEnvironment* input_environment = input->GetEnvironment();
318   while (user_environment != nullptr || input_environment != nullptr) {
319     if (user_environment == nullptr || input_environment == nullptr) {
320       // Different environment chain length. This happens when a method is called
321       // once directly and once indirectly through another inlined method.
322       return false;
323     }
324     if (user_environment->GetDexPc() != input_environment->GetDexPc() ||
325         user_environment->GetMethod() != input_environment->GetMethod()) {
326       return false;
327     }
328     user_environment = user_environment->GetParent();
329     input_environment = input_environment->GetParent();
330   }
331 
332   // Check for code motion taking the input to a different block.
333   if (user->GetBlock() != input->GetBlock()) {
334     return false;
335   }
336 
337   // If there's a instruction between them that can throw or it has side effects, we cannot move the
338   // responsibility.
339   for (HInstruction* between = input->GetNext(); between != user; between = between->GetNext()) {
340     DCHECK(between != nullptr) << " User must be after input in the same block. input: " << *input
341                                << ", user: " << *user;
342     if (between->CanThrow() || between->HasSideEffects()) {
343       return false;
344     }
345   }
346 
347   return true;
348 }
349 
VisitTypeConversion(HTypeConversion * instruction)350 void PrepareForRegisterAllocationVisitor::VisitTypeConversion(HTypeConversion* instruction) {
351   // For simplicity, our code generators don't handle implicit type conversion, so ensure
352   // there are none before hitting codegen.
353   if (instruction->IsImplicitConversion()) {
354     instruction->ReplaceWith(instruction->GetInput());
355     instruction->GetBlock()->RemoveInstruction(instruction);
356   }
357 }
358 
359 }  // namespace art
360