Make wide-volatile loads and stores atomic.
This implements the four wide-volatile instructions added in a previous
change, and modifies the verifier to substitute the opcodes into the
instruction stream when appropriate.
For mterp, the ARM wide get/put instructions now have conditional code
that replaces ldrd/strd with a call to the quasiatomic functions. The
C version does essentially the same thing. ARMv4T lacks ldrd/stdrd, and
uses separate implementations for the wide field accesses, so those were
updated as well. x86 will just use stubs.
The JIT should punt these to the interpreter.
Change-Id: Ife88559ed1a698c3267d43c454896f6b12081c0f
Also:
- We don't seem to be using the negative widths in the instruction
table. Not sure they're useful anymore.
- Tabs -> spaces in x86-atom throw-verification-error impl.
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index 7ef559b..8187354 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -2921,6 +2921,10 @@
* Replace an instruction with "throw-verification-error". This allows us to
* defer error reporting until the code path is first used.
*
+ * This is expected to be called during "just in time" verification, not
+ * from within dexopt. (Verification failures in dexopt will result in
+ * postponement of verification to first use of the class.)
+ *
* The throw-verification-error instruction requires two code units. Some
* of the replaced instructions require three; the third code unit will
* receive a "nop". The instruction's length will be left unchanged
@@ -2942,8 +2946,6 @@
u2 oldInsn = *oldInsns;
bool result = false;
- //dvmMakeCodeReadWrite(meth);
-
//LOGD(" was 0x%04x\n", oldInsn);
u2* newInsns = (u2*) meth->insns + insnIdx;
@@ -3040,10 +3042,53 @@
result = true;
bail:
- //dvmMakeCodeReadOnly(meth);
return result;
}
+/*
+ * Replace {iget,iput,sget,sput}-wide with the -wide-volatile form.
+ *
+ * If this is called during dexopt, we can modify the instruction in
+ * place. If this happens during just-in-time verification, we need to
+ * use the DEX read/write page feature.
+ *
+ * NOTE:
+ * This shouldn't really be tied to verification. It ought to be a
+ * separate pass that is run before or after the verifier. However, that
+ * requires a bunch of extra code, and the only advantage of doing so is
+ * that the feature isn't disabled when verification is turned off. At
+ * some point we may need to revisit this choice.
+ */
+static void replaceVolatileInstruction(Method* meth, InsnFlags* insnFlags,
+ int insnIdx)
+{
+ u2* oldInsns = (u2*)meth->insns + insnIdx;
+ u2 oldInsn = *oldInsns;
+ u2 newVal;
+
+ switch (oldInsn & 0xff) {
+ case OP_IGET_WIDE: newVal = OP_IGET_WIDE_VOLATILE; break;
+ case OP_IPUT_WIDE: newVal = OP_IPUT_WIDE_VOLATILE; break;
+ case OP_SGET_WIDE: newVal = OP_SGET_WIDE_VOLATILE; break;
+ case OP_SPUT_WIDE: newVal = OP_SPUT_WIDE_VOLATILE; break;
+ default:
+ LOGE("wide-volatile op mismatch (0x%x)\n", oldInsn);
+ dvmAbort();
+ return; // in-lieu-of noreturn attribute
+ }
+
+ /* merge new opcode into 16-bit code unit */
+ newVal |= (oldInsn & 0xff00);
+
+ if (gDvm.optimizing) {
+ /* dexopt time, alter the output */
+ *oldInsns = newVal;
+ } else {
+ /* runtime, make the page read/write */
+ dvmDexChangeDex2(meth->clazz->pDvmDex, oldInsns, newVal);
+ }
+}
+
/*
* ===========================================================================
@@ -4472,6 +4517,7 @@
}
break;
case OP_IGET_WIDE:
+ case OP_IGET_WIDE_VOLATILE:
{
RegType dstType;
ClassObject* fieldClass;
@@ -4506,6 +4552,13 @@
setRegisterType(workRegs, insnRegCount, decInsn.vA,
dstType, &failure);
}
+ if (VERIFY_OK(failure)) {
+ if (decInsn.opCode != OP_IGET_WIDE_VOLATILE &&
+ dvmIsVolatileField(&instField->field))
+ {
+ replaceVolatileInstruction(meth, insnFlags, insnIdx);
+ }
+ }
}
break;
case OP_IGET_OBJECT:
@@ -4602,6 +4655,7 @@
}
break;
case OP_IPUT_WIDE:
+ case OP_IPUT_WIDE_VOLATILE:
tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
if (VERIFY_OK(failure)) {
RegType typeHi =
@@ -4639,6 +4693,13 @@
failure = VERIFY_ERROR_GENERIC;
break;
}
+ if (VERIFY_OK(failure)) {
+ if (decInsn.opCode != OP_IPUT_WIDE_VOLATILE &&
+ dvmIsVolatileField(&instField->field))
+ {
+ replaceVolatileInstruction(meth, insnFlags, insnIdx);
+ }
+ }
}
break;
case OP_IPUT_OBJECT:
@@ -4748,6 +4809,7 @@
}
break;
case OP_SGET_WIDE:
+ case OP_SGET_WIDE_VOLATILE:
{
StaticField* staticField;
RegType dstType;
@@ -4775,6 +4837,13 @@
setRegisterType(workRegs, insnRegCount, decInsn.vA,
dstType, &failure);
}
+ if (VERIFY_OK(failure)) {
+ if (decInsn.opCode != OP_SGET_WIDE_VOLATILE &&
+ dvmIsVolatileField(&staticField->field))
+ {
+ replaceVolatileInstruction(meth, insnFlags, insnIdx);
+ }
+ }
}
break;
case OP_SGET_OBJECT:
@@ -4864,6 +4933,7 @@
}
break;
case OP_SPUT_WIDE:
+ case OP_SPUT_WIDE_VOLATILE:
tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure);
if (VERIFY_OK(failure)) {
RegType typeHi =
@@ -4894,6 +4964,13 @@
failure = VERIFY_ERROR_GENERIC;
break;
}
+ if (VERIFY_OK(failure)) {
+ if (decInsn.opCode != OP_SPUT_WIDE_VOLATILE &&
+ dvmIsVolatileField(&staticField->field))
+ {
+ replaceVolatileInstruction(meth, insnFlags, insnIdx);
+ }
+ }
}
break;
case OP_SPUT_OBJECT:
@@ -5420,10 +5497,6 @@
case OP_IPUT_QUICK:
case OP_IPUT_WIDE_QUICK:
case OP_IPUT_OBJECT_QUICK:
- case OP_IGET_WIDE_VOLATILE:
- case OP_IPUT_WIDE_VOLATILE:
- case OP_SGET_WIDE_VOLATILE:
- case OP_SPUT_WIDE_VOLATILE:
case OP_INVOKE_VIRTUAL_QUICK:
case OP_INVOKE_VIRTUAL_QUICK_RANGE:
case OP_INVOKE_SUPER_QUICK: