diff options
| -rw-r--r-- | compiler/optimizing/inliner.cc | 4 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_mips.cc | 322 | ||||
| -rw-r--r-- | compiler/optimizing/load_store_elimination.cc | 6 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 28 | ||||
| -rw-r--r-- | runtime/class_linker.h | 4 | ||||
| -rw-r--r-- | runtime/debugger.cc | 48 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.cc | 3 | ||||
| -rw-r--r-- | runtime/thread.cc | 1 | ||||
| -rw-r--r-- | test/586-checker-null-array-get/src/Main.java | 31 | ||||
| -rw-r--r-- | test/587-inline-class-error/expected.txt | 0 | ||||
| -rw-r--r-- | test/587-inline-class-error/info.txt | 2 | ||||
| -rw-r--r-- | test/587-inline-class-error/smali/SuperVerifyError.smali | 27 | ||||
| -rw-r--r-- | test/587-inline-class-error/smali/TestCase.smali | 33 | ||||
| -rw-r--r-- | test/587-inline-class-error/smali/VerifyError.smali | 28 | ||||
| -rw-r--r-- | test/587-inline-class-error/src/Main.java | 37 |
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()); + } + } + } +} |