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