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