Merge "ART: Set methods to preverified in verify-none"
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index e98e572..f3bda2f 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -165,6 +165,7 @@
   void StackArgsIntsFirstImpl();
   void StackArgsFloatsFirstImpl();
   void StackArgsMixedImpl();
+  void StackArgsSignExtendedMips64Impl();
 
   JNIEnv* env_;
   jmethodID jmethod_;
@@ -1715,4 +1716,49 @@
 
 JNI_TEST(StackArgsMixed)
 
+void Java_MyClassNatives_stackArgsSignExtendedMips64(JNIEnv*, jclass, jint i1, jint i2, jint i3,
+                                                     jint i4, jint i5, jint i6, jint i7, jint i8) {
+  EXPECT_EQ(i1, 1);
+  EXPECT_EQ(i2, 2);
+  EXPECT_EQ(i3, 3);
+  EXPECT_EQ(i4, 4);
+  EXPECT_EQ(i5, 5);
+  EXPECT_EQ(i6, 6);
+  EXPECT_EQ(i7, 7);
+  EXPECT_EQ(i8, -8);
+
+#if defined(__mips__) && defined(__LP64__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+  // Mips64 ABI requires that arguments passed through stack be sign-extended 8B slots.
+  // First 8 arguments are passed through registers, check i7 and i8.
+  uint32_t stack1_high = *(&i7 + 1);
+  uint32_t stack2_high = *(&i8 + 1);
+
+  EXPECT_EQ(stack1_high, static_cast<uint32_t>(0));
+  EXPECT_EQ(stack2_high, static_cast<uint32_t>(0xffffffff));
+#else
+  LOG(INFO) << "Skipping stackArgsSignExtendedMips64 as there is nothing to be done on "
+            << kRuntimeISA;
+  // Force-print to std::cout so it's also outside the logcat.
+  std::cout << "Skipping stackArgsSignExtendedMips64 as there is nothing to be done on "
+            << kRuntimeISA << std::endl;
+#endif
+}
+
+void JniCompilerTest::StackArgsSignExtendedMips64Impl() {
+  SetUpForTest(true, "stackArgsSignExtendedMips64", "(IIIIIIII)V",
+               reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsSignExtendedMips64));
+  jint i1 = 1;
+  jint i2 = 2;
+  jint i3 = 3;
+  jint i4 = 4;
+  jint i5 = 5;
+  jint i6 = 6;
+  jint i7 = 7;
+  jint i8 = -8;
+
+  env_->CallStaticVoidMethod(jklass_, jmethod_, i1, i2, i3, i4, i5, i6, i7, i8);
+}
+
+JNI_TEST(StackArgsSignExtendedMips64)
+
 }  // namespace art
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 92ebf06..3efe7c7 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -27,6 +27,7 @@
 #include "mirror/class_loader.h"
 #include "mirror/dex_cache.h"
 #include "nodes.h"
+#include "optimizing_compiler.h"
 #include "reference_type_propagation.h"
 #include "register_allocator.h"
 #include "ssa_phi_elimination.h"
@@ -64,14 +65,14 @@
         // We use the original invoke type to ensure the resolution of the called method
         // works properly.
         if (!TryInline(call, call->GetDexMethodIndex())) {
-          if (kIsDebugBuild) {
+          if (kIsDebugBuild && IsCompilingWithCoreImage()) {
             std::string callee_name =
                 PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
             bool should_inline = callee_name.find("$inline$") != std::string::npos;
             CHECK(!should_inline) << "Could not inline " << callee_name;
           }
         } else {
-          if (kIsDebugBuild) {
+          if (kIsDebugBuild && IsCompilingWithCoreImage()) {
             std::string callee_name =
                 PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
             bool must_not_inline = callee_name.find("$noinline$") != std::string::npos;
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 1944ba6..1e51530 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -614,7 +614,8 @@
   {
     PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer);
     if (!builder.BuildGraph(*code_item)) {
-      CHECK(!shouldCompile) << "Could not build graph in optimizing compiler";
+      DCHECK(!(IsCompilingWithCoreImage() && shouldCompile))
+          << "Could not build graph in optimizing compiler";
       pass_observer.SetGraphInBadState();
       return nullptr;
     }
@@ -705,4 +706,9 @@
   return new OptimizingCompiler(driver);
 }
 
+bool IsCompilingWithCoreImage() {
+  const std::string& image = Runtime::Current()->GetImageLocation();
+  return EndsWith(image, "core.art") || EndsWith(image, "core-optimizing.art");
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
index d076fb5..0c89da1 100644
--- a/compiler/optimizing/optimizing_compiler.h
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -24,6 +24,11 @@
 
 Compiler* CreateOptimizingCompiler(CompilerDriver* driver);
 
+// Returns whether we are compiling against a "core" image, which
+// is an indicative we are running tests. The compiler will use that
+// information for checking invariants.
+bool IsCompilingWithCoreImage();
+
 }  // namespace art
 
 #endif  // ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index a192d2f..3333cd2 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -1334,7 +1334,7 @@
   CHECK(size == 4 || size == 8) << size;
   if (size == 4) {
     LoadFromOffset(kLoadWord, scratch.AsGpuRegister(), SP, src.Int32Value());
-    StoreToOffset(kStoreWord, scratch.AsGpuRegister(), SP, dest.Int32Value());
+    StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, dest.Int32Value());
   } else if (size == 8) {
     LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(), SP, src.Int32Value());
     StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, dest.Int32Value());
@@ -1350,7 +1350,7 @@
   if (size == 4) {
     LoadFromOffset(kLoadWord, scratch, src_base.AsMips64().AsGpuRegister(),
                    src_offset.Int32Value());
-    StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
+    StoreToOffset(kStoreDoubleword, scratch, SP, dest.Int32Value());
   } else if (size == 8) {
     LoadFromOffset(kLoadDoubleword, scratch, src_base.AsMips64().AsGpuRegister(),
                    src_offset.Int32Value());
@@ -1366,7 +1366,7 @@
   CHECK(size == 4 || size == 8) << size;
   if (size == 4) {
     LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
-    StoreToOffset(kStoreWord, scratch, dest_base.AsMips64().AsGpuRegister(),
+    StoreToOffset(kStoreDoubleword, scratch, dest_base.AsMips64().AsGpuRegister(),
                   dest_offset.Int32Value());
   } else if (size == 8) {
     LoadFromOffset(kLoadDoubleword, scratch, SP, src.Int32Value());
@@ -1389,7 +1389,7 @@
   CHECK(size == 4 || size == 8) << size;
   if (size == 4) {
     LoadFromOffset(kLoadWord, scratch, src.AsMips64().AsGpuRegister(), src_offset.Int32Value());
-    StoreToOffset(kStoreWord, scratch, dest.AsMips64().AsGpuRegister(), dest_offset.Int32Value());
+    StoreToOffset(kStoreDoubleword, scratch, dest.AsMips64().AsGpuRegister(), dest_offset.Int32Value());
   } else if (size == 8) {
     LoadFromOffset(kLoadDoubleword, scratch, src.AsMips64().AsGpuRegister(),
                    src_offset.Int32Value());
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index b06d2ca..ca3ca1d 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1050,9 +1050,12 @@
     .cfi_adjust_cfa_offset 8
     .cfi_rel_offset r0, 0
     .cfi_rel_offset r1, 4
-    sub   sp, #8         @ space for return value argument
+    vpush {d0}           @ save fp return value
     .cfi_adjust_cfa_offset 8
-    strd r0, [sp]        @ r0/r1 -> [sp] for fpr_res
+    sub   sp, #8         @ space for return value argument. Note: AAPCS stack alignment is 8B, no
+                         @ need to align by 16.
+    .cfi_adjust_cfa_offset 8
+    vstr  d0, [sp]       @ d0 -> [sp] for fpr_res
     mov   r2, r0         @ pass return value as gpr_res
     mov   r3, r1
     mov   r0, r9         @ pass Thread::Current
@@ -1063,6 +1066,8 @@
 
     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
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 549eac2..45fb9c4 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -1188,7 +1188,7 @@
    * Create an over-sized buffer to hold the contents of "buf".  Copy it in,
    * filling in the area around it with guard data.
    */
-  static void* Create(const void* original_buf, size_t len, bool mod_okay) {
+  static void* Create(void* original_buf, size_t len, bool mod_okay) {
     const size_t new_len = LengthIncludingRedZones(len);
     uint8_t* const new_buf = DebugAlloc(new_len);
 
@@ -1227,13 +1227,14 @@
    * Create a guarded copy of a primitive array.  Modifications to the copied
    * data are allowed.  Returns a pointer to the copied data.
    */
-  static void* CreateGuardedPACopy(JNIEnv* env, const jarray java_array, jboolean* is_copy) {
+  static void* CreateGuardedPACopy(JNIEnv* env, const jarray java_array, jboolean* is_copy,
+                                   void* original_ptr) {
     ScopedObjectAccess soa(env);
 
     mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
     size_t component_size = a->GetClass()->GetComponentSize();
     size_t byte_count = a->GetLength() * component_size;
-    void* result = Create(a->GetRawData(component_size, 0), byte_count, true);
+    void* result = Create(original_ptr, byte_count, true);
     if (is_copy != nullptr) {
       *is_copy = JNI_TRUE;
     }
@@ -1244,22 +1245,22 @@
    * Perform the array "release" operation, which may or may not copy data
    * back into the managed heap, and may or may not release the underlying storage.
    */
-  static void* ReleaseGuardedPACopy(const char* function_name, JNIEnv* env, jarray java_array,
-                                   void* embedded_buf, int mode) {
+  static void* ReleaseGuardedPACopy(const char* function_name, JNIEnv* env,
+                                    jarray java_array ATTRIBUTE_UNUSED, void* embedded_buf,
+                                    int mode) {
     ScopedObjectAccess soa(env);
-    mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
-
     if (!GuardedCopy::Check(function_name, embedded_buf, true)) {
       return nullptr;
     }
+    GuardedCopy* const copy = FromEmbedded(embedded_buf);
+    void* original_ptr = copy->original_ptr_;
     if (mode != JNI_ABORT) {
-      size_t len = FromEmbedded(embedded_buf)->original_length_;
-      memcpy(a->GetRawData(a->GetClass()->GetComponentSize(), 0), embedded_buf, len);
+      memcpy(original_ptr, embedded_buf, copy->original_length_);
     }
     if (mode != JNI_COMMIT) {
-      return Destroy(embedded_buf);
+      Destroy(embedded_buf);
     }
-    return embedded_buf;
+    return original_ptr;
   }
 
 
@@ -1286,7 +1287,7 @@
   }
 
  private:
-  GuardedCopy(const void* original_buf, size_t len, uLong adler) :
+  GuardedCopy(void* original_buf, size_t len, uLong adler) :
     magic_(kGuardMagic), adler_(adler), original_ptr_(original_buf), original_length_(len) {
   }
 
@@ -1414,7 +1415,7 @@
 
   const uint32_t magic_;
   const uLong adler_;
-  const void* const original_ptr_;
+  void* const original_ptr_;
   const size_t original_length_;
 };
 const char* const GuardedCopy::kCanary = "JNI BUFFER RED ZONE";
@@ -2307,10 +2308,11 @@
     JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
     if (sc.Check(soa, true, "Eap", args)) {
       JniValueType result;
-      result.p = baseEnv(env)->GetPrimitiveArrayCritical(env, array, is_copy);
-      if (result.p != nullptr && soa.ForceCopy()) {
-        result.p = GuardedCopy::CreateGuardedPACopy(env, array, is_copy);
+      void* ptr = baseEnv(env)->GetPrimitiveArrayCritical(env, array, is_copy);
+      if (ptr != nullptr && soa.ForceCopy()) {
+        ptr = GuardedCopy::CreateGuardedPACopy(env, array, is_copy, ptr);
       }
+      result.p = ptr;
       if (sc.Check(soa, false, "p", &result)) {
         return const_cast<void*>(result.p);
       }
@@ -2325,7 +2327,7 @@
     JniValueType args[4] = {{.E = env}, {.a = array}, {.p = carray}, {.r = mode}};
     if (sc.Check(soa, true, "Eapr", args)) {
       if (soa.ForceCopy()) {
-        GuardedCopy::ReleaseGuardedPACopy(__FUNCTION__, env, array, carray, mode);
+        carray = GuardedCopy::ReleaseGuardedPACopy(__FUNCTION__, env, array, carray, mode);
       }
       baseEnv(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
       JniValueType result;
@@ -3062,26 +3064,26 @@
     JniValueType args[3] = {{.E = env}, {.s = string}, {.p = is_copy}};
     if (sc.Check(soa, true, "Esp", args)) {
       JniValueType result;
+      void* ptr;
       if (utf) {
         CHECK(!critical);
-        result.u = baseEnv(env)->GetStringUTFChars(env, string, is_copy);
+        ptr = const_cast<char*>(baseEnv(env)->GetStringUTFChars(env, string, is_copy));
+        result.u = reinterpret_cast<char*>(ptr);
       } else {
-        if (critical) {
-          result.p = baseEnv(env)->GetStringCritical(env, string, is_copy);
-        } else {
-          result.p = baseEnv(env)->GetStringChars(env, string, is_copy);
-        }
+        ptr = const_cast<jchar*>(critical ? baseEnv(env)->GetStringCritical(env, string, is_copy) :
+            baseEnv(env)->GetStringChars(env, string, is_copy));
+        result.p = ptr;
       }
       // TODO: could we be smarter about not copying when local_is_copy?
-      if (result.p != nullptr && soa.ForceCopy()) {
+      if (ptr != nullptr && soa.ForceCopy()) {
         if (utf) {
           size_t length_in_bytes = strlen(result.u) + 1;
           result.u =
-              reinterpret_cast<const char*>(GuardedCopy::Create(result.u, length_in_bytes, false));
+              reinterpret_cast<const char*>(GuardedCopy::Create(ptr, length_in_bytes, false));
         } else {
           size_t length_in_bytes = baseEnv(env)->GetStringLength(env, string) * 2;
           result.p =
-              reinterpret_cast<const jchar*>(GuardedCopy::Create(result.p, length_in_bytes, false));
+              reinterpret_cast<const jchar*>(GuardedCopy::Create(ptr, length_in_bytes, false));
         }
         if (is_copy != nullptr) {
           *is_copy = JNI_TRUE;
@@ -3175,47 +3177,43 @@
     JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
     if (sc.Check(soa, true, "Eap", args) && sc.CheckPrimitiveArrayType(soa, array, type)) {
       JniValueType result;
+      void* ptr = nullptr;
       switch (type) {
         case Primitive::kPrimBoolean:
-          result.p = baseEnv(env)->GetBooleanArrayElements(env, down_cast<jbooleanArray>(array),
-                                                           is_copy);
+          ptr = baseEnv(env)->GetBooleanArrayElements(env, down_cast<jbooleanArray>(array),
+                                                      is_copy);
           break;
         case Primitive::kPrimByte:
-          result.p = baseEnv(env)->GetByteArrayElements(env, down_cast<jbyteArray>(array),
-                                                        is_copy);
+          ptr = baseEnv(env)->GetByteArrayElements(env, down_cast<jbyteArray>(array), is_copy);
           break;
         case Primitive::kPrimChar:
-          result.p = baseEnv(env)->GetCharArrayElements(env, down_cast<jcharArray>(array),
-                                                        is_copy);
+          ptr = baseEnv(env)->GetCharArrayElements(env, down_cast<jcharArray>(array), is_copy);
           break;
         case Primitive::kPrimShort:
-          result.p = baseEnv(env)->GetShortArrayElements(env, down_cast<jshortArray>(array),
-                                                         is_copy);
+          ptr = baseEnv(env)->GetShortArrayElements(env, down_cast<jshortArray>(array), is_copy);
           break;
         case Primitive::kPrimInt:
-          result.p = baseEnv(env)->GetIntArrayElements(env, down_cast<jintArray>(array), is_copy);
+          ptr = baseEnv(env)->GetIntArrayElements(env, down_cast<jintArray>(array), is_copy);
           break;
         case Primitive::kPrimLong:
-          result.p = baseEnv(env)->GetLongArrayElements(env, down_cast<jlongArray>(array),
-                                                        is_copy);
+          ptr = baseEnv(env)->GetLongArrayElements(env, down_cast<jlongArray>(array), is_copy);
           break;
         case Primitive::kPrimFloat:
-          result.p = baseEnv(env)->GetFloatArrayElements(env, down_cast<jfloatArray>(array),
-                                                         is_copy);
+          ptr = baseEnv(env)->GetFloatArrayElements(env, down_cast<jfloatArray>(array), is_copy);
           break;
         case Primitive::kPrimDouble:
-          result.p = baseEnv(env)->GetDoubleArrayElements(env, down_cast<jdoubleArray>(array),
-                                                          is_copy);
+          ptr = baseEnv(env)->GetDoubleArrayElements(env, down_cast<jdoubleArray>(array), is_copy);
           break;
         default:
           LOG(FATAL) << "Unexpected primitive type: " << type;
       }
-      if (result.p != nullptr && soa.ForceCopy()) {
-        result.p = GuardedCopy::CreateGuardedPACopy(env, array, is_copy);
+      if (ptr != nullptr && soa.ForceCopy()) {
+        ptr = GuardedCopy::CreateGuardedPACopy(env, array, is_copy, ptr);
         if (is_copy != nullptr) {
           *is_copy = JNI_TRUE;
         }
       }
+      result.p = ptr;
       if (sc.Check(soa, false, "p", &result)) {
         return const_cast<void*>(result.p);
       }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 91b5000..7936dd3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -987,13 +987,18 @@
   // Fall back to running out of the original dex file if we couldn't load any
   // dex_files from the oat file.
   if (dex_files.empty()) {
-    if (Runtime::Current()->IsDexFileFallbackEnabled()) {
-      if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
-        LOG(WARNING) << error_msg;
-        error_msgs->push_back("Failed to open dex files from " + std::string(dex_location));
+    if (oat_file_assistant.HasOriginalDexFiles()) {
+      if (Runtime::Current()->IsDexFileFallbackEnabled()) {
+        if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
+          LOG(WARNING) << error_msg;
+          error_msgs->push_back("Failed to open dex files from " + std::string(dex_location));
+        }
+      } else {
+        error_msgs->push_back("Fallback mode disabled, skipping dex files.");
       }
     } else {
-      error_msgs->push_back("Fallback mode disabled, skipping dex files.");
+      error_msgs->push_back("No original dex files found for dex location "
+          + std::string(dex_location));
     }
   }
   return dex_files;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index f0ba0bd..f19cfcb 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -746,7 +746,7 @@
 
 void Heap::DecrementDisableMovingGC(Thread* self) {
   MutexLock mu(self, *gc_complete_lock_);
-  CHECK_GE(disable_moving_gc_count_, 0U);
+  CHECK_GT(disable_moving_gc_count_, 0U);
   --disable_moving_gc_count_;
 }
 
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 094d8b7..b28adf9 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -151,7 +151,7 @@
     return kSelfPatchOatNeeded;
   }
 
-  return kDex2OatNeeded;
+  return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
 }
 
 bool OatFileAssistant::MakeUpToDate(std::string* error_msg) {
@@ -241,6 +241,14 @@
   return dex_files;
 }
 
+bool OatFileAssistant::HasOriginalDexFiles() {
+  // Ensure GetRequiredDexChecksum has been run so that
+  // has_original_dex_files_ is initialized. We don't care about the result of
+  // GetRequiredDexChecksum.
+  GetRequiredDexChecksum();
+  return has_original_dex_files_;
+}
+
 const std::string* OatFileAssistant::OdexFileName() {
   if (!cached_odex_file_name_attempted_) {
     CHECK(dex_location_ != nullptr) << "OatFileAssistant: null dex location";
@@ -817,17 +825,19 @@
 }
 
 const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
-  if (!required_dex_checksum_attempted) {
-    required_dex_checksum_attempted = true;
-    required_dex_checksum_found = false;
+  if (!required_dex_checksum_attempted_) {
+    required_dex_checksum_attempted_ = true;
+    required_dex_checksum_found_ = false;
     std::string error_msg;
     CHECK(dex_location_ != nullptr) << "OatFileAssistant provided no dex location";
-    if (DexFile::GetChecksum(dex_location_, &cached_required_dex_checksum, &error_msg)) {
-      required_dex_checksum_found = true;
+    if (DexFile::GetChecksum(dex_location_, &cached_required_dex_checksum_, &error_msg)) {
+      required_dex_checksum_found_ = true;
+      has_original_dex_files_ = true;
     } else {
       // This can happen if the original dex file has been stripped from the
       // apk.
       VLOG(oat) << "OatFileAssistant: " << error_msg;
+      has_original_dex_files_ = false;
 
       // Get the checksum from the odex if we can.
       const OatFile* odex_file = GetOdexFile();
@@ -835,13 +845,13 @@
         const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(
             dex_location_, nullptr, false);
         if (odex_dex_file != nullptr) {
-          cached_required_dex_checksum = odex_dex_file->GetDexFileLocationChecksum();
-          required_dex_checksum_found = true;
+          cached_required_dex_checksum_ = odex_dex_file->GetDexFileLocationChecksum();
+          required_dex_checksum_found_ = true;
         }
       }
     }
   }
-  return required_dex_checksum_found ? &cached_required_dex_checksum : nullptr;
+  return required_dex_checksum_found_ ? &cached_required_dex_checksum_ : nullptr;
 }
 
 const OatFile* OatFileAssistant::GetOdexFile() {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 4c0b0e2..7216fc7 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -174,6 +174,12 @@
   static std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(
       const OatFile& oat_file, const char* dex_location);
 
+  // Returns true if there are dex files in the original dex location that can
+  // be compiled with dex2oat for this dex location.
+  // Returns false if there is no original dex file, or if the original dex
+  // file is an apk/zip without a classes.dex entry.
+  bool HasOriginalDexFiles();
+
   // If the dex file has been installed with a compiled oat file alongside
   // it, the compiled oat file will have the extension .odex, and is referred
   // to as the odex file. It is called odex for legacy reasons; the file is
@@ -312,6 +318,8 @@
   // Returns dex_checksum if a required checksum was located. Returns
   // null if the required checksum was not found.
   // The caller shouldn't clean up or free the returned pointer.
+  // This sets the has_original_dex_files_ field to true if a checksum was
+  // found for the dex_location_ dex file.
   const uint32_t* GetRequiredDexChecksum();
 
   // Returns the loaded odex file.
@@ -374,9 +382,10 @@
 
   // Cached value of the required dex checksum.
   // This should be accessed only by the GetRequiredDexChecksum() method.
-  uint32_t cached_required_dex_checksum;
-  bool required_dex_checksum_attempted = false;
-  bool required_dex_checksum_found;
+  uint32_t cached_required_dex_checksum_;
+  bool required_dex_checksum_attempted_ = false;
+  bool required_dex_checksum_found_;
+  bool has_original_dex_files_;
 
   // Cached value of the odex file name.
   // This should be accessed only by the OdexFileName() method.
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index d8e3797..570c59c 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -309,16 +309,24 @@
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
   EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have no DEX file and no OAT file.
-// Expect: Status is kDex2OatNeeded. Loading should fail, but not crash.
+// Expect: Status is kNoDexOptNeeded. Loading should fail, but not crash.
 TEST_F(OatFileAssistantTest, NoDexNoOat) {
   std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+
+  // Trying to make the oat file up to date should not fail or crash.
+  std::string error_msg;
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg));
+
+  // Trying to get the best oat file should fail, but not crash.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   EXPECT_EQ(nullptr, oat_file.get());
 }
@@ -342,6 +350,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
   EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a MultiDEX file and up-to-date OAT file for it.
@@ -353,6 +362,7 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load both dex files.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -378,6 +388,7 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
   EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a MultiDEX file and up-to-date OAT file for it with relative
@@ -432,6 +443,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a DEX file and an ODEX file, but no OAT file.
@@ -457,6 +469,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a stripped DEX file and an ODEX file, but no OAT file.
@@ -484,6 +497,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
   std::string error_msg;
@@ -498,6 +512,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load the dex files from it.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -538,6 +553,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
   std::string error_msg;
@@ -554,6 +570,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load the dex files from it.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -564,6 +581,45 @@
   EXPECT_EQ(1u, dex_files.size());
 }
 
+// Case: We have a stripped (or resource-only) DEX file, no ODEX file and no
+// OAT file. Expect: The status is kNoDexOptNeeded.
+TEST_F(OatFileAssistantTest, ResourceOnlyDex) {
+  std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar";
+
+  Copy(GetStrippedDexSrc1(), dex_location);
+
+  // Verify the status.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+
+  // Make the oat file up to date. This should have no effect.
+  std::string error_msg;
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+}
+
 // Case: We have a DEX file, no ODEX file and an OAT file that needs
 // relocation.
 // Expect: The status is kSelfPatchOatNeeded.
@@ -589,6 +645,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
   std::string error_msg;
@@ -605,6 +662,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -643,6 +701,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Things aren't relocated, so it should fall back to interpreted.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -688,6 +747,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a DEX file and up-to-date OAT file for it.
@@ -756,27 +816,6 @@
   EXPECT_FALSE(ofm.OatFileExists());
 }
 
-// Case: Non-existent Dex location.
-// Expect: The dex code is out of date, and trying to update it fails.
-TEST_F(OatFileAssistantTest, NonExsistentDexLocation) {
-  std::string dex_location = GetScratchDir() + "/BadDexLocation.jar";
-
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
-
-  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
-
-  std::string error_msg;
-  EXPECT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
-  EXPECT_FALSE(error_msg.empty());
-}
-
 // Turn an absolute path into a path relative to the current working
 // directory.
 static std::string MakePathRelative(std::string target) {
@@ -833,24 +872,26 @@
 }
 
 // Case: Very short, non-existent Dex location.
-// Expect: Dex code is out of date, and trying to update it fails.
+// Expect: kNoDexOptNeeded.
 TEST_F(OatFileAssistantTest, ShortDexLocation) {
   std::string dex_location = "/xx";
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
+  // Trying to make it up to date should have no effect.
   std::string error_msg;
-  EXPECT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
-  EXPECT_FALSE(error_msg.empty());
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg));
+  EXPECT_TRUE(error_msg.empty());
 }
 
 // Case: Non-standard extension for dex file.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 95b327e..04fe35e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4060,10 +4060,15 @@
       VerifyPrimitivePut(*field_type, insn_type, vregA);
     } else {
       if (!insn_type.IsAssignableFrom(*field_type)) {
-        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
-                                                << " to be compatible with type '" << insn_type
-                                                << "' but found type '" << *field_type
-                                                << "' in put-object";
+        // If the field type is not a reference, this is a global failure rather than
+        // a class change failure as the instructions and the descriptors for the type
+        // should have been consistent within the same file at compile time.
+        VerifyError error = field_type->IsReferenceTypes() ? VERIFY_ERROR_BAD_CLASS_SOFT
+                                                           : VERIFY_ERROR_BAD_CLASS_HARD;
+        Fail(error) << "expected field " << PrettyField(field)
+                    << " to be compatible with type '" << insn_type
+                    << "' but found type '" << *field_type
+                    << "' in put-object";
         return;
       }
       work_line_->VerifyRegisterType(this, vregA, *field_type);
@@ -4087,10 +4092,15 @@
       }
     } else {
       if (!insn_type.IsAssignableFrom(*field_type)) {
-        Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
-                                          << " to be compatible with type '" << insn_type
-                                          << "' but found type '" << *field_type
-                                          << "' in get-object";
+        // If the field type is not a reference, this is a global failure rather than
+        // a class change failure as the instructions and the descriptors for the type
+        // should have been consistent within the same file at compile time.
+        VerifyError error = field_type->IsReferenceTypes() ? VERIFY_ERROR_BAD_CLASS_SOFT
+                                                           : VERIFY_ERROR_BAD_CLASS_HARD;
+        Fail(error) << "expected field " << PrettyField(field)
+                    << " to be compatible with type '" << insn_type
+                    << "' but found type '" << *field_type
+                    << "' in get-object";
         work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
         return;
       }
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 9413c13..0d2087a 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -24,4 +24,5 @@
 b/22045582
 b/22045582 (int)
 b/22045582 (wide)
+b/21886894
 Done!
diff --git a/test/800-smali/smali/b_21886894.smali b/test/800-smali/smali/b_21886894.smali
new file mode 100644
index 0000000..f1ac3e9
--- /dev/null
+++ b/test/800-smali/smali/b_21886894.smali
@@ -0,0 +1,15 @@
+.class public LB21886894;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+    .registers 2
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+    return-void
+.end method
+
+.method public test()V
+    .registers 2
+    const v0, 0
+    iput-object v0, p0, Lsome/unresolved/Type;->a:I
+    return-void
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 28954f8..14d2e43 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -95,6 +95,8 @@
                 new VerifyError(), 0));
         testCases.add(new TestCase("b/22045582 (wide)", "B22045582Wide", "run", null,
                 new VerifyError(), 0));
+        testCases.add(new TestCase("b/21886894", "B21886894", "test", null, new VerifyError(),
+            null));
     }
 
     public void runTests() {
diff --git a/test/MyClassNatives/MyClassNatives.java b/test/MyClassNatives/MyClassNatives.java
index 8b4a9a4..19c13f7 100644
--- a/test/MyClassNatives/MyClassNatives.java
+++ b/test/MyClassNatives/MyClassNatives.java
@@ -94,6 +94,9 @@
         float f4, int i5, float f5, int i6, float f6, int i7, float f7, int i8, float f8, int i9,
         float f9, int i10, float f10);
 
+    native static void stackArgsSignExtendedMips64(int i1, int i2, int i3, int i4, int i5, int i6,
+        int i7, int i8);
+
     static native double logD(double d);
     static native float logF(float f);
     static native boolean returnTrue();
diff --git a/tools/art b/tools/art
index 2ee8940..676d6ae 100644
--- a/tools/art
+++ b/tools/art
@@ -93,6 +93,7 @@
   ANDROID_ROOT=$ANDROID_ROOT \
   LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
   PATH=$ANDROID_ROOT/bin:$PATH \
+  LD_USE_LOAD_BIAS=1 \
   $invoke_with $ANDROID_ROOT/bin/$DALVIKVM $lib \
     -XXlib:$LIBART \
     -Xnorelocate \
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index b053f0d..1784be2 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -110,9 +110,8 @@
   bug: 18869265
 },
 {
-  description: "Test sometimes timeouts on volantis",
+  description: "Test sometimes timeouts on volantis, and on most modes in debug mode",
   result: EXEC_TIMEOUT,
-  modes_variants: [[device,X64]],
   names: ["libcore.java.lang.SystemTest#testArrayCopyConcurrentModification"],
   bug: 19165288
 },