Home | History | Annotate | Download | only in optimizing
      1 /*
      2  * Copyright (C) 2015 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 "intrinsics.h"
     18 
     19 #include "art_field-inl.h"
     20 #include "art_method-inl.h"
     21 #include "class_linker.h"
     22 #include "driver/compiler_driver.h"
     23 #include "driver/compiler_options.h"
     24 #include "invoke_type.h"
     25 #include "mirror/dex_cache-inl.h"
     26 #include "nodes.h"
     27 #include "scoped_thread_state_change-inl.h"
     28 #include "thread-current-inl.h"
     29 #include "utils.h"
     30 
     31 namespace art {
     32 
     33 // Function that returns whether an intrinsic is static/direct or virtual.
     34 static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) {
     35   switch (i) {
     36     case Intrinsics::kNone:
     37       return kInterface;  // Non-sensical for intrinsic.
     38 #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
     39     case Intrinsics::k ## Name: \
     40       return IsStatic;
     41 #include "intrinsics_list.h"
     42 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
     43 #undef INTRINSICS_LIST
     44 #undef OPTIMIZING_INTRINSICS
     45   }
     46   return kInterface;
     47 }
     48 
     49 // Function that returns whether an intrinsic needs an environment or not.
     50 static inline IntrinsicNeedsEnvironmentOrCache NeedsEnvironmentOrCache(Intrinsics i) {
     51   switch (i) {
     52     case Intrinsics::kNone:
     53       return kNeedsEnvironmentOrCache;  // Non-sensical for intrinsic.
     54 #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
     55     case Intrinsics::k ## Name: \
     56       return NeedsEnvironmentOrCache;
     57 #include "intrinsics_list.h"
     58 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
     59 #undef INTRINSICS_LIST
     60 #undef OPTIMIZING_INTRINSICS
     61   }
     62   return kNeedsEnvironmentOrCache;
     63 }
     64 
     65 // Function that returns whether an intrinsic has side effects.
     66 static inline IntrinsicSideEffects GetSideEffects(Intrinsics i) {
     67   switch (i) {
     68     case Intrinsics::kNone:
     69       return kAllSideEffects;
     70 #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
     71     case Intrinsics::k ## Name: \
     72       return SideEffects;
     73 #include "intrinsics_list.h"
     74 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
     75 #undef INTRINSICS_LIST
     76 #undef OPTIMIZING_INTRINSICS
     77   }
     78   return kAllSideEffects;
     79 }
     80 
     81 // Function that returns whether an intrinsic can throw exceptions.
     82 static inline IntrinsicExceptions GetExceptions(Intrinsics i) {
     83   switch (i) {
     84     case Intrinsics::kNone:
     85       return kCanThrow;
     86 #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
     87     case Intrinsics::k ## Name: \
     88       return Exceptions;
     89 #include "intrinsics_list.h"
     90 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
     91 #undef INTRINSICS_LIST
     92 #undef OPTIMIZING_INTRINSICS
     93   }
     94   return kCanThrow;
     95 }
     96 
     97 static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
     98   // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual.
     99   //
    100   // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization
    101   // failure occured. We might be in a situation where we have inlined a method that calls an
    102   // intrinsic, but that method is in a different dex file on which we do not have a
    103   // verified_method that would have helped the compiler driver sharpen the call. In that case,
    104   // make sure that the intrinsic is actually for some final method (or in a final class), as
    105   // otherwise the intrinsics setup is broken.
    106   //
    107   // For the last direction, we have intrinsics for virtual functions that will perform a check
    108   // inline. If the precise type is known, however, the instruction will be sharpened to an
    109   // InvokeStaticOrDirect.
    110   InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic);
    111   InvokeType invoke_type = invoke->GetInvokeType();
    112   switch (intrinsic_type) {
    113     case kStatic:
    114       return (invoke_type == kStatic);
    115 
    116     case kDirect:
    117       if (invoke_type == kDirect) {
    118         return true;
    119       }
    120       if (invoke_type == kVirtual) {
    121         ArtMethod* art_method = invoke->GetResolvedMethod();
    122         ScopedObjectAccess soa(Thread::Current());
    123         return (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
    124       }
    125       return false;
    126 
    127     case kVirtual:
    128       // Call might be devirtualized.
    129       return (invoke_type == kVirtual || invoke_type == kDirect);
    130 
    131     default:
    132       return false;
    133   }
    134 }
    135 
    136 void IntrinsicsRecognizer::Run() {
    137   ScopedObjectAccess soa(Thread::Current());
    138   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
    139     for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
    140          inst_it.Advance()) {
    141       HInstruction* inst = inst_it.Current();
    142       if (inst->IsInvoke()) {
    143         HInvoke* invoke = inst->AsInvoke();
    144         ArtMethod* art_method = invoke->GetResolvedMethod();
    145         if (art_method != nullptr && art_method->IsIntrinsic()) {
    146           Intrinsics intrinsic = static_cast<Intrinsics>(art_method->GetIntrinsic());
    147           if (!CheckInvokeType(intrinsic, invoke)) {
    148             LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
    149                 << static_cast<uint32_t>(intrinsic) << " for "
    150                 << art_method->PrettyMethod()
    151                 << invoke->DebugName();
    152           } else {
    153             invoke->SetIntrinsic(intrinsic,
    154                                  NeedsEnvironmentOrCache(intrinsic),
    155                                  GetSideEffects(intrinsic),
    156                                  GetExceptions(intrinsic));
    157             MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
    158           }
    159         }
    160       }
    161     }
    162   }
    163 }
    164 
    165 std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
    166   switch (intrinsic) {
    167     case Intrinsics::kNone:
    168       os << "None";
    169       break;
    170 #define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
    171     case Intrinsics::k ## Name: \
    172       os << # Name; \
    173       break;
    174 #include "intrinsics_list.h"
    175 INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
    176 #undef STATIC_INTRINSICS_LIST
    177 #undef VIRTUAL_INTRINSICS_LIST
    178 #undef OPTIMIZING_INTRINSICS
    179   }
    180   return os;
    181 }
    182 
    183 void IntrinsicVisitor::ComputeIntegerValueOfLocations(HInvoke* invoke,
    184                                                       CodeGenerator* codegen,
    185                                                       Location return_location,
    186                                                       Location first_argument_location) {
    187   if (Runtime::Current()->IsAotCompiler()) {
    188     if (codegen->GetCompilerOptions().IsBootImage() ||
    189         codegen->GetCompilerOptions().GetCompilePic()) {
    190       // TODO(ngeoffray): Support boot image compilation.
    191       return;
    192     }
    193   }
    194 
    195   IntegerValueOfInfo info = ComputeIntegerValueOfInfo();
    196 
    197   // Most common case is that we have found all we needed (classes are initialized
    198   // and in the boot image). Bail if not.
    199   if (info.integer_cache == nullptr ||
    200       info.integer == nullptr ||
    201       info.cache == nullptr ||
    202       info.value_offset == 0 ||
    203       // low and high cannot be 0, per the spec.
    204       info.low == 0 ||
    205       info.high == 0) {
    206     LOG(INFO) << "Integer.valueOf will not be optimized";
    207     return;
    208   }
    209 
    210   // The intrinsic will call if it needs to allocate a j.l.Integer.
    211   LocationSummary* locations = new (invoke->GetBlock()->GetGraph()->GetArena()) LocationSummary(
    212       invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
    213   if (!invoke->InputAt(0)->IsConstant()) {
    214     locations->SetInAt(0, Location::RequiresRegister());
    215   }
    216   locations->AddTemp(first_argument_location);
    217   locations->SetOut(return_location);
    218 }
    219 
    220 IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo() {
    221   // Note that we could cache all of the data looked up here. but there's no good
    222   // location for it. We don't want to add it to WellKnownClasses, to avoid creating global
    223   // jni values. Adding it as state to the compiler singleton seems like wrong
    224   // separation of concerns.
    225   // The need for this data should be pretty rare though.
    226 
    227   // The most common case is that the classes are in the boot image and initialized,
    228   // which is easy to generate code for. We bail if not.
    229   Thread* self = Thread::Current();
    230   ScopedObjectAccess soa(self);
    231   Runtime* runtime = Runtime::Current();
    232   ClassLinker* class_linker = runtime->GetClassLinker();
    233   gc::Heap* heap = runtime->GetHeap();
    234   IntegerValueOfInfo info;
    235   info.integer_cache = class_linker->FindSystemClass(self, "Ljava/lang/Integer$IntegerCache;");
    236   if (info.integer_cache == nullptr) {
    237     self->ClearException();
    238     return info;
    239   }
    240   if (!heap->ObjectIsInBootImageSpace(info.integer_cache) || !info.integer_cache->IsInitialized()) {
    241     // Optimization only works if the class is initialized and in the boot image.
    242     return info;
    243   }
    244   info.integer = class_linker->FindSystemClass(self, "Ljava/lang/Integer;");
    245   if (info.integer == nullptr) {
    246     self->ClearException();
    247     return info;
    248   }
    249   if (!heap->ObjectIsInBootImageSpace(info.integer) || !info.integer->IsInitialized()) {
    250     // Optimization only works if the class is initialized and in the boot image.
    251     return info;
    252   }
    253 
    254   ArtField* field = info.integer_cache->FindDeclaredStaticField("cache", "[Ljava/lang/Integer;");
    255   if (field == nullptr) {
    256     return info;
    257   }
    258   info.cache = static_cast<mirror::ObjectArray<mirror::Object>*>(
    259       field->GetObject(info.integer_cache).Ptr());
    260   if (info.cache == nullptr) {
    261     return info;
    262   }
    263 
    264   if (!heap->ObjectIsInBootImageSpace(info.cache)) {
    265     // Optimization only works if the object is in the boot image.
    266     return info;
    267   }
    268 
    269   field = info.integer->FindDeclaredInstanceField("value", "I");
    270   if (field == nullptr) {
    271     return info;
    272   }
    273   info.value_offset = field->GetOffset().Int32Value();
    274 
    275   field = info.integer_cache->FindDeclaredStaticField("low", "I");
    276   if (field == nullptr) {
    277     return info;
    278   }
    279   info.low = field->GetInt(info.integer_cache);
    280 
    281   field = info.integer_cache->FindDeclaredStaticField("high", "I");
    282   if (field == nullptr) {
    283     return info;
    284   }
    285   info.high = field->GetInt(info.integer_cache);
    286 
    287   DCHECK_EQ(info.cache->GetLength(), info.high - info.low + 1);
    288   return info;
    289 }
    290 
    291 }  // namespace art
    292