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