Merge "Improve System.arraycopy's ArrayIndexOutOfBoundsException messages." into dalvik-dev
diff --git a/vm/DvmDex.c b/vm/DvmDex.c
index aefdb81..02dd758 100644
--- a/vm/DvmDex.c
+++ b/vm/DvmDex.c
@@ -227,10 +227,14 @@
  * This requires changing the access permissions to read-write, updating
  * the value, and then resetting the permissions.
  *
- * This does not make any synchronization guarantees.  It's important for the
- * caller(s) to work out mutual exclusion, at least on a page granularity,
- * to avoid a race where one threads sets read-write, another thread sets
- * read-only, and then the first thread does a write.
+ * We need to ensure mutual exclusion at a page granularity to avoid a race
+ * where one threads sets read-write, another thread sets read-only, and
+ * then the first thread does a write.  Since we don't do a lot of updates,
+ * and the window is small, we just use a lock across the entire DvmDex.
+ * We're only trying to make the page state change atomic; it's up to the
+ * caller to ensure that multiple threads aren't stomping on the same
+ * location (e.g. breakpoints and verifier/optimizer changes happening
+ * simultaneously).
  *
  * TODO: if we're back to the original state of the page, use
  * madvise(MADV_DONTNEED) to release the private/dirty copy.
@@ -244,6 +248,12 @@
         return true;
     }
 
+    /*
+     * We're not holding this for long, so we don't bother with switching
+     * to VMWAIT.
+     */
+    dvmLockMutex(&pDvmDex->modLock);
+
     LOGV("+++ change byte at %p from 0x%02x to 0x%02x\n", addr, *addr, newVal);
     if (sysChangeMapAccess(addr, 1, true, &pDvmDex->memMap) != 0) {
         LOGD("NOTE: DEX page access change (->RW) failed\n");
@@ -257,6 +267,8 @@
         /* expected on files mounted from FAT; keep going */
     }
 
+    dvmUnlockMutex(&pDvmDex->modLock);
+
     return true;
 }
 
@@ -273,6 +285,12 @@
         return true;
     }
 
+    /*
+     * We're not holding this for long, so we don't bother with switching
+     * to VMWAIT.
+     */
+    dvmLockMutex(&pDvmDex->modLock);
+
     LOGV("+++ change 2byte at %p from 0x%04x to 0x%04x\n", addr, *addr, newVal);
     if (sysChangeMapAccess(addr, 2, true, &pDvmDex->memMap) != 0) {
         LOGD("NOTE: DEX page access change (->RW) failed\n");
@@ -286,5 +304,7 @@
         /* expected on files mounted from FAT; keep going */
     }
 
+    dvmUnlockMutex(&pDvmDex->modLock);
+
     return true;
 }
diff --git a/vm/DvmDex.h b/vm/DvmDex.h
index 9f3903a..803be1f 100644
--- a/vm/DvmDex.h
+++ b/vm/DvmDex.h
@@ -59,6 +59,9 @@
 
     /* shared memory region with file contents */
     MemMapping          memMap;
+
+    /* lock ensuring mutual exclusion during updates */
+    pthread_mutex_t     modLock;
 } DvmDex;
 
 
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index 4310a06..3c7be11 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -2946,6 +2946,9 @@
     u2 oldInsn = *oldInsns;
     bool result = false;
 
+    if (gDvm.optimizing)
+        LOGD("Weird: RFI during dexopt?");
+
     //LOGD("  was 0x%04x\n", oldInsn);
     u2* newInsns = (u2*) meth->insns + insnIdx;
 
@@ -3045,50 +3048,6 @@
     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(const 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);
-    }
-}
-
 
 /*
  * ===========================================================================
@@ -4539,13 +4498,6 @@
                 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:
@@ -4680,13 +4632,6 @@
                 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:
@@ -4826,13 +4771,6 @@
                 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:
@@ -4955,13 +4893,6 @@
                 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:
diff --git a/vm/analysis/DexPrepare.c b/vm/analysis/DexPrepare.c
index 2bf33a3..d9871c9 100644
--- a/vm/analysis/DexPrepare.c
+++ b/vm/analysis/DexPrepare.c
@@ -965,7 +965,7 @@
             LOGV("DexOpt: not optimizing '%s': not verified\n",
                 classDescriptor);
         } else {
-            dvmOptimizeClass(clazz);
+            dvmOptimizeClass(clazz, false);
 
             /* set the flag whether or not we actually changed anything */
             ((DexClassDef*)pClassDef)->accessFlags |= CLASS_ISOPTIMIZED;
diff --git a/vm/analysis/Optimize.c b/vm/analysis/Optimize.c
index 6703553..d7379c7 100644
--- a/vm/analysis/Optimize.c
+++ b/vm/analysis/Optimize.c
@@ -36,8 +36,10 @@
 
 
 /* fwd */
-static bool optimizeMethod(Method* method);
-static void rewriteInstField(Method* method, u2* insns, OpCode newOpc);
+static void optimizeMethod(Method* method, bool essentialOnly);
+static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc,
+    OpCode volatileOpc);
+static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc);
 static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc);
 static bool rewriteEmptyDirectInvoke(Method* method, u2* insns);
 static bool rewriteExecuteInline(Method* method, u2* insns,
@@ -132,25 +134,23 @@
 
 /*
  * Optimize the specified class.
+ *
+ * If "essentialOnly" is true, we only do essential optimizations.  For
+ * example, accesses to volatile 64-bit fields must be replaced with
+ * "-wide-volatile" instructions or the program could behave incorrectly.
+ * (Skipping non-essential optimizations makes us a little bit faster, and
+ * more importantly avoids dirtying DEX pages.)
  */
-void dvmOptimizeClass(ClassObject* clazz)
+void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly)
 {
     int i;
 
     for (i = 0; i < clazz->directMethodCount; i++) {
-        if (!optimizeMethod(&clazz->directMethods[i] ))
-            goto fail;
+        optimizeMethod(&clazz->directMethods[i], essentialOnly);
     }
     for (i = 0; i < clazz->virtualMethodCount; i++) {
-        if (!optimizeMethod(&clazz->virtualMethods[i]))
-            goto fail;
+        optimizeMethod(&clazz->virtualMethods[i], essentialOnly);
     }
-
-    return;
-
-fail:
-    // TODO: show when in "verbose" mode
-    LOGV("DexOpt: ceasing optimization attempts on %s\n", clazz->descriptor);
 }
 
 /*
@@ -158,101 +158,152 @@
  *
  * This does a single pass through the code, examining each instruction.
  *
- * Returns "true" if all went well, "false" if we bailed out early when
- * something failed.
+ * This is not expected to fail if the class was successfully verified.
+ * The only significant failure modes occur when an "essential" update fails,
+ * but we can't generally identify those: if we can't look up a field,
+ * we can't know if the field access was supposed to be handled as volatile.
+ *
+ * Instead, we give it our best effort, and hope for the best.  For 100%
+ * reliability, only optimize a class after verification succeeds.
  */
-static bool optimizeMethod(Method* method)
+static void optimizeMethod(Method* method, bool essentialOnly)
 {
     u4 insnsSize;
     u2* insns;
     u2 inst;
 
+    if (!gDvm.optimizing && !essentialOnly) {
+        /* unexpected; will force copy-on-write of a lot of pages */
+        LOGD("NOTE: doing full bytecode optimization outside dexopt\n");
+    }
+
     if (dvmIsNativeMethod(method) || dvmIsAbstractMethod(method))
-        return true;
+        return;
 
     insns = (u2*) method->insns;
     assert(insns != NULL);
     insnsSize = dvmGetMethodInsnsSize(method);
 
     while (insnsSize > 0) {
+        OpCode quickOpc, volatileOpc;
         int width;
 
         inst = *insns & 0xff;
+        bool notMatched = false;
 
         switch (inst) {
-#ifndef PROFILE_FIELD_ACCESS    /* quickened instructions not instrumented */
         case OP_IGET:
         case OP_IGET_BOOLEAN:
         case OP_IGET_BYTE:
         case OP_IGET_CHAR:
         case OP_IGET_SHORT:
-            rewriteInstField(method, insns, OP_IGET_QUICK);
-            break;
+            quickOpc = OP_IGET_QUICK;
+            volatileOpc = OP_IGET_VOLATILE;
+            goto rewrite_inst_field;
         case OP_IGET_WIDE:
-            rewriteInstField(method, insns, OP_IGET_WIDE_QUICK);
-            break;
+            quickOpc = OP_IGET_WIDE_QUICK;
+            volatileOpc = OP_IGET_WIDE_VOLATILE;
+            goto rewrite_inst_field;
         case OP_IGET_OBJECT:
-            rewriteInstField(method, insns, OP_IGET_OBJECT_QUICK);
-            break;
+            quickOpc = OP_IGET_OBJECT_QUICK;
+            volatileOpc = OP_IGET_OBJECT_VOLATILE;
+            goto rewrite_inst_field;
         case OP_IPUT:
         case OP_IPUT_BOOLEAN:
         case OP_IPUT_BYTE:
         case OP_IPUT_CHAR:
         case OP_IPUT_SHORT:
-            rewriteInstField(method, insns, OP_IPUT_QUICK);
-            break;
+            quickOpc = OP_IPUT_QUICK;
+            volatileOpc = OP_IPUT_VOLATILE;
+            goto rewrite_inst_field;
         case OP_IPUT_WIDE:
-            rewriteInstField(method, insns, OP_IPUT_WIDE_QUICK);
-            break;
+            quickOpc = OP_IPUT_WIDE_QUICK;
+            volatileOpc = OP_IPUT_WIDE_VOLATILE;
+            goto rewrite_inst_field;
         case OP_IPUT_OBJECT:
-            rewriteInstField(method, insns, OP_IPUT_OBJECT_QUICK);
-            break;
-#endif
-
-        case OP_INVOKE_VIRTUAL:
-            if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) {
-                if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_VIRTUAL_QUICK))
-                    return false;
-            }
-            break;
-        case OP_INVOKE_VIRTUAL_RANGE:
-            if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) {
-                if (!rewriteVirtualInvoke(method, insns,
-                        OP_INVOKE_VIRTUAL_QUICK_RANGE))
-                {
-                    return false;
-                }
-            }
-            break;
-        case OP_INVOKE_SUPER:
-            if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK))
-                return false;
-            break;
-        case OP_INVOKE_SUPER_RANGE:
-            if (!rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE))
-                return false;
+            quickOpc = OP_IPUT_OBJECT_QUICK;
+            volatileOpc = OP_IPUT_OBJECT_VOLATILE;
+rewrite_inst_field:
+            if (essentialOnly)
+                quickOpc = OP_NOP;
+            rewriteInstField(method, insns, quickOpc, volatileOpc);
             break;
 
-        case OP_INVOKE_DIRECT:
-            if (!rewriteExecuteInline(method, insns, METHOD_DIRECT)) {
-                if (!rewriteEmptyDirectInvoke(method, insns))
-                    return false;
-            }
-            break;
-        case OP_INVOKE_DIRECT_RANGE:
-            rewriteExecuteInlineRange(method, insns, METHOD_DIRECT);
-            break;
-
-        case OP_INVOKE_STATIC:
-            rewriteExecuteInline(method, insns, METHOD_STATIC);
-            break;
-        case OP_INVOKE_STATIC_RANGE:
-            rewriteExecuteInlineRange(method, insns, METHOD_STATIC);
+        case OP_SGET:
+        case OP_SGET_BOOLEAN:
+        case OP_SGET_BYTE:
+        case OP_SGET_CHAR:
+        case OP_SGET_SHORT:
+            volatileOpc = OP_SGET_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SGET_WIDE:
+            volatileOpc = OP_SGET_WIDE_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SGET_OBJECT:
+            volatileOpc = OP_SGET_OBJECT_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SPUT:
+        case OP_SPUT_BOOLEAN:
+        case OP_SPUT_BYTE:
+        case OP_SPUT_CHAR:
+        case OP_SPUT_SHORT:
+            volatileOpc = OP_SPUT_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SPUT_WIDE:
+            volatileOpc = OP_SPUT_WIDE_VOLATILE;
+            goto rewrite_static_field;
+        case OP_SPUT_OBJECT:
+            volatileOpc = OP_SPUT_OBJECT_VOLATILE;
+rewrite_static_field:
+            rewriteStaticField(method, insns, volatileOpc);
             break;
 
         default:
-            // ignore this instruction
-            ;
+            /* not one of the "essential" replacements; check for more */
+            notMatched = true;
+        }
+
+        if (notMatched && !essentialOnly) {
+            switch (inst) {
+            case OP_INVOKE_VIRTUAL:
+                if (!rewriteExecuteInline(method, insns, METHOD_VIRTUAL)) {
+                    rewriteVirtualInvoke(method, insns,
+                            OP_INVOKE_VIRTUAL_QUICK);
+                }
+                break;
+            case OP_INVOKE_VIRTUAL_RANGE:
+                if (!rewriteExecuteInlineRange(method, insns, METHOD_VIRTUAL)) {
+                    rewriteVirtualInvoke(method, insns,
+                            OP_INVOKE_VIRTUAL_QUICK_RANGE);
+                }
+                break;
+            case OP_INVOKE_SUPER:
+                rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK);
+                break;
+            case OP_INVOKE_SUPER_RANGE:
+                rewriteVirtualInvoke(method, insns, OP_INVOKE_SUPER_QUICK_RANGE);
+                break;
+
+            case OP_INVOKE_DIRECT:
+                if (!rewriteExecuteInline(method, insns, METHOD_DIRECT)) {
+                    rewriteEmptyDirectInvoke(method, insns);
+                }
+                break;
+            case OP_INVOKE_DIRECT_RANGE:
+                rewriteExecuteInlineRange(method, insns, METHOD_DIRECT);
+                break;
+
+            case OP_INVOKE_STATIC:
+                rewriteExecuteInline(method, insns, METHOD_STATIC);
+                break;
+            case OP_INVOKE_STATIC_RANGE:
+                rewriteExecuteInlineRange(method, insns, METHOD_STATIC);
+                break;
+
+            default:
+                /* nothing to do for this instruction */
+                ;
+            }
         }
 
         width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns);
@@ -263,9 +314,21 @@
     }
 
     assert(insnsSize == 0);
-    return true;
 }
 
+/*
+ * Update a 16-bit code unit in "meth".
+ */
+static inline void updateCode(const Method* meth, u2* ptr, u2 newVal)
+{
+    if (gDvm.optimizing) {
+        /* dexopt time, alter the output directly */
+        *ptr = newVal;
+    } else {
+        /* runtime, toggle the page read/write status */
+        dvmDexChangeDex2(meth->clazz->pDvmDex, ptr, newVal);
+    }
+}
 
 /*
  * If "referrer" and "resClass" don't come from the same DEX file, and
@@ -541,36 +604,87 @@
  *   op vA, vB, field@CCCC
  *
  * Where vA holds the value, vB holds the object reference, and CCCC is
- * the field reference constant pool offset.  We want to replace CCCC
- * with the byte offset from the start of the object.
+ * the field reference constant pool offset.  For a non-volatile field,
+ * we want to replace the opcode with "quickOpc" and replace CCCC with
+ * the byte offset from the start of the object.  For a volatile field,
+ * we just want to replace the opcode with "volatileOpc".
  *
- * "clazz" is the referring class.  We need this because we verify
- * access rights here.
+ * If "quickOpc" is OP_NOP, and this is a non-volatile field, we don't
+ * do anything.
+ *
+ * "method" is the referring method.
  */
-static void rewriteInstField(Method* method, u2* insns, OpCode newOpc)
+static bool rewriteInstField(Method* method, u2* insns, OpCode quickOpc,
+    OpCode volatileOpc)
 {
     ClassObject* clazz = method->clazz;
     u2 fieldIdx = insns[1];
-    InstField* field;
+    InstField* instField;
 
-    field = dvmOptResolveInstField(clazz, fieldIdx, NULL);
-    if (field == NULL) {
-        LOGI("DexOpt: unable to optimize field ref 0x%04x at 0x%02x in %s.%s\n",
+    instField = dvmOptResolveInstField(clazz, fieldIdx, NULL);
+    if (instField == NULL) {
+        LOGI("DexOpt: unable to optimize instance field ref "
+             "0x%04x at 0x%02x in %s.%s\n",
             fieldIdx, (int) (insns - method->insns), clazz->descriptor,
             method->name);
-        return;
+        return false;
     }
 
-    if (field->byteOffset >= 65536) {
-        LOGI("DexOpt: field offset exceeds 64K (%d)\n", field->byteOffset);
-        return;
+    if (instField->byteOffset >= 65536) {
+        LOGI("DexOpt: field offset exceeds 64K (%d)\n", instField->byteOffset);
+        return false;
     }
 
-    insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
-    insns[1] = (u2) field->byteOffset;
-    LOGVV("DexOpt: rewrote access to %s.%s --> %d\n",
-        field->field.clazz->descriptor, field->field.name,
-        field->byteOffset);
+    if (dvmIsVolatileField(&instField->field)) {
+        updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc);
+        LOGV("DexOpt: rewrote ifield access %s.%s --> volatile\n",
+            instField->field.clazz->descriptor, instField->field.name);
+    } else if (quickOpc != OP_NOP) {
+        updateCode(method, insns, (insns[0] & 0xff00) | (u2) quickOpc);
+        updateCode(method, insns+1, (u2) instField->byteOffset);
+        LOGV("DexOpt: rewrote ifield access %s.%s --> %d\n",
+            instField->field.clazz->descriptor, instField->field.name,
+            instField->byteOffset);
+    } else {
+        LOGV("DexOpt: no rewrite of ifield access %s.%s\n",
+            instField->field.clazz->descriptor, instField->field.name);
+    }
+
+    return true;
+}
+
+/*
+ * Rewrite an sget/sput instruction.  These all have the form:
+ *   op vAA, field@BBBB
+ *
+ * Where vAA holds the value, and BBBB is the field reference constant
+ * pool offset.  There is no "quick" form of static field accesses, so
+ * this is only useful for volatile fields.
+ *
+ * "method" is the referring method.
+ */
+static bool rewriteStaticField(Method* method, u2* insns, OpCode volatileOpc)
+{
+    ClassObject* clazz = method->clazz;
+    u2 fieldIdx = insns[1];
+    StaticField* staticField;
+
+    staticField = dvmOptResolveStaticField(clazz, fieldIdx, NULL);
+    if (staticField == NULL) {
+        LOGI("DexOpt: unable to optimize static field ref "
+             "0x%04x at 0x%02x in %s.%s\n",
+            fieldIdx, (int) (insns - method->insns), clazz->descriptor,
+            method->name);
+        return false;
+    }
+
+    if (dvmIsVolatileField(&staticField->field)) {
+        updateCode(method, insns, (insns[0] & 0xff00) | (u2) volatileOpc);
+        LOGV("DexOpt: rewrote sfield access %s.%s --> volatile\n",
+            staticField->field.clazz->descriptor, staticField->field.name);
+    }
+
+    return true;
 }
 
 /*
@@ -738,8 +852,8 @@
      * Note: Method->methodIndex is a u2 and is range checked during the
      * initial load.
      */
-    insns[0] = (insns[0] & 0xff00) | (u2) newOpc;
-    insns[1] = baseMethod->methodIndex;
+    updateCode(method, insns, (insns[0] & 0xff00) | (u2) newOpc);
+    updateCode(method, insns+1, baseMethod->methodIndex);
 
     //LOGI("DexOpt: rewrote call to %s.%s --> %s.%s\n",
     //    method->clazz->descriptor, method->name,
@@ -783,7 +897,8 @@
          * OP_INVOKE_DIRECT when debugging is enabled.
          */
         assert((insns[0] & 0xff) == OP_INVOKE_DIRECT);
-        insns[0] = (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY;
+        updateCode(method, insns,
+            (insns[0] & 0xff00) | (u2) OP_INVOKE_DIRECT_EMPTY);
 
         //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n",
         //    method->clazz->descriptor, method->name,
@@ -914,8 +1029,9 @@
             assert((insns[0] & 0xff) == OP_INVOKE_DIRECT ||
                    (insns[0] & 0xff) == OP_INVOKE_STATIC ||
                    (insns[0] & 0xff) == OP_INVOKE_VIRTUAL);
-            insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE;
-            insns[1] = (u2) inlineSubs->inlineIdx;
+            updateCode(method, insns,
+                (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE);
+            updateCode(method, insns+1, (u2) inlineSubs->inlineIdx);
 
             //LOGI("DexOpt: execute-inline %s.%s --> %s.%s\n",
             //    method->clazz->descriptor, method->name,
@@ -954,8 +1070,9 @@
             assert((insns[0] & 0xff) == OP_INVOKE_DIRECT_RANGE ||
                    (insns[0] & 0xff) == OP_INVOKE_STATIC_RANGE ||
                    (insns[0] & 0xff) == OP_INVOKE_VIRTUAL_RANGE);
-            insns[0] = (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE;
-            insns[1] = (u2) inlineSubs->inlineIdx;
+            updateCode(method, insns,
+                (insns[0] & 0xff00) | (u2) OP_EXECUTE_INLINE_RANGE);
+            updateCode(method, insns+1, (u2) inlineSubs->inlineIdx);
 
             //LOGI("DexOpt: execute-inline/range %s.%s --> %s.%s\n",
             //    method->clazz->descriptor, method->name,
diff --git a/vm/analysis/Optimize.h b/vm/analysis/Optimize.h
index 1387663..30f7eef 100644
--- a/vm/analysis/Optimize.h
+++ b/vm/analysis/Optimize.h
@@ -29,7 +29,7 @@
 /*
  * Entry point from DEX preparation.
  */
-void dvmOptimizeClass(ClassObject* clazz);
+void dvmOptimizeClass(ClassObject* clazz, bool essentialOnly);
 
 /*
  * Abbreviated resolution functions, for use by optimization and verification
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
index cbb3d8f..2776100 100644
--- a/vm/oo/Class.c
+++ b/vm/oo/Class.c
@@ -23,6 +23,7 @@
 
 #include "Dalvik.h"
 #include "libdex/DexClass.h"
+#include "analysis/Optimize.h"
 
 #include <stdlib.h>
 #include <stddef.h>
@@ -1955,11 +1956,9 @@
     } while (0)
 
     /* arrays just point at Object's vtable; don't free vtable in this case.
-     * dvmIsArrayClass() checks clazz->descriptor, so we have to do this check
-     * before freeing the name.
      */
     clazz->vtableCount = -1;
-    if (dvmIsArrayClass(clazz)) {
+    if (clazz->vtable == gDvm.classJavaLangObject->vtable) {
         clazz->vtable = NULL;
     } else {
         NULL_AND_LINEAR_FREE(clazz->vtable);
@@ -4284,9 +4283,9 @@
 verify_failed:
             dvmThrowExceptionWithClassMessage("Ljava/lang/VerifyError;",
                 clazz->descriptor);
-            dvmSetFieldObject((Object *)clazz,
-                              offsetof(ClassObject, verifyErrorClass),
-                              (Object *)dvmGetException(self)->clazz);
+            dvmSetFieldObject((Object*) clazz,
+                offsetof(ClassObject, verifyErrorClass),
+                (Object*) dvmGetException(self)->clazz);
             clazz->status = CLASS_ERROR;
             goto bail_unlock;
         }
@@ -4295,8 +4294,20 @@
     }
 noverify:
 
+    /*
+     * We need to ensure that certain instructions, notably accesses to
+     * volatile fields, are replaced before any code is executed.  This
+     * must happen even if DEX optimizations are disabled.
+     */
+    if (!IS_CLASS_FLAG_SET(clazz, CLASS_ISOPTIMIZED)) {
+        LOGV("+++ late optimize on %s (pv=%d)\n",
+            clazz->descriptor, IS_CLASS_FLAG_SET(clazz, CLASS_ISPREVERIFIED));
+        dvmOptimizeClass(clazz, true);
+        SET_CLASS_FLAG(clazz, CLASS_ISOPTIMIZED);
+    }
+
 #ifdef WITH_DEBUGGER
-    /* update instruction stream now that the verifier is done */
+    /* update instruction stream now that verification + optimization is done */
     dvmFlushBreakpoints(clazz);
 #endif
 
diff --git a/vm/test/AtomicTest.c b/vm/test/AtomicTest.c
index ce018bb..e976d8d 100644
--- a/vm/test/AtomicTest.c
+++ b/vm/test/AtomicTest.c
@@ -345,7 +345,7 @@
     swapTest = 0x11111111;
     android_atomic_and(0xfffdaf96, &andTest);
     android_atomic_or(0xdeaaeb00, &orTest);
-    int oldSwap = android_atomic_acquire_swap(0x22222222, &swapTest);
+    int oldSwap = android_atomic_swap(0x22222222, &swapTest);
     int oldSwap2 = android_atomic_release_swap(0x33333333, &swapTest);
     if (android_atomic_release_cas(failingCasTest+1, failingCasTest-1,
             &failingCasTest) == 0)