// We cache the result of NeedsDexPcEvents in the shadow frame so we don't need to call // NeedsDexPcEvents on every instruction for better performance. NeedsDexPcEvents only gets // updated asynchronoulsy in a SuspendAll scope and any existing shadow frames are updated with // new value. So it is safe to cache it here.
shadow_frame.SetNotifyDexPcMoveEvents(
Runtime::Current()->GetInstrumentation()->NeedsDexPcEvents(shadow_frame.GetMethod(), self));
if (LIKELY(!from_deoptimize)) { // Entering the method, but not via deoptimization. if (kIsDebugBuild) { // TODO(b/346542404): Check this precondition prorperly, and shouldn't emit method enter event // when unparking a virtual thread. bool is_virtual = kIsVirtualThreadEnabled && self->IsVirtualThreadMounted(); if (!is_virtual) {
CHECK_EQ(shadow_frame.GetDexPC(), 0u);
}
self->AssertNoPendingException();
}
ArtMethod *method = shadow_frame.GetMethod();
// If we can continue in JIT and have JITed code available execute JITed code. if (!stay_in_interpreter &&
!self->IsForceInterpreter() &&
!shadow_frame.GetForcePopFrame() &&
!shadow_frame.GetNotifyDexPcMoveEvents()) {
jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr) {
jit->MethodEntered(self, shadow_frame.GetMethod()); if (jit->CanInvokeCompiledCode(method)) {
JValue result;
// Pop the shadow frame before calling into compiled code.
self->PopShadowFrame(); // Calculate the offset of the first input reg. The input registers are in the high regs. // It's ok to access the code item here since JIT code will have been touched by the // interpreter and compiler already.
uint16_t arg_offset = accessor.RegistersSize() - accessor.InsSize();
ArtInterpreterToCompiledCodeBridge(self, &shadow_frame, arg_offset, &result); // Push the shadow frame back as the caller will expect it.
self->PushShadowFrame(&shadow_frame);
return result;
}
}
}
TraceLowOverhead::RecordTraceEventIfNeeded(self, method, /*is_entry=*/true);
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasMethodEntryListeners() || shadow_frame.GetForcePopFrame())) {
instrumentation->MethodEnterEvent(self, method); if (UNLIKELY(shadow_frame.GetForcePopFrame())) { // The caller will retry this invoke or ignore the result. Just return immediately without // any value.
DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
JValue ret = JValue();
PerformNonStandardReturn(self,
shadow_frame,
ret,
instrumentation, /* unlock_monitors= */ false); return ret;
} if (UNLIKELY(self->IsExceptionPending())) {
instrumentation->MethodUnwindEvent(self,
method, 0); // We notified method has exited so don't call trace listeners anymore.
shadow_frame.SetSkipTraceMethodExitEvent(true);
JValue ret = JValue(); if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
PerformNonStandardReturn(self,
shadow_frame,
ret,
instrumentation, /* unlock_monitors= */ false);
} return ret;
}
}
}
ArtMethod* method = shadow_frame.GetMethod();
DCheckStaticState(self, method);
// Lock counting is a special version of accessibility checks, and for simplicity and // reduction of template parameters, we gate it behind access-checks mode.
DCHECK_IMPLIES(method->SkipAccessChecks(), !method->MustCountLocks());
// This can happen if we are in forced interpreter mode and an obsolete method is called using // reflection. if (UNLIKELY(method->IsObsolete())) {
ThrowInternalError("Attempting to invoke obsolete version of '%s'.",
method->PrettyMethod().c_str()); return;
}
self->EndAssertNoThreadSuspension(old_cause); if (!EnsureInitialized(self, shadow_frame)) { return;
}
ScopedShadowFrame pusher(self, shadow_frame); if (LIKELY(!method->IsNative())) {
JValue r = Execute(self, accessor, *shadow_frame, JValue(), stay_in_interpreter); if (result != nullptr) {
*result = r;
}
} else { // We don't expect to be asked to interpret native code (which is entered via a JNI compiler // generated stub) except during testing and image writing. // Update args to be the args in the shadow frame since the input ones could hold stale // references pointers due to moving GC.
args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1); if (!Runtime::Current()->IsStarted()) {
UnstartedRuntime::Jni(self, method, receiver.Ptr(), args, result);
} else {
InterpreterJni(self, method, shorty, receiver, args, result);
}
}
}
void EnterInterpreterFromDeoptimize(Thread* self,
ShadowFrame* shadow_frame,
JValue* ret_val, bool from_code,
DeoptimizationMethodType deopt_method_type)
REQUIRES_SHARED(Locks::mutator_lock_) {
JValue value; // Set value to last known result in case the shadow frame chain is empty.
value.SetJ(ret_val->GetJ()); // How many frames we have executed.
size_t frame_cnt = 0; while (shadow_frame != nullptr) { // We do not want to recover lock state for lock counting when deoptimizing. Currently, // the compiler should not have compiled a method that failed structured-locking checks.
DCHECK(!shadow_frame->GetMethod()->MustCountLocks());
self->SetTopOfShadowStack(shadow_frame);
CodeItemDataAccessor accessor(shadow_frame->GetMethod()->DexInstructionData()); const uint32_t dex_pc = shadow_frame->GetDexPC();
uint32_t new_dex_pc = dex_pc; if (UNLIKELY(self->IsExceptionPending())) {
DCHECK(self->GetException() != Thread::GetDeoptimizationException()); if (shadow_frame->GetForcePopFrame()) { // Just continue with next instruction which will pop the frame.
new_dex_pc = shadow_frame->GetDexPC();
} else { // If we deoptimize from the QuickExceptionHandler, we already reported the exception throw // event to the instrumentation. Skip throw listeners for the first frame. The deopt check // should happen after the throw listener is called as throw listener can trigger a // deoptimization.
new_dex_pc = MoveToExceptionHandler(self,
*shadow_frame, /* skip_listeners= */ false, /* skip_throw_listener= */ frame_cnt == 0) ?
shadow_frame->GetDexPC() :
dex::kDexNoIndex;
}
} elseif (!from_code) { // Deoptimization is not called from code directly. const Instruction* instr = &accessor.InstructionAt(dex_pc); if (deopt_method_type == DeoptimizationMethodType::kKeepDexPc ||
shadow_frame->GetForceRetryInstruction()) {
DCHECK(frame_cnt == 0 || shadow_frame->GetForceRetryInstruction())
<< "frame_cnt: " << frame_cnt
<< " force-retry: " << shadow_frame->GetForceRetryInstruction(); // Need to re-execute the dex instruction. // (1) An invocation might be split into class initialization and invoke. // In this case, the invoke should not be skipped. // (2) A suspend check should also execute the dex instruction at the // corresponding dex pc. // If the ForceRetryInstruction bit is set this must be the second frame (the first being // the one that is being popped).
DCHECK_EQ(new_dex_pc, dex_pc);
shadow_frame->SetForceRetryInstruction(false);
} elseif (instr->Opcode() == Instruction::MONITOR_ENTER ||
instr->Opcode() == Instruction::MONITOR_EXIT) {
DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
DCHECK_EQ(frame_cnt, 0u); // Non-idempotent dex instruction should not be re-executed. // On the other hand, if a MONITOR_ENTER is at the dex_pc of a suspend // check, that MONITOR_ENTER should be executed. That case is handled // above.
new_dex_pc = dex_pc + instr->SizeInCodeUnits();
} elseif (instr->IsInvoke()) {
DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault); if (IsStringInit(*instr, shadow_frame->GetMethod())) {
uint16_t this_obj_vreg = GetReceiverRegisterForStringInit(instr); // Move the StringFactory.newStringFromChars() result into the register representing // "this object" when invoking the string constructor in the original dex instruction. // Also move the result into all aliases.
DCHECK(value.GetL()->IsString());
SetStringInitValueToAllAliases(shadow_frame, this_obj_vreg, value); // Calling string constructor in the original dex code doesn't generate a result value.
value.SetJ(0);
}
new_dex_pc = dex_pc + instr->SizeInCodeUnits();
} elseif (instr->Opcode() == Instruction::NEW_INSTANCE) { // A NEW_INSTANCE is simply re-executed, including // "new-instance String" which is compiled into a call into // StringFactory.newEmptyString().
DCHECK_EQ(new_dex_pc, dex_pc);
} else {
DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
DCHECK_EQ(frame_cnt, 0u); // By default, we re-execute the dex instruction since if they are not // an invoke, so that we don't have to decode the dex instruction to move // result into the right vreg. All slow paths have been audited to be // idempotent except monitor-enter/exit and invocation stubs. // TODO: move result and advance dex pc. That also requires that we // can tell the return type of a runtime method, possibly by decoding // the dex instruction at the caller.
DCHECK_EQ(new_dex_pc, dex_pc);
}
} else { // Nothing to do, the dex_pc is the one at which the code requested // the deoptimization.
DCHECK_EQ(frame_cnt, 0u);
DCHECK_EQ(new_dex_pc, dex_pc);
} if (new_dex_pc != dex::kDexNoIndex) {
shadow_frame->SetDexPC(new_dex_pc);
value = Execute(self,
accessor,
*shadow_frame,
value, /* stay_in_interpreter= */ true, /* from_deoptimize= */ true);
}
ShadowFrame* old_frame = shadow_frame;
shadow_frame = shadow_frame->GetLink();
ShadowFrame::DeleteDeoptimizedFrame(old_frame); // Following deoptimizations of shadow frames must be at invocation point // and should advance dex pc past the invoke instruction.
from_code = false;
deopt_method_type = DeoptimizationMethodType::kDefault;
frame_cnt++;
}
ret_val->SetJ(value.GetJ());
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.14Bemerkung:
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.