Quickening support.

This CL adds quickening support for methods which are interpreted at runtime.

This CL introduces a DEX-to-DEX compiler. A method is now compiled in one of
the two following modes:
- Native compilation: the method is compiled by the Quick or Portable backends.
At runtime, the generated native target-dependent code is executed.
- DEX-to-DEX compilation: the method is executed by the interpreter at runtime.
Its DEX code is compiled so some instructions can be replaced by special
instructions only valid at runtime. No native code is generated.

The quickening adds special instructions to improve runtime performance. They
are "-quick" versions of the following instructions:
- iget/iput
- iget-wide/iput-wide
- iget-object/iput-object
- invoke-virtual/range.

These special instructions cannot be treated by the verifier since they lose
the field/method index referencing the field/method being accessed/invoked.
To prevent this, the DEX-to-DEX compiler is run only on methods of preverified
classes (without verification error at compilation time).

The DEX-to-DEX compiler implements quickening support using the CompilerDriver
interface like the native compiler does (Quick or Portable backends).
To replace instructions, the DEX-to-DEX compiler must be able to modify the
mmapped DEX file. Since it can be read-only protected, the DEX-to-DEX compiler
must be able to temporarily change its protection to read-write mmapped file.
To achieve this, this CL adds support for changing DEX file protection with
DexFile::EnableWrite and DexFile::DisableWrite methods. Besides, it also adds
a dedicated lock (DexFile::modification_lock) to ensure thread-safety and avoid
concurrent DEX file protection change (from a parallel DEX-to-DEX compiler on
the same DEX file).

Change-Id: Iaafd103b9766810d7fc94a2c424a8fafba66e26a
diff --git a/src/common_throws.cc b/src/common_throws.cc
index dc3627a..1e114bb 100644
--- a/src/common_throws.cc
+++ b/src/common_throws.cc
@@ -27,6 +27,7 @@
 #include "mirror/object_array-inl.h"
 #include "object_utils.h"
 #include "thread.h"
+#include "verifier/method_verifier.h"
 
 #include <sstream>
 
@@ -283,16 +284,34 @@
   ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str());
 }
 
-void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx,
-                                              InvokeType type) {
-  mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache();
-  const DexFile& dex_file = *dex_cache->GetDexFile();
+static void ThrowNullPointerExceptionForMethodAccessImpl(const ThrowLocation& throw_location,
+                                                         uint32_t method_idx,
+                                                         const DexFile& dex_file,
+                                                         InvokeType type)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::ostringstream msg;
   msg << "Attempt to invoke " << type << " method '"
       << PrettyMethod(method_idx, dex_file, true) << "' on a null object reference";
   ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str());
 }
 
+void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location, uint32_t method_idx,
+                                              InvokeType type) {
+  mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache();
+  const DexFile& dex_file = *dex_cache->GetDexFile();
+  ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method_idx,
+                                               dex_file, type);
+}
+
+void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
+                                              mirror::AbstractMethod* method,
+                                              InvokeType type) {
+  mirror::DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
+  const DexFile& dex_file = *dex_cache->GetDexFile();
+  ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method->GetDexMethodIndex(),
+                                               dex_file, type);
+}
+
 void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) {
   const DexFile::CodeItem* code = MethodHelper(throw_location.GetMethod()).GetCodeItem();
   uint32_t throw_dex_pc = throw_location.GetDexPc();
@@ -317,6 +336,23 @@
     case Instruction::INVOKE_INTERFACE_RANGE:
       ThrowNullPointerExceptionForMethodAccess(throw_location, instr->VRegB_3rc(), kInterface);
       break;
+    case Instruction::INVOKE_VIRTUAL_QUICK:
+    case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
+      // Since we replaced the method index, we ask the verifier to tell us which
+      // method is invoked at this location.
+      mirror::AbstractMethod* method =
+          verifier::MethodVerifier::FindInvokedMethodAtDexPc(throw_location.GetMethod(),
+                                                             throw_location.GetDexPc());
+      if (method != NULL) {
+        // NPE with precise message.
+        ThrowNullPointerExceptionForMethodAccess(throw_location, method, kVirtual);
+      } else {
+        // NPE with imprecise message.
+        ThrowNullPointerException(&throw_location,
+                                  "Attempt to invoke a virtual method on a null object reference");
+      }
+      break;
+    }
     case Instruction::IGET:
     case Instruction::IGET_WIDE:
     case Instruction::IGET_OBJECT:
@@ -330,6 +366,24 @@
       ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */);
       break;
     }
+    case Instruction::IGET_QUICK:
+    case Instruction::IGET_WIDE_QUICK:
+    case Instruction::IGET_OBJECT_QUICK: {
+      // Since we replaced the field index, we ask the verifier to tell us which
+      // field is accessed at this location.
+      mirror::Field* field =
+          verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(),
+                                                             throw_location.GetDexPc());
+      if (field != NULL) {
+        // NPE with precise message.
+        ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */);
+      } else {
+        // NPE with imprecise message.
+        ThrowNullPointerException(&throw_location,
+                                  "Attempt to read from a field on a null object reference");
+      }
+      break;
+    }
     case Instruction::IPUT:
     case Instruction::IPUT_WIDE:
     case Instruction::IPUT_OBJECT:
@@ -343,6 +397,24 @@
       ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */);
       break;
     }
+    case Instruction::IPUT_QUICK:
+    case Instruction::IPUT_WIDE_QUICK:
+    case Instruction::IPUT_OBJECT_QUICK: {
+      // Since we replaced the field index, we ask the verifier to tell us which
+      // field is accessed at this location.
+      mirror::Field* field =
+          verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(),
+                                                             throw_location.GetDexPc());
+      if (field != NULL) {
+        // NPE with precise message.
+        ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */);
+      } else {
+        // NPE with imprecise message.
+        ThrowNullPointerException(&throw_location,
+                                  "Attempt to write to a field on a null object reference");
+      }
+      break;
+    }
     case Instruction::AGET:
     case Instruction::AGET_WIDE:
     case Instruction::AGET_OBJECT:
diff --git a/src/common_throws.h b/src/common_throws.h
index 5555435..00d89f4 100644
--- a/src/common_throws.h
+++ b/src/common_throws.h
@@ -153,6 +153,11 @@
                                               InvokeType type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
+                                              mirror::AbstractMethod* method,
+                                              InvokeType type)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
 void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/src/compiler/dex/dex_to_dex_compiler.cc b/src/compiler/dex/dex_to_dex_compiler.cc
new file mode 100644
index 0000000..afb29f4
--- /dev/null
+++ b/src/compiler/dex/dex_to_dex_compiler.cc
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/logging.h"
+#include "base/mutex.h"
+#include "compiler/driver/compiler_driver.h"
+#include "compiler/driver/dex_compilation_unit.h"
+#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
+#include "mirror/abstract_method-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/dex_cache.h"
+#include "mirror/field-inl.h"
+
+namespace art {
+namespace optimizer {
+
+// Controls quickening activation.
+const bool kEnableQuickening = true;
+// Controls logging.
+const bool kEnableLogging = false;
+
+class DexCompiler {
+ public:
+  DexCompiler(art::CompilerDriver& compiler,
+              const DexCompilationUnit& unit)
+    : driver_(compiler),
+      unit_(unit) {};
+
+  ~DexCompiler() {};
+
+  void Compile();
+
+ private:
+  const DexFile& GetDexFile() const {
+    return *unit_.GetDexFile();
+  }
+
+  // TODO: since the whole compilation pipeline uses a "const DexFile", we need
+  // to "unconst" here. The DEX-to-DEX compiler should work on a non-const DexFile.
+  DexFile& GetModifiableDexFile() {
+    return *const_cast<DexFile*>(unit_.GetDexFile());
+  }
+
+  void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc,
+                                  Instruction::Code new_opcode, bool is_put);
+  void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
+                            Instruction::Code new_opcode, bool is_range);
+
+  CompilerDriver& driver_;
+  const DexCompilationUnit& unit_;
+
+  DISALLOW_COPY_AND_ASSIGN(DexCompiler);
+};
+
+// Ensures write access to a part of DEX file.
+//
+// If a DEX file is read-only, it modifies its protection (mprotect) so it allows
+// write access to the part of DEX file defined by an address and a length.
+// In this case, it also takes the DexFile::modification_lock to prevent from
+// concurrent protection modification from a parallel DEX-to-DEX compilation on
+// the same DEX file.
+// When the instance is destroyed, it recovers original protection and releases
+// the lock.
+// TODO: as this scoped class is similar to a MutexLock we should use annotalysis
+// to capture the locking behavior.
+class ScopedDexWriteAccess {
+ public:
+  ScopedDexWriteAccess(DexFile& dex_file, Instruction* inst,
+                       size_t length)
+    : dex_file_(dex_file),
+      address_(reinterpret_cast<uint8_t*>(inst)),
+      length_(length),
+      is_read_only_(dex_file_.IsReadOnly()) {
+    if (is_read_only_) {
+      // We need to enable DEX write access. To avoid concurrent DEX write access
+      // modification, we take the DexFile::modification_lock before.
+      dex_file_.GetModificationLock().ExclusiveLock(Thread::Current());
+      bool success = dex_file_.EnableWrite(address_, length_);
+      DCHECK(success) << "Failed to enable DEX write access";
+    }
+  }
+
+  ~ScopedDexWriteAccess() {
+    DCHECK_EQ(is_read_only_, dex_file_.IsReadOnly());
+    if (is_read_only_) {
+      bool success = dex_file_.DisableWrite(address_, length_);
+      DCHECK(success) << "Failed to disable DEX write access";
+      // Now we recovered original read-only protection, we can release the
+      // DexFile::modification_lock.
+      dex_file_.GetModificationLock().ExclusiveUnlock(Thread::Current());
+    }
+  }
+
+ private:
+  DexFile& dex_file_;
+  // TODO: make address_ const.
+  uint8_t* address_;
+  const size_t length_;
+  const bool is_read_only_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedDexWriteAccess);
+};
+
+void DexCompiler::Compile() {
+  const DexFile::CodeItem* code_item = unit_.GetCodeItem();
+  const uint16_t* insns = code_item->insns_;
+  const uint32_t insns_size = code_item->insns_size_in_code_units_;
+  Instruction* inst = const_cast<Instruction*>(Instruction::At(insns));
+
+  for (uint32_t dex_pc = 0; dex_pc < insns_size;
+       inst = const_cast<Instruction*>(inst->Next()), dex_pc = inst->GetDexPc(insns)) {
+    switch (inst->Opcode()) {
+      case Instruction::IGET:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false);
+        break;
+      case Instruction::IGET_WIDE:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false);
+        break;
+
+      case Instruction::IGET_OBJECT:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false);
+        break;
+
+      case Instruction::IPUT:
+      case Instruction::IPUT_BOOLEAN:
+      case Instruction::IPUT_BYTE:
+      case Instruction::IPUT_CHAR:
+      case Instruction::IPUT_SHORT:
+        // These opcodes have the same implementation in interpreter so group
+        // them under IPUT_QUICK.
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
+        break;
+
+      case Instruction::IPUT_WIDE:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true);
+        break;
+
+      case Instruction::IPUT_OBJECT:
+        CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true);
+        break;
+
+      case Instruction::INVOKE_VIRTUAL:
+        CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false);
+        break;
+
+      case Instruction::INVOKE_VIRTUAL_RANGE:
+        CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true);
+        break;
+
+      default:
+        // No optimization.
+        break;
+    }
+  }
+}
+
+void DexCompiler::CompileInstanceFieldAccess(Instruction* inst,
+                                             uint32_t dex_pc,
+                                             Instruction::Code new_opcode,
+                                             bool is_put) {
+  if (!kEnableQuickening) {
+    return;
+  }
+  uint32_t field_idx = inst->VRegC_22c();
+  int field_offset;
+  bool is_volatile;
+  bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset,
+                                                    is_volatile, is_put);
+  if (fast_path && !is_volatile && IsUint(16, field_offset)) {
+    // TODO: use VLOG ?
+    if (kEnableLogging) {
+      LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode())
+                << " to " << Instruction::Name(new_opcode)
+                << " by replacing field index " << field_idx
+                << " by field offset " << field_offset
+                << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+                << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
+    }
+    // We are modifying 4 consecutive bytes.
+    ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
+    inst->SetOpcode(new_opcode);
+    // Replace field index by field offset.
+    inst->SetVRegC_22c(static_cast<uint16_t>(field_offset));
+  }
+}
+
+void DexCompiler::CompileInvokeVirtual(Instruction* inst,
+                                uint32_t dex_pc,
+                                Instruction::Code new_opcode,
+                                bool is_range) {
+  if (!kEnableQuickening) {
+    return;
+  }
+  uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+  CompilerDriver::MethodReference target_method(&GetDexFile(), method_idx);
+  InvokeType invoke_type = kVirtual;
+  InvokeType original_invoke_type = invoke_type;
+  int vtable_idx;
+  uintptr_t direct_code;
+  uintptr_t direct_method;
+  bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type,
+                                             target_method, vtable_idx,
+                                             direct_code, direct_method,
+                                             false);
+  // TODO: support devirtualization.
+  if (fast_path && original_invoke_type == invoke_type) {
+    if (vtable_idx >= 0 && IsUint(16, vtable_idx)) {
+      // TODO: use VLOG ?
+      if (kEnableLogging) {
+        LOG(INFO) << "Quickening " << Instruction::Name(inst->Opcode())
+                  << "(" << PrettyMethod(method_idx, GetDexFile(), true) << ")"
+                  << " to " << Instruction::Name(new_opcode)
+                  << " by replacing method index " << method_idx
+                  << " by vtable index " << vtable_idx
+                  << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
+                  << PrettyMethod(unit_.GetDexMethodIndex(), GetDexFile(), true);
+      }
+      // We are modifying 4 consecutive bytes.
+      ScopedDexWriteAccess sdwa(GetModifiableDexFile(), inst, 4u);
+      inst->SetOpcode(new_opcode);
+      // Replace method index by vtable index.
+      if (is_range) {
+        inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx));
+      } else {
+        inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx));
+      }
+    }
+  }
+}
+
+}  // namespace optimizer
+}  // namespace art
+
+extern "C" art::CompiledMethod*
+    ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item,
+                  uint32_t access_flags, art::InvokeType invoke_type,
+                  uint32_t class_def_idx, uint32_t method_idx, jobject class_loader,
+                  const art::DexFile& dex_file) {
+  art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(),
+                               dex_file, code_item, class_def_idx, method_idx, access_flags);
+  art::optimizer::DexCompiler dex_compiler(compiler, unit);
+  dex_compiler.Compile();
+  return NULL;
+}
diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc
index 6050108..4a6eb96 100644
--- a/src/compiler/driver/compiler_driver.cc
+++ b/src/compiler/driver/compiler_driver.cc
@@ -372,6 +372,8 @@
     compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "ArtQuickCompileMethod");
   }
 
+  dex_to_dex_compiler_ = FindFunction<CompilerFn>(compiler_so_name, compiler_library_, "ArtCompileDEX");
+
   init_compiler_context(*this);
 
   if (compiler_backend_ == kPortable) {
@@ -531,10 +533,33 @@
   }
 }
 
+static bool IsDexToDexCompilationAllowed(mirror::ClassLoader* class_loader,
+                                         const DexFile& dex_file,
+                                         const DexFile::ClassDef& class_def)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  // Do not allow DEX-to-DEX compilation of image classes. This is to prevent the
+  // verifier from passing on "quick" instruction at compilation time. It must
+  // only pass on quick instructions at runtime.
+  if (class_loader == NULL) {
+    return false;
+  }
+  const char* descriptor = dex_file.GetClassDescriptor(class_def);
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  mirror::Class* klass = class_linker->FindClass(descriptor, class_loader);
+  if (klass == NULL) {
+    Thread* self = Thread::Current();
+    CHECK(self->IsExceptionPending());
+    self->ClearException();
+    return false;
+  }
+  // DEX-to-DEX compilation is only allowed on preverified classes.
+  return klass->IsVerified();
+}
+
 void CompilerDriver::CompileOne(const mirror::AbstractMethod* method) {
   DCHECK(!Runtime::Current()->IsStarted());
   Thread* self = Thread::Current();
-  jobject class_loader;
+  jobject jclass_loader;
   const DexFile* dex_file;
   uint32_t class_def_idx;
   {
@@ -542,7 +567,7 @@
     ScopedLocalRef<jobject>
       local_class_loader(soa.Env(),
                     soa.AddLocalReference<jobject>(method->GetDeclaringClass()->GetClassLoader()));
-    class_loader = soa.Env()->NewGlobalRef(local_class_loader.get());
+    jclass_loader = soa.Env()->NewGlobalRef(local_class_loader.get());
     // Find the dex_file
     MethodHelper mh(method);
     dex_file = &mh.GetDexFile();
@@ -555,14 +580,22 @@
 
   UniquePtr<ThreadPool> thread_pool(new ThreadPool(1U));
   TimingLogger timings("CompileOne", false);
-  PreCompile(class_loader, dex_files, *thread_pool.get(), timings);
+  PreCompile(jclass_loader, dex_files, *thread_pool.get(), timings);
 
   uint32_t method_idx = method->GetDexMethodIndex();
   const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset());
+  // Can we run DEX-to-DEX compiler on this class ?
+  bool allow_dex_compilation;
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
+    mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
+    allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, *dex_file, class_def);
+  }
   CompileMethod(code_item, method->GetAccessFlags(), method->GetInvokeType(),
-                class_def_idx, method_idx, class_loader, *dex_file);
+                class_def_idx, method_idx, jclass_loader, *dex_file, allow_dex_compilation);
 
-  self->GetJniEnv()->DeleteGlobalRef(class_loader);
+  self->GetJniEnv()->DeleteGlobalRef(jclass_loader);
 
   self->TransitionFromSuspendedToRunnable();
 }
@@ -2015,12 +2048,12 @@
 }
 
 void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, size_t class_def_index) {
-  jobject class_loader = manager->GetClassLoader();
+  jobject jclass_loader = manager->GetClassLoader();
   const DexFile& dex_file = *manager->GetDexFile();
   const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
   {
     ScopedObjectAccess soa(Thread::Current());
-    mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader());
+    mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
     if (SkipClass(class_loader, dex_file, class_def)) {
       return;
     }
@@ -2035,6 +2068,13 @@
     // empty class, probably a marker interface
     return;
   }
+  // Can we run DEX-to-DEX compiler on this class ?
+  bool allow_dex_compilation;
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
+    allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, dex_file, class_def);
+  }
   ClassDataItemIterator it(dex_file, class_data);
   // Skip fields
   while (it.HasNextStaticField()) {
@@ -2056,7 +2096,7 @@
     previous_direct_method_idx = method_idx;
     manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
                                           it.GetMethodInvokeType(class_def), class_def_index,
-                                          method_idx, class_loader, dex_file);
+                                          method_idx, jclass_loader, dex_file, allow_dex_compilation);
     it.Next();
   }
   // Compile virtual methods
@@ -2072,7 +2112,7 @@
     previous_virtual_method_idx = method_idx;
     manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
                                           it.GetMethodInvokeType(class_def), class_def_index,
-                                          method_idx, class_loader, dex_file);
+                                          method_idx, jclass_loader, dex_file, allow_dex_compilation);
     it.Next();
   }
   DCHECK(!it.HasNext());
@@ -2088,7 +2128,8 @@
 void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
                                    InvokeType invoke_type, uint32_t class_def_idx,
                                    uint32_t method_idx, jobject class_loader,
-                                   const DexFile& dex_file) {
+                                   const DexFile& dex_file,
+                                   bool allow_dex_to_dex_compilation) {
   CompiledMethod* compiled_method = NULL;
   uint64_t start_ns = NanoTime();
 
@@ -2113,6 +2154,13 @@
       compiled_method = (*compiler_)(*this, code_item, access_flags, invoke_type, class_def_idx,
                                      method_idx, class_loader, dex_file);
       CHECK(compiled_method != NULL) << PrettyMethod(method_idx, dex_file);
+    } else if (allow_dex_to_dex_compilation) {
+      // TODO: add a mode to disable DEX-to-DEX compilation ?
+      compiled_method = (*dex_to_dex_compiler_)(*this, code_item, access_flags,
+                                                invoke_type, class_def_idx,
+                                                method_idx, class_loader, dex_file);
+      // No native code is generated.
+      CHECK(compiled_method == NULL) << PrettyMethod(method_idx, dex_file);
     }
   }
   uint64_t duration_ns = NanoTime() - start_ns;
diff --git a/src/compiler/driver/compiler_driver.h b/src/compiler/driver/compiler_driver.h
index fbfcadb..fdd2149 100644
--- a/src/compiler/driver/compiler_driver.h
+++ b/src/compiler/driver/compiler_driver.h
@@ -349,7 +349,8 @@
       LOCKS_EXCLUDED(Locks::mutator_lock_);
   void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
                      InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx,
-                     jobject class_loader, const DexFile& dex_file)
+                     jobject class_loader, const DexFile& dex_file,
+                     bool allow_dex_to_dex_compilation)
       LOCKS_EXCLUDED(compiled_methods_lock_);
 
   static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
@@ -404,6 +405,8 @@
                                         jobject class_loader, const DexFile& dex_file);
   CompilerFn compiler_;
 
+  CompilerFn dex_to_dex_compiler_;
+
   void* compiler_context_;
 
   typedef CompiledMethod* (*JniCompilerFn)(CompilerDriver& driver,
diff --git a/src/dex_file.cc b/src/dex_file.cc
index dad083c..80465f2 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -108,6 +108,28 @@
   }
 }
 
+bool DexFile::IsReadOnly() const {
+  return GetPermissions() == PROT_READ;
+}
+
+bool DexFile::EnableWrite(uint8_t* addr, size_t length) const {
+  CHECK(IsReadOnly());
+  if (mem_map_.get() == NULL) {
+    return false;
+  } else {
+    return mem_map_->ProtectRegion(addr, length, PROT_READ | PROT_WRITE);
+  }
+}
+
+bool DexFile::DisableWrite(uint8_t* addr, size_t length) const {
+  CHECK(!IsReadOnly());
+  if (mem_map_.get() == NULL) {
+    return false;
+  } else {
+    return mem_map_->ProtectRegion(addr, length, PROT_READ);
+  }
+}
+
 const DexFile* DexFile::OpenFile(const std::string& filename,
                                  const std::string& location,
                                  bool verify) {
diff --git a/src/dex_file.h b/src/dex_file.h
index ecc985f..e09270e 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "base/mutex.h"
 #include "base/stringpiece.h"
 #include "globals.h"
 #include "invoke_type.h"
@@ -384,6 +385,10 @@
     return *header_;
   }
 
+  Mutex& GetModificationLock() {
+    return modification_lock;
+  }
+
   // Decode the dex magic version
   uint32_t GetVersion() const;
 
@@ -798,6 +803,12 @@
 
   int GetPermissions() const;
 
+  bool IsReadOnly() const;
+
+  bool EnableWrite(uint8_t* addr, size_t size) const;
+
+  bool DisableWrite(uint8_t* addr, size_t size) const;
+
  private:
   // Opens a .dex file
   static const DexFile* OpenFile(const std::string& filename,
@@ -830,6 +841,7 @@
         location_checksum_(location_checksum),
         mem_map_(mem_map),
         dex_object_(NULL),
+        modification_lock("DEX modification lock"),
         header_(0),
         string_ids_(0),
         type_ids_(0),
@@ -890,6 +902,11 @@
   // TODO: this is mutable as it shouldn't be here. We should move it to the dex cache or similar.
   mutable jobject dex_object_;
 
+  // The DEX-to-DEX compiler uses this lock to ensure thread safety when
+  // enabling write access to a read-only DEX file.
+  // TODO: move to Locks::dex_file_modification_lock.
+  Mutex modification_lock;
+
   // Points to the header section.
   const Header* header_;
 
diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc
index b18b4d0..e002727 100644
--- a/src/dex_instruction.cc
+++ b/src/dex_instruction.cc
@@ -354,6 +354,14 @@
                << PrettyField(field_idx, *file, true) << " // field@" << field_idx;
             break;
           }  // else fall-through
+        case IGET_QUICK:
+        case IGET_OBJECT_QUICK:
+          if (file != NULL) {
+            uint32_t field_idx = VRegC_22c();
+            os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
+               << "// offset@" << field_idx;
+            break;
+          }  // else fall-through
         case IPUT:
         case IPUT_WIDE:
         case IPUT_OBJECT:
@@ -367,6 +375,14 @@
                << PrettyField(field_idx, *file, true) << " // field@" << field_idx;
             break;
           }  // else fall-through
+        case IPUT_QUICK:
+        case IPUT_OBJECT_QUICK:
+          if (file != NULL) {
+            uint32_t field_idx = VRegC_22c();
+            os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
+               << "// offset@" << field_idx;
+            break;
+          }  // else fall-through
         case INSTANCE_OF:
           if (file != NULL) {
             uint32_t type_idx = VRegC_22c();
@@ -413,6 +429,19 @@
             os << "}, " << PrettyMethod(method_idx, *file) << " // method@" << method_idx;
             break;
           }  // else fall-through
+        case INVOKE_VIRTUAL_QUICK:
+          if (file != NULL) {
+            os << opcode << " {";
+            uint32_t method_idx = VRegB_35c();
+            for (size_t i = 0; i < VRegA_35c(); ++i) {
+              if (i != 0) {
+                os << ", ";
+              }
+              os << "v" << arg[i];
+            }
+            os << "}, // vtable@" << method_idx;
+            break;
+          }  // else fall-through
         default:
           os << opcode << " {v" << arg[0] << ", v" << arg[1] << ", v" << arg[2]
                        << ", v" << arg[3] << ", v" << arg[4] << "}, thing@" << VRegB_35c();
@@ -433,6 +462,13 @@
                << PrettyMethod(method_idx, *file) << " // method@" << method_idx;
             break;
           }  // else fall-through
+        case INVOKE_VIRTUAL_RANGE_QUICK:
+          if (file != NULL) {
+            uint32_t method_idx = VRegB_3rc();
+            os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1))
+               << "// vtable@" << method_idx;
+            break;
+          }  // else fall-through
         default:
           os << StringPrintf("%s, {v%d .. v%d}, thing@%d", opcode, VRegC_3rc(),
                              (VRegC_3rc() + VRegA_3rc() - 1), VRegB_3rc());
diff --git a/src/dex_instruction.h b/src/dex_instruction.h
index adaada7..8a49216 100644
--- a/src/dex_instruction.h
+++ b/src/dex_instruction.h
@@ -278,6 +278,30 @@
     return static_cast<Code>(opcode);
   }
 
+  void SetOpcode(Code opcode) {
+    DCHECK_LT(static_cast<uint16_t>(opcode), 256u);
+    uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+    insns[0] = (insns[0] & 0xff00) | static_cast<uint16_t>(opcode);
+  }
+
+  void SetVRegB_3rc(uint16_t val) {
+    DCHECK(FormatOf(Opcode()) == k3rc);
+    uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+    insns[1] = val;
+  }
+
+  void SetVRegB_35c(uint16_t val) {
+    DCHECK(FormatOf(Opcode()) == k35c);
+    uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+    insns[1] = val;
+  }
+
+  void SetVRegC_22c(uint16_t val) {
+    DCHECK(FormatOf(Opcode()) == k22c);
+    uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+    insns[1] = val;
+  }
+
   // Returns the format of the given opcode.
   static Format FormatOf(Code opcode) {
     return kInstructionFormats[opcode];
diff --git a/src/dex_instruction_list.h b/src/dex_instruction_list.h
index 3083d22..9daec61 100644
--- a/src/dex_instruction_list.h
+++ b/src/dex_instruction_list.h
@@ -242,14 +242,14 @@
   V(0xE0, SHL_INT_LIT8, "shl-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
   V(0xE1, SHR_INT_LIT8, "shr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
   V(0xE2, USHR_INT_LIT8, "ushr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
-  V(0xE3, UNUSED_E3, "unused-e3", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xE4, UNUSED_E4, "unused-e4", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xE5, UNUSED_E5, "unused-e5", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xE6, UNUSED_E6, "unused-e6", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xE7, UNUSED_E7, "unused-e7", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xE8, UNUSED_E8, "unused-e8", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xE9, UNUSED_E9, "unused-e9", k10x, false, kUnknown, 0, kVerifyError) \
-  V(0xEA, UNUSED_EA, "unused-ea", k10x, false, kUnknown, 0, kVerifyError) \
+  V(0xE3, IGET_QUICK, "iget-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+  V(0xE4, IGET_WIDE_QUICK, "iget-wide-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB) \
+  V(0xE5, IGET_OBJECT_QUICK, "iget-object-quick", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+  V(0xE6, IPUT_QUICK, "iput-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+  V(0xE7, IPUT_WIDE_QUICK, "iput-wide-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB) \
+  V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+  V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArg) \
+  V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgRange) \
   V(0xEB, UNUSED_EB, "unused-eb", k10x, false, kUnknown, 0, kVerifyError) \
   V(0xEC, UNUSED_EC, "unused-ec", k10x, false, kUnknown, 0, kVerifyError) \
   V(0xED, UNUSED_ED, "unused-ed", k10x, false, kUnknown, 0, kVerifyError) \
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index dd96f8d..685ca21 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -470,6 +470,98 @@
   }
 }
 
+// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
+// specialization.
+template<bool is_range>
+static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
+                                 const Instruction* inst, JValue* result)
+    NO_THREAD_SAFETY_ANALYSIS;
+
+template<bool is_range>
+static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
+                                 const Instruction* inst, JValue* result) {
+  uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+  Object* receiver = shadow_frame.GetVRegReference(vregC);
+  if (UNLIKELY(receiver == NULL)) {
+    // We lost the reference to the method index so we cannot get a more
+    // precised exception message.
+    ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+    return;
+  }
+  uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+  AbstractMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx);
+  if (UNLIKELY(method == NULL)) {
+    CHECK(self->IsExceptionPending());
+    result->SetJ(0);
+    return;
+  }
+  MethodHelper mh(method);
+
+  const DexFile::CodeItem* code_item = mh.GetCodeItem();
+  uint16_t num_regs;
+  uint16_t num_ins;
+  if (code_item != NULL) {
+    num_regs = code_item->registers_size_;
+    num_ins = code_item->ins_size_;
+  } else if (method->IsAbstract()) {
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;",
+                             "abstract method \"%s\"", PrettyMethod(method).c_str());
+    return;
+  } else {
+    DCHECK(method->IsNative() || method->IsProxyMethod());
+    num_regs = num_ins = AbstractMethod::NumArgRegisters(mh.GetShorty());
+    if (!method->IsStatic()) {
+      num_regs++;
+      num_ins++;
+    }
+  }
+
+  void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
+  ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame,
+                                                    method, 0, memory));
+  size_t cur_reg = num_regs - num_ins;
+  if (receiver != NULL) {
+    new_shadow_frame->SetVRegReference(cur_reg, receiver);
+    ++cur_reg;
+  }
+
+  size_t arg_offset = (receiver == NULL) ? 0 : 1;
+  const char* shorty = mh.GetShorty();
+  uint32_t arg[5];
+  if (!is_range) {
+    inst->GetArgs(arg);
+  }
+  for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) {
+    DCHECK_LT(shorty_pos + 1, mh.GetShortyLength());
+    size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset];
+    switch (shorty[shorty_pos + 1]) {
+      case 'L': {
+        Object* o = shadow_frame.GetVRegReference(arg_pos);
+        new_shadow_frame->SetVRegReference(cur_reg, o);
+        break;
+      }
+      case 'J': case 'D': {
+        uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(arg_pos + 1)) << 32) |
+                              static_cast<uint32_t>(shadow_frame.GetVReg(arg_pos));
+        new_shadow_frame->SetVRegLong(cur_reg, wide_value);
+        cur_reg++;
+        arg_offset++;
+        break;
+      }
+      default:
+        new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos));
+        break;
+    }
+  }
+
+  if (LIKELY(Runtime::Current()->IsStarted())) {
+    (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result);
+  } else {
+    UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins);
+  }
+}
+
 // We use template functions to optimize compiler inlining process. Otherwise,
 // some parts of the code (like a switch statement) which depend on a constant
 // parameter would not be inlined while it should be. These constant parameters
@@ -535,6 +627,41 @@
 
 // TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
 // specialization.
+template<Primitive::Type field_type>
+static void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame,
+                       const Instruction* inst)
+    NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
+
+template<Primitive::Type field_type>
+static inline void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame,
+                               const Instruction* inst) {
+  Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
+  if (UNLIKELY(obj == NULL)) {
+    // We lost the reference to the field index so we cannot get a more
+    // precised exception message.
+    ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+    return;
+  }
+  MemberOffset field_offset(inst->VRegC_22c());
+  const bool is_volatile = false; // iget-x-quick only on non volatile fields.
+  const uint32_t vregA = inst->VRegA_22c();
+  switch (field_type) {
+    case Primitive::kPrimInt:
+      shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetField32(field_offset, is_volatile)));
+      break;
+    case Primitive::kPrimLong:
+      shadow_frame.SetVRegLong(vregA, static_cast<int64_t>(obj->GetField64(field_offset, is_volatile)));
+      break;
+    case Primitive::kPrimNot:
+      shadow_frame.SetVRegReference(vregA, obj->GetFieldObject<mirror::Object*>(field_offset, is_volatile));
+      break;
+    default:
+      LOG(FATAL) << "Unreachable: " << field_type;
+  }
+}
+
+// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
+// specialization.
 template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
 static void DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
                        const Instruction* inst)
@@ -591,6 +718,41 @@
   }
 }
 
+// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
+// specialization.
+template<Primitive::Type field_type>
+static void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame,
+                       const Instruction* inst)
+    NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
+
+template<Primitive::Type field_type>
+static inline void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame,
+                               const Instruction* inst) {
+  Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
+  if (UNLIKELY(obj == NULL)) {
+    // We lost the reference to the field index so we cannot get a more
+    // precised exception message.
+    ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+    return;
+  }
+  MemberOffset field_offset(inst->VRegC_22c());
+  const bool is_volatile = false; // iput-x-quick only on non volatile fields.
+  const uint32_t vregA = inst->VRegA_22c();
+  switch (field_type) {
+    case Primitive::kPrimInt:
+      obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
+      break;
+    case Primitive::kPrimLong:
+      obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile);
+      break;
+    case Primitive::kPrimNot:
+      obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile);
+      break;
+    default:
+      LOG(FATAL) << "Unreachable: " << field_type;
+  }
+}
+
 static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   Class* java_lang_string_class = String::GetJavaLangString();
@@ -1770,6 +1932,21 @@
         DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
         break;
+      case Instruction::IGET_QUICK:
+        PREAMBLE();
+        DoIGetQuick<Primitive::kPrimInt>(self, shadow_frame, inst);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+        break;
+      case Instruction::IGET_WIDE_QUICK:
+        PREAMBLE();
+        DoIGetQuick<Primitive::kPrimLong>(self, shadow_frame, inst);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+        break;
+      case Instruction::IGET_OBJECT_QUICK:
+        PREAMBLE();
+        DoIGetQuick<Primitive::kPrimNot>(self, shadow_frame, inst);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+        break;
       case Instruction::SGET_BOOLEAN:
         PREAMBLE();
         DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
@@ -1840,6 +2017,21 @@
         DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
         break;
+      case Instruction::IPUT_QUICK:
+        PREAMBLE();
+        DoIPutQuick<Primitive::kPrimInt>(self, shadow_frame, inst);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+        break;
+      case Instruction::IPUT_WIDE_QUICK:
+        PREAMBLE();
+        DoIPutQuick<Primitive::kPrimLong>(self, shadow_frame, inst);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+        break;
+      case Instruction::IPUT_OBJECT_QUICK:
+        PREAMBLE();
+        DoIPutQuick<Primitive::kPrimNot>(self, shadow_frame, inst);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+        break;
       case Instruction::SPUT_BOOLEAN:
         PREAMBLE();
         DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
@@ -1925,6 +2117,16 @@
         DoInvoke<kStatic, true, do_access_check>(self, shadow_frame, inst, &result_register);
         POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx);
         break;
+      case Instruction::INVOKE_VIRTUAL_QUICK:
+        PREAMBLE();
+        DoInvokeVirtualQuick<false>(self, shadow_frame, inst, &result_register);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx);
+        break;
+      case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
+        PREAMBLE();
+        DoInvokeVirtualQuick<true>(self, shadow_frame, inst, &result_register);
+        POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx);
+        break;
       case Instruction::NEG_INT:
         PREAMBLE();
         shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x()));
@@ -2715,7 +2917,7 @@
         inst = inst->Next_2xx();
         break;
       case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
-      case Instruction::UNUSED_E3 ... Instruction::UNUSED_FF:
+      case Instruction::UNUSED_EB ... Instruction::UNUSED_FF:
       case Instruction::UNUSED_73:
       case Instruction::UNUSED_79:
       case Instruction::UNUSED_7A:
diff --git a/src/mem_map.cc b/src/mem_map.cc
index fb19424..c75dffa 100644
--- a/src/mem_map.cc
+++ b/src/mem_map.cc
@@ -183,4 +183,26 @@
   return false;
 }
 
+bool MemMap::ProtectRegion(uint8_t* addr, size_t length, int prot) {
+  CHECK_GE(addr, base_begin_);
+  CHECK_LT(addr + length, reinterpret_cast<const uint8_t*>(base_begin_) + base_size_);
+
+  /*
+   * Align "addr" to a page boundary and adjust "length" appropriately.
+   * (The address must be page-aligned, the length doesn't need to be,
+   * but we do need to ensure we cover the same range.)
+   */
+  uint8_t* alignAddr = (uint8_t*) ((uintptr_t) addr & ~(kPageSize-1));
+  size_t alignLength = length + (addr - alignAddr);
+
+  if (mprotect(alignAddr, alignLength, prot) == 0) {
+    prot_ = prot;
+    return true;
+  }
+
+  PLOG(ERROR) << "mprotect(" << reinterpret_cast<void*>(alignAddr) << ", " << alignLength << ", "
+              << prot << ") failed";
+  return false;
+}
+
 }  // namespace art
diff --git a/src/mem_map.h b/src/mem_map.h
index 7310f78..2eb7772 100644
--- a/src/mem_map.h
+++ b/src/mem_map.h
@@ -61,6 +61,8 @@
 
   bool Protect(int prot);
 
+  bool ProtectRegion(uint8_t* addr, size_t length, int prot);
+
   int GetProtect() const {
     return prot_;
   }
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 021e984..87cc328 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -278,6 +278,8 @@
       declaring_class_(NULL),
       interesting_dex_pc_(-1),
       monitor_enter_dex_pcs_(NULL),
+      accessed_field(NULL),
+      invoked_method(NULL),
       have_pending_hard_failure_(false),
       have_pending_runtime_throw_failure_(false),
       new_instance_count_(0),
@@ -308,6 +310,54 @@
   Verify();
 }
 
+mirror::Field* MethodVerifier::FindAccessedFieldAtDexPc(mirror::AbstractMethod* m,
+                                                        uint32_t dex_pc) {
+  MethodHelper mh(m);
+  MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
+                          mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(),
+                          m, m->GetAccessFlags(), false, true);
+  mirror::Field* field = NULL;
+  verifier.interesting_dex_pc_ = dex_pc;
+  verifier.accessed_field = &field;
+  verifier.FindAccessedFieldAtDexPc();
+  return field;
+}
+
+void MethodVerifier::FindAccessedFieldAtDexPc() {
+  CHECK(accessed_field != NULL);
+  CHECK(code_item_ != NULL); // This only makes sense for methods with code.
+
+  // Strictly speaking, we ought to be able to get away with doing a subset of the full method
+  // verification. In practice, the phase we want relies on data structures set up by all the
+  // earlier passes, so we just run the full method verification and bail out early when we've
+  // got what we wanted.
+  Verify();
+}
+
+mirror::AbstractMethod* MethodVerifier::FindInvokedMethodAtDexPc(mirror::AbstractMethod* m,
+                                                                 uint32_t dex_pc) {
+  MethodHelper mh(m);
+  MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
+                          mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(),
+                          m, m->GetAccessFlags(), false, true);
+  mirror::AbstractMethod* method = NULL;
+  verifier.interesting_dex_pc_ = dex_pc;
+  verifier.invoked_method = &method;
+  verifier.FindInvokedMethodAtDexPc();
+  return method;
+}
+
+void MethodVerifier::FindInvokedMethodAtDexPc() {
+  CHECK(invoked_method != NULL);
+  CHECK(code_item_ != NULL); // This only makes sense for methods with code.
+
+  // Strictly speaking, we ought to be able to get away with doing a subset of the full method
+  // verification. In practice, the phase we want relies on data structures set up by all the
+  // earlier passes, so we just run the full method verification and bail out early when we've
+  // got what we wanted.
+  Verify();
+}
+
 bool MethodVerifier::Verify() {
   // If there aren't any instructions, make sure that's expected, then exit successfully.
   if (code_item_ == NULL) {
@@ -2361,10 +2411,63 @@
       work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, false);
       break;
 
+    // Special instructions.
+    //
+    // Note: the following instructions encode offsets derived from class linking.
+    // As such they use Class*/Field*/AbstractMethod* as these offsets only have
+    // meaning if the class linking and resolution were successful.
+    case Instruction::IGET_QUICK:
+      VerifyIGetQuick(inst, reg_types_.Integer(), true);
+      break;
+    case Instruction::IGET_WIDE_QUICK:
+      VerifyIGetQuick(inst, reg_types_.LongLo(), true);
+      break;
+    case Instruction::IGET_OBJECT_QUICK:
+      VerifyIGetQuick(inst, reg_types_.JavaLangObject(false), false);
+      break;
+    case Instruction::IPUT_QUICK:
+      VerifyIPutQuick(inst, reg_types_.Integer(), true);
+      break;
+    case Instruction::IPUT_WIDE_QUICK:
+      VerifyIPutQuick(inst, reg_types_.LongLo(), true);
+      break;
+    case Instruction::IPUT_OBJECT_QUICK:
+      VerifyIPutQuick(inst, reg_types_.JavaLangObject(false), false);
+      break;
+    case Instruction::INVOKE_VIRTUAL_QUICK:
+    case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
+      bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK);
+      mirror::AbstractMethod* called_method = VerifyInvokeVirtualQuickArgs(inst, is_range);
+      if (called_method != NULL) {
+        const char* descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
+        const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
+        if (!return_type.IsLowHalf()) {
+          work_line_->SetResultRegisterType(return_type);
+        } else {
+          work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
+        }
+        just_set_result = true;
+      }
+      break;
+    }
+
     /* These should never appear during verification. */
+    case Instruction::UNUSED_3E:
+    case Instruction::UNUSED_3F:
+    case Instruction::UNUSED_40:
+    case Instruction::UNUSED_41:
+    case Instruction::UNUSED_42:
+    case Instruction::UNUSED_43:
+    case Instruction::UNUSED_73:
+    case Instruction::UNUSED_79:
+    case Instruction::UNUSED_7A:
+    case Instruction::UNUSED_EB:
+    case Instruction::UNUSED_EC:
     case Instruction::UNUSED_ED:
     case Instruction::UNUSED_EE:
     case Instruction::UNUSED_EF:
+    case Instruction::UNUSED_F0:
+    case Instruction::UNUSED_F1:
     case Instruction::UNUSED_F2:
     case Instruction::UNUSED_F3:
     case Instruction::UNUSED_F4:
@@ -2375,30 +2478,9 @@
     case Instruction::UNUSED_F9:
     case Instruction::UNUSED_FA:
     case Instruction::UNUSED_FB:
-    case Instruction::UNUSED_F0:
-    case Instruction::UNUSED_F1:
-    case Instruction::UNUSED_E3:
-    case Instruction::UNUSED_E8:
-    case Instruction::UNUSED_E7:
-    case Instruction::UNUSED_E4:
-    case Instruction::UNUSED_E9:
     case Instruction::UNUSED_FC:
-    case Instruction::UNUSED_E5:
-    case Instruction::UNUSED_EA:
     case Instruction::UNUSED_FD:
-    case Instruction::UNUSED_E6:
-    case Instruction::UNUSED_EB:
     case Instruction::UNUSED_FE:
-    case Instruction::UNUSED_3E:
-    case Instruction::UNUSED_3F:
-    case Instruction::UNUSED_40:
-    case Instruction::UNUSED_41:
-    case Instruction::UNUSED_42:
-    case Instruction::UNUSED_43:
-    case Instruction::UNUSED_73:
-    case Instruction::UNUSED_79:
-    case Instruction::UNUSED_7A:
-    case Instruction::UNUSED_EC:
     case Instruction::UNUSED_FF:
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_);
       break;
@@ -2802,7 +2884,7 @@
     }
   }
   // We use vAA as our expected arg count, rather than res_method->insSize, because we need to
-  // match the call to the signature. Also, we might might be calling through an abstract method
+  // match the call to the signature. Also, we might be calling through an abstract method
   // definition (which doesn't have register count values).
   const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c();
   /* caught by static verifier */
@@ -2882,6 +2964,121 @@
   }
 }
 
+mirror::AbstractMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst,
+                                                                     bool is_range) {
+  DCHECK(Runtime::Current()->IsStarted());
+  CHECK(invoked_method != NULL || accessed_field != NULL)
+      << "We should not be verifying " << inst->Name();
+  const RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range);
+  if (actual_arg_type.IsConflict()) {  // GetInvocationThis failed.
+    return NULL;
+  }
+  mirror::Class* this_class = NULL;
+  if (!actual_arg_type.IsUnresolvedTypes()) {
+    this_class = actual_arg_type.GetClass();
+  } else {
+    const std::string& descriptor(actual_arg_type.GetDescriptor());
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    this_class = class_linker->FindClass(descriptor.c_str(), class_loader_);
+    if (this_class == NULL) {
+      Thread::Current()->ClearException();
+      // Look for a system class
+      this_class = class_linker->FindClass(descriptor.c_str(), NULL);
+    }
+  }
+  CHECK(this_class != NULL) << "Cannot get Class* for type " << actual_arg_type;
+  mirror::ObjectArray<mirror::AbstractMethod>* vtable = this_class->GetVTable();
+  CHECK(vtable != NULL);
+  uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+  CHECK(vtable_index < vtable->GetLength());
+  mirror::AbstractMethod* res_method = vtable->Get(vtable_index);
+  CHECK(!Thread::Current()->IsExceptionPending());
+  // TODO: we should move the code below to FindInvokedMethodAtDexPc. Once the
+  // method is verified, we could access the information we need from register
+  // lines for the dex pc we are looking for.
+  if (invoked_method != NULL && work_insn_idx_ == interesting_dex_pc_) {
+    // We've been requested to tell which method is invoked at this dex pc.
+    *invoked_method = res_method;
+  }
+  if (res_method == NULL) {
+    return NULL;
+  }
+  CHECK(!res_method->IsDirect() && !res_method->IsStatic());
+
+  // We use vAA as our expected arg count, rather than res_method->insSize, because we need to
+  // match the call to the signature. Also, we might be calling through an abstract method
+  // definition (which doesn't have register count values).
+  const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c();
+  /* caught by static verifier */
+  DCHECK(is_range || expected_args <= 5);
+  if (expected_args > code_item_->outs_size_) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args
+        << ") exceeds outsSize (" << code_item_->outs_size_ << ")";
+    return NULL;
+  }
+
+  /*
+   * Check the "this" argument, which must be an instance of the class that declared the method.
+   * For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a
+   * rigorous check here (which is okay since we have to do it at runtime).
+   */
+  if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized";
+    return NULL;
+  }
+  if (!actual_arg_type.IsZero()) {
+    mirror::Class* klass = res_method->GetDeclaringClass();
+    const RegType& res_method_class =
+        reg_types_.FromClass(ClassHelper(klass).GetDescriptor(), klass,
+                             klass->CannotBeAssignedFromOtherTypes());
+    if (!res_method_class.IsAssignableFrom(actual_arg_type)) {
+      Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type
+          << "' not instance of '" << res_method_class << "'";
+      return NULL;
+    }
+  }
+  /*
+   * Process the target method's signature. This signature may or may not
+   * have been verified, so we can't assume it's properly formed.
+   */
+  MethodHelper mh(res_method);
+  const DexFile::TypeList* params = mh.GetParameterTypeList();
+  size_t params_size = params == NULL ? 0 : params->Size();
+  uint32_t arg[5];
+  if (!is_range) {
+    inst->GetArgs(arg);
+  }
+  size_t actual_args = 1;
+  for (size_t param_index = 0; param_index < params_size; param_index++) {
+    if (actual_args >= expected_args) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invalid call to '" << PrettyMethod(res_method)
+                << "'. Expected " << expected_args << " arguments, processing argument " << actual_args
+                << " (where longs/doubles count twice).";
+      return NULL;
+    }
+    const char* descriptor =
+        mh.GetTypeDescriptorFromTypeIdx(params->GetTypeItem(param_index).type_idx_);
+    if (descriptor == NULL) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method)
+                << " missing signature component";
+      return NULL;
+    }
+    const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
+    uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args];
+    if (!work_line_->VerifyRegisterType(get_reg, reg_type)) {
+      return res_method;
+    }
+    actual_args = reg_type.IsLongOrDoubleTypes() ? actual_args + 2 : actual_args + 1;
+  }
+  if (actual_args != expected_args) {
+    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method)
+              << " expected " << expected_args << " arguments, found " << actual_args;
+    return NULL;
+  } else {
+    return res_method;
+  }
+}
+
 void MethodVerifier::VerifyNewArray(const Instruction* inst, bool is_filled, bool is_range) {
   uint32_t type_idx;
   if (!is_filled) {
@@ -3244,6 +3441,185 @@
   }
 }
 
+// Look for an instance field with this offset.
+// TODO: we may speed up the search if offsets are sorted by doing a quick search.
+static mirror::Field* FindInstanceFieldWithOffset(mirror::Class* klass,
+                                                  uint32_t field_offset)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::ObjectArray<mirror::Field>* instance_fields = klass->GetIFields();
+  if (instance_fields != NULL) {
+    for (int32_t i = 0, e = instance_fields->GetLength(); i < e; ++i) {
+      mirror::Field* field = instance_fields->Get(i);
+      if (field->GetOffset().Uint32Value() == field_offset) {
+        return field;
+      }
+    }
+  }
+  if (klass->GetSuperClass() != NULL) {
+    return FindInstanceFieldWithOffset(klass->GetSuperClass(), field_offset);
+  } else {
+    return NULL;
+  }
+}
+
+void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& insn_type,
+                                     bool is_primitive) {
+  DCHECK(Runtime::Current()->IsStarted());
+  CHECK(accessed_field != NULL || invoked_method != NULL)
+        << "We should not be verifying " << inst->Name();
+  const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
+  mirror::Class* object_class = NULL;
+  if (!object_type.IsUnresolvedTypes()) {
+    object_class = object_type.GetClass();
+  } else {
+    // We need to resolve the class from its descriptor.
+    const std::string& descriptor(object_type.GetDescriptor());
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    object_class = class_linker->FindClass(descriptor.c_str(), class_loader_);
+    if (object_class == NULL) {
+      Thread::Current()->ClearException();
+      // Look for a system class
+      object_class = class_linker->FindClass(descriptor.c_str(), NULL);
+    }
+  }
+  CHECK(object_class != NULL) << "Cannot get Class* for type " << object_type;
+  uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c());
+  mirror::Field* field = FindInstanceFieldWithOffset(object_class, field_offset);
+  CHECK(field != NULL);
+  // TODO: we should move the code below to FindAccessedFieldAtDexPc. Once the
+  // method is verified, we could access the information we need from register
+  // lines for the dex pc we are looking for.
+  if (accessed_field != NULL && work_insn_idx_ == interesting_dex_pc_) {
+    // We've been requested to tell which field is accessed at this dex pc.
+    *accessed_field = field;
+  }
+  const char* descriptor = FieldHelper(field).GetTypeDescriptor();
+  mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader();
+  const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
+  const uint32_t vregA = inst->VRegA_22c();
+  if (is_primitive) {
+    if (field_type.Equals(insn_type) ||
+        (field_type.IsFloat() && insn_type.IsIntegralTypes()) ||
+        (field_type.IsDouble() && insn_type.IsLongTypes())) {
+      // expected that read is of the correct primitive type or that int reads are reading
+      // floats or long reads are reading doubles
+    } else {
+      // 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
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
+                                              << " to be of type '" << insn_type
+                                              << "' but found type '" << field_type << "' in get";
+      return;
+    }
+  } 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";
+      work_line_->SetRegisterType(vregA, reg_types_.Conflict());
+      return;
+    }
+  }
+  if (!field_type.IsLowHalf()) {
+    work_line_->SetRegisterType(vregA, field_type);
+  } else {
+    work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(&reg_types_));
+  }
+}
+
+void MethodVerifier::VerifyIPutQuick(const Instruction* inst, const RegType& insn_type,
+                                     bool is_primitive) {
+  DCHECK(Runtime::Current()->IsStarted());
+  CHECK(accessed_field != NULL || invoked_method != NULL)
+    << "We should not be verifying " << inst->Name();
+  const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
+  mirror::Class* object_class = NULL;
+  if (!object_type.IsUnresolvedTypes()) {
+    object_class = object_type.GetClass();
+  } else {
+    // We need to resolve the class from its descriptor.
+    const std::string& descriptor(object_type.GetDescriptor());
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    object_class = class_linker->FindClass(descriptor.c_str(), class_loader_);
+    if (object_class == NULL) {
+      Thread::Current()->ClearException();
+      // Look for a system class
+      object_class = class_linker->FindClass(descriptor.c_str(), NULL);
+    }
+  }
+  CHECK(object_class != NULL) << "Cannot get Class* for type " << object_type;
+  uint32_t field_offset = static_cast<uint32_t>(inst->VRegC_22c());
+  mirror::Field* field = FindInstanceFieldWithOffset(object_class, field_offset);
+  CHECK(field != NULL);
+  // TODO: like VerifyIGetQuick, we should move the code below to
+  // FindAccessedFieldAtDexPc.
+  if (accessed_field != NULL && work_insn_idx_ == interesting_dex_pc_) {
+    // We've been requested to tell which field is accessed at this dex pc.
+    *accessed_field = field;
+  }
+  const char* descriptor = FieldHelper(field).GetTypeDescriptor();
+  mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader();
+  const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
+  if (field != NULL) {
+    if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
+      Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
+                                            << " from other class " << GetDeclaringClass();
+      return;
+    }
+  }
+  const uint32_t vregA = inst->VRegA_22c();
+  if (is_primitive) {
+    // Primitive field assignability rules are weaker than regular assignability rules
+    bool instruction_compatible;
+    bool value_compatible;
+    const RegType& value_type = work_line_->GetRegisterType(vregA);
+    if (field_type.IsIntegralTypes()) {
+      instruction_compatible = insn_type.IsIntegralTypes();
+      value_compatible = value_type.IsIntegralTypes();
+    } else if (field_type.IsFloat()) {
+      instruction_compatible = insn_type.IsInteger();  // no [is]put-float, so expect [is]put-int
+      value_compatible = value_type.IsFloatTypes();
+    } else if (field_type.IsLong()) {
+      instruction_compatible = insn_type.IsLong();
+      value_compatible = value_type.IsLongTypes();
+    } else if (field_type.IsDouble()) {
+      instruction_compatible = insn_type.IsLong();  // no [is]put-double, so expect [is]put-long
+      value_compatible = value_type.IsDoubleTypes();
+    } else {
+      instruction_compatible = false;  // reference field with primitive store
+      value_compatible = false;  // unused
+    }
+    if (!instruction_compatible) {
+      // 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
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
+                                              << " to be of type '" << insn_type
+                                              << "' but found type '" << field_type
+                                              << "' in put";
+      return;
+    }
+    if (!value_compatible) {
+      Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA
+          << " of type " << value_type
+          << " but expected " << field_type
+          << " for store to " << PrettyField(field) << " in put";
+      return;
+    }
+  } 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";
+      return;
+    }
+    work_line_->VerifyRegisterType(vregA, field_type);
+  }
+}
+
 bool MethodVerifier::CheckNotMoveException(const uint16_t* insns, int insn_idx) {
   if ((insns[insn_idx] & 0xff) == Instruction::MOVE_EXCEPTION) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid use of move-exception";
diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h
index 198d8cb..da64979 100644
--- a/src/verifier/method_verifier.h
+++ b/src/verifier/method_verifier.h
@@ -197,11 +197,23 @@
         LOCKS_EXCLUDED(safecast_map_lock_);
 
   // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding
-  // to the locks held at 'dex_pc' in 'm'.
+  // to the locks held at 'dex_pc' in method 'm'.
   static void FindLocksAtDexPc(mirror::AbstractMethod* m, uint32_t dex_pc,
                                std::vector<uint32_t>& monitor_enter_dex_pcs)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Returns the accessed field corresponding to the quick instruction's field
+  // offset at 'dex_pc' in method 'm'.
+  static mirror::Field* FindAccessedFieldAtDexPc(mirror::AbstractMethod* m,
+                                                 uint32_t dex_pc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Returns the invoked method corresponding to the quick instruction's vtable
+  // index at 'dex_pc' in method 'm'.
+  static mirror::AbstractMethod* FindInvokedMethodAtDexPc(mirror::AbstractMethod* m,
+                                                          uint32_t dex_pc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void Shutdown();
 
@@ -254,6 +266,10 @@
 
   void FindLocksAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void FindAccessedFieldAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void FindInvokedMethodAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   /*
    * Compute the width of the instruction at each address in the instruction stream, and store it in
    * insn_flags_. Addresses that are in the middle of an instruction, or that are part of switch
@@ -484,6 +500,16 @@
                    bool is_primitive, bool is_static)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Perform verification of an iget-quick instruction.
+  void VerifyIGetQuick(const Instruction* inst, const RegType& insn_type,
+                       bool is_primitive)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  // Perform verification of an iput-quick instruction.
+  void VerifyIPutQuick(const Instruction* inst, const RegType& insn_type,
+                       bool is_primitive)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Resolves a class based on an index and performs access checks to ensure the referrer can
   // access the resolved class.
   const RegType& ResolveClassAndCheckAccess(uint32_t class_idx)
@@ -532,6 +558,10 @@
                                                bool is_range, bool is_super)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  mirror::AbstractMethod* VerifyInvokeVirtualQuickArgs(const Instruction* inst,
+                                                       bool is_range)
+  SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   /*
    * Verify that the target instruction is not "move-exception". It's important that the only way
    * to execute a move-exception is as the first instruction of an exception handler.
@@ -646,11 +676,18 @@
   const RegType* declaring_class_;  // Lazily computed reg type of the method's declaring class.
   // Instruction widths and flags, one entry per code unit.
   UniquePtr<InstructionFlags[]> insn_flags_;
-  // The dex PC of a FindLocksAtDexPc request, -1 otherwise.
+  // The dex PC of a FindLocksAtDexPc, FindAccessedFieldAtDexPc or
+  // FindInvokedMethodAtDexPc request, -1 otherwise.
   uint32_t interesting_dex_pc_;
   // The container into which FindLocksAtDexPc should write the registers containing held locks,
   // NULL if we're not doing FindLocksAtDexPc.
   std::vector<uint32_t>* monitor_enter_dex_pcs_;
+  // The pointer into which FindAccessedFieldAtDexPc should write the accessed field,
+  // NULL if we're not doing FindAccessedFieldAtDexPc.
+  mirror::Field** accessed_field;
+  // The pointer into which FindInvokedMethodAtDexPc should write the invoked method,
+  // NULL if we're not doing FindInvokedMethodAtDexPc.
+  mirror::AbstractMethod** invoked_method;
 
   // The types of any error that occurs.
   std::vector<VerifyError> failures_;