Revert "Revert "Revert "Allow deoptimization when returning from a runtime method."""
Bug: 33616143
deopt string test still failing on occasion.
This reverts commit 047abb20d02546d3dd6e8630befc31e5568fa90e.
Change-Id: I89fc28696290da52317d0e3dd07ecf0d1bdac823
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index ea8d501..cf2bfee 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1832,53 +1832,55 @@
mov r12, r0 @ r12 holds reference to code
ldr r0, [sp, #4] @ restore r0
RESTORE_SAVE_REFS_AND_ARGS_FRAME
- adr lr, art_quick_instrumentation_exit + /* thumb mode */ 1
- @ load art_quick_instrumentation_exit into lr in thumb mode
REFRESH_MARKING_REGISTER
- bx r12 @ call method with lr set to art_quick_instrumentation_exit
+ blx r12 @ call method with lr set to art_quick_instrumentation_exit
+@ Deliberate fall-through into art_quick_instrumentation_exit.
+ .type art_quick_instrumentation_exit, #function
+ .global art_quick_instrumentation_exit
+art_quick_instrumentation_exit:
+ mov lr, #0 @ link register is to here, so clobber with 0 for later checks
+ SETUP_SAVE_REFS_ONLY_FRAME r2 @ set up frame knowing r2 and r3 must be dead on exit
+ mov r12, sp @ remember bottom of caller's frame
+ push {r0-r1} @ save return value
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset r0, 0
+ .cfi_rel_offset r1, 4
+ mov r2, sp @ store gpr_res pointer.
+ vpush {d0} @ save fp return value
+ .cfi_adjust_cfa_offset 8
+ mov r3, sp @ store fpr_res pointer
+ mov r1, r12 @ pass SP
+ mov r0, r9 @ pass Thread::Current
+ blx artInstrumentationMethodExitFromCode @ (Thread*, SP, gpr_res*, fpr_res*)
+
+ mov r2, r0 @ link register saved by instrumentation
+ mov lr, r1 @ r1 is holding link register if we're to bounce to deoptimize
+ vpop {d0} @ restore fp return value
+ .cfi_adjust_cfa_offset -8
+ pop {r0, r1} @ restore return value
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore r0
+ .cfi_restore r1
+ RESTORE_SAVE_REFS_ONLY_FRAME
+ REFRESH_MARKING_REGISTER
+ cbz r2, .Ldo_deliver_instrumentation_exception
+ @ Deliver exception if we got nullptr as function.
+ bx r2 @ Otherwise, return
.Ldeliver_instrumentation_entry_exception:
@ Deliver exception for art_quick_instrumentation_entry placed after
@ art_quick_instrumentation_exit so that the fallthrough works.
RESTORE_SAVE_REFS_AND_ARGS_FRAME
+.Ldo_deliver_instrumentation_exception:
DELIVER_PENDING_EXCEPTION
END art_quick_instrumentation_entry
-ENTRY art_quick_instrumentation_exit
- mov lr, #0 @ link register is to here, so clobber with 0 for later checks
- SETUP_SAVE_EVERYTHING_FRAME r2
-
- add r3, sp, #8 @ store fpr_res pointer, in kSaveEverything frame
- add r2, sp, #136 @ store gpr_res pointer, in kSaveEverything frame
- mov r1, sp @ pass SP
- mov r0, r9 @ pass Thread::Current
- blx artInstrumentationMethodExitFromCode @ (Thread*, SP, gpr_res*, fpr_res*)
-
- cbz r0, .Ldo_deliver_instrumentation_exception
- @ Deliver exception if we got nullptr as function.
- cbnz r1, .Ldeoptimize
- // Normal return.
- str r0, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 4]
- @ Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- REFRESH_MARKING_REGISTER
- bx lr
-.Ldo_deliver_instrumentation_exception:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-.Ldeoptimize:
- str r1, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 4]
- @ Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- // Jump to art_quick_deoptimize.
- b art_quick_deoptimize
-END art_quick_instrumentation_exit
-
/*
* Instrumentation has requested that we deoptimize into the interpreter. The deoptimization
* will long jump to the upcall with a special exception of -1.
*/
.extern artDeoptimize
ENTRY art_quick_deoptimize
- SETUP_SAVE_EVERYTHING_FRAME r0
+ SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r0
mov r0, r9 @ pass Thread::Current
blx artDeoptimize @ (Thread*)
END art_quick_deoptimize
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 6c9ce93..3d8ca40 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2355,31 +2355,32 @@
.extern artInstrumentationMethodExitFromCode
ENTRY art_quick_instrumentation_exit
mov xLR, #0 // Clobber LR for later checks.
- SETUP_SAVE_EVERYTHING_FRAME
- add x3, sp, #8 // Pass floating-point result pointer, in kSaveEverything frame.
- add x2, sp, #264 // Pass integer result pointer, in kSaveEverything frame.
- mov x1, sp // Pass SP.
+ SETUP_SAVE_REFS_ONLY_FRAME
+
+ str x0, [sp, #-16]! // Save integer result.
+ .cfi_adjust_cfa_offset 16
+ str d0, [sp, #8] // Save floating-point result.
+
+ add x3, sp, #8 // Pass floating-point result pointer.
+ mov x2, sp // Pass integer result pointer.
+ add x1, sp, #16 // Pass SP.
mov x0, xSELF // Pass Thread.
bl artInstrumentationMethodExitFromCode // (Thread*, SP, gpr_res*, fpr_res*)
- cbz x0, .Ldo_deliver_instrumentation_exception
- // Handle error
- cbnz x1, .Ldeoptimize
- // Normal return.
- str x0, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 8]
- // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
+ mov xIP0, x0 // Return address from instrumentation call.
+ mov xLR, x1 // r1 is holding link register if we're to bounce to deoptimize
+
+ ldr d0, [sp, #8] // Restore floating-point result.
+ ldr x0, [sp], #16 // Restore integer result, and drop stack area.
+ .cfi_adjust_cfa_offset -16
+
+ RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- br lr
-.Ldo_deliver_instrumentation_exception:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-.Ldeoptimize:
- str x1, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 8]
- // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- // Jump to art_quick_deoptimize.
- b art_quick_deoptimize
+ cbz xIP0, 1f // Handle error
+ br xIP0 // Tail-call out.
+1:
+ DELIVER_PENDING_EXCEPTION
END art_quick_instrumentation_exit
/*
@@ -2388,7 +2389,7 @@
*/
.extern artDeoptimize
ENTRY art_quick_deoptimize
- SETUP_SAVE_EVERYTHING_FRAME
+ SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
mov x0, xSELF // Pass thread.
bl artDeoptimize // (Thread*)
brk 0
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index af82e08..4e5e93a 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -2056,43 +2056,42 @@
DEFINE_FUNCTION_CUSTOM_CFA art_quick_instrumentation_exit, 0
pushl LITERAL(0) // Push a fake return PC as there will be none on the stack.
CFI_ADJUST_CFA_OFFSET(4)
- SETUP_SAVE_EVERYTHING_FRAME ebx, ebx
-
- movl %esp, %ecx // Remember SP
- subl LITERAL(8), %esp // Align stack.
+ SETUP_SAVE_REFS_ONLY_FRAME ebx, ebx
+ mov %esp, %ecx // Remember SP
+ subl LITERAL(8), %esp // Save float return value.
CFI_ADJUST_CFA_OFFSET(8)
- PUSH edx // Save gpr return value. edx and eax need to be together,
- // which isn't the case in kSaveEverything frame.
+ movq %xmm0, (%esp)
+ PUSH edx // Save gpr return value.
PUSH eax
- leal 32(%esp), %eax // Get pointer to fpr_result, in kSaveEverything frame
+ leal 8(%esp), %eax // Get pointer to fpr_result
movl %esp, %edx // Get pointer to gpr_result
PUSH eax // Pass fpr_result
PUSH edx // Pass gpr_result
- PUSH ecx // Pass SP
+ PUSH ecx // Pass SP.
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current.
CFI_ADJUST_CFA_OFFSET(4)
-
call SYMBOL(artInstrumentationMethodExitFromCode) // (Thread*, SP, gpr_result*, fpr_result*)
- // Return result could have been changed if it's a reference.
- movl 16(%esp), %ecx
- movl %ecx, (80+32)(%esp)
- addl LITERAL(32), %esp // Pop arguments and grp_result.
- CFI_ADJUST_CFA_OFFSET(-32)
-
testl %eax, %eax // Check if we returned error.
- jz .Ldo_deliver_instrumentation_exception
- testl %edx, %edx
- jnz .Ldeoptimize
- // Normal return.
- movl %eax, FRAME_SIZE_SAVE_EVERYTHING-4(%esp) // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- ret
-.Ldeoptimize:
- mov %edx, (FRAME_SIZE_SAVE_EVERYTHING-4)(%esp) // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- jmp SYMBOL(art_quick_deoptimize)
-.Ldo_deliver_instrumentation_exception:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
+ jz 1f
+ mov %eax, %ecx // Move returned link register.
+ addl LITERAL(16), %esp // Pop arguments.
+ CFI_ADJUST_CFA_OFFSET(-16)
+ movl %edx, %ebx // Move returned link register for deopt
+ // (ebx is pretending to be our LR).
+ POP eax // Restore gpr return value.
+ POP edx
+ movq (%esp), %xmm0 // Restore fpr return value.
+ addl LITERAL(8), %esp
+ CFI_ADJUST_CFA_OFFSET(-8)
+ RESTORE_SAVE_REFS_ONLY_FRAME
+ addl LITERAL(4), %esp // Remove fake return pc.
+ CFI_ADJUST_CFA_OFFSET(-4)
+ jmp *%ecx // Return.
+1:
+ addl LITERAL(32), %esp
+ CFI_ADJUST_CFA_OFFSET(-32)
+ RESTORE_SAVE_REFS_ONLY_FRAME
+ DELIVER_PENDING_EXCEPTION
END_FUNCTION art_quick_instrumentation_exit
/*
@@ -2100,7 +2099,8 @@
* will long jump to the upcall with a special exception of -1.
*/
DEFINE_FUNCTION art_quick_deoptimize
- SETUP_SAVE_EVERYTHING_FRAME ebx, ebx
+ PUSH ebx // Entry point for a jump. Fake that we were called.
+ SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx, ebx
subl LITERAL(12), %esp // Align stack.
CFI_ADJUST_CFA_OFFSET(12)
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current().
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 6bf0828..73e8610 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -2019,31 +2019,45 @@
pushq LITERAL(0) // Push a fake return PC as there will be none on the stack.
CFI_ADJUST_CFA_OFFSET(8)
- SETUP_SAVE_EVERYTHING_FRAME
+ SETUP_SAVE_REFS_ONLY_FRAME
- leaq 16(%rsp), %rcx // Pass floating-point result pointer, in kSaveEverything frame.
- leaq 144(%rsp), %rdx // Pass integer result pointer, in kSaveEverything frame.
- movq %rsp, %rsi // Pass SP.
- movq %gs:THREAD_SELF_OFFSET, %rdi // Pass Thread.
+ // We need to save rax and xmm0. We could use a callee-save from SETUP_REF_ONLY, but then
+ // we would need to fully restore it. As there are a good number of callee-save registers, it
+ // seems easier to have an extra small stack area. But this should be revisited.
+
+ movq %rsp, %rsi // Pass SP.
+
+ PUSH rax // Save integer result.
+ movq %rsp, %rdx // Pass integer result pointer.
+
+ subq LITERAL(8), %rsp // Save floating-point result.
+ CFI_ADJUST_CFA_OFFSET(8)
+ movq %xmm0, (%rsp)
+ movq %rsp, %rcx // Pass floating-point result pointer.
+
+ movq %gs:THREAD_SELF_OFFSET, %rdi // Pass Thread.
call SYMBOL(artInstrumentationMethodExitFromCode) // (Thread*, SP, gpr_res*, fpr_res*)
- testq %rax, %rax // Check if we have a return-pc to go to. If we don't then there was
+ movq %rax, %rdi // Store return PC
+ movq %rdx, %rsi // Store second return PC in hidden arg.
+
+ movq (%rsp), %xmm0 // Restore floating-point result.
+ addq LITERAL(8), %rsp
+ CFI_ADJUST_CFA_OFFSET(-8)
+ POP rax // Restore integer result.
+
+ RESTORE_SAVE_REFS_ONLY_FRAME
+
+ testq %rdi, %rdi // Check if we have a return-pc to go to. If we don't then there was
// an exception
- jz .Ldo_deliver_instrumentation_exception
- testq %rdx, %rdx
- jnz .Ldeoptimize
- // Normal return.
- movq %rax, FRAME_SIZE_SAVE_EVERYTHING-8(%rsp) // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- ret
-.Ldeoptimize:
- movq %rdx, FRAME_SIZE_SAVE_EVERYTHING-8(%rsp) // Set return pc.
- RESTORE_SAVE_EVERYTHING_FRAME
- // Jump to art_quick_deoptimize.
- jmp SYMBOL(art_quick_deoptimize)
-.Ldo_deliver_instrumentation_exception:
- DELIVER_PENDING_EXCEPTION_FRAME_READY
+ jz 1f
+
+ addq LITERAL(8), %rsp // Drop fake return pc.
+
+ jmp *%rdi // Return.
+1:
+ DELIVER_PENDING_EXCEPTION
END_FUNCTION art_quick_instrumentation_exit
/*
@@ -2051,7 +2065,10 @@
* will long jump to the upcall with a special exception of -1.
*/
DEFINE_FUNCTION art_quick_deoptimize
- SETUP_SAVE_EVERYTHING_FRAME // Stack should be aligned now.
+ pushq %rsi // Entry point for a jump. Fake that we were called.
+ // Use hidden arg.
+ SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+ // Stack should be aligned now.
movq %gs:THREAD_SELF_OFFSET, %rdi // Pass Thread.
call SYMBOL(artDeoptimize) // (Thread*)
UNREACHABLE
diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
index 5f40711..53f0727 100644
--- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
@@ -73,11 +73,7 @@
// 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 /* is_reference */,
- self->GetException(),
- true /* from_code */,
- DeoptimizationMethodType::kDefault);
+ self->PushDeoptimizationContext(return_value, false, /* from_code */ true, self->GetException());
artDeoptimizeImpl(self, kind, true);
}
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 5f71326..e08319d 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -744,11 +744,7 @@
ObjPtr<mirror::Throwable> pending_exception;
bool from_code = false;
- DeoptimizationMethodType method_type;
- self->PopDeoptimizationContext(/* out */ &result,
- /* out */ &pending_exception,
- /* out */ &from_code,
- /* out */ &method_type);
+ self->PopDeoptimizationContext(&result, &pending_exception, /* out */ &from_code);
// Push a transition back into managed code onto the linked list in thread.
self->PushManagedStackFragment(&fragment);
@@ -775,11 +771,7 @@
if (pending_exception != nullptr) {
self->SetException(pending_exception);
}
- interpreter::EnterInterpreterFromDeoptimize(self,
- deopt_frame,
- &result,
- from_code,
- DeoptimizationMethodType::kDefault);
+ interpreter::EnterInterpreterFromDeoptimize(self, deopt_frame, from_code, &result);
} else {
const char* old_cause = self->StartAssertNoThreadSuspension(
"Building interpreter shadow frame");
@@ -831,11 +823,7 @@
// 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' || shorty[0] == '[', /* class or array */
- self->GetException(),
- false /* from_code */,
- DeoptimizationMethodType::kDefault);
+ result, shorty[0] == 'L', /* from_code */ false, self->GetException());
// Set special exception to cause deoptimization.
self->SetException(Thread::GetDeoptimizationException());
@@ -1053,8 +1041,7 @@
CHECK(!self->IsExceptionPending()) << "Enter instrumentation exit stub with pending exception "
<< self->GetException()->Dump();
// Compute address of return PC and sanity check that it currently holds 0.
- size_t return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA,
- CalleeSaveType::kSaveEverything);
+ size_t return_pc_offset = GetCalleeSaveReturnPcOffset(kRuntimeISA, CalleeSaveType::kSaveRefsOnly);
uintptr_t* return_pc = reinterpret_cast<uintptr_t*>(reinterpret_cast<uint8_t*>(sp) +
return_pc_offset);
CHECK_EQ(*return_pc, 0U);
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index de25a6d..a8cf59b 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -26,7 +26,6 @@
#include "class_linker.h"
#include "debugger.h"
#include "dex_file-inl.h"
-#include "dex_instruction-inl.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/runtime_asm_entrypoints.h"
@@ -228,32 +227,39 @@
return true; // Continue.
}
uintptr_t return_pc = GetReturnPc();
+ if (m->IsRuntimeMethod()) {
+ if (return_pc == instrumentation_exit_pc_) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Handling quick to interpreter transition. Frame " << GetFrameId();
+ }
+ CHECK_LT(instrumentation_stack_depth_, instrumentation_stack_->size());
+ const InstrumentationStackFrame& frame =
+ instrumentation_stack_->at(instrumentation_stack_depth_);
+ CHECK(frame.interpreter_entry_);
+ // This is an interpreter frame so method enter event must have been reported. However we
+ // need to push a DEX pc into the dex_pcs_ list to match size of instrumentation stack.
+ // Since we won't report method entry here, we can safely push any DEX pc.
+ dex_pcs_.push_back(0);
+ last_return_pc_ = frame.return_pc_;
+ ++instrumentation_stack_depth_;
+ return true;
+ } else {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Skipping runtime method. Frame " << GetFrameId();
+ }
+ last_return_pc_ = GetReturnPc();
+ return true; // Ignore unresolved methods since they will be instrumented after resolution.
+ }
+ }
if (kVerboseInstrumentation) {
LOG(INFO) << " Installing exit stub in " << DescribeLocation();
}
if (return_pc == instrumentation_exit_pc_) {
- CHECK_LT(instrumentation_stack_depth_, instrumentation_stack_->size());
-
- if (m->IsRuntimeMethod()) {
- const InstrumentationStackFrame& frame =
- instrumentation_stack_->at(instrumentation_stack_depth_);
- if (frame.interpreter_entry_) {
- // This instrumentation frame is for an interpreter bridge and is
- // pushed when executing the instrumented interpreter bridge. So method
- // enter event must have been reported. However we need to push a DEX pc
- // into the dex_pcs_ list to match size of instrumentation stack.
- uint32_t dex_pc = DexFile::kDexNoIndex;
- dex_pcs_.push_back(dex_pc);
- last_return_pc_ = frame.return_pc_;
- ++instrumentation_stack_depth_;
- return true;
- }
- }
-
// We've reached a frame which has already been installed with instrumentation exit stub.
// We should have already installed instrumentation on previous frames.
reached_existing_instrumentation_frames_ = true;
+ CHECK_LT(instrumentation_stack_depth_, instrumentation_stack_->size());
const InstrumentationStackFrame& frame =
instrumentation_stack_->at(instrumentation_stack_depth_);
CHECK_EQ(m, frame.method_) << "Expected " << ArtMethod::PrettyMethod(m)
@@ -265,12 +271,8 @@
} else {
CHECK_NE(return_pc, 0U);
CHECK(!reached_existing_instrumentation_frames_);
- InstrumentationStackFrame instrumentation_frame(
- m->IsRuntimeMethod() ? nullptr : GetThisObject(),
- m,
- return_pc,
- GetFrameId(), // A runtime method still gets a frame id.
- false);
+ InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, return_pc, GetFrameId(),
+ false);
if (kVerboseInstrumentation) {
LOG(INFO) << "Pushing frame " << instrumentation_frame.Dump();
}
@@ -287,12 +289,9 @@
instrumentation_stack_->insert(it, instrumentation_frame);
SetReturnPc(instrumentation_exit_pc_);
}
- uint32_t dex_pc = DexFile::kDexNoIndex;
- if (last_return_pc_ != 0 &&
- GetCurrentOatQuickMethodHeader() != nullptr) {
- dex_pc = GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_);
- }
- dex_pcs_.push_back(dex_pc);
+ dex_pcs_.push_back((GetCurrentOatQuickMethodHeader() == nullptr)
+ ? DexFile::kDexNoIndex
+ : GetCurrentOatQuickMethodHeader()->ToDexPc(m, last_return_pc_));
last_return_pc_ = return_pc;
++instrumentation_stack_depth_;
return true; // Continue.
@@ -390,8 +389,7 @@
CHECK(m == instrumentation_frame.method_) << ArtMethod::PrettyMethod(m);
}
SetReturnPc(instrumentation_frame.return_pc_);
- if (instrumentation_->ShouldNotifyMethodEnterExitEvents() &&
- !m->IsRuntimeMethod()) {
+ if (instrumentation_->ShouldNotifyMethodEnterExitEvents()) {
// Create the method exit events. As the methods didn't really exit the result is 0.
// We only do this if no debugger is attached to prevent from posting events twice.
instrumentation_->MethodExitEvent(thread_, instrumentation_frame.this_object_, m,
@@ -949,7 +947,6 @@
ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc) const {
- DCHECK(!method->IsRuntimeMethod());
if (HasMethodEntryListeners()) {
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
@@ -1154,54 +1151,6 @@
stack->push_front(instrumentation_frame);
}
-DeoptimizationMethodType Instrumentation::GetDeoptimizationMethodType(ArtMethod* method) {
- if (method->IsRuntimeMethod()) {
- // Certain methods have strict requirement on whether the dex instruction
- // should be re-executed upon deoptimization.
- if (method == Runtime::Current()->GetCalleeSaveMethod(
- CalleeSaveType::kSaveEverythingForClinit)) {
- return DeoptimizationMethodType::kKeepDexPc;
- }
- if (method == Runtime::Current()->GetCalleeSaveMethod(
- CalleeSaveType::kSaveEverythingForSuspendCheck)) {
- return DeoptimizationMethodType::kKeepDexPc;
- }
- }
- return DeoptimizationMethodType::kDefault;
-}
-
-// Try to get the shorty of a runtime method if it's an invocation stub.
-struct RuntimeMethodShortyVisitor : public StackVisitor {
- explicit RuntimeMethodShortyVisitor(Thread* thread)
- : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
- shorty('V') {}
-
- bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtMethod* m = GetMethod();
- if (m != nullptr && !m->IsRuntimeMethod()) {
- // The first Java method.
- if (m->IsNative()) {
- // Use JNI method's shorty for the jni stub.
- shorty = m->GetShorty()[0];
- return false;
- }
- const DexFile::CodeItem* code_item = m->GetCodeItem();
- const Instruction* instr = Instruction::At(&code_item->insns_[GetDexPc()]);
- if (instr->IsInvoke()) {
- // If it's an invoke, use its shorty.
- uint32_t method_idx = instr->VRegB();
- shorty = m->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetDexFile()
- ->GetMethodShorty(method_idx)[0];
- }
- // Stop stack walking since we've seen a Java frame.
- return false;
- }
- return true;
- }
-
- char shorty;
-};
-
TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self,
uintptr_t* return_pc,
uint64_t* gpr_result,
@@ -1222,36 +1171,7 @@
ArtMethod* method = instrumentation_frame.method_;
uint32_t length;
const PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- char return_shorty;
-
- // Runtime method does not call into MethodExitEvent() so there should not be
- // suspension point below.
- ScopedAssertNoThreadSuspension ants(__FUNCTION__, method->IsRuntimeMethod());
- if (method->IsRuntimeMethod()) {
- if (method != Runtime::Current()->GetCalleeSaveMethod(
- CalleeSaveType::kSaveEverythingForClinit)) {
- // If the caller is at an invocation point and the runtime method is not
- // for clinit, we need to pass return results to the caller.
- // We need the correct shorty to decide whether we need to pass the return
- // result for deoptimization below.
- RuntimeMethodShortyVisitor visitor(self);
- visitor.WalkStack();
- return_shorty = visitor.shorty;
- } else {
- // Some runtime methods such as allocations, unresolved field getters, etc.
- // have return value. We don't need to set return_value since MethodExitEvent()
- // below isn't called for runtime methods. Deoptimization doesn't need the
- // value either since the dex instruction will be re-executed by the
- // interpreter, except these two cases:
- // (1) For an invoke, which is handled above to get the correct shorty.
- // (2) For MONITOR_ENTER/EXIT, which cannot be re-executed since it's not
- // idempotent. However there is no return value for it anyway.
- return_shorty = 'V';
- }
- } else {
- return_shorty = method->GetInterfaceMethodIfProxy(pointer_size)->GetShorty(&length)[0];
- }
-
+ char return_shorty = method->GetInterfaceMethodIfProxy(pointer_size)->GetShorty(&length)[0];
bool is_ref = return_shorty == '[' || return_shorty == 'L';
StackHandleScope<1> hs(self);
MutableHandle<mirror::Object> res(hs.NewHandle<mirror::Object>(nullptr));
@@ -1271,7 +1191,7 @@
// return_pc.
uint32_t dex_pc = DexFile::kDexNoIndex;
mirror::Object* this_object = instrumentation_frame.this_object_;
- if (!method->IsRuntimeMethod() && !instrumentation_frame.interpreter_entry_) {
+ if (!instrumentation_frame.interpreter_entry_) {
MethodExitEvent(self, this_object, instrumentation_frame.method_, dex_pc, return_value);
}
@@ -1297,12 +1217,10 @@
<< " in "
<< *self;
}
- DeoptimizationMethodType deopt_method_type = GetDeoptimizationMethodType(method);
self->PushDeoptimizationContext(return_value,
- return_shorty == 'L' || return_shorty == '[',
- nullptr /* no pending exception */,
+ return_shorty == 'L',
false /* from_code */,
- deopt_method_type);
+ nullptr /* no pending exception */);
return GetTwoWordSuccessValue(*return_pc,
reinterpret_cast<uintptr_t>(GetQuickDeoptimizationEntryPoint()));
} else {
@@ -1339,9 +1257,7 @@
// TODO: improve the dex pc information here, requires knowledge of current PC as opposed to
// return_pc.
uint32_t dex_pc = DexFile::kDexNoIndex;
- if (!method->IsRuntimeMethod()) {
- MethodUnwindEvent(self, instrumentation_frame.this_object_, method, dex_pc);
- }
+ MethodUnwindEvent(self, instrumentation_frame.this_object_, method, dex_pc);
}
// TODO: bring back CheckStackDepth(self, instrumentation_frame, 2);
CHECK_EQ(stack->size(), idx);
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 577bbf9..9969489 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -39,7 +39,6 @@
template <typename T> class Handle;
union JValue;
class Thread;
-enum class DeoptimizationMethodType;
namespace instrumentation {
@@ -436,9 +435,6 @@
bool interpreter_entry)
REQUIRES_SHARED(Locks::mutator_lock_);
- DeoptimizationMethodType GetDeoptimizationMethodType(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Called when an instrumented method is exited. Removes the pushed instrumentation frame
// returning the intended link register. Generates method exit events. The gpr_result and
// fpr_result pointers are pointers to the locations where the integer/pointer and floating point
@@ -665,15 +661,9 @@
// An element in the instrumentation side stack maintained in art::Thread.
struct InstrumentationStackFrame {
- InstrumentationStackFrame(mirror::Object* this_object,
- ArtMethod* method,
- uintptr_t return_pc,
- size_t frame_id,
- bool interpreter_entry)
- : this_object_(this_object),
- method_(method),
- return_pc_(return_pc),
- frame_id_(frame_id),
+ InstrumentationStackFrame(mirror::Object* this_object, ArtMethod* method,
+ uintptr_t return_pc, size_t frame_id, bool interpreter_entry)
+ : this_object_(this_object), method_(method), return_pc_(return_pc), frame_id_(frame_id),
interpreter_entry_(interpreter_entry) {
}
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index bfd36cc..d25655f 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -473,23 +473,7 @@
// Test instrumentation listeners for each event.
TEST_F(InstrumentationTest, MethodEntryEvent) {
- ScopedObjectAccess soa(Thread::Current());
- jobject class_loader = LoadDex("Instrumentation");
- Runtime* const runtime = Runtime::Current();
- ClassLinker* class_linker = runtime->GetClassLinker();
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
- mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
- ASSERT_TRUE(klass != nullptr);
- ArtMethod* method =
- klass->FindClassMethod("returnReference", "()Ljava/lang/Object;", kRuntimePointerSize);
- ASSERT_TRUE(method != nullptr);
- ASSERT_TRUE(method->IsDirect());
- ASSERT_TRUE(method->GetDeclaringClass() == klass);
- TestEvent(instrumentation::Instrumentation::kMethodEntered,
- /*event_method*/ method,
- /*event_field*/ nullptr,
- /*with_object*/ true);
+ TestEvent(instrumentation::Instrumentation::kMethodEntered);
}
TEST_F(InstrumentationTest, MethodExitObjectEvent) {
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 34332d7..9cb74f7 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -499,9 +499,8 @@
void EnterInterpreterFromDeoptimize(Thread* self,
ShadowFrame* shadow_frame,
- JValue* ret_val,
bool from_code,
- DeoptimizationMethodType deopt_method_type)
+ JValue* ret_val)
REQUIRES_SHARED(Locks::mutator_lock_) {
JValue value;
// Set value to last known result in case the shadow frame chain is empty.
@@ -528,27 +527,11 @@
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) {
- // Deoptimization is not called from code directly.
+ // 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.
const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
- 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 (instr->IsInvoke()) {
if (IsStringInit(instr, shadow_frame->GetMethod())) {
uint16_t this_obj_vreg = GetReceiverRegisterForStringInit(instr);
// Move the StringFactory.newStringFromChars() result into the register representing
@@ -561,27 +544,30 @@
}
new_dex_pc = dex_pc + instr->SizeInCodeUnits();
} else if (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);
+ // 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());
+ }
} else {
- 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);
+ CHECK(false) << "Unexpected instruction opcode " << instr->Opcode()
+ << " at dex_pc " << dex_pc
+ << " of method: " << ArtMethod::PrettyMethod(shadow_frame->GetMethod(), false);
}
} 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);
@@ -590,10 +576,8 @@
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.
+ // Following deoptimizations of shadow frames must pass the invoke instruction.
from_code = false;
- deopt_method_type = DeoptimizationMethodType::kDefault;
first = false;
}
ret_val->SetJ(value.GetJ());
diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h
index df8568e..65cfade 100644
--- a/runtime/interpreter/interpreter.h
+++ b/runtime/interpreter/interpreter.h
@@ -30,7 +30,6 @@
union JValue;
class ShadowFrame;
class Thread;
-enum class DeoptimizationMethodType;
namespace interpreter {
@@ -45,11 +44,8 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// 'from_code' denotes whether the deoptimization was explicitly triggered by compiled code.
-extern void EnterInterpreterFromDeoptimize(Thread* self,
- ShadowFrame* shadow_frame,
- JValue* ret_val,
- bool from_code,
- DeoptimizationMethodType method_type)
+extern void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, bool from_code,
+ JValue* ret_val)
REQUIRES_SHARED(Locks::mutator_lock_);
extern JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* code_item,
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 01560f8..cdbb908 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -166,13 +166,11 @@
bool is_reference,
bool from_code,
ObjPtr<mirror::Throwable> pending_exception,
- DeoptimizationMethodType method_type,
DeoptimizationContextRecord* link)
: ret_val_(ret_val),
is_reference_(is_reference),
from_code_(from_code),
pending_exception_(pending_exception.Ptr()),
- deopt_method_type_(method_type),
link_(link) {}
JValue GetReturnValue() const { return ret_val_; }
@@ -187,9 +185,6 @@
mirror::Object** GetPendingExceptionAsGCRoot() {
return reinterpret_cast<mirror::Object**>(&pending_exception_);
}
- DeoptimizationMethodType GetDeoptimizationMethodType() const {
- return deopt_method_type_;
- }
private:
// The value returned by the method at the top of the stack before deoptimization.
@@ -205,9 +200,6 @@
// exception).
mirror::Throwable* pending_exception_;
- // Whether the context was created for an (idempotent) runtime method.
- const DeoptimizationMethodType deopt_method_type_;
-
// A link to the previous DeoptimizationContextRecord.
DeoptimizationContextRecord* const link_;
@@ -237,30 +229,26 @@
void Thread::PushDeoptimizationContext(const JValue& return_value,
bool is_reference,
- ObjPtr<mirror::Throwable> exception,
bool from_code,
- DeoptimizationMethodType method_type) {
+ ObjPtr<mirror::Throwable> exception) {
DeoptimizationContextRecord* record = new DeoptimizationContextRecord(
return_value,
is_reference,
from_code,
exception,
- method_type,
tlsPtr_.deoptimization_context_stack);
tlsPtr_.deoptimization_context_stack = record;
}
void Thread::PopDeoptimizationContext(JValue* result,
ObjPtr<mirror::Throwable>* exception,
- bool* from_code,
- DeoptimizationMethodType* method_type) {
+ 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();
- *method_type = record->GetDeoptimizationMethodType();
delete record;
}
@@ -3096,16 +3084,10 @@
NthCallerVisitor visitor(this, 0, false);
visitor.WalkStack();
if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller_pc)) {
- // method_type shouldn't matter due to exception handling.
- const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
// Save the exception into the deoptimization context so it can be restored
// before entering the interpreter.
PushDeoptimizationContext(
- JValue(),
- false /* is_reference */,
- exception,
- false /* from_code */,
- method_type);
+ JValue(), /*is_reference */ false, /* from_code */ false, exception);
artDeoptimize(this);
UNREACHABLE();
} else {
@@ -3665,8 +3647,7 @@
PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame);
ObjPtr<mirror::Throwable> pending_exception;
bool from_code = false;
- DeoptimizationMethodType method_type;
- PopDeoptimizationContext(result, &pending_exception, &from_code, &method_type);
+ PopDeoptimizationContext(result, &pending_exception, &from_code);
SetTopOfStack(nullptr);
SetTopOfShadowStack(shadow_frame);
@@ -3675,11 +3656,7 @@
if (pending_exception != nullptr) {
SetException(pending_exception);
}
- interpreter::EnterInterpreterFromDeoptimize(this,
- shadow_frame,
- result,
- from_code,
- method_type);
+ interpreter::EnterInterpreterFromDeoptimize(this, shadow_frame, from_code, result);
}
void Thread::SetException(ObjPtr<mirror::Throwable> new_exception) {
diff --git a/runtime/thread.h b/runtime/thread.h
index ad4506e..7540fd2 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -117,13 +117,6 @@
kDeoptimizationShadowFrame,
};
-// The type of method that triggers deoptimization. It contains info on whether
-// the deoptimized method should advance dex_pc.
-enum class DeoptimizationMethodType {
- kKeepDexPc, // dex pc is required to be kept upon deoptimization.
- kDefault // dex pc may or may not advance depending on other conditions.
-};
-
// This should match RosAlloc::kNumThreadLocalSizeBrackets.
static constexpr size_t kNumRosAllocThreadLocalSizeBracketsInThread = 16;
@@ -967,18 +960,14 @@
// values on stacks.
// 'from_code' denotes whether the deoptimization was explicitly made from
// compiled code.
- // 'method_type' contains info on whether deoptimization should advance
- // dex_pc.
void PushDeoptimizationContext(const JValue& return_value,
bool is_reference,
- ObjPtr<mirror::Throwable> exception,
bool from_code,
- DeoptimizationMethodType method_type)
+ ObjPtr<mirror::Throwable> exception)
REQUIRES_SHARED(Locks::mutator_lock_);
void PopDeoptimizationContext(JValue* result,
ObjPtr<mirror::Throwable>* exception,
- bool* from_code,
- DeoptimizationMethodType* method_type)
+ bool* from_code)
REQUIRES_SHARED(Locks::mutator_lock_);
void AssertHasDeoptimizationContext()
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/test/597-deopt-busy-loop/expected.txt b/test/597-deopt-busy-loop/expected.txt
deleted file mode 100644
index f993efc..0000000
--- a/test/597-deopt-busy-loop/expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-JNI_OnLoad called
-Finishing
diff --git a/test/597-deopt-busy-loop/info.txt b/test/597-deopt-busy-loop/info.txt
deleted file mode 100644
index 2c50dbb..0000000
--- a/test/597-deopt-busy-loop/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Test deoptimizing when returning from suspend-check runtime method.
diff --git a/test/597-deopt-busy-loop/run b/test/597-deopt-busy-loop/run
deleted file mode 100644
index bc04498..0000000
--- a/test/597-deopt-busy-loop/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2017 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.
-
-# We want to run in debuggable mode and compiled.
-exec ${RUN} --jit -Xcompiler-option --debuggable "${@}"
diff --git a/test/597-deopt-busy-loop/src/Main.java b/test/597-deopt-busy-loop/src/Main.java
deleted file mode 100644
index 46b6bbf..0000000
--- a/test/597-deopt-busy-loop/src/Main.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-public class Main implements Runnable {
- static final int numberOfThreads = 2;
- volatile static boolean sExitFlag = false;
- volatile static boolean sEntered = false;
- int threadIndex;
-
- private static native void deoptimizeAll();
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native void ensureJitCompiled(Class<?> cls, String methodName);
-
- Main(int index) {
- threadIndex = index;
- }
-
- public static void main(String[] args) throws Exception {
- System.loadLibrary(args[0]);
-
- final Thread[] threads = new Thread[numberOfThreads];
- for (int t = 0; t < threads.length; t++) {
- threads[t] = new Thread(new Main(t));
- threads[t].start();
- }
- for (Thread t : threads) {
- t.join();
- }
- System.out.println("Finishing");
- }
-
- public void $noinline$busyLoop() {
- assertIsManaged();
- sEntered = true;
- for (;;) {
- if (sExitFlag) {
- break;
- }
- }
- assertIsInterpreted();
- }
-
- public void run() {
- if (threadIndex == 0) {
- while (!sEntered) {
- Thread.yield();
- }
- deoptimizeAll();
- sExitFlag = true;
- } else {
- ensureJitCompiled(Main.class, "$noinline$busyLoop");
- $noinline$busyLoop();
- }
- }
-}
diff --git a/test/597-deopt-invoke-stub/expected.txt b/test/597-deopt-invoke-stub/expected.txt
deleted file mode 100644
index f993efc..0000000
--- a/test/597-deopt-invoke-stub/expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-JNI_OnLoad called
-Finishing
diff --git a/test/597-deopt-invoke-stub/info.txt b/test/597-deopt-invoke-stub/info.txt
deleted file mode 100644
index 31960a9..0000000
--- a/test/597-deopt-invoke-stub/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Test deoptimizing when returning from a quick-to-interpreter bridge.
diff --git a/test/597-deopt-invoke-stub/run b/test/597-deopt-invoke-stub/run
deleted file mode 100644
index bc04498..0000000
--- a/test/597-deopt-invoke-stub/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2017 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.
-
-# We want to run in debuggable mode and compiled.
-exec ${RUN} --jit -Xcompiler-option --debuggable "${@}"
diff --git a/test/597-deopt-invoke-stub/src/Main.java b/test/597-deopt-invoke-stub/src/Main.java
deleted file mode 100644
index 0751783..0000000
--- a/test/597-deopt-invoke-stub/src/Main.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-public class Main implements Runnable {
- static final int numberOfThreads = 2;
- volatile static boolean sExitFlag = false;
- volatile static boolean sEntered = false;
- int threadIndex;
-
- private static native void deoptimizeAll();
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native void ensureJitCompiled(Class<?> cls, String methodName);
-
- Main(int index) {
- threadIndex = index;
- }
-
- public static void main(String[] args) throws Exception {
- System.loadLibrary(args[0]);
-
- final Thread[] threads = new Thread[numberOfThreads];
- for (int t = 0; t < threads.length; t++) {
- threads[t] = new Thread(new Main(t));
- threads[t].start();
- }
- for (Thread t : threads) {
- t.join();
- }
- System.out.println("Finishing");
- }
-
- private static int $noinline$bar() {
- // Should be entered via interpreter bridge.
- assertIsInterpreted();
- sEntered = true;
- while (!sExitFlag) {}
- assertIsInterpreted();
- return 0x1234;
- }
-
- public void $noinline$foo() {
- assertIsManaged();
- if ($noinline$bar() != 0x1234) {
- System.out.println("Bad return value");
- }
- assertIsInterpreted();
- }
-
- public void run() {
- if (threadIndex == 0) {
- while (!sEntered) {
- Thread.yield();
- }
- deoptimizeAll();
- sExitFlag = true;
- } else {
- ensureJitCompiled(Main.class, "$noinline$foo");
- $noinline$foo();
- }
- }
-}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index d76103a..20cfc34 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -207,9 +207,7 @@
"variant": "trace | stream"
},
{
- "tests": ["597-deopt-busy-loop",
- "597-deopt-invoke-stub",
- "604-hot-static-interface",
+ "tests": ["604-hot-static-interface",
"612-jit-dex-cache",
"613-inlining-dex-cache",
"626-set-resolved-string"],