Make vdex and dexlayout compatible.

Unquicken the vdex before dexlayout, to keep the dex integrity.

bug: 37558732
Test: run-test with speed-profile

Change-Id: Ifcd5c2e4378ccb0df0a66d07f68df31d94b83220
diff --git a/compiler/Android.bp b/compiler/Android.bp
index dec8b57..6ef866a 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -28,7 +28,6 @@
         "compiled_method.cc",
         "debug/elf_debug_writer.cc",
         "dex/dex_to_dex_compiler.cc",
-        "dex/dex_to_dex_decompiler.cc",
         "dex/inline_method_analyser.cc",
         "dex/verified_method.cc",
         "dex/verification_results.cc",
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index 43100c9..e486e2e 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "dex/dex_to_dex_decompiler.h"
+#include "dex_to_dex_decompiler.h"
 
 #include "class_linker.h"
 #include "compiler/common_compiler_test.h"
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 4c50797..3f0df3b 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -104,11 +104,12 @@
   // This method should only be called for classes verified at compile time,
   // which have no verifier error, nor has methods that we know will throw
   // at runtime.
-  AtomicMap::InsertResult result = atomic_verified_methods_.Insert(
+  atomic_verified_methods_.Insert(
       ref,
       /*expected*/ nullptr,
       new VerifiedMethod(/* encountered_error_types */ 0, /* has_runtime_throw */ false));
-  DCHECK_EQ(result, AtomicMap::kInsertResultSuccess);
+  // We don't check the result of `Insert` as we could insert twice for the same
+  // MethodReference in the presence of duplicate methods.
 }
 
 void VerificationResults::AddRejectedClass(ClassReference ref) {
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 4f55ed6..fbfa087 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -45,7 +45,6 @@
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
 #include "dex/dex_to_dex_compiler.h"
-#include "dex/dex_to_dex_decompiler.h"
 #include "dex/verification_results.h"
 #include "dex/verified_method.h"
 #include "driver/compiler_options.h"
@@ -431,61 +430,6 @@
   FreeThreadPools();
 }
 
-// In-place unquicken the given `dex_files` based on `quickening_info`.
-static void Unquicken(const std::vector<const DexFile*>& dex_files,
-                      const ArrayRef<const uint8_t>& quickening_info,
-                      bool decompile_return_instruction) {
-  const uint8_t* quickening_info_ptr = quickening_info.data();
-  const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size();
-  for (const DexFile* dex_file : dex_files) {
-    for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
-      const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
-      const uint8_t* class_data = dex_file->GetClassData(class_def);
-      if (class_data == nullptr) {
-        continue;
-      }
-      ClassDataItemIterator it(*dex_file, class_data);
-      // Skip fields
-      while (it.HasNextStaticField()) {
-        it.Next();
-      }
-      while (it.HasNextInstanceField()) {
-        it.Next();
-      }
-
-      while (it.HasNextDirectMethod()) {
-        const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
-        if (code_item != nullptr) {
-          uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
-          quickening_info_ptr += sizeof(uint32_t);
-          optimizer::ArtDecompileDEX(*code_item,
-                                     ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
-                                     decompile_return_instruction);
-          quickening_info_ptr += quickening_size;
-        }
-        it.Next();
-      }
-
-      while (it.HasNextVirtualMethod()) {
-        const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
-        if (code_item != nullptr) {
-          uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
-          quickening_info_ptr += sizeof(uint32_t);
-          optimizer::ArtDecompileDEX(*code_item,
-                                     ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
-                                     decompile_return_instruction);
-          quickening_info_ptr += quickening_size;
-        }
-        it.Next();
-      }
-      DCHECK(!it.HasNext());
-    }
-  }
-  if (quickening_info_ptr != quickening_info_end) {
-    LOG(FATAL) << "Failed to use all quickening info";
-  }
-}
-
 void CompilerDriver::CompileAll(jobject class_loader,
                                 const std::vector<const DexFile*>& dex_files,
                                 VdexFile* vdex_file,
@@ -494,15 +438,12 @@
     // TODO: we unquicken unconditionnally, as we don't know
     // if the boot image has changed. How exactly we'll know is under
     // experimentation.
-    if (vdex_file->GetQuickeningInfo().size() != 0) {
-      TimingLogger::ScopedTiming t("Unquicken", timings);
-      // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening
-      // optimization does not depend on the boot image (the optimization relies on not
-      // having final fields in a class, which does not change for an app).
-      Unquicken(dex_files,
-                vdex_file->GetQuickeningInfo(),
-                /* decompile_return_instruction */ false);
-    }
+    TimingLogger::ScopedTiming t("Unquicken", timings);
+    // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening
+    // optimization does not depend on the boot image (the optimization relies on not
+    // having final fields in a class, which does not change for an app).
+    VdexFile::Unquicken(dex_files, vdex_file->GetQuickeningInfo());
+
     Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
         new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData()));
   }
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 9646927..6b5387a 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -2474,11 +2474,28 @@
                              /* verify */ true,
                              /* verify_checksum */ true,
                              &error_msg);
-  } else {
-    CHECK(oat_dex_file->source_.IsRawFile())
-        << static_cast<size_t>(oat_dex_file->source_.GetType());
+  } else if (oat_dex_file->source_.IsRawFile()) {
     File* raw_file = oat_dex_file->source_.GetRawFile();
     dex_file = DexFile::OpenDex(raw_file->Fd(), location, /* verify_checksum */ true, &error_msg);
+  } else {
+    // The source data is a vdex file.
+    CHECK(oat_dex_file->source_.IsRawData())
+        << static_cast<size_t>(oat_dex_file->source_.GetType());
+    const uint8_t* raw_dex_file = oat_dex_file->source_.GetRawData();
+    // Note: The raw data has already been checked to contain the header
+    // and all the data that the header specifies as the file size.
+    DCHECK(raw_dex_file != nullptr);
+    DCHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file->GetLocation()));
+    const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file);
+    // Since the source may have had its layout changed, or may be quickened, don't verify it.
+    dex_file = DexFile::Open(raw_dex_file,
+                             header->file_size_,
+                             location,
+                             oat_dex_file->dex_file_location_checksum_,
+                             nullptr,
+                             /* verify */ false,
+                             /* verify_checksum */ false,
+                             &error_msg);
   }
   if (dex_file == nullptr) {
     LOG(ERROR) << "Failed to open dex file for layout: " << error_msg;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 68864da..4cba36a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -718,6 +718,10 @@
       Usage("Can't have both --input-vdex-fd and --input-vdex");
     }
 
+    if (output_vdex_fd_ != -1 && !output_vdex_.empty()) {
+      Usage("Can't have both --output-vdex-fd and --output-vdex");
+    }
+
     if (!oat_filenames_.empty() && oat_fd_ != -1) {
       Usage("--oat-file should not be used with --oat-fd");
     }
@@ -1122,6 +1126,8 @@
         ParseInputVdexFd(option);
       } else if (option.starts_with("--input-vdex=")) {
         input_vdex_ = option.substr(strlen("--input-vdex=")).data();
+      } else if (option.starts_with("--output-vdex=")) {
+        output_vdex_ = option.substr(strlen("--output-vdex=")).data();
       } else if (option.starts_with("--output-vdex-fd=")) {
         ParseOutputVdexFd(option);
       } else if (option.starts_with("--oat-file=")) {
@@ -1257,6 +1263,7 @@
     }
 
     // OAT and VDEX file handling
+    bool eagerly_unquicken_vdex = DoDexLayoutOptimizations();
 
     if (oat_fd_ == -1) {
       DCHECK(!oat_filenames_.empty());
@@ -1278,12 +1285,15 @@
           input_vdex_file_ = VdexFile::Open(input_vdex_,
                                             /* writable */ false,
                                             /* low_4gb */ false,
+                                            eagerly_unquicken_vdex,
                                             &error_msg);
         }
 
         DCHECK_EQ(output_vdex_fd_, -1);
-        std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex");
-        if (vdex_filename == input_vdex_) {
+        std::string vdex_filename = output_vdex_.empty()
+            ? ReplaceFileExtension(oat_filename, "vdex")
+            : output_vdex_;
+        if (vdex_filename == input_vdex_ && output_vdex_.empty()) {
           update_input_vdex_ = true;
           std::unique_ptr<File> vdex_file(OS::OpenFileReadWrite(vdex_filename.c_str()));
           vdex_files_.push_back(std::move(vdex_file));
@@ -1325,6 +1335,7 @@
                                             "vdex",
                                             /* writable */ false,
                                             /* low_4gb */ false,
+                                            eagerly_unquicken_vdex,
                                             &error_msg);
           // If there's any problem with the passed vdex, just warn and proceed
           // without it.
@@ -2089,10 +2100,6 @@
     return DoProfileGuidedOptimizations();
   }
 
-  bool HasInputVdexFile() const {
-    return input_vdex_file_ != nullptr || input_vdex_fd_ != -1 || !input_vdex_.empty();
-  }
-
   bool LoadProfile() {
     DCHECK(UseProfile());
 
@@ -2148,16 +2155,6 @@
     return dex_files_size >= very_large_threshold_;
   }
 
-  template <typename T>
-  static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
-    std::vector<T*> result;
-    result.reserve(src.size());
-    for (const std::unique_ptr<T>& t : src) {
-      result.push_back(t.get());
-    }
-    return result;
-  }
-
   std::vector<std::string> GetClassPathLocations(const std::string& class_path) {
     // This function is used only for apps and for an app we have exactly one oat file.
     DCHECK(!IsBootImage());
@@ -2690,6 +2687,7 @@
   int input_vdex_fd_;
   int output_vdex_fd_;
   std::string input_vdex_;
+  std::string output_vdex_;
   std::unique_ptr<VdexFile> input_vdex_file_;
   std::vector<const char*> dex_filenames_;
   std::vector<const char*> dex_locations_;
@@ -2896,13 +2894,6 @@
     }
   }
 
-  if (dex2oat->DoDexLayoutOptimizations()) {
-    if (dex2oat->HasInputVdexFile()) {
-      LOG(ERROR) << "Dexlayout is incompatible with an input VDEX";
-      return dex2oat::ReturnCode::kOther;
-    }
-  }
-
   art::MemMap::Init();  // For ZipEntry::ExtractToMemMap, and vdex.
 
   // Check early that the result of compilation can be written
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 9d9f8c6..a267766 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -736,12 +736,12 @@
                          /* use_fd */ true,
                          /* num_profile_classes */ 1,
                          { input_vdex, output_vdex },
-                         /* expect_success */ false);
-      EXPECT_EQ(vdex_file2.GetFile()->GetLength(), 0u);
+                         /* expect_success */ true);
+      EXPECT_GT(vdex_file2.GetFile()->GetLength(), 0u);
     }
     ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
     CheckValidity();
-    ASSERT_FALSE(success_);
+    ASSERT_TRUE(success_);
   }
 
   void CheckResult(const std::string& dex_location,
diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc
index 688201b..ea2679a 100644
--- a/dexlayout/dexdiag.cc
+++ b/dexlayout/dexdiag.cc
@@ -313,6 +313,7 @@
   std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_name,
                                                 false /*writeable*/,
                                                 false /*low_4gb*/,
+                                                false /*unquicken */,
                                                 &error_msg /*out*/));
   if (vdex == nullptr) {
     std::cerr << "Could not open vdex file "
diff --git a/runtime/Android.bp b/runtime/Android.bp
index cff2cbc..1869968 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -57,6 +57,7 @@
         "dex_file_annotations.cc",
         "dex_file_verifier.cc",
         "dex_instruction.cc",
+        "dex_to_dex_decompiler.cc",
         "elf_file.cc",
         "exec_utils.cc",
         "fault_handler.cc",
diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h
index d5f375a..cfe27f3 100644
--- a/runtime/base/stl_util.h
+++ b/runtime/base/stl_util.h
@@ -194,6 +194,17 @@
   to_update.insert(other.begin(), other.end());
 }
 
+// Returns a copy of the passed vector that doesn't memory-own its entries.
+template <typename T>
+static inline std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
+  std::vector<T*> result;
+  result.reserve(src.size());
+  for (const std::unique_ptr<T>& t : src) {
+    result.push_back(t.get());
+  }
+  return result;
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_STL_UTIL_H_
diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/runtime/dex_to_dex_decompiler.cc
similarity index 100%
rename from compiler/dex/dex_to_dex_decompiler.cc
rename to runtime/dex_to_dex_decompiler.cc
diff --git a/compiler/dex/dex_to_dex_decompiler.h b/runtime/dex_to_dex_decompiler.h
similarity index 88%
rename from compiler/dex/dex_to_dex_decompiler.h
rename to runtime/dex_to_dex_decompiler.h
index b5d5b91..d7cb164 100644
--- a/compiler/dex/dex_to_dex_decompiler.h
+++ b/runtime/dex_to_dex_decompiler.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_
-#define ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_
+#ifndef ART_RUNTIME_DEX_TO_DEX_DECOMPILER_H_
+#define ART_RUNTIME_DEX_TO_DEX_DECOMPILER_H_
 
 #include "base/array_ref.h"
 #include "dex_file.h"
@@ -36,4 +36,4 @@
 }  // namespace optimizer
 }  // namespace art
 
-#endif  // ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_
+#endif  // ART_RUNTIME_DEX_TO_DEX_DECOMPILER_H_
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 4a85d479..9affeb0 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -193,7 +193,7 @@
                            bool writable,
                            bool low_4gb,
                            std::string* error_msg) {
-  vdex_ = VdexFile::Open(vdex_filename, writable, low_4gb, error_msg);
+  vdex_ = VdexFile::Open(vdex_filename, writable, low_4gb, /* unquicken*/ false, error_msg);
   if (vdex_.get() == nullptr) {
     *error_msg = StringPrintf("Failed to load vdex file '%s' %s",
                               vdex_filename.c_str(),
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 9471cba..2c2b6fd 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -808,6 +808,7 @@
       std::unique_ptr<VdexFile> vdex = VdexFile::Open(vdex_filename,
                                                       /*writeable*/false,
                                                       /*low_4gb*/false,
+                                                      /*unquicken*/false,
                                                       &error_msg);
       if (vdex == nullptr) {
         status_ = kOatCannotOpen;
diff --git a/runtime/openjdkjvmti/fixed_up_dex_file.cc b/runtime/openjdkjvmti/fixed_up_dex_file.cc
index 3338358..29aebae 100644
--- a/runtime/openjdkjvmti/fixed_up_dex_file.cc
+++ b/runtime/openjdkjvmti/fixed_up_dex_file.cc
@@ -32,10 +32,8 @@
 #include "fixed_up_dex_file.h"
 #include "dex_file-inl.h"
 
-// Compiler includes.
-#include "dex/dex_to_dex_decompiler.h"
-
 // Runtime includes.
+#include "dex_to_dex_decompiler.h"
 #include "oat_file.h"
 #include "vdex_file.h"
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 9ca7abf..e61ec23 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -905,6 +905,7 @@
     std::unique_ptr<VdexFile> vdex_file(VdexFile::Open(vdex_filename,
                                                        false /* writable */,
                                                        false /* low_4gb */,
+                                                       false, /* unquicken */
                                                        &error_msg));
     if (vdex_file.get() == nullptr) {
       return false;
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 945f08b..e93f04d 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -21,8 +21,10 @@
 #include <memory>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "dex_file.h"
+#include "dex_to_dex_decompiler.h"
 
 namespace art {
 
@@ -54,6 +56,7 @@
 std::unique_ptr<VdexFile> VdexFile::Open(const std::string& vdex_filename,
                                          bool writable,
                                          bool low_4gb,
+                                         bool unquicken,
                                          std::string* error_msg) {
   if (!OS::FileExists(vdex_filename.c_str())) {
     *error_msg = "File " + vdex_filename + " does not exist.";
@@ -78,7 +81,7 @@
     return nullptr;
   }
 
-  return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, error_msg);
+  return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, unquicken, error_msg);
 }
 
 std::unique_ptr<VdexFile> VdexFile::Open(int file_fd,
@@ -86,15 +89,17 @@
                                          const std::string& vdex_filename,
                                          bool writable,
                                          bool low_4gb,
+                                         bool unquicken,
                                          std::string* error_msg) {
-  std::unique_ptr<MemMap> mmap(MemMap::MapFile(vdex_length,
-                                               writable ? PROT_READ | PROT_WRITE : PROT_READ,
-                                               MAP_SHARED,
-                                               file_fd,
-                                               0 /* start offset */,
-                                               low_4gb,
-                                               vdex_filename.c_str(),
-                                               error_msg));
+  std::unique_ptr<MemMap> mmap(MemMap::MapFile(
+      vdex_length,
+      (writable || unquicken) ? PROT_READ | PROT_WRITE : PROT_READ,
+      unquicken ? MAP_PRIVATE : MAP_SHARED,
+      file_fd,
+      0 /* start offset */,
+      low_4gb,
+      vdex_filename.c_str(),
+      error_msg));
   if (mmap == nullptr) {
     *error_msg = "Failed to mmap file " + vdex_filename + " : " + *error_msg;
     return nullptr;
@@ -106,6 +111,16 @@
     return nullptr;
   }
 
+  if (unquicken) {
+    std::vector<std::unique_ptr<const DexFile>> unique_ptr_dex_files;
+    if (!vdex->OpenAllDexFiles(&unique_ptr_dex_files, error_msg)) {
+      return nullptr;
+    }
+    Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), vdex->GetQuickeningInfo());
+    // Update the quickening info size to pretend there isn't any.
+    reinterpret_cast<Header*>(vdex->mmap_->Begin())->quickening_info_size_ = 0;
+  }
+
   *error_msg = "Success";
   return vdex;
 }
@@ -148,4 +163,62 @@
   return true;
 }
 
+void VdexFile::Unquicken(const std::vector<const DexFile*>& dex_files,
+                         const ArrayRef<const uint8_t>& quickening_info) {
+  if (quickening_info.size() == 0) {
+    // If there is no quickening info, we bail early, as the code below expects at
+    // least the size of quickening data for each method that has a code item.
+    return;
+  }
+  const uint8_t* quickening_info_ptr = quickening_info.data();
+  const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size();
+  for (const DexFile* dex_file : dex_files) {
+    for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+      const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+      const uint8_t* class_data = dex_file->GetClassData(class_def);
+      if (class_data == nullptr) {
+        continue;
+      }
+      ClassDataItemIterator it(*dex_file, class_data);
+      // Skip fields
+      while (it.HasNextStaticField()) {
+        it.Next();
+      }
+      while (it.HasNextInstanceField()) {
+        it.Next();
+      }
+
+      while (it.HasNextDirectMethod()) {
+        const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+        if (code_item != nullptr) {
+          uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+          quickening_info_ptr += sizeof(uint32_t);
+          optimizer::ArtDecompileDEX(*code_item,
+                                     ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+                                     /* decompile_return_instruction */ false);
+          quickening_info_ptr += quickening_size;
+        }
+        it.Next();
+      }
+
+      while (it.HasNextVirtualMethod()) {
+        const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+        if (code_item != nullptr) {
+          uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+          quickening_info_ptr += sizeof(uint32_t);
+          optimizer::ArtDecompileDEX(*code_item,
+                                     ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+                                     /* decompile_return_instruction */ false);
+          quickening_info_ptr += quickening_size;
+        }
+        it.Next();
+      }
+      DCHECK(!it.HasNext());
+    }
+  }
+  if (quickening_info_ptr != quickening_info_end) {
+    LOG(FATAL) << "Failed to use all quickening info";
+  }
+}
+
 }  // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 9840555..9c0d9db 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -71,6 +71,8 @@
     uint32_t dex_size_;
     uint32_t verifier_deps_size_;
     uint32_t quickening_info_size_;
+
+    friend class VdexFile;
   };
 
   typedef uint32_t VdexChecksum;
@@ -79,6 +81,7 @@
   static std::unique_ptr<VdexFile> Open(const std::string& vdex_filename,
                                         bool writable,
                                         bool low_4gb,
+                                        bool unquicken,
                                         std::string* error_msg);
 
   // Returns nullptr if the vdex file cannot be opened or is not valid.
@@ -87,6 +90,7 @@
                                         const std::string& vdex_filename,
                                         bool writable,
                                         bool low_4gb,
+                                        bool unquicken,
                                         std::string* error_msg);
 
   const uint8_t* Begin() const { return mmap_->Begin(); }
@@ -124,12 +128,14 @@
     return reinterpret_cast<const uint32_t*>(Begin() + sizeof(Header))[dex_file_index];
   }
 
-  // Opens all the dex files contained in this vdex file.  This is currently
-  // used for dumping tools only, and has not been tested for use by the
-  // remainder of the runtime.
+  // Opens all the dex files contained in this vdex file.
   bool OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files,
                        std::string* error_msg);
 
+  // In-place unquicken the given `dex_files` based on `quickening_info`.
+  static void Unquicken(const std::vector<const DexFile*>& dex_files,
+                        const ArrayRef<const uint8_t>& quickening_info);
+
  private:
   explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
 
diff --git a/runtime/vdex_file_test.cc b/runtime/vdex_file_test.cc
index 909e117..ced6e28 100644
--- a/runtime/vdex_file_test.cc
+++ b/runtime/vdex_file_test.cc
@@ -36,10 +36,12 @@
                                                   tmp.GetFilename(),
                                                   /*writable*/false,
                                                   /*low_4gb*/false,
+                                                  /*quicken*/false,
                                                   &error_msg);
   EXPECT_TRUE(vdex == nullptr);
 
-  vdex = VdexFile::Open(tmp.GetFilename(), /*writable*/false, /*low_4gb*/false, &error_msg);
+  vdex = VdexFile::Open(
+      tmp.GetFilename(), /*writable*/false, /*low_4gb*/false, /*quicken*/ false, &error_msg);
   EXPECT_TRUE(vdex == nullptr);
 }
 
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 2b0c612..cb9c605 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3764,7 +3764,7 @@
     const RegType& referrer = GetDeclaringClass();
     if (!referrer.IsUnresolvedTypes() && !referrer.CanAccess(*result)) {
       Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '"
-                                      << referrer << "' -> '" << result << "'";
+                                      << referrer << "' -> '" << *result << "'";
     }
   }
   return *result;
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 5e5b409..f725b89 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -605,6 +605,8 @@
   fi
   if [ "$TEST_VDEX" = "y" ]; then
     vdex_cmdline="${dex2oat_cmdline} ${VDEX_FILTER} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+  elif [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
+    vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
   fi
 fi
 
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 9ab9bfa..c7ad5bf 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -665,5 +665,23 @@
         "description": "The tests above fail with --build-with-javac-dx.",
         "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"},
         "bug": "b/37636792"
+    },
+    {
+        "tests": "648-many-direct-methods",
+        "variant": "optimizing",
+        "description": "Test disabled with AOT because of dex2oatd timeouts.",
+        "bug": "b/33650497"
+    },
+    {
+        "tests": [
+            "536-checker-needs-access-check",
+            "537-checker-inline-and-unverified",
+            "569-checker-pattern-replacement",
+            "586-checker-null-array-get"
+        ],
+        "description": [
+            "Tests that have verify-at-runtime classes, but being compiled when using vdex."
+        ],
+        "variant": "speed-profile"
     }
 ]