Jit: Monitor exit, possible fix for Issue 2396073
Two problems with monitor-exit:
1. The Jit code wasn't checking for exception thrown following
unlocks of fat locks using dvmUnlockObject().
2. The mterp interpreter unlock code branched to handle exceptions
thrown during dvmUnlockObject() with the wrong dalvik PC (the
dPC of the unlock, rather than the instruction following the unlock).
Similar issue with the x86 interpreter fixed. Also, deleted armv7-a
MONITOR_ENTER template, which turned out to be identical to the armv5te
one.
diff --git a/vm/compiler/codegen/arm/CodegenDriver.c b/vm/compiler/codegen/arm/CodegenDriver.c
index 99d392d..4048c0a 100644
--- a/vm/compiler/codegen/arm/CodegenDriver.c
+++ b/vm/compiler/codegen/arm/CodegenDriver.c
@@ -1276,6 +1276,15 @@
loadConstant(cUnit, r2, (int)dvmUnlockObject);
/* Do the call */
opReg(cUnit, kOpBlx, r2);
+ opRegImm(cUnit, kOpCmp, r0, 0); /* Did we throw? */
+ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+ loadConstant(cUnit, r0,
+ (int) (cUnit->method->insns + mir->offset +
+ dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_EXIT)));
+ genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branchOver->generic.target = (LIR *) target;
dvmCompilerColbberCallRegs(cUnit);
}
}
diff --git a/vm/compiler/codegen/arm/Thumb2/Gen.c b/vm/compiler/codegen/arm/Thumb2/Gen.c
index 071964a..1390d64 100644
--- a/vm/compiler/codegen/arm/Thumb2/Gen.c
+++ b/vm/compiler/codegen/arm/Thumb2/Gen.c
@@ -253,6 +253,15 @@
sizeof(StackSaveArea) -
offsetof(StackSaveArea, xtra.currentPc));
opReg(cUnit, kOpBlx, r7);
+ opRegImm(cUnit, kOpCmp, r0, 0); /* Did we throw? */
+ ArmLIR *branchOver = opCondBranch(cUnit, kArmCondNe);
+ loadConstant(cUnit, r0,
+ (int) (cUnit->method->insns + mir->offset +
+ dexGetInstrWidthAbs(gDvm.instrWidth, OP_MONITOR_EXIT)));
+ genDispatchToHandler(cUnit, TEMPLATE_THROW_EXCEPTION_COMMON);
+ ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
+ target->defMask = ENCODE_ALL;
+ branchOver->generic.target = (LIR *) target;
dvmCompilerColbberCallRegs(cUnit);
}
diff --git a/vm/compiler/template/armv7-a/TEMPLATE_MONITOR_ENTER.S b/vm/compiler/template/armv7-a/TEMPLATE_MONITOR_ENTER.S
deleted file mode 100644
index 114bbdc..0000000
--- a/vm/compiler/template/armv7-a/TEMPLATE_MONITOR_ENTER.S
+++ /dev/null
@@ -1,32 +0,0 @@
- /*
- * thumb2 specific.
- *
- * In this variant of the MONITOR_ENTER handler, we assume that
- * the test for the simple thin lock case has already been done.
- * So, we'll just call dvmLockObject(), refresh the
- * jit's on/off switch on return and then bail out to the interpreter.
- * We have to bail to the interpreter because the translation cache
- * may have been cleared while we were blocked on a monitor in
- * dvmLockObject.
- *
- * On entry:
- * r0 - self pointer
- * r1 - the object (which has already been null-checked by the caller
- * r4 - the Dalvik PC of the following instruction.
- *
- */
- ldr r2, .LdvmLockObject
- mov r3, #0 @ Record that we're not returning
- str r3, [r0, #offThread_inJitCodeCache]
- blx r2 @ dvmLockObject(self, obj)
- @ refresh Jit's on/off status
- ldr r0, [rGLUE, #offGlue_ppJitProfTable]
- ldr r0, [r0]
- ldr r2, .LdvmJitToInterpNoChain
- str r0, [rGLUE, #offGlue_pJitProfTable]
- @ Bail to interpreter - no chain [note - r4 still contains rPC]
-#if defined(EXIT_STATS)
- mov r0, #kHeavyweightMonitor
-#endif
- bx r2
-
diff --git a/vm/compiler/template/config-armv7-a b/vm/compiler/template/config-armv7-a
index 29d9019..be7af31 100644
--- a/vm/compiler/template/config-armv7-a
+++ b/vm/compiler/template/config-armv7-a
@@ -46,7 +46,7 @@
op TEMPLATE_STRING_COMPARETO armv5te
op TEMPLATE_STRING_INDEXOF armv5te
op TEMPLATE_INTERPRET armv5te
- op TEMPLATE_MONITOR_ENTER armv7-a
+ op TEMPLATE_MONITOR_ENTER armv5te
op TEMPLATE_MONITOR_ENTER_DEBUG armv5te
op-end
diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
index 6d9b7f0..e83e773 100644
--- a/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
+++ b/vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S
@@ -1371,23 +1371,17 @@
.balign 4
.global dvmCompiler_TEMPLATE_MONITOR_ENTER
dvmCompiler_TEMPLATE_MONITOR_ENTER:
-/* File: armv7-a/TEMPLATE_MONITOR_ENTER.S */
+/* File: armv5te/TEMPLATE_MONITOR_ENTER.S */
/*
- * thumb2 specific.
- *
- * In this variant of the MONITOR_ENTER handler, we assume that
- * the test for the simple thin lock case has already been done.
- * So, we'll just call dvmLockObject(), refresh the
- * jit's on/off switch on return and then bail out to the interpreter.
- * We have to bail to the interpreter because the translation cache
- * may have been cleared while we were blocked on a monitor in
- * dvmLockObject.
+ * Call out to the runtime to lock an object. Because this thread
+ * may have been suspended in THREAD_MONITOR state and the Jit's
+ * translation cache subsequently cleared, we cannot return directly.
+ * Instead, unconditionally transition to the interpreter to resume.
*
* On entry:
* r0 - self pointer
* r1 - the object (which has already been null-checked by the caller
* r4 - the Dalvik PC of the following instruction.
- *
*/
ldr r2, .LdvmLockObject
mov r3, #0 @ Record that we're not returning
diff --git a/vm/mterp/armv5te/OP_MONITOR_EXIT.S b/vm/mterp/armv5te/OP_MONITOR_EXIT.S
index b334ae9..c9aedf0 100644
--- a/vm/mterp/armv5te/OP_MONITOR_EXIT.S
+++ b/vm/mterp/armv5te/OP_MONITOR_EXIT.S
@@ -13,12 +13,15 @@
EXPORT_PC() @ before fetch: export the PC
GET_VREG(r1, r2) @ r1<- vAA (object)
cmp r1, #0 @ null object?
- beq common_errNullObject @ yes
+ beq 1f @ yes
ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
bl dvmUnlockObject @ r0<- success for unlock(self, obj)
cmp r0, #0 @ failed?
- beq common_exceptionThrown @ yes, exception is pending
FETCH_ADVANCE_INST(1) @ before throw: advance rPC, load rINST
+ beq common_exceptionThrown @ yes, exception is pending
GET_INST_OPCODE(ip) @ extract opcode from rINST
GOTO_OPCODE(ip) @ jump to next instruction
+1:
+ FETCH_ADVANCE_INST(1) @ advance before throw
+ b common_errNullObject
diff --git a/vm/mterp/out/InterpAsm-armv4t.S b/vm/mterp/out/InterpAsm-armv4t.S
index 2b1897e..c3cebb7 100644
--- a/vm/mterp/out/InterpAsm-armv4t.S
+++ b/vm/mterp/out/InterpAsm-armv4t.S
@@ -908,14 +908,17 @@
EXPORT_PC() @ before fetch: export the PC
GET_VREG(r1, r2) @ r1<- vAA (object)
cmp r1, #0 @ null object?
- beq common_errNullObject @ yes
+ beq 1f @ yes
ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
bl dvmUnlockObject @ r0<- success for unlock(self, obj)
cmp r0, #0 @ failed?
- beq common_exceptionThrown @ yes, exception is pending
FETCH_ADVANCE_INST(1) @ before throw: advance rPC, load rINST
+ beq common_exceptionThrown @ yes, exception is pending
GET_INST_OPCODE(ip) @ extract opcode from rINST
GOTO_OPCODE(ip) @ jump to next instruction
+1:
+ FETCH_ADVANCE_INST(1) @ advance before throw
+ b common_errNullObject
/* ------------------------------ */
diff --git a/vm/mterp/out/InterpAsm-armv5te-vfp.S b/vm/mterp/out/InterpAsm-armv5te-vfp.S
index 0e82c92..2064668 100644
--- a/vm/mterp/out/InterpAsm-armv5te-vfp.S
+++ b/vm/mterp/out/InterpAsm-armv5te-vfp.S
@@ -908,14 +908,17 @@
EXPORT_PC() @ before fetch: export the PC
GET_VREG(r1, r2) @ r1<- vAA (object)
cmp r1, #0 @ null object?
- beq common_errNullObject @ yes
+ beq 1f @ yes
ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
bl dvmUnlockObject @ r0<- success for unlock(self, obj)
cmp r0, #0 @ failed?
- beq common_exceptionThrown @ yes, exception is pending
FETCH_ADVANCE_INST(1) @ before throw: advance rPC, load rINST
+ beq common_exceptionThrown @ yes, exception is pending
GET_INST_OPCODE(ip) @ extract opcode from rINST
GOTO_OPCODE(ip) @ jump to next instruction
+1:
+ FETCH_ADVANCE_INST(1) @ advance before throw
+ b common_errNullObject
/* ------------------------------ */
diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S
index 73bf602..3a41054 100644
--- a/vm/mterp/out/InterpAsm-armv5te.S
+++ b/vm/mterp/out/InterpAsm-armv5te.S
@@ -908,14 +908,17 @@
EXPORT_PC() @ before fetch: export the PC
GET_VREG(r1, r2) @ r1<- vAA (object)
cmp r1, #0 @ null object?
- beq common_errNullObject @ yes
+ beq 1f @ yes
ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
bl dvmUnlockObject @ r0<- success for unlock(self, obj)
cmp r0, #0 @ failed?
- beq common_exceptionThrown @ yes, exception is pending
FETCH_ADVANCE_INST(1) @ before throw: advance rPC, load rINST
+ beq common_exceptionThrown @ yes, exception is pending
GET_INST_OPCODE(ip) @ extract opcode from rINST
GOTO_OPCODE(ip) @ jump to next instruction
+1:
+ FETCH_ADVANCE_INST(1) @ advance before throw
+ b common_errNullObject
/* ------------------------------ */
diff --git a/vm/mterp/out/InterpAsm-armv7-a.S b/vm/mterp/out/InterpAsm-armv7-a.S
index ccd02b3..bc3b7eb 100644
--- a/vm/mterp/out/InterpAsm-armv7-a.S
+++ b/vm/mterp/out/InterpAsm-armv7-a.S
@@ -905,14 +905,17 @@
EXPORT_PC() @ before fetch: export the PC
GET_VREG(r1, r2) @ r1<- vAA (object)
cmp r1, #0 @ null object?
- beq common_errNullObject @ yes
+ beq 1f @ yes
ldr r0, [rGLUE, #offGlue_self] @ r0<- glue->self
bl dvmUnlockObject @ r0<- success for unlock(self, obj)
cmp r0, #0 @ failed?
- beq common_exceptionThrown @ yes, exception is pending
FETCH_ADVANCE_INST(1) @ before throw: advance rPC, load rINST
+ beq common_exceptionThrown @ yes, exception is pending
GET_INST_OPCODE(ip) @ extract opcode from rINST
GOTO_OPCODE(ip) @ jump to next instruction
+1:
+ FETCH_ADVANCE_INST(1) @ advance before throw
+ b common_errNullObject
/* ------------------------------ */
diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S
index 3783abe..1d9f36b 100644
--- a/vm/mterp/out/InterpAsm-x86.S
+++ b/vm/mterp/out/InterpAsm-x86.S
@@ -703,7 +703,7 @@
GET_GLUE(%ecx)
EXPORT_PC()
testl %eax,%eax # null object?
- je common_errNullObject # go if so
+ je .LOP_MONITOR_EXIT_errNullObject # go if so
movl offGlue_self(%ecx),%ecx # ecx<- glue->self
movl %eax,OUT_ARG1(%esp)
SPILL(rPC)
@@ -6281,9 +6281,12 @@
UNSPILL(rPC)
FETCH_INST_WORD(1)
testl %eax,%eax # success?
- je common_exceptionThrown # no, exception pending
ADVANCE_PC(1)
+ je common_exceptionThrown # no, exception pending
GOTO_NEXT
+.LOP_MONITOR_EXIT_errNullObject:
+ ADVANCE_PC(1) # advance before throw
+ jmp common_errNullObject
/* continuation for OP_CHECK_CAST */
diff --git a/vm/mterp/x86/OP_MONITOR_EXIT.S b/vm/mterp/x86/OP_MONITOR_EXIT.S
index b360a3e..788b7a7 100644
--- a/vm/mterp/x86/OP_MONITOR_EXIT.S
+++ b/vm/mterp/x86/OP_MONITOR_EXIT.S
@@ -14,7 +14,7 @@
GET_GLUE(%ecx)
EXPORT_PC()
testl %eax,%eax # null object?
- je common_errNullObject # go if so
+ je .L${opcode}_errNullObject # go if so
movl offGlue_self(%ecx),%ecx # ecx<- glue->self
movl %eax,OUT_ARG1(%esp)
SPILL(rPC)
@@ -27,6 +27,9 @@
UNSPILL(rPC)
FETCH_INST_WORD(1)
testl %eax,%eax # success?
- je common_exceptionThrown # no, exception pending
ADVANCE_PC(1)
+ je common_exceptionThrown # no, exception pending
GOTO_NEXT
+.L${opcode}_errNullObject:
+ ADVANCE_PC(1) # advance before throw
+ jmp common_errNullObject