summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/inliner.cc4
-rw-r--r--compiler/optimizing/intrinsics_mips.cc322
-rw-r--r--compiler/optimizing/load_store_elimination.cc6
-rw-r--r--runtime/class_linker.cc28
-rw-r--r--runtime/class_linker.h4
-rw-r--r--runtime/debugger.cc48
-rw-r--r--runtime/jit/jit_code_cache.cc3
-rw-r--r--runtime/thread.cc1
-rw-r--r--test/586-checker-null-array-get/src/Main.java31
-rw-r--r--test/587-inline-class-error/expected.txt0
-rw-r--r--test/587-inline-class-error/info.txt2
-rw-r--r--test/587-inline-class-error/smali/SuperVerifyError.smali27
-rw-r--r--test/587-inline-class-error/smali/TestCase.smali33
-rw-r--r--test/587-inline-class-error/smali/VerifyError.smali28
-rw-r--r--test/587-inline-class-error/src/Main.java37
15 files changed, 482 insertions, 92 deletions
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 573b58340c..440a2821c1 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -144,6 +144,10 @@ static ArtMethod* FindVirtualOrInterfaceTarget(HInvoke* invoke, ArtMethod* resol
} else if (!resolved_method->GetDeclaringClass()->IsAssignableFrom(info.GetTypeHandle().Get())) {
// The method that we're trying to call is not in the receiver's class or super classes.
return nullptr;
+ } else if (info.GetTypeHandle()->IsErroneous()) {
+ // If the type is erroneous, do not go further, as we are going to query the vtable or
+ // imt table, that we can only safely do on non-erroneous classes.
+ return nullptr;
}
ClassLinker* cl = Runtime::Current()->GetClassLinker();
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index c306cf93a1..76e6bbd880 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1475,6 +1475,311 @@ void IntrinsicCodeGeneratorMIPS::VisitThreadCurrentThread(HInvoke* invoke) {
Thread::PeerOffset<kMipsPointerSize>().Int32Value());
}
+static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ bool can_call =
+ invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
+ invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile;
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ can_call ?
+ LocationSummary::kCallOnSlowPath :
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void GenUnsafeGet(HInvoke* invoke,
+ Primitive::Type type,
+ bool is_volatile,
+ bool is_R6,
+ CodeGeneratorMIPS* codegen) {
+ LocationSummary* locations = invoke->GetLocations();
+ DCHECK((type == Primitive::kPrimInt) ||
+ (type == Primitive::kPrimLong) ||
+ (type == Primitive::kPrimNot)) << type;
+ MipsAssembler* assembler = codegen->GetAssembler();
+ // Object pointer.
+ Register base = locations->InAt(1).AsRegister<Register>();
+ // The "offset" argument is passed as a "long". Since this code is for
+ // a 32-bit processor, we can only use 32-bit addresses, so we only
+ // need the low 32-bits of offset.
+ Register offset_lo = invoke->GetLocations()->InAt(2).AsRegisterPairLow<Register>();
+
+ __ Addu(TMP, base, offset_lo);
+ if (is_volatile) {
+ __ Sync(0);
+ }
+ if (type == Primitive::kPrimLong) {
+ Register trg_lo = locations->Out().AsRegisterPairLow<Register>();
+ Register trg_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+ if (is_R6) {
+ __ Lw(trg_lo, TMP, 0);
+ __ Lw(trg_hi, TMP, 4);
+ } else {
+ __ Lwr(trg_lo, TMP, 0);
+ __ Lwl(trg_lo, TMP, 3);
+ __ Lwr(trg_hi, TMP, 4);
+ __ Lwl(trg_hi, TMP, 7);
+ }
+ } else {
+ Register trg = locations->Out().AsRegister<Register>();
+
+ if (is_R6) {
+ __ Lw(trg, TMP, 0);
+ } else {
+ __ Lwr(trg, TMP, 0);
+ __ Lwl(trg, TMP, 3);
+ }
+ }
+}
+
+// int sun.misc.Unsafe.getInt(Object o, long offset)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafeGet(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafeGet(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, IsR6(), codegen_);
+}
+
+// int sun.misc.Unsafe.getIntVolatile(Object o, long offset)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, IsR6(), codegen_);
+}
+
+// long sun.misc.Unsafe.getLong(Object o, long offset)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetLong(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetLong(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, IsR6(), codegen_);
+}
+
+// long sun.misc.Unsafe.getLongVolatile(Object o, long offset)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, IsR6(), codegen_);
+}
+
+// Object sun.misc.Unsafe.getObject(Object o, long offset)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetObject(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetObject(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, IsR6(), codegen_);
+}
+
+// Object sun.misc.Unsafe.getObjectVolatile(Object o, long offset)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+ CreateIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
+ GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, IsR6(), codegen_);
+}
+
+static void CreateIntIntIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+}
+
+static void GenUnsafePut(LocationSummary* locations,
+ Primitive::Type type,
+ bool is_volatile,
+ bool is_ordered,
+ bool is_R6,
+ CodeGeneratorMIPS* codegen) {
+ DCHECK((type == Primitive::kPrimInt) ||
+ (type == Primitive::kPrimLong) ||
+ (type == Primitive::kPrimNot)) << type;
+ MipsAssembler* assembler = codegen->GetAssembler();
+ // Object pointer.
+ Register base = locations->InAt(1).AsRegister<Register>();
+ // The "offset" argument is passed as a "long", i.e., it's 64-bits in
+ // size. Since this code is for a 32-bit processor, we can only use
+ // 32-bit addresses, so we only need the low 32-bits of offset.
+ Register offset_lo = locations->InAt(2).AsRegisterPairLow<Register>();
+
+ __ Addu(TMP, base, offset_lo);
+ if (is_volatile || is_ordered) {
+ __ Sync(0);
+ }
+ if ((type == Primitive::kPrimInt) || (type == Primitive::kPrimNot)) {
+ Register value = locations->InAt(3).AsRegister<Register>();
+
+ if (is_R6) {
+ __ Sw(value, TMP, 0);
+ } else {
+ __ Swr(value, TMP, 0);
+ __ Swl(value, TMP, 3);
+ }
+ } else {
+ Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>();
+ Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>();
+
+ if (is_R6) {
+ __ Sw(value_lo, TMP, 0);
+ __ Sw(value_hi, TMP, 4);
+ } else {
+ __ Swr(value_lo, TMP, 0);
+ __ Swl(value_lo, TMP, 3);
+ __ Swr(value_hi, TMP, 4);
+ __ Swl(value_hi, TMP, 7);
+ }
+ }
+
+ if (is_volatile) {
+ __ Sync(0);
+ }
+
+ if (type == Primitive::kPrimNot) {
+ codegen->MarkGCCard(base, locations->InAt(3).AsRegister<Register>());
+ }
+}
+
+// void sun.misc.Unsafe.putInt(Object o, long offset, int x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafePut(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafePut(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(),
+ Primitive::kPrimInt,
+ /* is_volatile */ false,
+ /* is_ordered */ false,
+ IsR6(),
+ codegen_);
+}
+
+// void sun.misc.Unsafe.putOrderedInt(Object o, long offset, int x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafePutOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafePutOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(),
+ Primitive::kPrimInt,
+ /* is_volatile */ false,
+ /* is_ordered */ true,
+ IsR6(),
+ codegen_);
+}
+
+// void sun.misc.Unsafe.putIntVolatile(Object o, long offset, int x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafePutVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafePutVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(),
+ Primitive::kPrimInt,
+ /* is_volatile */ true,
+ /* is_ordered */ false,
+ IsR6(),
+ codegen_);
+}
+
+// void sun.misc.Unsafe.putObject(Object o, long offset, Object x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObject(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObject(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(),
+ Primitive::kPrimNot,
+ /* is_volatile */ false,
+ /* is_ordered */ false,
+ IsR6(),
+ codegen_);
+}
+
+// void sun.misc.Unsafe.putOrderedObject(Object o, long offset, Object x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObjectOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(),
+ Primitive::kPrimNot,
+ /* is_volatile */ false,
+ /* is_ordered */ true,
+ IsR6(),
+ codegen_);
+}
+
+// void sun.misc.Unsafe.putObjectVolatile(Object o, long offset, Object x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObjectVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(),
+ Primitive::kPrimNot,
+ /* is_volatile */ true,
+ /* is_ordered */ false,
+ IsR6(),
+ codegen_);
+}
+
+// void sun.misc.Unsafe.putLong(Object o, long offset, long x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLong(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLong(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(),
+ Primitive::kPrimLong,
+ /* is_volatile */ false,
+ /* is_ordered */ false,
+ IsR6(),
+ codegen_);
+}
+
+// void sun.misc.Unsafe.putOrderedLong(Object o, long offset, long x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLongOrdered(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(),
+ Primitive::kPrimLong,
+ /* is_volatile */ false,
+ /* is_ordered */ true,
+ IsR6(),
+ codegen_);
+}
+
+// void sun.misc.Unsafe.putLongVolatile(Object o, long offset, long x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+ CreateIntIntIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLongVolatile(HInvoke* invoke) {
+ GenUnsafePut(invoke->GetLocations(),
+ Primitive::kPrimLong,
+ /* is_volatile */ true,
+ /* is_ordered */ false,
+ IsR6(),
+ codegen_);
+}
+
// char java.lang.String.charAt(int index)
void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
@@ -1482,7 +1787,7 @@ void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) {
kIntrinsified);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
- // The inputs will be considered live at the last instruction and restored. This will overwrite
+ // The inputs will be considered live at the last instruction and restored. This would overwrite
// the output with kNoOutputOverlap.
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
}
@@ -2042,21 +2347,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS, MathFloor)
UNIMPLEMENTED_INTRINSIC(MIPS, MathRint)
UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundDouble)
UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundFloat)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGet)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetVolatile)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLong)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLongVolatile)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetObject)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetObjectVolatile)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePut)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutOrdered)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutVolatile)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObject)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObjectOrdered)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObjectVolatile)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLong)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongOrdered)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongVolatile)
UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASInt)
UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong)
UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASObject)
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 561dcfbb1f..9601b066e5 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -738,10 +738,12 @@ class LSEVisitor : public HGraphVisitor {
DCHECK(instruction->IsArrayGet()) << instruction->DebugName();
HInstruction* array = instruction->AsArrayGet()->GetArray();
DCHECK(array->IsNullCheck()) << array->DebugName();
- DCHECK(array->InputAt(0)->IsNullConstant()) << array->InputAt(0)->DebugName();
+ HInstruction* input = HuntForOriginalReference(array->InputAt(0));
+ DCHECK(input->IsNullConstant()) << input->DebugName();
array = heap_value->AsArrayGet()->GetArray();
DCHECK(array->IsNullCheck()) << array->DebugName();
- DCHECK(array->InputAt(0)->IsNullConstant()) << array->InputAt(0)->DebugName();
+ input = HuntForOriginalReference(array->InputAt(0));
+ DCHECK(input->IsNullConstant()) << input->DebugName();
}
return;
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index e2ef7ac671..2b43dfb598 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -7557,34 +7557,6 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) {
return descriptor;
}
-bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) {
- Runtime* const runtime = Runtime::Current();
- if (runtime->UseJit()) {
- // JIT can have direct code pointers from any method to any other method.
- return true;
- }
- // Non-image methods don't use direct code pointer.
- if (!m->GetDeclaringClass()->IsBootStrapClassLoaded()) {
- return false;
- }
- if (m->IsPrivate()) {
- // The method can only be called inside its own oat file. Therefore it won't be called using
- // its direct code if the oat file has been compiled in PIC mode.
- const DexFile& dex_file = m->GetDeclaringClass()->GetDexFile();
- const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
- if (oat_dex_file == nullptr) {
- // No oat file: the method has not been compiled.
- return false;
- }
- const OatFile* oat_file = oat_dex_file->GetOatFile();
- return oat_file != nullptr && !oat_file->IsPic();
- } else {
- // The method can be called outside its own oat file. Therefore it won't be called using its
- // direct code pointer only if all loaded oat files have been compiled in PIC mode.
- return runtime->GetOatFileManager().HaveNonPicOatFile();
- }
-}
-
jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) {
// SOAAlreadyRunnable is protected, and we need something to add a global reference.
// We could move the jobject to the callers, but all call-sites do this...
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 36ed8204a6..c368a3adb3 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -549,10 +549,6 @@ class ClassLinker {
REQUIRES(!Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
- // Returns true if the method can be called with its direct code pointer, false otherwise.
- bool MayBeCalledWithDirectCodePointer(ArtMethod* m)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
-
// Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files.
// Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
jobject CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files)
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index c375bba4c3..109e03d24f 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -44,7 +44,6 @@
#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
#include "mirror/throwable.h"
-#include "quick/inline_method_analyser.h"
#include "reflection.h"
#include "safe_map.h"
#include "scoped_thread_state_change.h"
@@ -53,7 +52,6 @@
#include "handle_scope-inl.h"
#include "thread_list.h"
#include "utf.h"
-#include "verifier/method_verifier-inl.h"
#include "well_known_classes.h"
namespace art {
@@ -3239,27 +3237,6 @@ void Dbg::ManageDeoptimization() {
CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
}
-static bool IsMethodPossiblyInlined(Thread* self, ArtMethod* m)
- SHARED_REQUIRES(Locks::mutator_lock_) {
- const DexFile::CodeItem* code_item = m->GetCodeItem();
- if (code_item == nullptr) {
- // TODO We should not be asked to watch location in a native or abstract method so the code item
- // should never be null. We could just check we never encounter this case.
- return false;
- }
- // Note: method verifier may cause thread suspension.
- self->AssertThreadSuspensionIsAllowable();
- StackHandleScope<2> hs(self);
- mirror::Class* declaring_class = m->GetDeclaringClass();
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
- verifier::MethodVerifier verifier(self, dex_cache->GetDexFile(), dex_cache, class_loader,
- &m->GetClassDef(), code_item, m->GetDexMethodIndex(), m,
- m->GetAccessFlags(), false, true, false, true);
- // Note: we don't need to verify the method.
- return InlineMethodAnalyser::AnalyseMethodCode(&verifier, nullptr);
-}
-
static const Breakpoint* FindFirstBreakpointForMethod(ArtMethod* m)
SHARED_REQUIRES(Locks::mutator_lock_, Locks::breakpoint_lock_) {
for (Breakpoint& breakpoint : gBreakpoints) {
@@ -3322,33 +3299,22 @@ static DeoptimizationRequest::Kind GetRequiredDeoptimizationKind(Thread* self,
}
if (first_breakpoint == nullptr) {
- // There is no breakpoint on this method yet: we need to deoptimize. If this method may be
- // inlined or default, we deoptimize everything; otherwise we deoptimize only this method. We
+ // There is no breakpoint on this method yet: we need to deoptimize. If this method is default,
+ // we deoptimize everything; otherwise we deoptimize only this method. We
// deoptimize with defaults because we do not know everywhere they are used. It is possible some
- // of the copies could be inlined or otherwise missed.
+ // of the copies could be missed.
// TODO Deoptimizing on default methods might not be necessary in all cases.
- // Note: IsMethodPossiblyInlined goes into the method verifier and may cause thread suspension.
- // Therefore we must not hold any lock when we call it.
- bool need_full_deoptimization = m->IsDefault() || IsMethodPossiblyInlined(self, m);
+ bool need_full_deoptimization = m->IsDefault();
if (need_full_deoptimization) {
- VLOG(jdwp) << "Need full deoptimization because of possible inlining or copying of method "
+ VLOG(jdwp) << "Need full deoptimization because of copying of method "
<< PrettyMethod(m);
return DeoptimizationRequest::kFullDeoptimization;
} else {
// We don't need to deoptimize if the method has not been compiled.
const bool is_compiled = m->HasAnyCompiledCode();
if (is_compiled) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- // If the method may be called through its direct code pointer (without loading
- // its updated entrypoint), we need full deoptimization to not miss the breakpoint.
- if (class_linker->MayBeCalledWithDirectCodePointer(m)) {
- VLOG(jdwp) << "Need full deoptimization because of possible direct code call "
- << "into image for compiled method " << PrettyMethod(m);
- return DeoptimizationRequest::kFullDeoptimization;
- } else {
- VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m);
- return DeoptimizationRequest::kSelectiveDeoptimization;
- }
+ VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m);
+ return DeoptimizationRequest::kSelectiveDeoptimization;
} else {
// Method is not compiled: we don't need to deoptimize.
VLOG(jdwp) << "No need for deoptimization for non-compiled method " << PrettyMethod(m);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index c681ed77f2..344fcb98ee 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -366,7 +366,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
if (osr) {
number_of_osr_compilations_++;
osr_code_map_.Put(method, code_ptr);
- } else {
+ } else if (!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
+ // TODO(ngeoffray): Clean up instrumentation and code cache interactions.
Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(
method, method_header->GetEntryPoint());
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 217644417c..2a7cd0787d 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -561,6 +561,7 @@ void Thread::InstallImplicitProtection() {
// Read every page from the high address to the low.
volatile uint8_t dont_optimize_this;
+ UNUSED(dont_optimize_this);
for (uint8_t* p = stack_top; p >= pregion; p -= kPageSize) {
dont_optimize_this = *p;
}
diff --git a/test/586-checker-null-array-get/src/Main.java b/test/586-checker-null-array-get/src/Main.java
index 4b03ff28eb..332cfb0a53 100644
--- a/test/586-checker-null-array-get/src/Main.java
+++ b/test/586-checker-null-array-get/src/Main.java
@@ -17,6 +17,7 @@
public class Main {
public static Object[] getObjectArray() { return null; }
public static long[] getLongArray() { return null; }
+ public static Object getNull() { return null; }
public static void main(String[] args) {
try {
@@ -37,6 +38,36 @@ public class Main {
objectField = getObjectArray()[0];
}
+ /// CHECK-START: void Main.bar() load_store_elimination (after)
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: <<BoundType:l\d+>> BoundType [<<Null>>]
+ /// CHECK-DAG: <<CheckL:l\d+>> NullCheck [<<BoundType>>]
+ /// CHECK-DAG: <<GetL0:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+ /// CHECK-DAG: <<GetL1:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+ /// CHECK-DAG: <<GetL2:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+ /// CHECK-DAG: <<GetL3:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+ /// CHECK-DAG: <<CheckJ:l\d+>> NullCheck [<<Null>>]
+ /// CHECK-DAG: <<GetJ0:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+ /// CHECK-DAG: <<GetJ1:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+ /// CHECK-DAG: <<GetJ2:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+ /// CHECK-DAG: <<GetJ3:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+ public static void bar() {
+ // We create multiple accesses that will lead the bounds check
+ // elimination pass to add a HDeoptimize. Not having the bounds check helped
+ // the load store elimination think it could merge two ArrayGet with different
+ // types.
+ String[] array = ((String[])getNull());
+ objectField = array[0];
+ objectField = array[1];
+ objectField = array[2];
+ objectField = array[3];
+ long[] longArray = getLongArray();
+ longField = longArray[0];
+ longField = longArray[1];
+ longField = longArray[2];
+ longField = longArray[3];
+ }
+
public static long longField;
public static Object objectField;
}
diff --git a/test/587-inline-class-error/expected.txt b/test/587-inline-class-error/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/587-inline-class-error/expected.txt
diff --git a/test/587-inline-class-error/info.txt b/test/587-inline-class-error/info.txt
new file mode 100644
index 0000000000..7f244f673c
--- /dev/null
+++ b/test/587-inline-class-error/info.txt
@@ -0,0 +1,2 @@
+Regression test for the inliner that used to crash while
+trying to find a method for an erroneous class.
diff --git a/test/587-inline-class-error/smali/SuperVerifyError.smali b/test/587-inline-class-error/smali/SuperVerifyError.smali
new file mode 100644
index 0000000000..b63cba0a3b
--- /dev/null
+++ b/test/587-inline-class-error/smali/SuperVerifyError.smali
@@ -0,0 +1,27 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LSuperVerifyError;
+
+.super Ljava/lang/Object;
+
+.method public final foo()V
+ .registers 1
+ return-void
+.end method
+
+.method public bar()V
+ .registers 1
+ return-void
+.end method
diff --git a/test/587-inline-class-error/smali/TestCase.smali b/test/587-inline-class-error/smali/TestCase.smali
new file mode 100644
index 0000000000..7c991ed003
--- /dev/null
+++ b/test/587-inline-class-error/smali/TestCase.smali
@@ -0,0 +1,33 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LTestCase;
+
+.super Ljava/lang/Object;
+
+.method public static topLevel()V
+ .registers 2
+ const v0, 0x1
+ new-array v0, v0, [LVerifyError;
+ invoke-static {v0}, LTestCase;->test([LVerifyError;)V
+ return-void
+.end method
+
+.method public static test([LVerifyError;)V
+ .registers 2
+ const v0, 0x0
+ aget-object v1, v1, v0
+ invoke-virtual {v1}, LSuperVerifyError;->bar()V
+ return-void
+.end method
diff --git a/test/587-inline-class-error/smali/VerifyError.smali b/test/587-inline-class-error/smali/VerifyError.smali
new file mode 100644
index 0000000000..b821b7132e
--- /dev/null
+++ b/test/587-inline-class-error/smali/VerifyError.smali
@@ -0,0 +1,28 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public final LVerifyError;
+
+.super LSuperVerifyError;
+
+# Override a final method to put this class in the error state.
+.method public foo()V
+ .registers 1
+ return-void
+.end method
+
+# Having a static field in the class is needed to get the
+# right initialization for the embedded vtable length of a
+# class.
+.field public static i:I
diff --git a/test/587-inline-class-error/src/Main.java b/test/587-inline-class-error/src/Main.java
new file mode 100644
index 0000000000..3402fabb2c
--- /dev/null
+++ b/test/587-inline-class-error/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try {
+ Class<?> v = Class.forName("VerifyError");
+ throw new Error("Expected LinkageError");
+ } catch (LinkageError e) {
+ // expected
+ }
+
+ try {
+ Class.forName("TestCase").getMethod("topLevel").invoke(null);
+ throw new Error("Expected InvocationTargetException");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof NullPointerException)) {
+ throw new Error("Expected NullPointerException, got " + e.getCause());
+ }
+ }
+ }
+}