| Nicolas Geoffray | 26a25ef | 2014-09-30 13:54:09 +0100 | [diff] [blame] | 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 | namespace art { |
| 20 | |
| 21 | void PrepareForRegisterAllocation::Run() { |
| 22 | // Order does not matter. |
| 23 | for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) { |
| 24 | HBasicBlock* block = it.Current(); |
| 25 | // No need to visit the phis. |
| Andreas Gampe | 277ccbd | 2014-11-03 21:36:10 -0800 | [diff] [blame] | 26 | for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done(); |
| 27 | inst_it.Advance()) { |
| 28 | inst_it.Current()->Accept(this); |
| Nicolas Geoffray | 26a25ef | 2014-09-30 13:54:09 +0100 | [diff] [blame] | 29 | } |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | void PrepareForRegisterAllocation::VisitNullCheck(HNullCheck* check) { |
| 34 | check->ReplaceWith(check->InputAt(0)); |
| 35 | } |
| 36 | |
| Calin Juravle | d0d4852 | 2014-11-04 16:40:20 +0000 | [diff] [blame] | 37 | void PrepareForRegisterAllocation::VisitDivZeroCheck(HDivZeroCheck* check) { |
| 38 | check->ReplaceWith(check->InputAt(0)); |
| 39 | } |
| 40 | |
| Nicolas Geoffray | 26a25ef | 2014-09-30 13:54:09 +0100 | [diff] [blame] | 41 | void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) { |
| 42 | check->ReplaceWith(check->InputAt(0)); |
| 43 | } |
| 44 | |
| Calin Juravle | b1498f6 | 2015-02-16 13:13:29 +0000 | [diff] [blame] | 45 | void PrepareForRegisterAllocation::VisitBoundType(HBoundType* bound_type) { |
| 46 | bound_type->ReplaceWith(bound_type->InputAt(0)); |
| 47 | bound_type->GetBlock()->RemoveInstruction(bound_type); |
| 48 | } |
| 49 | |
| Nicolas Geoffray | 19a19cf | 2014-10-22 16:07:05 +0100 | [diff] [blame] | 50 | void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) { |
| Nicolas Geoffray | 729645a | 2015-11-19 13:29:02 +0000 | [diff] [blame] | 51 | // Try to find a static invoke or a new-instance from which this check originated. |
| 52 | HInstruction* implicit_clinit = nullptr; |
| Vladimir Marko | fbb184a | 2015-11-13 14:47:00 +0000 | [diff] [blame] | 53 | for (HUseIterator<HInstruction*> it(check->GetUses()); !it.Done(); it.Advance()) { |
| 54 | HInstruction* user = it.Current()->GetUser(); |
| Nicolas Geoffray | 729645a | 2015-11-19 13:29:02 +0000 | [diff] [blame] | 55 | if ((user->IsInvokeStaticOrDirect() || user->IsNewInstance()) && |
| 56 | CanMoveClinitCheck(check, user)) { |
| 57 | implicit_clinit = user; |
| 58 | if (user->IsInvokeStaticOrDirect()) { |
| 59 | DCHECK(user->AsInvokeStaticOrDirect()->IsStaticWithExplicitClinitCheck()); |
| 60 | user->AsInvokeStaticOrDirect()->RemoveExplicitClinitCheck( |
| 61 | HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit); |
| 62 | } else { |
| 63 | DCHECK(user->IsNewInstance()); |
| 64 | // We delegate the initialization duty to the allocation. |
| 65 | if (user->AsNewInstance()->GetEntrypoint() == kQuickAllocObjectInitialized) { |
| 66 | user->AsNewInstance()->SetEntrypoint(kQuickAllocObjectResolved); |
| 67 | } |
| 68 | } |
| Vladimir Marko | fbb184a | 2015-11-13 14:47:00 +0000 | [diff] [blame] | 69 | break; |
| 70 | } |
| 71 | } |
| Nicolas Geoffray | 729645a | 2015-11-19 13:29:02 +0000 | [diff] [blame] | 72 | // If we found a static invoke or new-instance for merging, remove the check |
| 73 | // from dominated static invokes. |
| 74 | if (implicit_clinit != nullptr) { |
| Vladimir Marko | fbb184a | 2015-11-13 14:47:00 +0000 | [diff] [blame] | 75 | for (HUseIterator<HInstruction*> it(check->GetUses()); !it.Done(); ) { |
| 76 | HInstruction* user = it.Current()->GetUser(); |
| Nicolas Geoffray | 729645a | 2015-11-19 13:29:02 +0000 | [diff] [blame] | 77 | // All other uses must be dominated. |
| 78 | DCHECK(implicit_clinit->StrictlyDominates(user) || (implicit_clinit == user)); |
| Vladimir Marko | fbb184a | 2015-11-13 14:47:00 +0000 | [diff] [blame] | 79 | it.Advance(); // Advance before we remove the node, reference to the next node is preserved. |
| 80 | if (user->IsInvokeStaticOrDirect()) { |
| 81 | user->AsInvokeStaticOrDirect()->RemoveExplicitClinitCheck( |
| 82 | HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); |
| 83 | } |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | HLoadClass* load_class = check->GetLoadClass(); |
| 88 | bool can_merge_with_load_class = CanMoveClinitCheck(load_class, check); |
| 89 | |
| 90 | check->ReplaceWith(load_class); |
| 91 | |
| Nicolas Geoffray | 729645a | 2015-11-19 13:29:02 +0000 | [diff] [blame] | 92 | if (implicit_clinit != nullptr) { |
| 93 | // Remove the check from the graph. It has been merged into the invoke or new-instance. |
| Vladimir Marko | fbb184a | 2015-11-13 14:47:00 +0000 | [diff] [blame] | 94 | check->GetBlock()->RemoveInstruction(check); |
| 95 | // Check if we can merge the load class as well. |
| 96 | if (can_merge_with_load_class && !load_class->HasUses()) { |
| 97 | load_class->GetBlock()->RemoveInstruction(load_class); |
| 98 | } |
| Nicolas Geoffray | 0580d96 | 2016-01-06 17:40:20 +0000 | [diff] [blame] | 99 | } else if (can_merge_with_load_class && !load_class->NeedsAccessCheck()) { |
| Nicolas Geoffray | 424f676 | 2014-11-03 14:51:25 +0000 | [diff] [blame] | 100 | // Pass the initialization duty to the `HLoadClass` instruction, |
| 101 | // and remove the instruction from the graph. |
| Vladimir Marko | fbb184a | 2015-11-13 14:47:00 +0000 | [diff] [blame] | 102 | load_class->SetMustGenerateClinitCheck(true); |
| Nicolas Geoffray | 424f676 | 2014-11-03 14:51:25 +0000 | [diff] [blame] | 103 | check->GetBlock()->RemoveInstruction(check); |
| 104 | } |
| Nicolas Geoffray | 19a19cf | 2014-10-22 16:07:05 +0100 | [diff] [blame] | 105 | } |
| 106 | |
| Nicolas Geoffray | 729645a | 2015-11-19 13:29:02 +0000 | [diff] [blame] | 107 | void PrepareForRegisterAllocation::VisitNewInstance(HNewInstance* instruction) { |
| 108 | HLoadClass* load_class = instruction->InputAt(0)->AsLoadClass(); |
| 109 | bool has_only_one_use = load_class->HasOnlyOneNonEnvironmentUse(); |
| 110 | // Change the entrypoint to kQuickAllocObject if either: |
| 111 | // - the class is finalizable (only kQuickAllocObject handles finalizable classes), |
| 112 | // - the class needs access checks (we do not know if it's finalizable), |
| 113 | // - or the load class has only one use. |
| 114 | if (instruction->IsFinalizable() || has_only_one_use || load_class->NeedsAccessCheck()) { |
| 115 | instruction->SetEntrypoint(kQuickAllocObject); |
| 116 | instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex()), 0); |
| Nicolas Geoffray | 8e1ef53 | 2015-11-23 12:04:37 +0000 | [diff] [blame] | 117 | // The allocation entry point that deals with access checks does not work with inlined |
| 118 | // methods, so we need to check whether this allocation comes from an inlined method. |
| 119 | if (has_only_one_use && !instruction->GetEnvironment()->IsFromInlinedInvoke()) { |
| Nicolas Geoffray | 729645a | 2015-11-19 13:29:02 +0000 | [diff] [blame] | 120 | // We can remove the load class from the graph. If it needed access checks, we delegate |
| 121 | // the access check to the allocation. |
| 122 | if (load_class->NeedsAccessCheck()) { |
| 123 | instruction->SetEntrypoint(kQuickAllocObjectWithAccessCheck); |
| 124 | } |
| 125 | load_class->GetBlock()->RemoveInstruction(load_class); |
| 126 | } |
| 127 | } |
| 128 | } |
| 129 | |
| David Brazdil | b3e773e | 2016-01-26 11:28:37 +0000 | [diff] [blame] | 130 | bool PrepareForRegisterAllocation::CanEmitConditionAt(HCondition* condition, |
| 131 | HInstruction* user) const { |
| 132 | if (condition->GetNext() != user) { |
| 133 | return false; |
| Nicolas Geoffray | 360231a | 2014-10-08 21:07:48 +0100 | [diff] [blame] | 134 | } |
| David Brazdil | b3e773e | 2016-01-26 11:28:37 +0000 | [diff] [blame] | 135 | |
| 136 | if (user->IsIf() || user->IsDeoptimize()) { |
| 137 | return true; |
| 138 | } |
| 139 | |
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 140 | if (user->IsSelect() && user->AsSelect()->GetCondition() == condition) { |
| 141 | if (GetGraph()->GetInstructionSet() == kX86) { |
| 142 | // Long values and long condition inputs result in 8 required core registers. |
| 143 | // We don't have that many on x86. Materialize the condition in such case. |
| 144 | return user->GetType() != Primitive::kPrimLong || |
| 145 | condition->InputAt(1)->GetType() != Primitive::kPrimLong || |
| 146 | condition->InputAt(1)->IsConstant(); |
| 147 | } else { |
| 148 | return true; |
| 149 | } |
| 150 | } |
| 151 | |
| David Brazdil | b3e773e | 2016-01-26 11:28:37 +0000 | [diff] [blame] | 152 | return false; |
| 153 | } |
| 154 | |
| 155 | void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) { |
| 156 | if (condition->HasOnlyOneNonEnvironmentUse()) { |
| 157 | HInstruction* user = condition->GetUses().GetFirst()->GetUser(); |
| 158 | if (CanEmitConditionAt(condition, user)) { |
| 159 | condition->MarkEmittedAtUseSite(); |
| 160 | } |
| Nicolas Geoffray | 360231a | 2014-10-08 21:07:48 +0100 | [diff] [blame] | 161 | } |
| 162 | } |
| 163 | |
| Roland Levillain | 4c0eb42 | 2015-04-24 16:43:49 +0100 | [diff] [blame] | 164 | void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { |
| 165 | if (invoke->IsStaticWithExplicitClinitCheck()) { |
| 166 | size_t last_input_index = invoke->InputCount() - 1; |
| Calin Juravle | 0ba218d | 2015-05-19 18:46:01 +0100 | [diff] [blame] | 167 | HLoadClass* last_input = invoke->InputAt(last_input_index)->AsLoadClass(); |
| 168 | DCHECK(last_input != nullptr) |
| 169 | << "Last input is not HLoadClass. It is " << last_input->DebugName(); |
| 170 | |
| Vladimir Marko | fbb184a | 2015-11-13 14:47:00 +0000 | [diff] [blame] | 171 | // Detach the explicit class initialization check from the invoke. |
| 172 | // Keeping track of the initializing instruction is no longer required |
| 173 | // at this stage (i.e., after inlining has been performed). |
| 174 | invoke->RemoveExplicitClinitCheck(HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); |
| Roland Levillain | 4c0eb42 | 2015-04-24 16:43:49 +0100 | [diff] [blame] | 175 | |
| Vladimir Marko | fbb184a | 2015-11-13 14:47:00 +0000 | [diff] [blame] | 176 | // Merging with load class should have happened in VisitClinitCheck(). |
| 177 | DCHECK(!CanMoveClinitCheck(last_input, invoke)); |
| 178 | } |
| 179 | } |
| Nicolas Geoffray | 78f4fa7 | 2015-06-12 09:35:05 +0100 | [diff] [blame] | 180 | |
| David Brazdil | b3e773e | 2016-01-26 11:28:37 +0000 | [diff] [blame] | 181 | bool PrepareForRegisterAllocation::CanMoveClinitCheck(HInstruction* input, |
| 182 | HInstruction* user) const { |
| Vladimir Marko | fbb184a | 2015-11-13 14:47:00 +0000 | [diff] [blame] | 183 | // Determine if input and user come from the same dex instruction, so that we can move |
| 184 | // the clinit check responsibility from one to the other, i.e. from HClinitCheck (user) |
| 185 | // to HLoadClass (input), or from HClinitCheck (input) to HInvokeStaticOrDirect (user). |
| 186 | |
| 187 | // Start with a quick dex pc check. |
| 188 | if (user->GetDexPc() != input->GetDexPc()) { |
| 189 | return false; |
| 190 | } |
| 191 | |
| 192 | // Now do a thorough environment check that this is really coming from the same instruction in |
| 193 | // the same inlined graph. Unfortunately, we have to go through the whole environment chain. |
| 194 | HEnvironment* user_environment = user->GetEnvironment(); |
| 195 | HEnvironment* input_environment = input->GetEnvironment(); |
| 196 | while (user_environment != nullptr || input_environment != nullptr) { |
| 197 | if (user_environment == nullptr || input_environment == nullptr) { |
| 198 | // Different environment chain length. This happens when a method is called |
| 199 | // once directly and once indirectly through another inlined method. |
| 200 | return false; |
| 201 | } |
| 202 | if (user_environment->GetDexPc() != input_environment->GetDexPc() || |
| 203 | user_environment->GetMethodIdx() != input_environment->GetMethodIdx() || |
| 204 | !IsSameDexFile(user_environment->GetDexFile(), input_environment->GetDexFile())) { |
| 205 | return false; |
| 206 | } |
| 207 | user_environment = user_environment->GetParent(); |
| 208 | input_environment = input_environment->GetParent(); |
| 209 | } |
| 210 | |
| 211 | // Check for code motion taking the input to a different block. |
| 212 | if (user->GetBlock() != input->GetBlock()) { |
| 213 | return false; |
| 214 | } |
| 215 | |
| 216 | // In debug mode, check that we have not inserted a throwing instruction |
| 217 | // or an instruction with side effects between input and user. |
| 218 | if (kIsDebugBuild) { |
| 219 | for (HInstruction* between = input->GetNext(); between != user; between = between->GetNext()) { |
| 220 | CHECK(between != nullptr); // User must be after input in the same block. |
| 221 | CHECK(!between->CanThrow()); |
| 222 | CHECK(!between->HasSideEffects()); |
| Roland Levillain | 4c0eb42 | 2015-04-24 16:43:49 +0100 | [diff] [blame] | 223 | } |
| 224 | } |
| Vladimir Marko | fbb184a | 2015-11-13 14:47:00 +0000 | [diff] [blame] | 225 | return true; |
| Roland Levillain | 4c0eb42 | 2015-04-24 16:43:49 +0100 | [diff] [blame] | 226 | } |
| 227 | |
| Nicolas Geoffray | 26a25ef | 2014-09-30 13:54:09 +0100 | [diff] [blame] | 228 | } // namespace art |