intrinsics.cc revision c88ef3a10c474045a3476a02ae75d07ddd3230b7
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_method.h" 20#include "class_linker.h" 21#include "dex/quick/dex_file_method_inliner.h" 22#include "dex/quick/dex_file_to_method_inliner_map.h" 23#include "driver/compiler_driver.h" 24#include "invoke_type.h" 25#include "mirror/dex_cache-inl.h" 26#include "nodes.h" 27#include "quick/inline_method_analyser.h" 28#include "scoped_thread_state_change.h" 29#include "thread-inl.h" 30#include "utils.h" 31 32namespace art { 33 34// Function that returns whether an intrinsic is static/direct or virtual. 35static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) { 36 switch (i) { 37 case Intrinsics::kNone: 38 return kInterface; // Non-sensical for intrinsic. 39#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache) \ 40 case Intrinsics::k ## Name: \ 41 return IsStatic; 42#include "intrinsics_list.h" 43INTRINSICS_LIST(OPTIMIZING_INTRINSICS) 44#undef INTRINSICS_LIST 45#undef OPTIMIZING_INTRINSICS 46 } 47 return kInterface; 48} 49 50// Function that returns whether an intrinsic needs an environment or not. 51static inline IntrinsicNeedsEnvironmentOrCache NeedsEnvironmentOrCache(Intrinsics i) { 52 switch (i) { 53 case Intrinsics::kNone: 54 return kNeedsEnvironmentOrCache; // Non-sensical for intrinsic. 55#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache) \ 56 case Intrinsics::k ## Name: \ 57 return NeedsEnvironmentOrCache; 58#include "intrinsics_list.h" 59INTRINSICS_LIST(OPTIMIZING_INTRINSICS) 60#undef INTRINSICS_LIST 61#undef OPTIMIZING_INTRINSICS 62 } 63 return kNeedsEnvironmentOrCache; 64} 65 66static Primitive::Type GetType(uint64_t data, bool is_op_size) { 67 if (is_op_size) { 68 switch (static_cast<OpSize>(data)) { 69 case kSignedByte: 70 return Primitive::kPrimByte; 71 case kSignedHalf: 72 return Primitive::kPrimShort; 73 case k32: 74 return Primitive::kPrimInt; 75 case k64: 76 return Primitive::kPrimLong; 77 default: 78 LOG(FATAL) << "Unknown/unsupported op size " << data; 79 UNREACHABLE(); 80 } 81 } else { 82 if ((data & kIntrinsicFlagIsLong) != 0) { 83 return Primitive::kPrimLong; 84 } 85 if ((data & kIntrinsicFlagIsObject) != 0) { 86 return Primitive::kPrimNot; 87 } 88 return Primitive::kPrimInt; 89 } 90} 91 92static Intrinsics GetIntrinsic(InlineMethod method) { 93 switch (method.opcode) { 94 // Floating-point conversions. 95 case kIntrinsicDoubleCvt: 96 return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? 97 Intrinsics::kDoubleDoubleToRawLongBits : Intrinsics::kDoubleLongBitsToDouble; 98 case kIntrinsicFloatCvt: 99 return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? 100 Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat; 101 102 // Bit manipulations. 103 case kIntrinsicReverseBits: 104 switch (GetType(method.d.data, true)) { 105 case Primitive::kPrimInt: 106 return Intrinsics::kIntegerReverse; 107 case Primitive::kPrimLong: 108 return Intrinsics::kLongReverse; 109 default: 110 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 111 UNREACHABLE(); 112 } 113 case kIntrinsicReverseBytes: 114 switch (GetType(method.d.data, true)) { 115 case Primitive::kPrimShort: 116 return Intrinsics::kShortReverseBytes; 117 case Primitive::kPrimInt: 118 return Intrinsics::kIntegerReverseBytes; 119 case Primitive::kPrimLong: 120 return Intrinsics::kLongReverseBytes; 121 default: 122 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 123 UNREACHABLE(); 124 } 125 case kIntrinsicRotateRight: 126 switch (GetType(method.d.data, true)) { 127 case Primitive::kPrimInt: 128 return Intrinsics::kIntegerRotateRight; 129 case Primitive::kPrimLong: 130 return Intrinsics::kLongRotateRight; 131 default: 132 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 133 UNREACHABLE(); 134 } 135 case kIntrinsicRotateLeft: 136 switch (GetType(method.d.data, true)) { 137 case Primitive::kPrimInt: 138 return Intrinsics::kIntegerRotateLeft; 139 case Primitive::kPrimLong: 140 return Intrinsics::kLongRotateLeft; 141 default: 142 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 143 UNREACHABLE(); 144 } 145 146 // Misc data processing. 147 case kIntrinsicNumberOfLeadingZeros: 148 switch (GetType(method.d.data, true)) { 149 case Primitive::kPrimInt: 150 return Intrinsics::kIntegerNumberOfLeadingZeros; 151 case Primitive::kPrimLong: 152 return Intrinsics::kLongNumberOfLeadingZeros; 153 default: 154 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 155 UNREACHABLE(); 156 } 157 case kIntrinsicNumberOfTrailingZeros: 158 switch (GetType(method.d.data, true)) { 159 case Primitive::kPrimInt: 160 return Intrinsics::kIntegerNumberOfTrailingZeros; 161 case Primitive::kPrimLong: 162 return Intrinsics::kLongNumberOfTrailingZeros; 163 default: 164 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 165 UNREACHABLE(); 166 } 167 168 // Abs. 169 case kIntrinsicAbsDouble: 170 return Intrinsics::kMathAbsDouble; 171 case kIntrinsicAbsFloat: 172 return Intrinsics::kMathAbsFloat; 173 case kIntrinsicAbsInt: 174 return Intrinsics::kMathAbsInt; 175 case kIntrinsicAbsLong: 176 return Intrinsics::kMathAbsLong; 177 178 // Min/max. 179 case kIntrinsicMinMaxDouble: 180 return ((method.d.data & kIntrinsicFlagMin) == 0) ? 181 Intrinsics::kMathMaxDoubleDouble : Intrinsics::kMathMinDoubleDouble; 182 case kIntrinsicMinMaxFloat: 183 return ((method.d.data & kIntrinsicFlagMin) == 0) ? 184 Intrinsics::kMathMaxFloatFloat : Intrinsics::kMathMinFloatFloat; 185 case kIntrinsicMinMaxInt: 186 return ((method.d.data & kIntrinsicFlagMin) == 0) ? 187 Intrinsics::kMathMaxIntInt : Intrinsics::kMathMinIntInt; 188 case kIntrinsicMinMaxLong: 189 return ((method.d.data & kIntrinsicFlagMin) == 0) ? 190 Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong; 191 192 // Misc math. 193 case kIntrinsicSqrt: 194 return Intrinsics::kMathSqrt; 195 case kIntrinsicCeil: 196 return Intrinsics::kMathCeil; 197 case kIntrinsicFloor: 198 return Intrinsics::kMathFloor; 199 case kIntrinsicRint: 200 return Intrinsics::kMathRint; 201 case kIntrinsicRoundDouble: 202 return Intrinsics::kMathRoundDouble; 203 case kIntrinsicRoundFloat: 204 return Intrinsics::kMathRoundFloat; 205 206 // System.arraycopy. 207 case kIntrinsicSystemArrayCopyCharArray: 208 return Intrinsics::kSystemArrayCopyChar; 209 210 case kIntrinsicSystemArrayCopy: 211 return Intrinsics::kSystemArrayCopy; 212 213 // Thread.currentThread. 214 case kIntrinsicCurrentThread: 215 return Intrinsics::kThreadCurrentThread; 216 217 // Memory.peek. 218 case kIntrinsicPeek: 219 switch (GetType(method.d.data, true)) { 220 case Primitive::kPrimByte: 221 return Intrinsics::kMemoryPeekByte; 222 case Primitive::kPrimShort: 223 return Intrinsics::kMemoryPeekShortNative; 224 case Primitive::kPrimInt: 225 return Intrinsics::kMemoryPeekIntNative; 226 case Primitive::kPrimLong: 227 return Intrinsics::kMemoryPeekLongNative; 228 default: 229 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 230 UNREACHABLE(); 231 } 232 233 // Memory.poke. 234 case kIntrinsicPoke: 235 switch (GetType(method.d.data, true)) { 236 case Primitive::kPrimByte: 237 return Intrinsics::kMemoryPokeByte; 238 case Primitive::kPrimShort: 239 return Intrinsics::kMemoryPokeShortNative; 240 case Primitive::kPrimInt: 241 return Intrinsics::kMemoryPokeIntNative; 242 case Primitive::kPrimLong: 243 return Intrinsics::kMemoryPokeLongNative; 244 default: 245 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 246 UNREACHABLE(); 247 } 248 249 // String. 250 case kIntrinsicCharAt: 251 return Intrinsics::kStringCharAt; 252 case kIntrinsicCompareTo: 253 return Intrinsics::kStringCompareTo; 254 case kIntrinsicEquals: 255 return Intrinsics::kStringEquals; 256 case kIntrinsicGetCharsNoCheck: 257 return Intrinsics::kStringGetCharsNoCheck; 258 case kIntrinsicIsEmptyOrLength: 259 // The inliner can handle these two cases - and this is the preferred approach 260 // since after inlining the call is no longer visible (as opposed to waiting 261 // until codegen to handle intrinsic). 262 return Intrinsics::kNone; 263 case kIntrinsicIndexOf: 264 return ((method.d.data & kIntrinsicFlagBase0) == 0) ? 265 Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf; 266 case kIntrinsicNewStringFromBytes: 267 return Intrinsics::kStringNewStringFromBytes; 268 case kIntrinsicNewStringFromChars: 269 return Intrinsics::kStringNewStringFromChars; 270 case kIntrinsicNewStringFromString: 271 return Intrinsics::kStringNewStringFromString; 272 273 case kIntrinsicCas: 274 switch (GetType(method.d.data, false)) { 275 case Primitive::kPrimNot: 276 return Intrinsics::kUnsafeCASObject; 277 case Primitive::kPrimInt: 278 return Intrinsics::kUnsafeCASInt; 279 case Primitive::kPrimLong: 280 return Intrinsics::kUnsafeCASLong; 281 default: 282 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 283 UNREACHABLE(); 284 } 285 case kIntrinsicUnsafeGet: { 286 const bool is_volatile = (method.d.data & kIntrinsicFlagIsVolatile); 287 switch (GetType(method.d.data, false)) { 288 case Primitive::kPrimInt: 289 return is_volatile ? Intrinsics::kUnsafeGetVolatile : Intrinsics::kUnsafeGet; 290 case Primitive::kPrimLong: 291 return is_volatile ? Intrinsics::kUnsafeGetLongVolatile : Intrinsics::kUnsafeGetLong; 292 case Primitive::kPrimNot: 293 return is_volatile ? Intrinsics::kUnsafeGetObjectVolatile : Intrinsics::kUnsafeGetObject; 294 default: 295 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 296 UNREACHABLE(); 297 } 298 } 299 case kIntrinsicUnsafePut: { 300 enum Sync { kNoSync, kVolatile, kOrdered }; 301 const Sync sync = 302 ((method.d.data & kIntrinsicFlagIsVolatile) != 0) ? kVolatile : 303 ((method.d.data & kIntrinsicFlagIsOrdered) != 0) ? kOrdered : 304 kNoSync; 305 switch (GetType(method.d.data, false)) { 306 case Primitive::kPrimInt: 307 switch (sync) { 308 case kNoSync: 309 return Intrinsics::kUnsafePut; 310 case kVolatile: 311 return Intrinsics::kUnsafePutVolatile; 312 case kOrdered: 313 return Intrinsics::kUnsafePutOrdered; 314 } 315 break; 316 case Primitive::kPrimLong: 317 switch (sync) { 318 case kNoSync: 319 return Intrinsics::kUnsafePutLong; 320 case kVolatile: 321 return Intrinsics::kUnsafePutLongVolatile; 322 case kOrdered: 323 return Intrinsics::kUnsafePutLongOrdered; 324 } 325 break; 326 case Primitive::kPrimNot: 327 switch (sync) { 328 case kNoSync: 329 return Intrinsics::kUnsafePutObject; 330 case kVolatile: 331 return Intrinsics::kUnsafePutObjectVolatile; 332 case kOrdered: 333 return Intrinsics::kUnsafePutObjectOrdered; 334 } 335 break; 336 default: 337 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 338 UNREACHABLE(); 339 } 340 break; 341 } 342 343 // Virtual cases. 344 345 case kIntrinsicReferenceGetReferent: 346 return Intrinsics::kReferenceGetReferent; 347 348 // Quick inliner cases. Remove after refactoring. They are here so that we can use the 349 // compiler to warn on missing cases. 350 351 case kInlineOpNop: 352 case kInlineOpReturnArg: 353 case kInlineOpNonWideConst: 354 case kInlineOpIGet: 355 case kInlineOpIPut: 356 return Intrinsics::kNone; 357 358 // String init cases, not intrinsics. 359 360 case kInlineStringInit: 361 return Intrinsics::kNone; 362 363 // No default case to make the compiler warn on missing cases. 364 } 365 return Intrinsics::kNone; 366} 367 368static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke, const DexFile& dex_file) { 369 // The DexFileMethodInliner should have checked whether the methods are agreeing with 370 // what we expect, i.e., static methods are called as such. Add another check here for 371 // our expectations: 372 // 373 // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual. 374 // 375 // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization 376 // failure occured. We might be in a situation where we have inlined a method that calls an 377 // intrinsic, but that method is in a different dex file on which we do not have a 378 // verified_method that would have helped the compiler driver sharpen the call. In that case, 379 // make sure that the intrinsic is actually for some final method (or in a final class), as 380 // otherwise the intrinsics setup is broken. 381 // 382 // For the last direction, we have intrinsics for virtual functions that will perform a check 383 // inline. If the precise type is known, however, the instruction will be sharpened to an 384 // InvokeStaticOrDirect. 385 InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic); 386 InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ? 387 invoke->AsInvokeStaticOrDirect()->GetInvokeType() : 388 invoke->IsInvokeVirtual() ? kVirtual : kSuper; 389 switch (intrinsic_type) { 390 case kStatic: 391 return (invoke_type == kStatic); 392 393 case kDirect: 394 if (invoke_type == kDirect) { 395 return true; 396 } 397 if (invoke_type == kVirtual) { 398 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); 399 ScopedObjectAccess soa(Thread::Current()); 400 ArtMethod* art_method = 401 class_linker->FindDexCache(soa.Self(), dex_file)->GetResolvedMethod( 402 invoke->GetDexMethodIndex(), class_linker->GetImagePointerSize()); 403 return art_method != nullptr && 404 (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal()); 405 } 406 return false; 407 408 case kVirtual: 409 // Call might be devirtualized. 410 return (invoke_type == kVirtual || invoke_type == kDirect); 411 412 default: 413 return false; 414 } 415} 416 417// TODO: Refactor DexFileMethodInliner and have something nicer than InlineMethod. 418void IntrinsicsRecognizer::Run() { 419 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { 420 HBasicBlock* block = it.Current(); 421 for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done(); 422 inst_it.Advance()) { 423 HInstruction* inst = inst_it.Current(); 424 if (inst->IsInvoke()) { 425 HInvoke* invoke = inst->AsInvoke(); 426 InlineMethod method; 427 const DexFile& dex_file = invoke->GetDexFile(); 428 DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(&dex_file); 429 DCHECK(inliner != nullptr); 430 if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) { 431 Intrinsics intrinsic = GetIntrinsic(method); 432 433 if (intrinsic != Intrinsics::kNone) { 434 if (!CheckInvokeType(intrinsic, invoke, dex_file)) { 435 LOG(WARNING) << "Found an intrinsic with unexpected invoke type: " 436 << intrinsic << " for " 437 << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile()) 438 << invoke->DebugName(); 439 } else { 440 invoke->SetIntrinsic(intrinsic, NeedsEnvironmentOrCache(intrinsic)); 441 } 442 } 443 } 444 } 445 } 446 } 447} 448 449std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) { 450 switch (intrinsic) { 451 case Intrinsics::kNone: 452 os << "None"; 453 break; 454#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache) \ 455 case Intrinsics::k ## Name: \ 456 os << # Name; \ 457 break; 458#include "intrinsics_list.h" 459INTRINSICS_LIST(OPTIMIZING_INTRINSICS) 460#undef STATIC_INTRINSICS_LIST 461#undef VIRTUAL_INTRINSICS_LIST 462#undef OPTIMIZING_INTRINSICS 463 } 464 return os; 465} 466 467} // namespace art 468