Simplify our intrinsic recognizer.
- Use the modifiers for storing the intrinsic kind.
- Delete dex_file_method_inliner and its associated map.
This work was also motivated by the fact that the inline
method analyzer leaks intrinsic tables, and even worse, might re-use
a table from one dex file to another unrelated dex file in the presence
of class unloading and the unlikely event of the dex files getting
the same address.
test: m test-art-host m test-art-target
Change-Id: Ia653d2c72df13889dc85dd8c84997582c034ea4b
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 2131279..412ccfc 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -18,13 +18,10 @@
#include "art_method.h"
#include "class_linker.h"
-#include "dex/quick/dex_file_method_inliner.h"
-#include "dex/quick/dex_file_to_method_inliner_map.h"
#include "driver/compiler_driver.h"
#include "invoke_type.h"
#include "mirror/dex_cache-inl.h"
#include "nodes.h"
-#include "quick/inline_method_analyser.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
#include "utils.h"
@@ -36,7 +33,7 @@
switch (i) {
case Intrinsics::kNone:
return kInterface; // Non-sensical for intrinsic.
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
case Intrinsics::k ## Name: \
return IsStatic;
#include "intrinsics_list.h"
@@ -52,7 +49,7 @@
switch (i) {
case Intrinsics::kNone:
return kNeedsEnvironmentOrCache; // Non-sensical for intrinsic.
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
case Intrinsics::k ## Name: \
return NeedsEnvironmentOrCache;
#include "intrinsics_list.h"
@@ -68,7 +65,7 @@
switch (i) {
case Intrinsics::kNone:
return kAllSideEffects;
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
case Intrinsics::k ## Name: \
return SideEffects;
#include "intrinsics_list.h"
@@ -84,7 +81,7 @@
switch (i) {
case Intrinsics::kNone:
return kCanThrow;
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
case Intrinsics::k ## Name: \
return Exceptions;
#include "intrinsics_list.h"
@@ -95,430 +92,7 @@
return kCanThrow;
}
-static Primitive::Type GetType(uint64_t data, bool is_op_size) {
- if (is_op_size) {
- switch (static_cast<OpSize>(data)) {
- case kSignedByte:
- return Primitive::kPrimByte;
- case kSignedHalf:
- return Primitive::kPrimShort;
- case k32:
- return Primitive::kPrimInt;
- case k64:
- return Primitive::kPrimLong;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << data;
- UNREACHABLE();
- }
- } else {
- if ((data & kIntrinsicFlagIsLong) != 0) {
- return Primitive::kPrimLong;
- }
- if ((data & kIntrinsicFlagIsObject) != 0) {
- return Primitive::kPrimNot;
- }
- return Primitive::kPrimInt;
- }
-}
-
-static Intrinsics GetIntrinsic(InlineMethod method) {
- switch (method.opcode) {
- // Floating-point conversions.
- case kIntrinsicDoubleCvt:
- return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
- Intrinsics::kDoubleDoubleToRawLongBits : Intrinsics::kDoubleLongBitsToDouble;
- case kIntrinsicFloatCvt:
- return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
- Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat;
- case kIntrinsicFloat2Int:
- return Intrinsics::kFloatFloatToIntBits;
- case kIntrinsicDouble2Long:
- return Intrinsics::kDoubleDoubleToLongBits;
-
- // Floating-point tests.
- case kIntrinsicFloatIsInfinite:
- return Intrinsics::kFloatIsInfinite;
- case kIntrinsicDoubleIsInfinite:
- return Intrinsics::kDoubleIsInfinite;
- case kIntrinsicFloatIsNaN:
- return Intrinsics::kFloatIsNaN;
- case kIntrinsicDoubleIsNaN:
- return Intrinsics::kDoubleIsNaN;
-
- // Bit manipulations.
- case kIntrinsicReverseBits:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimInt:
- return Intrinsics::kIntegerReverse;
- case Primitive::kPrimLong:
- return Intrinsics::kLongReverse;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- case kIntrinsicReverseBytes:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimShort:
- return Intrinsics::kShortReverseBytes;
- case Primitive::kPrimInt:
- return Intrinsics::kIntegerReverseBytes;
- case Primitive::kPrimLong:
- return Intrinsics::kLongReverseBytes;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- case kIntrinsicRotateRight:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimInt:
- return Intrinsics::kIntegerRotateRight;
- case Primitive::kPrimLong:
- return Intrinsics::kLongRotateRight;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- case kIntrinsicRotateLeft:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimInt:
- return Intrinsics::kIntegerRotateLeft;
- case Primitive::kPrimLong:
- return Intrinsics::kLongRotateLeft;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
-
- // Misc data processing.
- case kIntrinsicBitCount:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimInt:
- return Intrinsics::kIntegerBitCount;
- case Primitive::kPrimLong:
- return Intrinsics::kLongBitCount;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- case kIntrinsicCompare:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimInt:
- return Intrinsics::kIntegerCompare;
- case Primitive::kPrimLong:
- return Intrinsics::kLongCompare;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- case kIntrinsicHighestOneBit:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimInt:
- return Intrinsics::kIntegerHighestOneBit;
- case Primitive::kPrimLong:
- return Intrinsics::kLongHighestOneBit;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- case kIntrinsicLowestOneBit:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimInt:
- return Intrinsics::kIntegerLowestOneBit;
- case Primitive::kPrimLong:
- return Intrinsics::kLongLowestOneBit;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- case kIntrinsicNumberOfLeadingZeros:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimInt:
- return Intrinsics::kIntegerNumberOfLeadingZeros;
- case Primitive::kPrimLong:
- return Intrinsics::kLongNumberOfLeadingZeros;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- case kIntrinsicNumberOfTrailingZeros:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimInt:
- return Intrinsics::kIntegerNumberOfTrailingZeros;
- case Primitive::kPrimLong:
- return Intrinsics::kLongNumberOfTrailingZeros;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- case kIntrinsicSignum:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimInt:
- return Intrinsics::kIntegerSignum;
- case Primitive::kPrimLong:
- return Intrinsics::kLongSignum;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
-
- // Abs.
- case kIntrinsicAbsDouble:
- return Intrinsics::kMathAbsDouble;
- case kIntrinsicAbsFloat:
- return Intrinsics::kMathAbsFloat;
- case kIntrinsicAbsInt:
- return Intrinsics::kMathAbsInt;
- case kIntrinsicAbsLong:
- return Intrinsics::kMathAbsLong;
-
- // Min/max.
- case kIntrinsicMinMaxDouble:
- return ((method.d.data & kIntrinsicFlagMin) == 0) ?
- Intrinsics::kMathMaxDoubleDouble : Intrinsics::kMathMinDoubleDouble;
- case kIntrinsicMinMaxFloat:
- return ((method.d.data & kIntrinsicFlagMin) == 0) ?
- Intrinsics::kMathMaxFloatFloat : Intrinsics::kMathMinFloatFloat;
- case kIntrinsicMinMaxInt:
- return ((method.d.data & kIntrinsicFlagMin) == 0) ?
- Intrinsics::kMathMaxIntInt : Intrinsics::kMathMinIntInt;
- case kIntrinsicMinMaxLong:
- return ((method.d.data & kIntrinsicFlagMin) == 0) ?
- Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong;
-
- // More math builtins.
- case kIntrinsicCos:
- return Intrinsics::kMathCos;
- case kIntrinsicSin:
- return Intrinsics::kMathSin;
- case kIntrinsicAcos:
- return Intrinsics::kMathAcos;
- case kIntrinsicAsin:
- return Intrinsics::kMathAsin;
- case kIntrinsicAtan:
- return Intrinsics::kMathAtan;
- case kIntrinsicAtan2:
- return Intrinsics::kMathAtan2;
- case kIntrinsicCbrt:
- return Intrinsics::kMathCbrt;
- case kIntrinsicCosh:
- return Intrinsics::kMathCosh;
- case kIntrinsicExp:
- return Intrinsics::kMathExp;
- case kIntrinsicExpm1:
- return Intrinsics::kMathExpm1;
- case kIntrinsicHypot:
- return Intrinsics::kMathHypot;
- case kIntrinsicLog:
- return Intrinsics::kMathLog;
- case kIntrinsicLog10:
- return Intrinsics::kMathLog10;
- case kIntrinsicNextAfter:
- return Intrinsics::kMathNextAfter;
- case kIntrinsicSinh:
- return Intrinsics::kMathSinh;
- case kIntrinsicTan:
- return Intrinsics::kMathTan;
- case kIntrinsicTanh:
- return Intrinsics::kMathTanh;
-
- // Misc math.
- case kIntrinsicSqrt:
- return Intrinsics::kMathSqrt;
- case kIntrinsicCeil:
- return Intrinsics::kMathCeil;
- case kIntrinsicFloor:
- return Intrinsics::kMathFloor;
- case kIntrinsicRint:
- return Intrinsics::kMathRint;
- case kIntrinsicRoundDouble:
- return Intrinsics::kMathRoundDouble;
- case kIntrinsicRoundFloat:
- return Intrinsics::kMathRoundFloat;
-
- // System.arraycopy.
- case kIntrinsicSystemArrayCopyCharArray:
- return Intrinsics::kSystemArrayCopyChar;
-
- case kIntrinsicSystemArrayCopy:
- return Intrinsics::kSystemArrayCopy;
-
- // Thread.currentThread.
- case kIntrinsicCurrentThread:
- return Intrinsics::kThreadCurrentThread;
-
- // Memory.peek.
- case kIntrinsicPeek:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimByte:
- return Intrinsics::kMemoryPeekByte;
- case Primitive::kPrimShort:
- return Intrinsics::kMemoryPeekShortNative;
- case Primitive::kPrimInt:
- return Intrinsics::kMemoryPeekIntNative;
- case Primitive::kPrimLong:
- return Intrinsics::kMemoryPeekLongNative;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
-
- // Memory.poke.
- case kIntrinsicPoke:
- switch (GetType(method.d.data, true)) {
- case Primitive::kPrimByte:
- return Intrinsics::kMemoryPokeByte;
- case Primitive::kPrimShort:
- return Intrinsics::kMemoryPokeShortNative;
- case Primitive::kPrimInt:
- return Intrinsics::kMemoryPokeIntNative;
- case Primitive::kPrimLong:
- return Intrinsics::kMemoryPokeLongNative;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
-
- // String.
- case kIntrinsicCharAt:
- return Intrinsics::kStringCharAt;
- case kIntrinsicCompareTo:
- return Intrinsics::kStringCompareTo;
- case kIntrinsicEquals:
- return Intrinsics::kStringEquals;
- case kIntrinsicGetCharsNoCheck:
- return Intrinsics::kStringGetCharsNoCheck;
- case kIntrinsicIsEmptyOrLength:
- return ((method.d.data & kIntrinsicFlagIsEmpty) == 0) ?
- Intrinsics::kStringLength : Intrinsics::kStringIsEmpty;
- case kIntrinsicIndexOf:
- return ((method.d.data & kIntrinsicFlagBase0) == 0) ?
- Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf;
- case kIntrinsicNewStringFromBytes:
- return Intrinsics::kStringNewStringFromBytes;
- case kIntrinsicNewStringFromChars:
- return Intrinsics::kStringNewStringFromChars;
- case kIntrinsicNewStringFromString:
- return Intrinsics::kStringNewStringFromString;
-
- case kIntrinsicCas:
- switch (GetType(method.d.data, false)) {
- case Primitive::kPrimNot:
- return Intrinsics::kUnsafeCASObject;
- case Primitive::kPrimInt:
- return Intrinsics::kUnsafeCASInt;
- case Primitive::kPrimLong:
- return Intrinsics::kUnsafeCASLong;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- case kIntrinsicUnsafeGet: {
- const bool is_volatile = (method.d.data & kIntrinsicFlagIsVolatile);
- switch (GetType(method.d.data, false)) {
- case Primitive::kPrimInt:
- return is_volatile ? Intrinsics::kUnsafeGetVolatile : Intrinsics::kUnsafeGet;
- case Primitive::kPrimLong:
- return is_volatile ? Intrinsics::kUnsafeGetLongVolatile : Intrinsics::kUnsafeGetLong;
- case Primitive::kPrimNot:
- return is_volatile ? Intrinsics::kUnsafeGetObjectVolatile : Intrinsics::kUnsafeGetObject;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- }
- case kIntrinsicUnsafePut: {
- enum Sync { kNoSync, kVolatile, kOrdered };
- const Sync sync =
- ((method.d.data & kIntrinsicFlagIsVolatile) != 0) ? kVolatile :
- ((method.d.data & kIntrinsicFlagIsOrdered) != 0) ? kOrdered :
- kNoSync;
- switch (GetType(method.d.data, false)) {
- case Primitive::kPrimInt:
- switch (sync) {
- case kNoSync:
- return Intrinsics::kUnsafePut;
- case kVolatile:
- return Intrinsics::kUnsafePutVolatile;
- case kOrdered:
- return Intrinsics::kUnsafePutOrdered;
- }
- break;
- case Primitive::kPrimLong:
- switch (sync) {
- case kNoSync:
- return Intrinsics::kUnsafePutLong;
- case kVolatile:
- return Intrinsics::kUnsafePutLongVolatile;
- case kOrdered:
- return Intrinsics::kUnsafePutLongOrdered;
- }
- break;
- case Primitive::kPrimNot:
- switch (sync) {
- case kNoSync:
- return Intrinsics::kUnsafePutObject;
- case kVolatile:
- return Intrinsics::kUnsafePutObjectVolatile;
- case kOrdered:
- return Intrinsics::kUnsafePutObjectOrdered;
- }
- break;
- default:
- LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
- UNREACHABLE();
- }
- break;
- }
-
- // 1.8.
- case kIntrinsicUnsafeGetAndAddInt:
- return Intrinsics::kUnsafeGetAndAddInt;
- case kIntrinsicUnsafeGetAndAddLong:
- return Intrinsics::kUnsafeGetAndAddLong;
- case kIntrinsicUnsafeGetAndSetInt:
- return Intrinsics::kUnsafeGetAndSetInt;
- case kIntrinsicUnsafeGetAndSetLong:
- return Intrinsics::kUnsafeGetAndSetLong;
- case kIntrinsicUnsafeGetAndSetObject:
- return Intrinsics::kUnsafeGetAndSetObject;
- case kIntrinsicUnsafeLoadFence:
- return Intrinsics::kUnsafeLoadFence;
- case kIntrinsicUnsafeStoreFence:
- return Intrinsics::kUnsafeStoreFence;
- case kIntrinsicUnsafeFullFence:
- return Intrinsics::kUnsafeFullFence;
-
- // Virtual cases.
-
- case kIntrinsicReferenceGetReferent:
- return Intrinsics::kReferenceGetReferent;
-
- // Quick inliner cases. Remove after refactoring. They are here so that we can use the
- // compiler to warn on missing cases.
-
- case kInlineOpNop:
- case kInlineOpReturnArg:
- case kInlineOpNonWideConst:
- case kInlineOpIGet:
- case kInlineOpIPut:
- case kInlineOpConstructor:
- return Intrinsics::kNone;
-
- // String init cases, not intrinsics.
-
- case kInlineStringInit:
- return Intrinsics::kNone;
-
- // No default case to make the compiler warn on missing cases.
- }
- return Intrinsics::kNone;
-}
-
-static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke, const DexFile& dex_file) {
- // The DexFileMethodInliner should have checked whether the methods are agreeing with
- // what we expect, i.e., static methods are called as such. Add another check here for
- // our expectations:
- //
+static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) {
// Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual.
//
// Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization
@@ -542,13 +116,9 @@
return true;
}
if (invoke_type == kVirtual) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ArtMethod* art_method = invoke->GetResolvedMethod();
ScopedObjectAccess soa(Thread::Current());
- ArtMethod* art_method =
- class_linker->FindDexCache(soa.Self(), dex_file)->GetResolvedMethod(
- invoke->GetDexMethodIndex(), class_linker->GetImagePointerSize());
- return art_method != nullptr &&
- (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
+ return (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal());
}
return false;
@@ -561,8 +131,8 @@
}
}
-// TODO: Refactor DexFileMethodInliner and have something nicer than InlineMethod.
void IntrinsicsRecognizer::Run() {
+ ScopedObjectAccess soa(Thread::Current());
for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
@@ -570,26 +140,20 @@
HInstruction* inst = inst_it.Current();
if (inst->IsInvoke()) {
HInvoke* invoke = inst->AsInvoke();
- InlineMethod method;
- const DexFile& dex_file = invoke->GetDexFile();
- DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(&dex_file);
- DCHECK(inliner != nullptr);
- if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) {
- Intrinsics intrinsic = GetIntrinsic(method);
-
- if (intrinsic != Intrinsics::kNone) {
- if (!CheckInvokeType(intrinsic, invoke, dex_file)) {
- LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
- << intrinsic << " for "
- << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile())
- << invoke->DebugName();
- } else {
- invoke->SetIntrinsic(intrinsic,
- NeedsEnvironmentOrCache(intrinsic),
- GetSideEffects(intrinsic),
- GetExceptions(intrinsic));
- MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
- }
+ ArtMethod* art_method = invoke->GetResolvedMethod();
+ if (art_method != nullptr && art_method->IsIntrinsic()) {
+ Intrinsics intrinsic = static_cast<Intrinsics>(art_method->GetIntrinsic());
+ if (!CheckInvokeType(intrinsic, invoke)) {
+ LOG(WARNING) << "Found an intrinsic with unexpected invoke type: "
+ << intrinsic << " for "
+ << PrettyMethod(invoke->GetDexMethodIndex(), invoke->GetDexFile())
+ << invoke->DebugName();
+ } else {
+ invoke->SetIntrinsic(intrinsic,
+ NeedsEnvironmentOrCache(intrinsic),
+ GetSideEffects(intrinsic),
+ GetExceptions(intrinsic));
+ MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
}
}
}
@@ -602,7 +166,7 @@
case Intrinsics::kNone:
os << "None";
break;
-#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions) \
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
case Intrinsics::k ## Name: \
os << # Name; \
break;