Revert^4 "Allow deoptimization when returning from a runtime method."
This reverts commit 07c7028e518b98d3267a77dfe0d149db1adbe858.
Need some special treatment of string init's shorty.
Test: run-test/gtest on both host and target
Test: 597-deopt-busy-loop, 597-deopt-invoke-stub
Bug: 33616143
Change-Id: Id4c64910acfdd088835b6db6fc503e6ade0218e7
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 9cb74f7..ec183b3 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -467,29 +467,6 @@
self->PopShadowFrame();
}
-static bool IsStringInit(const Instruction* instr, ArtMethod* caller)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (instr->Opcode() == Instruction::INVOKE_DIRECT ||
- instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) {
- // Instead of calling ResolveMethod() which has suspend point and can trigger
- // GC, look up the callee method symbolically.
- uint16_t callee_method_idx = (instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) ?
- instr->VRegB_3rc() : instr->VRegB_35c();
- const DexFile* dex_file = caller->GetDexFile();
- const DexFile::MethodId& method_id = dex_file->GetMethodId(callee_method_idx);
- const char* class_name = dex_file->StringByTypeIdx(method_id.class_idx_);
- const char* method_name = dex_file->GetMethodName(method_id);
- // Compare method's class name and method name against string init.
- // It's ok since it's not allowed to create your own java/lang/String.
- // TODO: verify that assumption.
- if ((strcmp(class_name, "Ljava/lang/String;") == 0) &&
- (strcmp(method_name, "<init>") == 0)) {
- return true;
- }
- }
- return false;
-}
-
static int16_t GetReceiverRegisterForStringInit(const Instruction* instr) {
DCHECK(instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE ||
instr->Opcode() == Instruction::INVOKE_DIRECT);
@@ -499,8 +476,9 @@
void EnterInterpreterFromDeoptimize(Thread* self,
ShadowFrame* shadow_frame,
+ JValue* ret_val,
bool from_code,
- JValue* ret_val)
+ 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.
@@ -527,11 +505,27 @@
new_dex_pc = found_dex_pc; // the dex pc of a matching catch handler
// or DexFile::kDexNoIndex if there is none.
} 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.
+ // Deoptimization is not called from code directly.
const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
- if (instr->IsInvoke()) {
+ if (deopt_method_type == DeoptimizationMethodType::kKeepDexPc) {
+ DCHECK(first);
+ // 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.
+ DCHECK_EQ(new_dex_pc, dex_pc);
+ } else if (instr->Opcode() == Instruction::MONITOR_ENTER ||
+ instr->Opcode() == Instruction::MONITOR_EXIT) {
+ DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
+ DCHECK(first);
+ // 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();
+ } else if (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
@@ -544,30 +538,27 @@
}
new_dex_pc = dex_pc + instr->SizeInCodeUnits();
} else if (instr->Opcode() == Instruction::NEW_INSTANCE) {
- // It's possible to deoptimize at a NEW_INSTANCE dex instruciton that's for a
- // java string, which is turned into a call into StringFactory.newEmptyString();
- // Move the StringFactory.newEmptyString() result into the destination register.
- DCHECK(value.GetL()->IsString());
- shadow_frame->SetVRegReference(instr->VRegA_21c(), value.GetL());
- // new-instance doesn't generate a result value.
- value.SetJ(0);
- // Skip the dex instruction since we essentially come back from an invocation.
- new_dex_pc = dex_pc + instr->SizeInCodeUnits();
- if (kIsDebugBuild) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- // This is a suspend point. But it's ok since value has been set into shadow_frame.
- ObjPtr<mirror::Class> klass = class_linker->ResolveType(
- dex::TypeIndex(instr->VRegB_21c()), shadow_frame->GetMethod());
- DCHECK(klass->IsStringClass());
- }
+ // 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 {
- CHECK(false) << "Unexpected instruction opcode " << instr->Opcode()
- << " at dex_pc " << dex_pc
- << " of method: " << ArtMethod::PrettyMethod(shadow_frame->GetMethod(), false);
+ DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
+ DCHECK(first);
+ // 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(first);
+ DCHECK_EQ(new_dex_pc, dex_pc);
}
if (new_dex_pc != DexFile::kDexNoIndex) {
shadow_frame->SetDexPC(new_dex_pc);
@@ -576,8 +567,10 @@
ShadowFrame* old_frame = shadow_frame;
shadow_frame = shadow_frame->GetLink();
ShadowFrame::DeleteDeoptimizedFrame(old_frame);
- // Following deoptimizations of shadow frames must pass the invoke instruction.
+ // 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;
first = false;
}
ret_val->SetJ(value.GetJ());