Improve interpreter to interpreter invokes.

The interpreter constructs a shadow frame instead of arg array to make
interpreter to interpreter transitions faster. This adds a pointer to
an entry for the interpreter to each method.

Change-Id: If48911d3aa3470847b8548a9e92090b829f4f254
diff --git a/src/class_linker.cc b/src/class_linker.cc
index e55fe78..759bc0c 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -74,6 +74,8 @@
 
 namespace art {
 
+extern "C" JValue artInterpreterToQuickEntry(Thread* self, ShadowFrame* shadow_frame);
+
 static void ThrowNoClassDefFoundError(const char* fmt, ...)
     __attribute__((__format__(__printf__, 1, 2)))
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -1037,6 +1039,12 @@
   // Check if object is a method without its code set and point it to the resolution trampoline.
   if (obj->IsMethod()) {
     mirror::AbstractMethod* method = obj->AsMethod();
+    // Install entry point from interpreter.
+    if (method->GetCode() == NULL && !method->IsNative() && !method->IsProxyMethod()) {
+      method->SetEntryPointFromInterpreter(interpreter::EnterInterpreterFromInterpreter);
+    } else {
+      method->SetEntryPointFromInterpreter(artInterpreterToQuickEntry);
+    }
     if (method->GetCode() == NULL) {
       method->SetCode(GetResolutionTrampoline());
     }
@@ -1598,6 +1606,13 @@
   const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);
   oat_method.LinkMethod(method.get());
 
+  // Install entry point from interpreter.
+  if (method->GetCode() == NULL && !method->IsNative() && !method->IsProxyMethod()) {
+    method->SetEntryPointFromInterpreter(interpreter::EnterInterpreterFromInterpreter);
+  } else {
+    method->SetEntryPointFromInterpreter(artInterpreterToQuickEntry);
+  }
+
   Runtime* runtime = Runtime::Current();
   if (method->IsAbstract()) {
     method->SetCode(GetAbstractMethodErrorStub());
@@ -2551,6 +2566,7 @@
 #else
   method->SetCode(reinterpret_cast<void*>(art_portable_proxy_invoke_handler));
 #endif
+  method->SetEntryPointFromInterpreter(artInterpreterToQuickEntry);
 
   return method;
 }
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index c47ce4a..94ea9d7 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -471,18 +471,19 @@
     offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, dex_cache_strings_),                    "dexCacheStrings"));
 
     // alphabetical 32-bit
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, access_flags_),        "accessFlags"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, code_),                "code"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, code_item_offset_),    "codeItemOffset"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, core_spill_mask_),     "coreSpillMask"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, fp_spill_mask_),       "fpSpillMask"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, frame_size_in_bytes_), "frameSizeInBytes"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, native_gc_map_),       "gcMap"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, mapping_table_),       "mappingTable"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, method_dex_index_),    "methodDexIndex"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, method_index_),        "methodIndex"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, native_method_),       "nativeMethod"));
-    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, vmap_table_),          "vmapTable"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, access_flags_),                 "accessFlags"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, code_),                         "code"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, code_item_offset_),             "codeItemOffset"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, core_spill_mask_),              "coreSpillMask"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, entry_point_from_interpreter_), "entryPointFromInterpreter"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, fp_spill_mask_),                "fpSpillMask"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, frame_size_in_bytes_),          "frameSizeInBytes"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, gc_map_),                       "gcMap"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, mapping_table_),                "mappingTable"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, method_dex_index_),             "methodDexIndex"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, method_index_),                 "methodIndex"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, native_method_),                "nativeMethod"));
+    offsets.push_back(CheckOffset(OFFSETOF_MEMBER(AbstractMethod, vmap_table_),                   "vmapTable"));
   };
 };
 
diff --git a/src/compiler/driver/compiler_driver.cc b/src/compiler/driver/compiler_driver.cc
index 5ddd0a6..6b7d9e6 100644
--- a/src/compiler/driver/compiler_driver.cc
+++ b/src/compiler/driver/compiler_driver.cc
@@ -492,7 +492,7 @@
 
 bool CompilerDriver::IsImageClass(const std::string& descriptor) const {
   if (image_classes_ == NULL) {
-    return true;
+    return false;
   }
   return image_classes_->find(descriptor) != image_classes_->end();
 }
@@ -1649,7 +1649,6 @@
     } else if (code_item->insns_size_in_code_units_ < Runtime::Current()->GetSmallModeMethodDexSizeLimit()) {
     // Do compile small methods.
       dont_compile = false;
-      LOG(INFO) << "Compiling a small method: " << PrettyMethod(method_idx, dex_file);
     }
 
     if (!dont_compile) {
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index b82c632..9f48e78 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -42,6 +42,7 @@
 using namespace art::mirror;
 
 namespace art {
+
 namespace interpreter {
 
 static const int32_t kMaxInt = std::numeric_limits<int32_t>::max();
@@ -50,13 +51,13 @@
 static const int64_t kMinLong = std::numeric_limits<int64_t>::min();
 
 static void UnstartedRuntimeInvoke(Thread* self, AbstractMethod* target_method,
-                                   Object* receiver, uint32_t* args, JValue* result)
+                                   ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   // In a runtime that's not started we intercept certain methods to avoid complicated dependency
   // problems in core libraries.
   std::string name(PrettyMethod(target_method));
   if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") {
-    std::string descriptor(DotToDescriptor(reinterpret_cast<Object*>(args[0])->AsString()->ToModifiedUtf8().c_str()));
+    std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str()));
     ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader();
     Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
                                                                    class_loader);
@@ -64,7 +65,7 @@
         << PrettyDescriptor(descriptor);
     result->SetL(found);
   } else if (name == "java.lang.Object java.lang.Class.newInstance()") {
-    Class* klass = receiver->AsClass();
+    Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
     AbstractMethod* c = klass->FindDeclaredDirectMethod("<init>", "()V");
     CHECK(c != NULL);
     Object* obj = klass->AllocObject(self);
@@ -74,8 +75,8 @@
   } else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") {
     // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
     // going the reflective Dex way.
-    Class* klass = receiver->AsClass();
-    String* name = reinterpret_cast<Object*>(args[0])->AsString();
+    Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
+    String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
     Field* found = NULL;
     FieldHelper fh;
     ObjectArray<Field>* fields = klass->GetIFields();
@@ -104,25 +105,25 @@
     result->SetL(found);
   } else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") {
     // Special case array copying without initializing System.
-    Class* ctype = reinterpret_cast<Object*>(args[0])->GetClass()->GetComponentType();
-    jint srcPos = args[1];
-    jint dstPos = args[3];
-    jint length = args[4];
+    Class* ctype = shadow_frame->GetVRegReference(arg_offset)->GetClass()->GetComponentType();
+    jint srcPos = shadow_frame->GetVReg(arg_offset + 1);
+    jint dstPos = shadow_frame->GetVReg(arg_offset + 3);
+    jint length = shadow_frame->GetVReg(arg_offset + 4);
     if (!ctype->IsPrimitive()) {
-      ObjectArray<Object>* src = reinterpret_cast<Object*>(args[0])->AsObjectArray<Object>();
-      ObjectArray<Object>* dst = reinterpret_cast<Object*>(args[2])->AsObjectArray<Object>();
+      ObjectArray<Object>* src = shadow_frame->GetVRegReference(arg_offset)->AsObjectArray<Object>();
+      ObjectArray<Object>* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray<Object>();
       for (jint i = 0; i < length; ++i) {
         dst->Set(dstPos + i, src->Get(srcPos + i));
       }
     } else if (ctype->IsPrimitiveChar()) {
-      CharArray* src = reinterpret_cast<Object*>(args[0])->AsCharArray();
-      CharArray* dst = reinterpret_cast<Object*>(args[2])->AsCharArray();
+      CharArray* src = shadow_frame->GetVRegReference(arg_offset)->AsCharArray();
+      CharArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsCharArray();
       for (jint i = 0; i < length; ++i) {
         dst->Set(dstPos + i, src->Get(srcPos + i));
       }
     } else if (ctype->IsPrimitiveInt()) {
-      IntArray* src = reinterpret_cast<Object*>(args[0])->AsIntArray();
-      IntArray* dst = reinterpret_cast<Object*>(args[2])->AsIntArray();
+      IntArray* src = shadow_frame->GetVRegReference(arg_offset)->AsIntArray();
+      IntArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsIntArray();
       for (jint i = 0; i < length; ++i) {
         dst->Set(dstPos + i, src->Get(srcPos + i));
       }
@@ -131,7 +132,7 @@
     }
   } else {
     // Not special, continue with regular interpreter execution.
-    EnterInterpreterFromInvoke(self, target_method, receiver, args, result);
+    result->SetJ(EnterInterpreterFromInterpreter(self, shadow_frame).GetJ());
   }
 }
 
@@ -402,21 +403,65 @@
     return;
   }
   mh.ChangeMethod(target_method);
-  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
-  if (is_range) {
-    arg_array.BuildArgArray(shadow_frame, receiver, dec_insn.vC + (type != kStatic ? 1 : 0));
+
+  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 (target_method->IsAbstract()) {
+    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+    self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;",
+                             "abstract method \"%s\"", PrettyMethod(target_method).c_str());
+    return;
   } else {
-    arg_array.BuildArgArray(shadow_frame, receiver, dec_insn.arg + (type != kStatic ? 1 : 0));
-  }
-  if (LIKELY(Runtime::Current()->IsStarted())) {
-    target_method->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result,
-                          mh.GetShorty()[0]);
-  } else {
-    uint32_t* args = arg_array.GetArray();
-    if (type != kStatic) {
-      args++;
+    DCHECK(target_method->IsNative() || target_method->IsProxyMethod());
+    num_regs = num_ins = AbstractMethod::NumArgRegisters(mh.GetShorty());
+    if (!target_method->IsStatic()) {
+      num_regs++;
+      num_ins++;
     }
-    UnstartedRuntimeInvoke(self, target_method, receiver, args, result);
+  }
+
+  Runtime* runtime = Runtime::Current();
+  UniquePtr<ShadowFrame> new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame,
+                                                              target_method, 0));
+  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();
+  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 ? dec_insn.vC + arg_offset : dec_insn.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->IsStarted())) {
+    result->SetJ((target_method->GetEntryPointFromInterpreter())(self, new_shadow_frame.get()).GetJ());
+  } else {
+    UnstartedRuntimeInvoke(self, target_method, new_shadow_frame.get(), result, num_regs - num_ins);
   }
   mh.ChangeMethod(shadow_frame.GetMethod());
 }
@@ -1925,5 +1970,42 @@
   return Execute(self, mh, code_item, shadow_frame, JValue());
 }
 
+JValue EnterInterpreterFromInterpreter(Thread* self, ShadowFrame* shadow_frame)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (__builtin_frame_address(0) < self->GetStackEnd()) {
+    ThrowStackOverflowError(self);
+    return JValue();
+  }
+
+  AbstractMethod* method = shadow_frame->GetMethod();
+  if (method->IsStatic() && !method->GetDeclaringClass()->IsInitializing()) {
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(),
+                                                                 true, true)) {
+      DCHECK(Thread::Current()->IsExceptionPending());
+      return JValue();
+    }
+    CHECK(method->GetDeclaringClass()->IsInitializing());
+  }
+
+  self->PushShadowFrame(shadow_frame);
+
+  MethodHelper mh(method);
+  const DexFile::CodeItem* code_item = mh.GetCodeItem();
+  JValue result;
+  if (LIKELY(!method->IsNative())) {
+    result = Execute(self, mh, code_item, *shadow_frame, JValue());
+  } else {
+    // We don't expect to be asked to interpret native code (which is entered via a JNI compiler
+    // generated stub) except during testing and image writing.
+    CHECK(!Runtime::Current()->IsStarted());
+    Object* receiver = method->IsStatic() ? NULL : shadow_frame->GetVRegReference(0);
+    uint32_t* args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);
+    UnstartedRuntimeJni(self, method, receiver, args, &result);
+  }
+
+  self->PopShadowFrame();
+  return result;
+}
+
 }  // namespace interpreter
 }  // namespace art
diff --git a/src/interpreter/interpreter.h b/src/interpreter/interpreter.h
index cf47b68..94fdff1 100644
--- a/src/interpreter/interpreter.h
+++ b/src/interpreter/interpreter.h
@@ -47,6 +47,9 @@
                                        ShadowFrame& shadow_frame)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+extern JValue EnterInterpreterFromInterpreter(Thread* self, ShadowFrame* shadow_frame)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
 }  // namespace interpreter
 }  // namespace art
 
diff --git a/src/invoke_arg_array_builder.h b/src/invoke_arg_array_builder.h
index a6e99a5..b57d60a 100644
--- a/src/invoke_arg_array_builder.h
+++ b/src/invoke_arg_array_builder.h
@@ -162,60 +162,10 @@
     }
   }
 
-  void BuildArgArray(const ShadowFrame& shadow_frame, mirror::Object* receiver, uint32_t range_start)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    // Set receiver if non-null (method is not static)
-    if (receiver != NULL) {
-      Append(reinterpret_cast<int32_t>(receiver));
-    }
-    for (size_t i = 1, reg_offset = 0; i < shorty_len_; ++i, ++reg_offset) {
-      switch (shorty_[i]) {
-        case 'Z':
-        case 'B':
-        case 'C':
-        case 'S':
-        case 'I':
-        case 'F':
-          Append(shadow_frame.GetVReg(range_start + reg_offset));
-          break;
-        case 'L':
-          Append(reinterpret_cast<int32_t>(shadow_frame.GetVRegReference(range_start + reg_offset)));
-          break;
-        case 'D':
-        case 'J':
-          AppendWide(shadow_frame.GetVRegLong(range_start + reg_offset));
-          reg_offset++;
-          break;
-      }
-    }
-  }
-
-  void BuildArgArray(const ShadowFrame& shadow_frame, mirror::Object* receiver, const uint32_t* arg_regs)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    // Set receiver if non-null (method is not static)
-    if (receiver != NULL) {
-      Append(reinterpret_cast<int32_t>(receiver));
-    }
-    for (size_t i = 1, reg_offset = 0; i < shorty_len_; ++i, ++reg_offset) {
-      switch (shorty_[i]) {
-        case 'Z':
-        case 'B':
-        case 'C':
-        case 'S':
-        case 'I':
-        case 'F':
-          Append(shadow_frame.GetVReg(arg_regs[reg_offset]));
-          break;
-        case 'L':
-          Append(reinterpret_cast<int32_t>(shadow_frame.GetVRegReference(arg_regs[reg_offset])));
-          break;
-        case 'D':
-        case 'J':
-          AppendWide(shadow_frame.GetVRegLong(arg_regs[reg_offset]));
-          reg_offset++;
-          break;
-      }
-    }
+  void BuildArgArray(ShadowFrame* shadow_frame, uint32_t arg_offset)
+  SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    arg_array_ = shadow_frame->GetVRegArgs(arg_offset);
+    num_bytes_ = (shadow_frame->NumberOfVRegs() - arg_offset) * 4;
   }
 
  private:
diff --git a/src/mirror/abstract_method.cc b/src/mirror/abstract_method.cc
index 559b558..c351ce8 100644
--- a/src/mirror/abstract_method.cc
+++ b/src/mirror/abstract_method.cc
@@ -344,7 +344,7 @@
 #else
     UNIMPLEMENTED(FATAL);
 #endif
-    SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(AbstractMethod, native_gc_map_),
+    SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(AbstractMethod, gc_map_),
         reinterpret_cast<const uint8_t*>(native_method), false);
   }
 }
diff --git a/src/mirror/abstract_method.h b/src/mirror/abstract_method.h
index 9440915..e955376 100644
--- a/src/mirror/abstract_method.h
+++ b/src/mirror/abstract_method.h
@@ -31,11 +31,14 @@
 struct MethodClassOffsets;
 struct MethodOffsets;
 class StringPiece;
+class ShadowFrame;
 
 namespace mirror {
 
 class StaticStorageBase;
 
+typedef JValue (EntryPointFromInterpreter)(Thread* self, ShadowFrame* shadow_frame);
+
 // C++ mirror of java.lang.reflect.Method and java.lang.reflect.Constructor
 class MANAGED AbstractMethod : public Object {
  public:
@@ -189,6 +192,14 @@
   void Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, char result_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  EntryPointFromInterpreter* GetEntryPointFromInterpreter() const {
+    return GetFieldPtr<EntryPointFromInterpreter*>(OFFSET_OF_OBJECT_MEMBER(AbstractMethod, entry_point_from_interpreter_), false);
+  }
+
+  void SetEntryPointFromInterpreter(EntryPointFromInterpreter* entry_point_from_interpreter) {
+    SetFieldPtr<EntryPointFromInterpreter*>(OFFSET_OF_OBJECT_MEMBER(AbstractMethod, entry_point_from_interpreter_), entry_point_from_interpreter, false);
+  }
+
   const void* GetCode() const {
     return GetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(AbstractMethod, code_), false);
   }
@@ -293,11 +304,10 @@
   void SetOatVmapTableOffset(uint32_t vmap_table_offset);
 
   const uint8_t* GetNativeGcMap() const {
-    return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(AbstractMethod, native_gc_map_), false);
+    return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(AbstractMethod, gc_map_), false);
   }
   void SetNativeGcMap(const uint8_t* data) {
-    SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(AbstractMethod, native_gc_map_), data,
-        false);
+    SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(AbstractMethod, gc_map_), data, false);
   }
 
   // When building the oat need a convenient place to stuff the offset of the native GC map.
@@ -424,6 +434,7 @@
 
   // Compiled code associated with this method for callers from managed code.
   // May be compiled managed code or a bridge for invoking a native method.
+  // TODO: Break apart this into portable and quick.
   const void* code_;
 
   // Offset to the CodeItem.
@@ -432,14 +443,17 @@
   // Architecture-dependent register spill mask
   uint32_t core_spill_mask_;
 
+  // Called by the interpreter to execute this method.
+  EntryPointFromInterpreter* entry_point_from_interpreter_;
+
   // Architecture-dependent register spill mask
   uint32_t fp_spill_mask_;
 
   // Total size in bytes of the frame
   size_t frame_size_in_bytes_;
 
-  // Garbage collection map of native PC offsets to reference bitmaps.
-  const uint8_t* native_gc_map_;
+  // Garbage collection map of native PC offsets (quick) or dex PCs (portable) to reference bitmaps.
+  const uint8_t* gc_map_;
 
   // Mapping from native pc to dex pc
   const uint32_t* mapping_table_;
diff --git a/src/oat/runtime/support_interpreter.cc b/src/oat/runtime/support_interpreter.cc
index 3ab234f..6a65cea 100644
--- a/src/oat/runtime/support_interpreter.cc
+++ b/src/oat/runtime/support_interpreter.cc
@@ -18,6 +18,7 @@
 #include "callee_save_frame.h"
 #include "dex_file-inl.h"
 #include "interpreter/interpreter.h"
+#include "invoke_arg_array_builder.h"
 #include "mirror/abstract_method-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
@@ -108,4 +109,19 @@
   return result.GetJ();
 }
 
+extern "C" JValue artInterpreterToQuickEntry(Thread* self, ShadowFrame* shadow_frame)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  mirror::AbstractMethod* method = shadow_frame->GetMethod();
+  MethodHelper mh(method);
+  const DexFile::CodeItem* code_item = mh.GetCodeItem();
+
+  uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_;
+  ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
+  arg_array.BuildArgArray(shadow_frame, arg_offset);
+  JValue result;
+  method->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), &result, mh.GetShorty()[0]);
+
+  return result;
+}
+
 }  // namespace art
diff --git a/src/stack.h b/src/stack.h
index e2148a3..8e597b2 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -145,6 +145,11 @@
     }
   }
 
+  // Get view of vregs as range of consecutive arguments starting at i.
+  uint32_t* GetVRegArgs(size_t i) {
+    return &vregs_[i];
+  }
+
   void SetVReg(size_t i, int32_t val) {
     DCHECK_LT(i, NumberOfVRegs());
     uint32_t* vreg = &vregs_[i];