diff options
| -rw-r--r-- | compiler/jit/jit_compiler.cc | 14 | ||||
| -rw-r--r-- | compiler/optimizing/inliner.cc | 218 | ||||
| -rw-r--r-- | compiler/optimizing/inliner.h | 28 | ||||
| -rw-r--r-- | compiler/optimizing/licm.cc | 2 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 14 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 5 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler_stats.h | 8 | ||||
| -rw-r--r-- | runtime/art_method.cc | 6 | ||||
| -rw-r--r-- | runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc | 2 | ||||
| -rw-r--r-- | runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 9 | ||||
| -rw-r--r-- | runtime/instrumentation.cc | 4 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter.cc | 22 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter.h | 3 | ||||
| -rw-r--r-- | runtime/jit/jit.cc | 15 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.cc | 43 | ||||
| -rw-r--r-- | runtime/jit/jit_code_cache.h | 8 | ||||
| -rw-r--r-- | runtime/jit/profiling_info.cc | 15 | ||||
| -rw-r--r-- | runtime/jit/profiling_info.h | 91 | ||||
| -rw-r--r-- | runtime/quick_exception_handler.cc | 17 | ||||
| -rw-r--r-- | runtime/thread.cc | 26 | ||||
| -rw-r--r-- | runtime/thread.h | 8 |
21 files changed, 439 insertions, 119 deletions
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 2125c9a26a..d001495442 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -170,18 +170,6 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method) { self->AssertNoPendingException(); Runtime* runtime = Runtime::Current(); - // Check if the method is already compiled. - if (runtime->GetJit()->GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) { - VLOG(jit) << "Already compiled " << PrettyMethod(method); - return true; - } - - // Don't compile the method if we are supposed to be deoptimized. - instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); - if (instrumentation->AreAllMethodsDeoptimized() || instrumentation->IsDeoptimized(method)) { - return false; - } - // Ensure the class is initialized. Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass())); if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) { @@ -190,13 +178,13 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method) { } // Do the compilation. - JitCodeCache* const code_cache = runtime->GetJit()->GetCodeCache(); bool success = false; { TimingLogger::ScopedTiming t2("Compiling", &logger); // If we get a request to compile a proxy method, we pass the actual Java method // of that proxy method, as the compiler does not expect a proxy method. ArtMethod* method_to_compile = method->GetInterfaceMethodIfProxy(sizeof(void*)); + JitCodeCache* const code_cache = runtime->GetJit()->GetCodeCache(); success = compiler_driver_->GetCompiler()->JitCompile(self, code_cache, method_to_compile); } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 6d93be37a7..a4dcb3aeba 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -171,13 +171,37 @@ static uint32_t FindMethodIndexIn(ArtMethod* method, const DexFile& dex_file, uint32_t referrer_index) SHARED_REQUIRES(Locks::mutator_lock_) { - if (method->GetDexFile()->GetLocation().compare(dex_file.GetLocation()) == 0) { + if (IsSameDexFile(*method->GetDexFile(), dex_file)) { return method->GetDexMethodIndex(); } else { return method->FindDexMethodIndexInOtherDexFile(dex_file, referrer_index); } } +static uint32_t FindClassIndexIn(mirror::Class* cls, const DexFile& dex_file) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (cls->GetDexCache() == nullptr) { + DCHECK(cls->IsArrayClass()); + // TODO: find the class in `dex_file`. + return DexFile::kDexNoIndex; + } else if (cls->GetDexTypeIndex() == DexFile::kDexNoIndex16) { + // TODO: deal with proxy classes. + return DexFile::kDexNoIndex; + } else if (IsSameDexFile(cls->GetDexFile(), dex_file)) { + // Update the dex cache to ensure the class is in. The generated code will + // consider it is. We make it safe by updating the dex cache, as other + // dex files might also load the class, and there is no guarantee the dex + // cache of the dex file of the class will be updated. + if (cls->GetDexCache()->GetResolvedType(cls->GetDexTypeIndex()) == nullptr) { + cls->GetDexCache()->SetResolvedType(cls->GetDexTypeIndex(), cls); + } + return cls->GetDexTypeIndex(); + } else { + // TODO: find the class in `dex_file`. + return DexFile::kDexNoIndex; + } +} + bool HInliner::TryInline(HInvoke* invoke_instruction) { if (invoke_instruction->IsInvokeUnresolved()) { return false; // Don't bother to move further if we know the method is unresolved. @@ -214,53 +238,176 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { return false; } - if (!invoke_instruction->IsInvokeStaticOrDirect()) { - resolved_method = FindVirtualOrInterfaceTarget(invoke_instruction, resolved_method); - if (resolved_method == nullptr) { + if (invoke_instruction->IsInvokeStaticOrDirect()) { + return TryInline(invoke_instruction, resolved_method); + } + + // Check if we can statically find the method. + ArtMethod* actual_method = FindVirtualOrInterfaceTarget(invoke_instruction, resolved_method); + if (actual_method != nullptr) { + return TryInline(invoke_instruction, actual_method); + } + + // Check if we can use an inline cache. + ArtMethod* caller = graph_->GetArtMethod(); + size_t pointer_size = class_linker->GetImagePointerSize(); + // Under JIT, we should always know the caller. + DCHECK(!Runtime::Current()->UseJit() || (caller != nullptr)); + if (caller != nullptr && caller->GetProfilingInfo(pointer_size) != nullptr) { + ProfilingInfo* profiling_info = caller->GetProfilingInfo(pointer_size); + const InlineCache& ic = *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()); + if (ic.IsUnitialized()) { VLOG(compiler) << "Interface or virtual call to " << PrettyMethod(method_index, caller_dex_file) - << " could not be statically determined"; + << " is not hit and not inlined"; return false; - } - // We have found a method, but we need to find where that method is for the caller's - // dex file. - method_index = FindMethodIndexIn(resolved_method, caller_dex_file, method_index); - if (method_index == DexFile::kDexNoIndex) { + } else if (ic.IsMonomorphic()) { + MaybeRecordStat(kMonomorphicCall); + return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic); + } else if (ic.IsPolymorphic()) { + MaybeRecordStat(kPolymorphicCall); + return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic); + } else { + DCHECK(ic.IsMegamorphic()); VLOG(compiler) << "Interface or virtual call to " - << PrettyMethod(resolved_method) - << " cannot be inlined because unaccessible to caller"; + << PrettyMethod(method_index, caller_dex_file) + << " is megamorphic and not inlined"; + MaybeRecordStat(kMegamorphicCall); return false; } } - bool same_dex_file = - IsSameDexFile(*outer_compilation_unit_.GetDexFile(), *resolved_method->GetDexFile()); + VLOG(compiler) << "Interface or virtual call to " + << PrettyMethod(method_index, caller_dex_file) + << " could not be statically determined"; + return false; +} - const DexFile::CodeItem* code_item = resolved_method->GetCodeItem(); +bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, + ArtMethod* resolved_method, + const InlineCache& ic) { + const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); + uint32_t class_index = FindClassIndexIn(ic.GetMonomorphicType(), caller_dex_file); + if (class_index == DexFile::kDexNoIndex) { + VLOG(compiler) << "Call to " << PrettyMethod(resolved_method) + << " from inline cache is not inlined because its class is not" + << " accessible to the caller"; + return false; + } + + ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); + size_t pointer_size = class_linker->GetImagePointerSize(); + if (invoke_instruction->IsInvokeInterface()) { + resolved_method = ic.GetMonomorphicType()->FindVirtualMethodForInterface( + resolved_method, pointer_size); + } else { + DCHECK(invoke_instruction->IsInvokeVirtual()); + resolved_method = ic.GetMonomorphicType()->FindVirtualMethodForVirtual( + resolved_method, pointer_size); + } + DCHECK(resolved_method != nullptr); + HInstruction* receiver = invoke_instruction->InputAt(0); + HInstruction* cursor = invoke_instruction->GetPrevious(); + HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); + + if (!TryInline(invoke_instruction, resolved_method, /* do_rtp */ false)) { + return false; + } + + // We successfully inlined, now add a guard. + ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0); + DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_"); + HInstanceFieldGet* field_get = new (graph_->GetArena()) HInstanceFieldGet( + receiver, + Primitive::kPrimNot, + field->GetOffset(), + field->IsVolatile(), + field->GetDexFieldIndex(), + field->GetDeclaringClass()->GetDexClassDefIndex(), + *field->GetDexFile(), + handles_->NewHandle(field->GetDexCache()), + invoke_instruction->GetDexPc()); + + bool is_referrer = + (ic.GetMonomorphicType() == outermost_graph_->GetArtMethod()->GetDeclaringClass()); + HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(), + class_index, + caller_dex_file, + is_referrer, + invoke_instruction->GetDexPc(), + /* needs_access_check */ false, + /* is_in_dex_cache */ true); + + HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, field_get); + HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize( + compare, invoke_instruction->GetDexPc()); + // TODO: Extend reference type propagation to understand the guard. + if (cursor != nullptr) { + bb_cursor->InsertInstructionAfter(load_class, cursor); + } else { + bb_cursor->InsertInstructionBefore(load_class, bb_cursor->GetFirstInstruction()); + } + bb_cursor->InsertInstructionAfter(field_get, load_class); + bb_cursor->InsertInstructionAfter(compare, field_get); + bb_cursor->InsertInstructionAfter(deoptimize, compare); + deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment()); + + // Run type propagation to get the guard typed, and eventually propagate the + // type of the receiver. + ReferenceTypePropagation rtp_fixup(graph_, handles_); + rtp_fixup.Run(); + + MaybeRecordStat(kInlinedMonomorphicCall); + return true; +} + +bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction ATTRIBUTE_UNUSED, + ArtMethod* resolved_method, + const InlineCache& ic ATTRIBUTE_UNUSED) { + // TODO + VLOG(compiler) << "Unimplemented polymorphic inlining for " + << PrettyMethod(resolved_method); + return false; +} + +bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) { + const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); + uint32_t method_index = FindMethodIndexIn( + method, caller_dex_file, invoke_instruction->GetDexMethodIndex()); + if (method_index == DexFile::kDexNoIndex) { + VLOG(compiler) << "Call to " + << PrettyMethod(method) + << " cannot be inlined because unaccessible to caller"; + return false; + } + + bool same_dex_file = IsSameDexFile(*outer_compilation_unit_.GetDexFile(), *method->GetDexFile()); + + const DexFile::CodeItem* code_item = method->GetCodeItem(); if (code_item == nullptr) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) + VLOG(compiler) << "Method " << PrettyMethod(method) << " is not inlined because it is native"; return false; } size_t inline_max_code_units = compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits(); if (code_item->insns_size_in_code_units_ > inline_max_code_units) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) + VLOG(compiler) << "Method " << PrettyMethod(method) << " is too big to inline"; return false; } if (code_item->tries_size_ != 0) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) + VLOG(compiler) << "Method " << PrettyMethod(method) << " is not inlined because of try block"; return false; } - if (!resolved_method->GetDeclaringClass()->IsVerified()) { - uint16_t class_def_idx = resolved_method->GetDeclaringClass()->GetDexClassDefIndex(); + if (!method->GetDeclaringClass()->IsVerified()) { + uint16_t class_def_idx = method->GetDeclaringClass()->GetDexClassDefIndex(); if (!compiler_driver_->IsMethodVerifiedWithoutFailures( - resolved_method->GetDexMethodIndex(), class_def_idx, *resolved_method->GetDexFile())) { + method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) { VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) << " couldn't be verified, so it cannot be inlined"; return false; @@ -277,7 +424,7 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { return false; } - if (!TryBuildAndInline(resolved_method, invoke_instruction, same_dex_file)) { + if (!TryBuildAndInline(method, invoke_instruction, same_dex_file, do_rtp)) { return false; } @@ -288,7 +435,8 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, HInvoke* invoke_instruction, - bool same_dex_file) { + bool same_dex_file, + bool do_rtp) { ScopedObjectAccess soa(Thread::Current()); const DexFile::CodeItem* code_item = resolved_method->GetCodeItem(); const DexFile& callee_dex_file = *resolved_method->GetDexFile(); @@ -341,6 +489,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, invoke_type, graph_->IsDebuggable(), graph_->GetCurrentInstructionId()); + callee_graph->SetArtMethod(resolved_method); OptimizingCompilerStats inline_stats; HGraphBuilder builder(callee_graph, @@ -422,6 +571,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, size_t number_of_instructions_budget = kMaximumNumberOfHInstructions; if (depth_ + 1 < compiler_driver_->GetCompilerOptions().GetInlineDepthLimit()) { HInliner inliner(callee_graph, + outermost_graph_, codegen_, outer_compilation_unit_, dex_compilation_unit, @@ -533,9 +683,9 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, HNullConstant* null_constant = graph_->GetNullConstant(); if (!null_constant->GetReferenceTypeInfo().IsValid()) { ReferenceTypeInfo::TypeHandle obj_handle = - handles_->NewHandle(class_linker->GetClassRoot(ClassLinker::kJavaLangObject)); + handles_->NewHandle(class_linker->GetClassRoot(ClassLinker::kJavaLangObject)); null_constant->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(obj_handle, false /* is_exact */)); + ReferenceTypeInfo::Create(obj_handle, false /* is_exact */)); } // Check the integrity of reference types and run another type propagation if needed. @@ -554,14 +704,16 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */)); } - // If the return type is a refinement of the declared type run the type propagation again. - ReferenceTypeInfo return_rti = return_replacement->GetReferenceTypeInfo(); - ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo(); - if (invoke_rti.IsStrictSupertypeOf(return_rti) - || (return_rti.IsExact() && !invoke_rti.IsExact()) - || !return_replacement->CanBeNull()) { - ReferenceTypePropagation rtp_fixup(graph_, handles_); - rtp_fixup.Run(); + if (do_rtp) { + // If the return type is a refinement of the declared type run the type propagation again. + ReferenceTypeInfo return_rti = return_replacement->GetReferenceTypeInfo(); + ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo(); + if (invoke_rti.IsStrictSupertypeOf(return_rti) + || (return_rti.IsExact() && !invoke_rti.IsExact()) + || !return_replacement->CanBeNull()) { + ReferenceTypePropagation rtp_fixup(graph_, handles_); + rtp_fixup.Run(); + } } } diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 0f6a9453be..7b9fb73ccf 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -27,11 +27,13 @@ class CompilerDriver; class DexCompilationUnit; class HGraph; class HInvoke; +class InlineCache; class OptimizingCompilerStats; class HInliner : public HOptimization { public: HInliner(HGraph* outer_graph, + HGraph* outermost_graph, CodeGenerator* codegen, const DexCompilationUnit& outer_compilation_unit, const DexCompilationUnit& caller_compilation_unit, @@ -40,6 +42,7 @@ class HInliner : public HOptimization { OptimizingCompilerStats* stats, size_t depth = 0) : HOptimization(outer_graph, kInlinerPassName, stats), + outermost_graph_(outermost_graph), outer_compilation_unit_(outer_compilation_unit), caller_compilation_unit_(caller_compilation_unit), codegen_(codegen), @@ -54,10 +57,33 @@ class HInliner : public HOptimization { private: bool TryInline(HInvoke* invoke_instruction); + + // Try to inline `resolved_method` in place of `invoke_instruction`. `do_rtp` is whether + // reference type propagation can run after the inlining. + bool TryInline(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp = true) + SHARED_REQUIRES(Locks::mutator_lock_); + + // Try to inline the target of a monomorphic call. If successful, the code + // in the graph will look like: + // if (receiver.getClass() != ic.GetMonomorphicType()) deopt + // ... // inlined code + bool TryInlineMonomorphicCall(HInvoke* invoke_instruction, + ArtMethod* resolved_method, + const InlineCache& ic) + SHARED_REQUIRES(Locks::mutator_lock_); + + // Try to inline targets of a polymorphic call. Currently unimplemented. + bool TryInlinePolymorphicCall(HInvoke* invoke_instruction, + ArtMethod* resolved_method, + const InlineCache& ic) + SHARED_REQUIRES(Locks::mutator_lock_); + bool TryBuildAndInline(ArtMethod* resolved_method, HInvoke* invoke_instruction, - bool same_dex_file); + bool same_dex_file, + bool do_rtp = true); + HGraph* const outermost_graph_; const DexCompilationUnit& outer_compilation_unit_; const DexCompilationUnit& caller_compilation_unit_; CodeGenerator* const codegen_; diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index c38bbe3477..02befc011a 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -121,6 +121,8 @@ void LICM::Run() { // phi in it. if (instruction->NeedsEnvironment()) { UpdateLoopPhisIn(instruction->GetEnvironment(), loop_info); + } else { + DCHECK(!instruction->HasEnvironment()); } instruction->MoveBefore(pre_header->GetLastInstruction()); } else if (instruction->CanThrow()) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 19614f11c6..9d3c88c79e 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -371,6 +371,9 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { bool HasTryCatch() const { return has_try_catch_; } void SetHasTryCatch(bool value) { has_try_catch_ = value; } + ArtMethod* GetArtMethod() const { return art_method_; } + void SetArtMethod(ArtMethod* method) { art_method_ = method; } + // Returns an instruction with the opposite boolean value from 'cond'. // The instruction has been inserted into the graph, either as a constant, or // before cursor. @@ -479,6 +482,11 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { HCurrentMethod* cached_current_method_; + // The ArtMethod this graph is for. Note that for AOT, it may be null, + // for example for methods whose declaring class could not be resolved + // (such as when the superclass could not be found). + ArtMethod* art_method_; + friend class SsaBuilder; // For caching constants. friend class SsaLivenessAnalysis; // For the linear order. ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1); @@ -2462,11 +2470,15 @@ class HTryBoundary : public HTemplateInstruction<0> { // Deoptimize to interpreter, upon checking a condition. class HDeoptimize : public HTemplateInstruction<1> { public: - explicit HDeoptimize(HInstruction* cond, uint32_t dex_pc) + HDeoptimize(HInstruction* cond, uint32_t dex_pc) : HTemplateInstruction(SideEffects::None(), dex_pc) { SetRawInputAt(0, cond); } + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + return true; + } bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index cae2d3f01b..34956039db 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -427,7 +427,7 @@ static void MaybeRunInliner(HGraph* graph, return; } HInliner* inliner = new (graph->GetArena()) HInliner( - graph, codegen, dex_compilation_unit, dex_compilation_unit, driver, handles, stats); + graph, graph, codegen, dex_compilation_unit, dex_compilation_unit, driver, handles, stats); HOptimization* optimizations[] = { inliner }; RunOptimizations(optimizations, arraysize(optimizations), pass_observer); @@ -763,8 +763,8 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, ArtMethod* art_method = compiler_driver->ResolveMethod( soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type); // We may not get a method, for example if its class is erroneous. - // TODO: Clean this up, the compiler driver should just pass the ArtMethod to compile. if (art_method != nullptr) { + graph->SetArtMethod(art_method); interpreter_metadata = art_method->GetQuickenedInfo(); } } @@ -948,6 +948,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, if (stack_map_data == nullptr) { return false; } + MaybeRecordStat(MethodCompilationStat::kCompiled); codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size)); const void* code = code_cache->CommitCode( self, diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index e5ea0f576b..6296eedfb0 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -49,6 +49,10 @@ enum MethodCompilationStat { kNotCompiledUnsupportedIsa, kNotCompiledVerificationError, kNotCompiledVerifyAtRuntime, + kInlinedMonomorphicCall, + kMonomorphicCall, + kPolymorphicCall, + kMegamorphicCall, kLastStat }; @@ -111,6 +115,10 @@ class OptimizingCompilerStats { case kNotCompiledUnsupportedIsa : name = "NotCompiledUnsupportedIsa"; break; case kNotCompiledVerificationError : name = "NotCompiledVerificationError"; break; case kNotCompiledVerifyAtRuntime : name = "NotCompiledVerifyAtRuntime"; break; + case kInlinedMonomorphicCall: name = "InlinedMonomorphicCall"; break; + case kMonomorphicCall: name = "MonomorphicCall"; break; + case kPolymorphicCall: name = "PolymorphicCall"; break; + case kMegamorphicCall: name = "kMegamorphicCall"; break; case kLastStat: LOG(FATAL) << "invalid stat " diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 47f2569b82..238d9f3fe2 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -298,7 +298,9 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* ShadowFrame* shadow_frame = self->PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame); mirror::Throwable* pending_exception = nullptr; - self->PopDeoptimizationContext(result, &pending_exception); + bool from_code = false; + self->PopDeoptimizationContext(result, &pending_exception, &from_code); + CHECK(!from_code); self->SetTopOfStack(nullptr); self->SetTopOfShadowStack(shadow_frame); @@ -307,7 +309,7 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* if (pending_exception != nullptr) { self->SetException(pending_exception); } - interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, result); + interpreter::EnterInterpreterFromDeoptimize(self, shadow_frame, from_code, result); } if (kLogInvocationStartAndReturn) { LOG(INFO) << StringPrintf("Returned '%s' quick code=%p", PrettyMethod(this).c_str(), diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc index dfd9fcddb8..c019cae722 100644 --- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc @@ -52,7 +52,7 @@ extern "C" NO_RETURN void artDeoptimizeFromCompiledCode(Thread* self) // Before deoptimizing to interpreter, we must push the deoptimization context. JValue return_value; return_value.SetJ(0); // we never deoptimize from compiled code with an invoke result. - self->PushDeoptimizationContext(return_value, false, self->GetException()); + self->PushDeoptimizationContext(return_value, false, /* from_code */ true, self->GetException()); QuickExceptionHandler exception_handler(self, true); exception_handler.DeoptimizeSingleFrame(); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index c41ee45e67..2c8ed88f69 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -685,7 +685,9 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, } mirror::Throwable* pending_exception = nullptr; - self->PopDeoptimizationContext(&result, &pending_exception); + bool from_code = false; + self->PopDeoptimizationContext(&result, &pending_exception, /* out */ &from_code); + CHECK(from_code); // Push a transition back into managed code onto the linked list in thread. self->PushManagedStackFragment(&fragment); @@ -712,7 +714,7 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, if (pending_exception != nullptr) { self->SetException(pending_exception); } - interpreter::EnterInterpreterFromDeoptimize(self, deopt_frame, &result); + interpreter::EnterInterpreterFromDeoptimize(self, deopt_frame, from_code, &result); } else { const char* old_cause = self->StartAssertNoThreadSuspension( "Building interpreter shadow frame"); @@ -754,7 +756,8 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, if (UNLIKELY(Dbg::IsForcedInterpreterNeededForUpcall(self, caller))) { // Push the context of the deoptimization stack so we can restore the return value and the // exception before executing the deoptimized frames. - self->PushDeoptimizationContext(result, shorty[0] == 'L', self->GetException()); + self->PushDeoptimizationContext( + result, shorty[0] == 'L', /* from_code */ false, self->GetException()); // Set special exception to cause deoptimization. self->SetException(Thread::GetDeoptimizationException()); diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index bc2c197d33..264cd2c785 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -1062,7 +1062,9 @@ TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self, uintpt PrettyMethod(method).c_str(), return_value.GetJ()) << *self; } - self->PushDeoptimizationContext(return_value, return_shorty == 'L', + self->PushDeoptimizationContext(return_value, + return_shorty == 'L', + false /* from_code */, nullptr /* no pending exception */); return GetTwoWordSuccessValue(*return_pc, reinterpret_cast<uintptr_t>(GetQuickDeoptimizationEntryPoint())); diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index d686f749f3..871fad7b80 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -397,7 +397,10 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive self->PopShadowFrame(); } -void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, JValue* ret_val) +void EnterInterpreterFromDeoptimize(Thread* self, + ShadowFrame* shadow_frame, + bool from_code, + JValue* ret_val) SHARED_REQUIRES(Locks::mutator_lock_) { JValue value; // Set value to last known result in case the shadow frame chain is empty. @@ -408,7 +411,7 @@ void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, JVa self->SetTopOfShadowStack(shadow_frame); const DexFile::CodeItem* code_item = shadow_frame->GetMethod()->GetCodeItem(); const uint32_t dex_pc = shadow_frame->GetDexPC(); - uint32_t new_dex_pc; + uint32_t new_dex_pc = dex_pc; if (UNLIKELY(self->IsExceptionPending())) { // If we deoptimize from the QuickExceptionHandler, we already reported the exception to // the instrumentation. To prevent from reporting it a second time, we simply pass a @@ -419,11 +422,16 @@ void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, JVa instrumentation); new_dex_pc = found_dex_pc; // the dex pc of a matching catch handler // or DexFile::kDexNoIndex if there is none. - } else { - const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]); - // For an invoke, use the dex pc of the next instruction. + } else if (!from_code) { + // For the debugger and full deoptimization stack, we must go past the invoke + // instruction, as it already executed. // TODO: should be tested more once b/17586779 is fixed. - new_dex_pc = dex_pc + (instr->IsInvoke() ? instr->SizeInCodeUnits() : 0); + const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]); + DCHECK(instr->IsInvoke()); + new_dex_pc = dex_pc + instr->SizeInCodeUnits(); + } else { + // Nothing to do, the dex_pc is the one at which the code requested + // the deoptimization. } if (new_dex_pc != DexFile::kDexNoIndex) { shadow_frame->SetDexPC(new_dex_pc); @@ -432,6 +440,8 @@ void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, JVa ShadowFrame* old_frame = shadow_frame; shadow_frame = shadow_frame->GetLink(); ShadowFrame::DeleteDeoptimizedFrame(old_frame); + // Following deoptimizations of shadow frames must pass the invoke instruction. + from_code = false; first = false; } ret_val->SetJ(value.GetJ()); diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h index b21ea84d8e..8e7f3da1ba 100644 --- a/runtime/interpreter/interpreter.h +++ b/runtime/interpreter/interpreter.h @@ -37,7 +37,8 @@ extern void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, mirror::Object* receiver, uint32_t* args, JValue* result) SHARED_REQUIRES(Locks::mutator_lock_); -extern void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, +// 'from_code' denotes whether the deoptimization was explicitly triggered by compiled code. +extern void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, bool from_code, JValue* ret_val) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 27a0e2d1af..92aa86ee53 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -142,11 +142,24 @@ bool Jit::LoadCompiler(std::string* error_msg) { bool Jit::CompileMethod(ArtMethod* method, Thread* self) { DCHECK(!method->IsRuntimeMethod()); + // Don't compile the method if it has breakpoints. if (Dbg::IsDebuggerActive() && Dbg::MethodHasAnyBreakpoints(method)) { VLOG(jit) << "JIT not compiling " << PrettyMethod(method) << " due to breakpoint"; return false; } - return jit_compile_method_(jit_compiler_handle_, method, self); + + // Don't compile the method if we are supposed to be deoptimized. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (instrumentation->AreAllMethodsDeoptimized() || instrumentation->IsDeoptimized(method)) { + return false; + } + + if (!code_cache_->NotifyCompilationOf(method, self)) { + return false; + } + bool success = jit_compile_method_(jit_compiler_handle_, method, self); + code_cache_->DoneCompiling(method, self); + return success; } void Jit::CreateThreadPool() { diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 804d69fbf8..3342e92a79 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -536,7 +536,9 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { instrumentation->UpdateMethodsCode(it.second, GetQuickToInterpreterBridge()); } for (ProfilingInfo* info : profiling_infos_) { - info->GetMethod()->SetProfilingInfo(nullptr); + if (!info->IsMethodBeingCompiled()) { + info->GetMethod()->SetProfilingInfo(nullptr); + } } } @@ -577,12 +579,17 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } } - // Free all profiling info. - for (ProfilingInfo* info : profiling_infos_) { - DCHECK(info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr); - mspace_free(data_mspace_, reinterpret_cast<uint8_t*>(info)); - } - profiling_infos_.clear(); + void* data_mspace = data_mspace_; + // Free all profiling infos of methods that were not being compiled. + auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(), + [data_mspace] (ProfilingInfo* info) { + if (info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { + mspace_free(data_mspace, reinterpret_cast<uint8_t*>(info)); + return true; + } + return false; + }); + profiling_infos_.erase(profiling_kept_end, profiling_infos_.end()); live_bitmap_.reset(nullptr); has_done_one_collection_ = true; @@ -643,7 +650,7 @@ ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self, ArtMethod* method, const std::vector<uint32_t>& entries) { size_t profile_info_size = RoundUp( - sizeof(ProfilingInfo) + sizeof(ProfilingInfo::InlineCache) * entries.size(), + sizeof(ProfilingInfo) + sizeof(InlineCache) * entries.size(), sizeof(void*)); ScopedThreadSuspension sts(self, kSuspended); MutexLock mu(self, lock_); @@ -694,5 +701,25 @@ uint64_t JitCodeCache::GetLastUpdateTimeNs() { MutexLock mu(Thread::Current(), lock_); return last_update_time_ns_; } + +bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self) { + if (ContainsPc(method->GetEntryPointFromQuickCompiledCode())) { + return false; + } + MutexLock mu(self, lock_); + ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); + if (info == nullptr || info->IsMethodBeingCompiled()) { + return false; + } + info->SetIsMethodBeingCompiled(true); + return true; +} + +void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self ATTRIBUTE_UNUSED) { + ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); + DCHECK(info->IsMethodBeingCompiled()); + info->SetIsMethodBeingCompiled(false); +} + } // namespace jit } // namespace art diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index acd7c62940..4032c7b832 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -66,6 +66,14 @@ class JitCodeCache { // of methods that got JIT compiled, as we might have collected some. size_t NumberOfCompiledCode() REQUIRES(!lock_); + bool NotifyCompilationOf(ArtMethod* method, Thread* self) + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!lock_); + + void DoneCompiling(ArtMethod* method, Thread* self) + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!lock_); + // Allocate and write code and its metadata to the code cache. uint8_t* CommitCode(Thread* self, ArtMethod* method, diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc index 2e52b1b4dc..dcb346cd3a 100644 --- a/runtime/jit/profiling_info.cc +++ b/runtime/jit/profiling_info.cc @@ -54,28 +54,29 @@ bool ProfilingInfo::Create(Thread* self, ArtMethod* method, bool retry_allocatio code_ptr += instruction.SizeInCodeUnits(); } - // If there is no instruction we are interested in, no need to create a `ProfilingInfo` - // object, it will never be filled. - if (entries.empty()) { - return true; - } + // We always create a `ProfilingInfo` object, even if there is no instruction we are + // interested in. The JIT code cache internally uses it. // Allocate the `ProfilingInfo` object int the JIT's data space. jit::JitCodeCache* code_cache = Runtime::Current()->GetJit()->GetCodeCache(); return code_cache->AddProfilingInfo(self, method, entries, retry_allocation) != nullptr; } -void ProfilingInfo::AddInvokeInfo(uint32_t dex_pc, mirror::Class* cls) { +InlineCache* ProfilingInfo::GetInlineCache(uint32_t dex_pc) { InlineCache* cache = nullptr; // TODO: binary search if array is too long. for (size_t i = 0; i < number_of_inline_caches_; ++i) { - if (cache_[i].dex_pc == dex_pc) { + if (cache_[i].dex_pc_ == dex_pc) { cache = &cache_[i]; break; } } DCHECK(cache != nullptr); + return cache; +} +void ProfilingInfo::AddInvokeInfo(uint32_t dex_pc, mirror::Class* cls) { + InlineCache* cache = GetInlineCache(dex_pc); for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) { mirror::Class* existing = cache->classes_[i].Read(); if (existing == cls) { diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index b13a315d64..ddaf02fdf5 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -25,6 +25,7 @@ namespace art { class ArtMethod; +class ProfilingInfo; namespace jit { class JitCodeCache; @@ -34,6 +35,49 @@ namespace mirror { class Class; } +// Structure to store the classes seen at runtime for a specific instruction. +// Once the classes_ array is full, we consider the INVOKE to be megamorphic. +class InlineCache { + public: + bool IsMonomorphic() const { + DCHECK_GE(kIndividualCacheSize, 2); + return !classes_[0].IsNull() && classes_[1].IsNull(); + } + + bool IsMegamorphic() const { + for (size_t i = 0; i < kIndividualCacheSize; ++i) { + if (classes_[i].IsNull()) { + return false; + } + } + return true; + } + + mirror::Class* GetMonomorphicType() const SHARED_REQUIRES(Locks::mutator_lock_) { + // Note that we cannot ensure the inline cache is actually monomorphic + // at this point, as other threads may have updated it. + return classes_[0].Read(); + } + + bool IsUnitialized() const { + return classes_[0].IsNull(); + } + + bool IsPolymorphic() const { + DCHECK_GE(kIndividualCacheSize, 3); + return !classes_[1].IsNull() && classes_[kIndividualCacheSize - 1].IsNull(); + } + + private: + static constexpr uint16_t kIndividualCacheSize = 5; + uint32_t dex_pc_; + GcRoot<mirror::Class> classes_[kIndividualCacheSize]; + + friend class ProfilingInfo; + + DISALLOW_COPY_AND_ASSIGN(InlineCache); +}; + /** * Profiling info for a method, created and filled by the interpreter once the * method is warm, and used by the compiler to drive optimizations. @@ -67,44 +111,24 @@ class ProfilingInfo { return method_; } - private: - // Structure to store the classes seen at runtime for a specific instruction. - // Once the classes_ array is full, we consider the INVOKE to be megamorphic. - struct InlineCache { - bool IsMonomorphic() const { - DCHECK_GE(kIndividualCacheSize, 2); - return !classes_[0].IsNull() && classes_[1].IsNull(); - } - - bool IsMegamorphic() const { - for (size_t i = 0; i < kIndividualCacheSize; ++i) { - if (classes_[i].IsNull()) { - return false; - } - } - return true; - } + InlineCache* GetInlineCache(uint32_t dex_pc); - bool IsUnitialized() const { - return classes_[0].IsNull(); - } - - bool IsPolymorphic() const { - DCHECK_GE(kIndividualCacheSize, 3); - return !classes_[1].IsNull() && classes_[kIndividualCacheSize - 1].IsNull(); - } + bool IsMethodBeingCompiled() const { + return is_method_being_compiled_; + } - static constexpr uint16_t kIndividualCacheSize = 5; - uint32_t dex_pc; - GcRoot<mirror::Class> classes_[kIndividualCacheSize]; - }; + void SetIsMethodBeingCompiled(bool value) { + is_method_being_compiled_ = value; + } + private: ProfilingInfo(ArtMethod* method, const std::vector<uint32_t>& entries) : number_of_inline_caches_(entries.size()), - method_(method) { + method_(method), + is_method_being_compiled_(false) { memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); for (size_t i = 0; i < number_of_inline_caches_; ++i) { - cache_[i].dex_pc = entries[i]; + cache_[i].dex_pc_ = entries[i]; } } @@ -114,6 +138,11 @@ class ProfilingInfo { // Method this profiling info is for. ArtMethod* const method_; + // Whether the ArtMethod is currently being compiled. This flag + // is implicitly guarded by the JIT code cache lock. + // TODO: Make the JIT code cache lock global. + bool is_method_being_compiled_; + // Dynamically allocated array of size `number_of_inline_caches_`. InlineCache cache_[0]; diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 1552318c1e..9cb37eed58 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -283,7 +283,12 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { prev_shadow_frame_(nullptr), stacked_shadow_frame_pushed_(false), single_frame_deopt_(single_frame), - single_frame_done_(false) { + single_frame_done_(false), + single_frame_deopt_method_(nullptr) { + } + + ArtMethod* GetSingleFrameDeoptMethod() const { + return single_frame_deopt_method_; } bool VisitFrame() OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { @@ -356,6 +361,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { // Single-frame deopt ends at the first non-inlined frame and needs to store that method. exception_handler_->SetHandlerQuickArg0(reinterpret_cast<uintptr_t>(method)); single_frame_done_ = true; + single_frame_deopt_method_ = method; } return true; } @@ -586,6 +592,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { bool stacked_shadow_frame_pushed_; const bool single_frame_deopt_; bool single_frame_done_; + ArtMethod* single_frame_deopt_method_; DISALLOW_COPY_AND_ASSIGN(DeoptimizeStackVisitor); }; @@ -614,6 +621,14 @@ void QuickExceptionHandler::DeoptimizeSingleFrame() { DeoptimizeStackVisitor visitor(self_, context_, this, true); visitor.WalkStack(true); + // Compiled code made an explicit deoptimization. Transfer the code + // to interpreter and clear the counter to JIT the method again. + ArtMethod* deopt_method = visitor.GetSingleFrameDeoptMethod(); + DCHECK(deopt_method != nullptr); + deopt_method->ClearCounter(); + Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( + deopt_method, GetQuickToInterpreterBridge()); + // PC needs to be of the quick-to-interpreter bridge. int32_t offset; #ifdef __LP64__ diff --git a/runtime/thread.cc b/runtime/thread.cc index 63e6326f2f..90539b479f 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -164,14 +164,20 @@ void Thread::ResetQuickAllocEntryPointsForThread() { class DeoptimizationContextRecord { public: - DeoptimizationContextRecord(const JValue& ret_val, bool is_reference, + DeoptimizationContextRecord(const JValue& ret_val, + bool is_reference, + bool from_code, mirror::Throwable* pending_exception, DeoptimizationContextRecord* link) - : ret_val_(ret_val), is_reference_(is_reference), pending_exception_(pending_exception), + : ret_val_(ret_val), + is_reference_(is_reference), + from_code_(from_code), + pending_exception_(pending_exception), link_(link) {} JValue GetReturnValue() const { return ret_val_; } bool IsReference() const { return is_reference_; } + bool GetFromCode() const { return from_code_; } mirror::Throwable* GetPendingException() const { return pending_exception_; } DeoptimizationContextRecord* GetLink() const { return link_; } mirror::Object** GetReturnValueAsGCRoot() { @@ -189,6 +195,9 @@ class DeoptimizationContextRecord { // Indicates whether the returned value is a reference. If so, the GC will visit it. const bool is_reference_; + // Whether the context was created from an explicit deoptimization in the code. + const bool from_code_; + // The exception that was pending before deoptimization (or null if there was no pending // exception). mirror::Throwable* pending_exception_; @@ -220,22 +229,28 @@ class StackedShadowFrameRecord { DISALLOW_COPY_AND_ASSIGN(StackedShadowFrameRecord); }; -void Thread::PushDeoptimizationContext(const JValue& return_value, bool is_reference, +void Thread::PushDeoptimizationContext(const JValue& return_value, + bool is_reference, + bool from_code, mirror::Throwable* exception) { DeoptimizationContextRecord* record = new DeoptimizationContextRecord( return_value, is_reference, + from_code, exception, tlsPtr_.deoptimization_context_stack); tlsPtr_.deoptimization_context_stack = record; } -void Thread::PopDeoptimizationContext(JValue* result, mirror::Throwable** exception) { +void Thread::PopDeoptimizationContext(JValue* result, + mirror::Throwable** exception, + bool* from_code) { AssertHasDeoptimizationContext(); DeoptimizationContextRecord* record = tlsPtr_.deoptimization_context_stack; tlsPtr_.deoptimization_context_stack = record->GetLink(); result->SetJ(record->GetReturnValue().GetJ()); *exception = record->GetPendingException(); + *from_code = record->GetFromCode(); delete record; } @@ -2546,7 +2561,8 @@ void Thread::QuickDeliverException() { if (is_deoptimization) { // Save the exception into the deoptimization context so it can be restored // before entering the interpreter. - PushDeoptimizationContext(JValue(), false, exception); + PushDeoptimizationContext( + JValue(), /*is_reference */ false, /* from_code */ false, exception); } } // Don't leave exception visible while we try to find the handler, which may cause class diff --git a/runtime/thread.h b/runtime/thread.h index 4624f27564..3abb3cf360 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -849,10 +849,14 @@ class Thread { // and execute Java code, so there might be nested deoptimizations happening. // We need to save the ongoing deoptimization shadow frames and return // values on stacks. - void PushDeoptimizationContext(const JValue& return_value, bool is_reference, + // 'from_code' denotes whether the deoptimization was explicitly made from + // compiled code. + void PushDeoptimizationContext(const JValue& return_value, + bool is_reference, + bool from_code, mirror::Throwable* exception) SHARED_REQUIRES(Locks::mutator_lock_); - void PopDeoptimizationContext(JValue* result, mirror::Throwable** exception) + void PopDeoptimizationContext(JValue* result, mirror::Throwable** exception, bool* from_code) SHARED_REQUIRES(Locks::mutator_lock_); void AssertHasDeoptimizationContext() SHARED_REQUIRES(Locks::mutator_lock_); |