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
VisitInvokeStaticOrDirect(HInvokeStaticOrDirect * invoke)170 void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
171 if (invoke->IsStaticWithExplicitClinitCheck()) {
172 HLoadClass* last_input = invoke->GetInputs().back()->AsLoadClass();
173 DCHECK(last_input != nullptr)
174 << "Last input is not HLoadClass. It is " << last_input->DebugName();
175
176 // Detach the explicit class initialization check from the invoke.
177 // Keeping track of the initializing instruction is no longer required
178 // at this stage (i.e., after inlining has been performed).
179 invoke->RemoveExplicitClinitCheck(HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
180
181 // Merging with load class should have happened in VisitClinitCheck().
182 DCHECK(!CanMoveClinitCheck(last_input, invoke));
183 }
184 }
185
CanMoveClinitCheck(HInstruction * input,HInstruction * user) const186 bool PrepareForRegisterAllocation::CanMoveClinitCheck(HInstruction* input,
187 HInstruction* user) const {
188 // Determine if input and user come from the same dex instruction, so that we can move
189 // the clinit check responsibility from one to the other, i.e. from HClinitCheck (user)
190 // to HLoadClass (input), or from HClinitCheck (input) to HInvokeStaticOrDirect (user),
191 // or from HLoadClass (input) to HNewInstance (user).
192
193 // Start with a quick dex pc check.
194 if (user->GetDexPc() != input->GetDexPc()) {
195 return false;
196 }
197
198 // Now do a thorough environment check that this is really coming from the same instruction in
199 // the same inlined graph. Unfortunately, we have to go through the whole environment chain.
200 HEnvironment* user_environment = user->GetEnvironment();
201 HEnvironment* input_environment = input->GetEnvironment();
202 while (user_environment != nullptr || input_environment != nullptr) {
203 if (user_environment == nullptr || input_environment == nullptr) {
204 // Different environment chain length. This happens when a method is called
205 // once directly and once indirectly through another inlined method.
206 return false;
207 }
208 if (user_environment->GetDexPc() != input_environment->GetDexPc() ||
209 user_environment->GetMethod() != input_environment->GetMethod()) {
210 return false;
211 }
212 user_environment = user_environment->GetParent();
213 input_environment = input_environment->GetParent();
214 }
215
216 // Check for code motion taking the input to a different block.
217 if (user->GetBlock() != input->GetBlock()) {
218 return false;
219 }
220
221 // In debug mode, check that we have not inserted a throwing instruction
222 // or an instruction with side effects between input and user.
223 if (kIsDebugBuild) {
224 for (HInstruction* between = input->GetNext(); between != user; between = between->GetNext()) {
225 CHECK(between != nullptr); // User must be after input in the same block.
226 CHECK(!between->CanThrow());
227 CHECK(!between->HasSideEffects());
228 }
229 }
230 return true;
231 }
232
233 } // namespace art
234