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