• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "jni_internal.h"
20 #include "well_known_classes.h"
21 
22 namespace art {
23 
Run()24 void PrepareForRegisterAllocation::Run() {
25   // Order does not matter.
26   for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
27     // No need to visit the phis.
28     for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
29          inst_it.Advance()) {
30       inst_it.Current()->Accept(this);
31     }
32   }
33 }
34 
VisitNullCheck(HNullCheck * check)35 void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) {
36   check->ReplaceWith(check->InputAt(0));
37 }
38 
VisitDivZeroCheck(HDivZeroCheck * check)39 void PrepareForRegisterAllocation::VisitDivZeroCheck(HDivZeroCheck* check) {
40   check->ReplaceWith(check->InputAt(0));
41 }
42 
VisitDeoptimize(HDeoptimize * deoptimize)43 void PrepareForRegisterAllocation::VisitDeoptimize(HDeoptimize* deoptimize) {
44   if (deoptimize->GuardsAnInput()) {
45     // Replace the uses with the actual guarded instruction.
46     deoptimize->ReplaceWith(deoptimize->GuardedInput());
47     deoptimize->RemoveGuard();
48   }
49 }
50 
VisitBoundsCheck(HBoundsCheck * check)51 void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) {
52   check->ReplaceWith(check->InputAt(0));
53   if (check->IsStringCharAt()) {
54     // Add a fake environment for String.charAt() inline info as we want
55     // the exception to appear as being thrown from there.
56     ArtMethod* char_at_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
57     ArenaAllocator* arena = GetGraph()->GetArena();
58     HEnvironment* environment = new (arena) HEnvironment(arena,
59                                                          /* number_of_vregs */ 0u,
60                                                          char_at_method,
61                                                          /* dex_pc */ DexFile::kDexNoIndex,
62                                                          check);
63     check->InsertRawEnvironment(environment);
64   }
65 }
66 
VisitBoundType(HBoundType * bound_type)67 void PrepareForRegisterAllocation::VisitBoundType(HBoundType* bound_type) {
68   bound_type->ReplaceWith(bound_type->InputAt(0));
69   bound_type->GetBlock()->RemoveInstruction(bound_type);
70 }
71 
VisitArraySet(HArraySet * instruction)72 void PrepareForRegisterAllocation::VisitArraySet(HArraySet* instruction) {
73   HInstruction* value = instruction->GetValue();
74   // PrepareForRegisterAllocation::VisitBoundType may have replaced a
75   // BoundType (as value input of this ArraySet) with a NullConstant.
76   // If so, this ArraySet no longer needs a type check.
77   if (value->IsNullConstant()) {
78     DCHECK_EQ(value->GetType(), Primitive::kPrimNot);
79     if (instruction->NeedsTypeCheck()) {
80       instruction->ClearNeedsTypeCheck();
81     }
82   }
83 }
84 
VisitClinitCheck(HClinitCheck * check)85 void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) {
86   // Try to find a static invoke or a new-instance from which this check originated.
87   HInstruction* implicit_clinit = nullptr;
88   for (const HUseListNode<HInstruction*>& use : check->GetUses()) {
89     HInstruction* user = use.GetUser();
90     if ((user->IsInvokeStaticOrDirect() || user->IsNewInstance()) &&
91         CanMoveClinitCheck(check, user)) {
92       implicit_clinit = user;
93       if (user->IsInvokeStaticOrDirect()) {
94         DCHECK(user->AsInvokeStaticOrDirect()->IsStaticWithExplicitClinitCheck());
95         user->AsInvokeStaticOrDirect()->RemoveExplicitClinitCheck(
96             HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
97       } else {
98         DCHECK(user->IsNewInstance());
99         // We delegate the initialization duty to the allocation.
100         if (user->AsNewInstance()->GetEntrypoint() == kQuickAllocObjectInitialized) {
101           user->AsNewInstance()->SetEntrypoint(kQuickAllocObjectResolved);
102         }
103       }
104       break;
105     }
106   }
107   // If we found a static invoke or new-instance for merging, remove the check
108   // from dominated static invokes.
109   if (implicit_clinit != nullptr) {
110     const HUseList<HInstruction*>& uses = check->GetUses();
111     for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
112       HInstruction* user = it->GetUser();
113       // All other uses must be dominated.
114       DCHECK(implicit_clinit->StrictlyDominates(user) || (implicit_clinit == user));
115       ++it;  // Advance before we remove the node, reference to the next node is preserved.
116       if (user->IsInvokeStaticOrDirect()) {
117         user->AsInvokeStaticOrDirect()->RemoveExplicitClinitCheck(
118             HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
119       }
120     }
121   }
122 
123   HLoadClass* load_class = check->GetLoadClass();
124   bool can_merge_with_load_class = CanMoveClinitCheck(load_class, check);
125 
126   check->ReplaceWith(load_class);
127 
128   if (implicit_clinit != nullptr) {
129     // Remove the check from the graph. It has been merged into the invoke or new-instance.
130     check->GetBlock()->RemoveInstruction(check);
131     // Check if we can merge the load class as well.
132     if (can_merge_with_load_class && !load_class->HasUses()) {
133       load_class->GetBlock()->RemoveInstruction(load_class);
134     }
135   } else if (can_merge_with_load_class && !load_class->NeedsAccessCheck()) {
136     // Pass the initialization duty to the `HLoadClass` instruction,
137     // and remove the instruction from the graph.
138     DCHECK(load_class->HasEnvironment());
139     load_class->SetMustGenerateClinitCheck(true);
140     check->GetBlock()->RemoveInstruction(check);
141   }
142 }
143 
CanEmitConditionAt(HCondition * condition,HInstruction * user) const144 bool PrepareForRegisterAllocation::CanEmitConditionAt(HCondition* condition,
145                                                       HInstruction* user) const {
146   if (condition->GetNext() != user) {
147     return false;
148   }
149 
150   if (user->IsIf() || user->IsDeoptimize()) {
151     return true;
152   }
153 
154   if (user->IsSelect() && user->AsSelect()->GetCondition() == condition) {
155     return true;
156   }
157 
158   return false;
159 }
160 
VisitCondition(HCondition * condition)161 void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
162   if (condition->HasOnlyOneNonEnvironmentUse()) {
163     HInstruction* user = condition->GetUses().front().GetUser();
164     if (CanEmitConditionAt(condition, user)) {
165       condition->MarkEmittedAtUseSite();
166     }
167   }
168 }
169 
VisitConstructorFence(HConstructorFence * constructor_fence)170 void PrepareForRegisterAllocation::VisitConstructorFence(HConstructorFence* constructor_fence) {
171   // Trivially remove redundant HConstructorFence when it immediately follows an HNewInstance
172   // to an uninitialized class. In this special case, the art_quick_alloc_object_resolved
173   // will already have the 'dmb' which is strictly stronger than an HConstructorFence.
174   //
175   // The instruction builder always emits "x = HNewInstance; HConstructorFence(x)" so this
176   // is effectively pattern-matching that particular case and undoing the redundancy the builder
177   // had introduced.
178   //
179   // TODO: Move this to a separate pass.
180   HInstruction* allocation_inst = constructor_fence->GetAssociatedAllocation();
181   if (allocation_inst != nullptr && allocation_inst->IsNewInstance()) {
182     HNewInstance* new_inst = allocation_inst->AsNewInstance();
183     // This relies on the entrypoint already being set to the more optimized version;
184     // as that happens in this pass, this redundancy removal also cannot happen any earlier.
185     if (new_inst != nullptr && new_inst->GetEntrypoint() == kQuickAllocObjectResolved) {
186       // If this was done in an earlier pass, we would want to match that `previous` was an input
187       // to the `constructor_fence`. However, since this pass removes the inputs to the fence,
188       // we can ignore the inputs and just remove the instruction from its block.
189       DCHECK_EQ(1u, constructor_fence->InputCount());
190       // TODO: GetAssociatedAllocation should not care about multiple inputs
191       // if we are in prepare_for_register_allocation pass only.
192       constructor_fence->GetBlock()->RemoveInstruction(constructor_fence);
193       return;
194       // TODO: actually remove the dmb from the .S entrypoints (initialized variants only).
195     }
196 
197     // HNewArray does not need this check because the art_quick_alloc_array does not itself
198     // have a dmb in any normal situation (i.e. the array class is never exactly in the
199     // "resolved" state). If the array class is not yet loaded, it will always go from
200     // Unloaded->Initialized state.
201   }
202 
203   // Remove all the inputs to the constructor fence;
204   // they aren't used by the InstructionCodeGenerator and this lets us avoid creating a
205   // LocationSummary in the LocationsBuilder.
206   constructor_fence->RemoveAllInputs();
207 }
208 
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)209 void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
210   if (invoke->IsStaticWithExplicitClinitCheck()) {
211     HLoadClass* last_input = invoke->GetInputs().back()->AsLoadClass();
212     DCHECK(last_input != nullptr)
213         << "Last input is not HLoadClass. It is " << last_input->DebugName();
214 
215     // Detach the explicit class initialization check from the invoke.
216     // Keeping track of the initializing instruction is no longer required
217     // at this stage (i.e., after inlining has been performed).
218     invoke->RemoveExplicitClinitCheck(HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
219 
220     // Merging with load class should have happened in VisitClinitCheck().
221     DCHECK(!CanMoveClinitCheck(last_input, invoke));
222   }
223 }
224 
CanMoveClinitCheck(HInstruction * input,HInstruction * user) const225 bool PrepareForRegisterAllocation::CanMoveClinitCheck(HInstruction* input,
226                                                       HInstruction* user) const {
227   // Determine if input and user come from the same dex instruction, so that we can move
228   // the clinit check responsibility from one to the other, i.e. from HClinitCheck (user)
229   // to HLoadClass (input), or from HClinitCheck (input) to HInvokeStaticOrDirect (user),
230   // or from HLoadClass (input) to HNewInstance (user).
231 
232   // Start with a quick dex pc check.
233   if (user->GetDexPc() != input->GetDexPc()) {
234     return false;
235   }
236 
237   // Now do a thorough environment check that this is really coming from the same instruction in
238   // the same inlined graph. Unfortunately, we have to go through the whole environment chain.
239   HEnvironment* user_environment = user->GetEnvironment();
240   HEnvironment* input_environment = input->GetEnvironment();
241   while (user_environment != nullptr || input_environment != nullptr) {
242     if (user_environment == nullptr || input_environment == nullptr) {
243       // Different environment chain length. This happens when a method is called
244       // once directly and once indirectly through another inlined method.
245       return false;
246     }
247     if (user_environment->GetDexPc() != input_environment->GetDexPc() ||
248         user_environment->GetMethod() != input_environment->GetMethod()) {
249       return false;
250     }
251     user_environment = user_environment->GetParent();
252     input_environment = input_environment->GetParent();
253   }
254 
255   // Check for code motion taking the input to a different block.
256   if (user->GetBlock() != input->GetBlock()) {
257     return false;
258   }
259 
260   // In debug mode, check that we have not inserted a throwing instruction
261   // or an instruction with side effects between input and user.
262   if (kIsDebugBuild) {
263     for (HInstruction* between = input->GetNext(); between != user; between = between->GetNext()) {
264       CHECK(between != nullptr);  // User must be after input in the same block.
265       CHECK(!between->CanThrow());
266       CHECK(!between->HasSideEffects());
267     }
268   }
269   return true;
270 }
271 
272 }  // namespace art
273