Static and direct resolution stub.

Ensure that invoke static and direct go through a stub that causes
resolution and initialization.

Change-Id: I872900560322817d8f4378b04ac410d9ea0b3b17
diff --git a/src/asm_support.h b/src/asm_support.h
index 1ef10b8..fb2f7cc 100644
--- a/src/asm_support.h
+++ b/src/asm_support.h
@@ -9,13 +9,13 @@
 #define rLR r14
 #define SUSPEND_CHECK_INTERVAL (1000)
 // Offset of field Thread::top_of_managed_stack_ verified in InitCpu
-#define THREAD_TOP_OF_MANAGED_STACK_OFFSET 276
+#define THREAD_TOP_OF_MANAGED_STACK_OFFSET 280
 // Offset of field Thread::top_of_managed_stack_pc_ verified in InitCpu
-#define THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET 280
+#define THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET 284
 
 #elif defined(__i386__)
 // Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 372
+#define THREAD_SELF_OFFSET 376
 #endif
 
 #endif  // ART_SRC_ASM_SUPPORT_H_
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 2c69651..ff3c6be 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -2401,6 +2401,14 @@
   return resolved;
 }
 
+const char* ClassLinker::MethodShorty(uint32_t method_idx, Method* referrer) {
+  Class* declaring_class = referrer->GetDeclaringClass();
+  DexCache* dex_cache = declaring_class->GetDexCache();
+  const DexFile& dex_file = FindDexFile(dex_cache);
+  const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
+  return dex_file.GetShorty(method_id.proto_idx_);
+}
+
 void ClassLinker::DumpAllClasses(int flags) const {
   // TODO: at the time this was written, it wasn't safe to call PrettyField with the ClassLinker
   // lock held, because it might need to resolve a field's type, which would try to take the lock.
diff --git a/src/class_linker.h b/src/class_linker.h
index 7f08a9b..75eefc9 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -155,6 +155,9 @@
                       const ClassLoader* class_loader,
                       bool is_static);
 
+  // Get shorty from method index without resolution. Used to do handlerization.
+  const char* MethodShorty(uint32_t method_idx, Method* referrer);
+
   // Returns true on success, false if there's an exception pending.
   // can_run_clinit=false allows the compiler to attempt to init a class,
   // given the restriction that no <clinit> execution is possible.
diff --git a/src/common_test.h b/src/common_test.h
index b321071..68e3019 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -130,12 +130,16 @@
 #if defined(__i386__)
     runtime_->SetJniStubArray(JniCompiler::CreateJniStub(kX86));
     runtime_->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(kX86));
+    runtime_->SetResolutionStubArray(Compiler::CreateResolutionStub(kX86, false), false);
+    runtime_->SetResolutionStubArray(Compiler::CreateResolutionStub(kX86, true), true);
     runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(kX86));
     compiler_.reset(new Compiler(kX86));
 #elif defined(__arm__)
     runtime_->SetJniStubArray(JniCompiler::CreateJniStub(kThumb2));
     runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(kThumb2));
     runtime_->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(kThumb2));
+    runtime_->SetResolutionStubArray(Compiler::CreateResolutionStub(kThumb2, false), false);
+    runtime_->SetResolutionStubArray(Compiler::CreateResolutionStub(kThumb2, true), true);
     compiler_.reset(new Compiler(kThumb2));
 #endif
 
diff --git a/src/compiler.cc b/src/compiler.cc
index af5cdd6..c6705a6 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -18,23 +18,13 @@
 
 namespace arm {
   ByteArray* CreateAbstractMethodErrorStub();
+  void ArmCreateInvokeStub(Method* method);
+  ByteArray* ArmCreateResolutionTrampoline(bool is_static);
 }
-
 namespace x86 {
   ByteArray* CreateAbstractMethodErrorStub();
-}
-
-ByteArray* Compiler::CreateAbstractMethodErrorStub(InstructionSet instruction_set) {
-  switch (instruction_set) {
-    case kArm:
-    case kThumb2:
-      return arm::CreateAbstractMethodErrorStub();
-    case kX86:
-      return x86::CreateAbstractMethodErrorStub();
-    default:
-      LOG(FATAL) << "Unknown InstructionSet " << (int) instruction_set;
-      return NULL;
-  }
+  void X86CreateInvokeStub(Method* method);
+  ByteArray* X86CreateResolutionTrampoline(bool is_static);
 }
 
 Compiler::Compiler(InstructionSet insns) : instruction_set_(insns), jni_compiler_(insns),
@@ -42,6 +32,26 @@
   CHECK(!Runtime::Current()->IsStarted());
 }
 
+ByteArray* Compiler::CreateResolutionStub(InstructionSet instruction_set, bool is_static) {
+  if (instruction_set == kX86) {
+    return x86::X86CreateResolutionTrampoline(is_static);
+  } else {
+    CHECK(instruction_set == kArm || instruction_set == kThumb2);
+    // Generates resolution stub using ARM instruction set
+    return arm::ArmCreateResolutionTrampoline(is_static);
+  }
+}
+
+ByteArray* Compiler::CreateAbstractMethodErrorStub(InstructionSet instruction_set) {
+  if (instruction_set == kX86) {
+    return x86::CreateAbstractMethodErrorStub();
+  } else {
+    CHECK(instruction_set == kArm || instruction_set == kThumb2);
+    // Generates resolution stub using ARM instruction set
+    return arm::CreateAbstractMethodErrorStub();
+  }
+}
+
 void Compiler::CompileAll(const ClassLoader* class_loader) {
   DCHECK(!Runtime::Current()->IsStarted());
   Resolve(class_loader);
@@ -265,13 +275,6 @@
   }
 }
 
-namespace arm {
-  void ArmCreateInvokeStub(Method* method);
-}
-namespace x86 {
-  void X86CreateInvokeStub(Method* method);
-}
-
 void Compiler::CompileMethod(Method* method) {
   if (method->IsNative()) {
     jni_compiler_.Compile(method);
@@ -313,13 +316,20 @@
 }
 
 void Compiler::SetCodeAndDirectMethodsDexFile(const DexFile& dex_file) {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Runtime* runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
   DexCache* dex_cache = class_linker->FindDexCache(dex_file);
   CodeAndDirectMethods* code_and_direct_methods = dex_cache->GetCodeAndDirectMethods();
   for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
     Method* method = dex_cache->GetResolvedMethod(i);
-    if (method == NULL) {
-      code_and_direct_methods->SetResolvedDirectMethodTrampoline(i);
+    if ((method == NULL) || (method->IsStatic() && !method->GetDeclaringClass()->IsInitialized())) {
+      ByteArray* res_trampoline = runtime->GetResolutionStubArray(method->IsStatic());
+      if (instruction_set_ == kX86) {
+        code_and_direct_methods->SetResolvedDirectMethodTrampoline(i, res_trampoline, kX86);
+      } else {
+        CHECK(instruction_set_ == kArm || instruction_set_ == kThumb2);
+        code_and_direct_methods->SetResolvedDirectMethodTrampoline(i, res_trampoline, kArm);
+      }
     } else if (method->IsDirect()) {
       code_and_direct_methods->SetResolvedDirectMethod(i, method);
     } else {
diff --git a/src/compiler.h b/src/compiler.h
index f9a5c29..3b316eb 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -31,9 +31,11 @@
   }
 
   // Stub to throw AbstractMethodError
-  // TODO: remove from Compiler
   static ByteArray* CreateAbstractMethodErrorStub(InstructionSet instruction_set);
 
+  // Generate the trampoline that's invoked by unresolved direct methods
+  static ByteArray* CreateResolutionStub(InstructionSet instruction_set, bool is_static);
+
  private:
   // Attempt to resolve all type, methods, fields, and strings
   // referenced from code in the dex file following PathClassLoader
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index d34a681..1ad71a6 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -200,6 +200,12 @@
   if (!runtime->HasAbstractMethodErrorStubArray()) {
     runtime->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(kThumb2));
   }
+  if (!runtime->HasResolutionStubArray(false)) {
+    runtime->SetResolutionStubArray(Compiler::CreateResolutionStub(kThumb2,false), false);
+  }
+  if (!runtime->HasResolutionStubArray(true)) {
+    runtime->SetResolutionStubArray(Compiler::CreateResolutionStub(kThumb2,true), true);
+  }
   if (!runtime->HasCalleeSaveMethod()) {
     runtime->SetCalleeSaveMethod(runtime->CreateCalleeSaveMethod(kThumb2));
   }
diff --git a/src/dex_cache.h b/src/dex_cache.h
index a75d6f4..7ca6825 100644
--- a/src/dex_cache.h
+++ b/src/dex_cache.h
@@ -26,9 +26,15 @@
     return reinterpret_cast<Method*>(Get(MethodIndex(method_idx)));
   }
 
-  void SetResolvedDirectMethodTrampoline(uint32_t method_idx) {
-    UNIMPLEMENTED(WARNING) << "need to install a trampoline to resolve the method_idx at runtime";
-    Set(CodeIndex(method_idx),   0xffffffff);
+  void SetResolvedDirectMethodTrampoline(uint32_t method_idx, ByteArray* code_array,
+                                         InstructionSet instruction_set) {
+    DCHECK(code_array != NULL);
+    int32_t code = reinterpret_cast<int32_t>(code_array->GetData());
+    if (instruction_set == kThumb2) {
+      // Set the low-order bit so a BLX will switch to Thumb mode
+      code = code | 0x1;
+    }
+    Set(CodeIndex(method_idx), code);
     Set(MethodIndex(method_idx), method_idx);
   }
 
diff --git a/src/heap.cc b/src/heap.cc
index 17b3f58..98552a7 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -207,7 +207,7 @@
         LOG(FATAL) << "Class of object is dead: " << c << " in object: " << obj;
       }
       // Check obj.getClass().getClass() == obj.getClass().getClass().getClass()
-      // NB we don't use the accessors here as they have internal sanity checks
+      // Note: we don't use the accessors here as they have internal sanity checks
       // that we don't want to run
       raw_addr = reinterpret_cast<const byte*>(c) +
           Object::ClassOffset().Int32Value();
diff --git a/src/image.h b/src/image.h
index a15ead7..a5cdeb7 100644
--- a/src/image.h
+++ b/src/image.h
@@ -68,6 +68,8 @@
   enum ImageRoot {
     kJniStubArray,
     kAbstractMethodErrorStubArray,
+    kInstanceResolutionStubArray,
+    kStaticResolutionStubArray,
     kCalleeSaveMethod,
     kOatLocation,
     kDexCaches,
diff --git a/src/image_writer.cc b/src/image_writer.cc
index f223136..c020a79 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -137,12 +137,12 @@
   // build an Object[] of the roots needed to restore the runtime
   ObjectArray<Object>* image_roots = ObjectArray<Object>::Alloc(object_array_class,
                                                                 ImageHeader::kImageRootsMax);
-  image_roots->Set(ImageHeader::kJniStubArray,
-                   runtime->GetJniStubArray());
+  image_roots->Set(ImageHeader::kJniStubArray, runtime->GetJniStubArray());
   image_roots->Set(ImageHeader::kAbstractMethodErrorStubArray,
                    runtime->GetAbstractMethodErrorStubArray());
-  image_roots->Set(ImageHeader::kCalleeSaveMethod,
-                   runtime->GetCalleeSaveMethod());
+  image_roots->Set(ImageHeader::kInstanceResolutionStubArray, runtime->GetResolutionStubArray(false));
+  image_roots->Set(ImageHeader::kStaticResolutionStubArray, runtime->GetResolutionStubArray(true));
+  image_roots->Set(ImageHeader::kCalleeSaveMethod, runtime->GetCalleeSaveMethod());
   image_roots->Set(ImageHeader::kOatLocation,
                    String::AllocFromModifiedUtf8(oat_file_->GetLocation().c_str()));
   image_roots->Set(ImageHeader::kDexCaches,
@@ -351,14 +351,58 @@
   CHECK(orig != NULL);
   CHECK(copy != NULL);
 
+  // The original array value
   CodeAndDirectMethods* orig_cadms = orig->GetCodeAndDirectMethods();
+  // The compacted object in local memory but not at the correct image address
   CodeAndDirectMethods* copy_cadms = down_cast<CodeAndDirectMethods*>(GetLocalAddress(orig_cadms));
+  // The lazy resolution stub
+  ByteArray* orig_res_stub_array[2];
+  orig_res_stub_array[0] = Runtime::Current()->GetResolutionStubArray(false);
+  orig_res_stub_array[1] = Runtime::Current()->GetResolutionStubArray(true);
+  DCHECK(orig_res_stub_array[0] != NULL);
+  DCHECK(orig_res_stub_array[1] != NULL);
+  uint32_t orig_res_stub_array_data[2];
+  orig_res_stub_array_data[0] = reinterpret_cast<uint32_t>(orig_res_stub_array[0]->GetData());
+  orig_res_stub_array_data[1] = reinterpret_cast<uint32_t>(orig_res_stub_array[1]->GetData());
+
   for (size_t i = 0; i < orig->NumResolvedMethods(); i++) {
     Method* orig_method = orig->GetResolvedMethod(i);
+    if (orig_method != NULL && !InSourceSpace(orig_method)) {
+      continue;
+    }
     // if it was resolved in the original, resolve it in the copy
-    if (orig_method != NULL
-        && InSourceSpace(orig_method)
-        && orig_method == orig_cadms->GetResolvedMethod(i)) {
+    if (orig_method == NULL || (orig_method->IsStatic() &&
+                                !orig_method->GetDeclaringClass()->IsInitialized())) {
+      // Do we need to relocate this for this space?
+      if (InSourceSpace(orig_res_stub_array[0])) {
+        bool is_static;  // is this static? hard to tell for null methods
+        uint32_t orig_res_stub_code = orig_cadms->Get(CodeAndDirectMethods::CodeIndex(i));
+        if (orig_res_stub_code == 0) {
+          continue;  // NULL maps the same in the image and the original
+        }
+        if (orig_res_stub_code - orig_res_stub_array_data[0] < 1) {
+          DCHECK(orig_method == NULL || !orig_method->IsStatic());
+          is_static = false;
+        } else {
+          DCHECK(orig_method == NULL || orig_method->IsStatic());
+          is_static = true;
+        }
+        // Compute the delta from the start of the resolution stub to its starting code.
+        // For ARM and X86 this is 0, for Thumb2 it is 1.
+        static size_t res_stub_delta = 0xFFFF;
+        if (res_stub_delta == 0xFFFF) {
+          res_stub_delta = orig_res_stub_code - orig_res_stub_array_data[is_static ? 1 : 0];
+          DCHECK(res_stub_delta == 0 || res_stub_delta == 1);
+        }
+        // Compute address in image of resolution stub and the code address
+        ByteArray* image_res_stub_array =
+            down_cast<ByteArray*>(GetImageAddress(orig_res_stub_array[is_static ? 1 : 0]));
+        int32_t image_res_stub_code =
+            reinterpret_cast<int32_t>(image_res_stub_array->GetData()) + res_stub_delta;
+        // Put the image code address in the array
+        copy_cadms->Set(CodeAndDirectMethods::CodeIndex(i), image_res_stub_code);
+      }
+    } else if (orig_method->IsDirect()) {
       Method* copy_method = down_cast<Method*>(GetLocalAddress(orig_method));
       copy_cadms->Set(CodeAndDirectMethods::CodeIndex(i),
                       reinterpret_cast<int32_t>(copy_method->code_));
diff --git a/src/indirect_reference_table.h b/src/indirect_reference_table.h
index 5595ae7..5dca3d3 100644
--- a/src/indirect_reference_table.h
+++ b/src/indirect_reference_table.h
@@ -310,6 +310,14 @@
 
   void VisitRoots(Heap::RootVisitor* visitor, void* arg);
 
+  uint32_t GetSegmentState() const {
+    return segment_state_.all;
+  }
+
+  void SetSegmentState(uint32_t new_state) {
+    segment_state_.all = new_state;
+  }
+
   static Offset SegmentStateOffset() {
     return Offset(OFFSETOF_MEMBER(IndirectReferenceTable, segment_state_));
   }
diff --git a/src/oatdump.cc b/src/oatdump.cc
index 3856f34..529f8e2 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -48,6 +48,8 @@
 const char* image_roots_descriptions_[] = {
   "kJniStubArray",
   "kAbstractMethodErrorStubArray",
+  "kInstanceResolutionStubArray",
+  "kStaticResolutionStubArray",
   "kCalleeSaveMethod",
   "kOatLocation",
   "kDexCaches",
diff --git a/src/runtime.cc b/src/runtime.cc
index a874464..231c010 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -42,6 +42,8 @@
       exit_(NULL),
       abort_(NULL),
       stats_enabled_(false) {
+  resolution_stub_array_[0] = NULL;
+  resolution_stub_array_[1] = NULL;
 }
 
 Runtime::~Runtime() {
@@ -550,6 +552,8 @@
   thread_list_->VisitRoots(visitor, arg);
   visitor(jni_stub_array_, arg);
   visitor(abstract_method_error_stub_array_, arg);
+  visitor(resolution_stub_array_[0], arg);
+  visitor(resolution_stub_array_[1], arg);
   visitor(callee_save_method_, arg);
 
   //(*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
@@ -588,6 +592,22 @@
   abstract_method_error_stub_array_ = abstract_method_error_stub_array;
 }
 
+bool Runtime::HasResolutionStubArray(bool is_static) const {
+  return resolution_stub_array_[is_static ? 1 : 0] != NULL;
+}
+
+ByteArray* Runtime::GetResolutionStubArray(bool is_static) const {
+  CHECK(HasResolutionStubArray(is_static));
+  return resolution_stub_array_[is_static ? 1 : 0];
+}
+
+void Runtime::SetResolutionStubArray(ByteArray* resolution_stub_array, bool is_static) {
+  CHECK(resolution_stub_array != NULL);
+  CHECK(!HasResolutionStubArray(is_static) ||
+        resolution_stub_array_[is_static ? 1 : 0] == resolution_stub_array);
+  resolution_stub_array_[is_static ? 1 : 0] = resolution_stub_array;
+}
+
 Method* Runtime::CreateCalleeSaveMethod(InstructionSet insns) {
   Class* method_class = Method::GetMethodClass();
   Method* method = down_cast<Method*>(method_class->AllocObject());
diff --git a/src/runtime.h b/src/runtime.h
index 8252a60..f99d994 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -156,6 +156,10 @@
   ByteArray* GetAbstractMethodErrorStubArray() const;
   void SetAbstractMethodErrorStubArray(ByteArray* abstract_method_error_stub_array);
 
+  bool HasResolutionStubArray(bool is_static) const;
+  ByteArray* GetResolutionStubArray(bool is_static) const;
+  void SetResolutionStubArray(ByteArray* resolution_stub_array, bool is_static);
+
   // Returns a special method that describes all callee saves being spilled to the stack.
   Method* CreateCalleeSaveMethod(InstructionSet insns);
   bool HasCalleeSaveMethod() const;
@@ -221,6 +225,8 @@
 
   ByteArray* abstract_method_error_stub_array_;
 
+  ByteArray* resolution_stub_array_[2];
+
   Method* callee_save_method_;
 
   bool started_;
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 039a749..2bcd7d9 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -309,6 +309,85 @@
   thread->DeliverException();
 }
 
+void* UnresolvedDirectMethodTrampolineFromCode(int32_t method_idx, void* sp, Thread* thread,
+                                               bool is_static) {
+  // TODO: this code is specific to ARM
+  // On entry the stack pointed by sp is:
+  // | argN       |  |
+  // | ...        |  |
+  // | arg4       |  |
+  // | arg3 spill |  |  Caller's frame
+  // | arg2 spill |  |
+  // | arg1 spill |  |
+  // | Method*    | ---
+  // | LR         |
+  // | R3         |    arg3
+  // | R2         |    arg2
+  // | R1         |    arg1
+  // | R0         | <- sp
+  uintptr_t* regs = reinterpret_cast<uintptr_t*>(sp);
+  Method** caller_sp = reinterpret_cast<Method**>(&regs[5]);
+  // Record the last top of the managed stack
+  thread->SetTopOfStack(caller_sp, regs[4]);
+  ClassLinker* linker = Runtime::Current()->GetClassLinker();
+  // Start new JNI local reference state
+  JNIEnvExt* env = thread->GetJniEnv();
+  uint32_t saved_local_ref_cookie = env->local_ref_cookie;
+  env->local_ref_cookie = env->locals.GetSegmentState();
+  // Discover shorty (avoid GCs)
+  const char* shorty = linker->MethodShorty(method_idx, *caller_sp);
+  size_t shorty_len = strlen(shorty);
+  size_t args_in_regs = shorty_len < 3 ? shorty_len : 3;
+  // Handlerize references in registers
+  int cur_arg = 1;   // skip method_idx in R0, first arg is in R1
+  if (!is_static) {
+    Object* obj = reinterpret_cast<Object*>(regs[cur_arg]);
+    cur_arg++;
+    AddLocalReference<jobject>(env, obj);
+  }
+  for(size_t i = 0; i < args_in_regs; i++) {
+    char c = shorty[i + 1];  // offset to skip return value
+    if (c == 'L') {
+      Object* obj = reinterpret_cast<Object*>(regs[cur_arg]);
+      AddLocalReference<jobject>(env, obj);
+    }
+    cur_arg = cur_arg + (c == 'J' || c == 'D' ? 2 : 1);
+  }
+  // Handlerize references in out going arguments
+  for(size_t i = 3; i < shorty_len; i++) {
+    char c = shorty[i + 1];  // offset to skip return value
+    if (c == 'L') {
+      Object* obj = reinterpret_cast<Object*>(regs[i + 3]);  // skip R0, LR and Method* of caller
+      AddLocalReference<jobject>(env, obj);
+    }
+    cur_arg = cur_arg + (c == 'J' || c == 'D' ? 2 : 1);
+  }
+  // Resolve method filling in dex cache
+  Method* called = linker->ResolveMethod(method_idx, *caller_sp, true);
+  if (!thread->IsExceptionPending()) {
+    // We got this far, ensure that the declaring class is initialized
+    linker->EnsureInitialized(called->GetDeclaringClass(), true);
+  }
+  // Restore JNI env state
+  env->locals.SetSegmentState(env->local_ref_cookie);
+  env->local_ref_cookie = saved_local_ref_cookie;
+
+  void* code;
+  if (thread->IsExceptionPending()) {
+    // Something went wrong, go into deliver exception with the pending exception in r0
+    code = reinterpret_cast<void*>(art_deliver_exception_from_code);
+    regs[0] =  reinterpret_cast<uintptr_t>(thread->GetException());
+    thread->ClearException();
+  } else {
+    // Expect class to at least be initializing
+    CHECK(called->GetDeclaringClass()->IsInitializing());
+    // Set up entry into main method
+    regs[0] =  reinterpret_cast<uintptr_t>(called);
+    code = const_cast<void*>(called->GetCode());
+  }
+  return code;
+}
+
 // TODO: placeholder.  Helper function to type
 Class* InitializeTypeFromCode(uint32_t type_idx, Method* method) {
   /*
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 740047d..78a5813 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -14,6 +14,7 @@
 extern Object* DecodeJObjectInThread(Thread* thread, jobject obj);
 extern void* FindNativeMethod(Thread* thread);
 extern void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread, Method** sp);
+void* UnresolvedDirectMethodTrampolineFromCode(int32_t, void*, Thread*, bool);
 extern Class* InitializeTypeFromCode(uint32_t type_idx, Method* method);
 extern void ResolveMethodFromCode(Method* method, uint32_t method_idx);
 extern void LockObjectFromCode(Thread* thread, Object* obj);
diff --git a/src/space.cc b/src/space.cc
index 911e49d..1852b0d 100644
--- a/src/space.cc
+++ b/src/space.cc
@@ -134,6 +134,11 @@
   Object* ame_stub_array = image_header.GetImageRoot(ImageHeader::kAbstractMethodErrorStubArray);
   Runtime::Current()->SetAbstractMethodErrorStubArray(down_cast<ByteArray*>(ame_stub_array));
 
+  Object* resolution_stub_array = image_header.GetImageRoot(ImageHeader::kInstanceResolutionStubArray);
+  Runtime::Current()->SetResolutionStubArray(down_cast<ByteArray*>(resolution_stub_array), false);
+  resolution_stub_array = image_header.GetImageRoot(ImageHeader::kStaticResolutionStubArray);
+  Runtime::Current()->SetResolutionStubArray(down_cast<ByteArray*>(resolution_stub_array), true);
+
   Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod);
   Runtime::Current()->SetCalleeSaveMethod(down_cast<Method*>(callee_save_method));
 
diff --git a/src/stub_arm.cc b/src/stub_arm.cc
index 75d60f5..772950c 100644
--- a/src/stub_arm.cc
+++ b/src/stub_arm.cc
@@ -9,6 +9,46 @@
 namespace art {
 namespace arm {
 
+ByteArray* ArmCreateResolutionTrampoline(bool is_static) {
+  UniquePtr<ArmAssembler> assembler( static_cast<ArmAssembler*>(Assembler::Create(kArm)) );
+  RegList save = (1 << R0) | (1 << R1) | (1 << R2) | (1 << R3) | (1 << LR);
+
+  // | Out args |
+  // | Method*  | <- SP on entry
+  // | LR       |    return address into caller
+  // | R3       |    possible argument
+  // | R2       |    possible argument
+  // | R1       |    possible argument
+  // | R0       |    method index (loaded from code and method array - will be converted to Method*)
+  __ PushList(save);
+  __ mov(R1, ShifterOperand(SP));  // Pass address of saved R0... in R1
+  __ LoadFromOffset(kLoadWord, R12, TR,
+                    OFFSETOF_MEMBER(Thread, pUnresolvedDirectMethodTrampolineFromCode));
+  __ mov(R2, ShifterOperand(TR));  // Pass Thread::Current() in R2
+  __ LoadImmediate(R3, is_static ? 1 : 0);
+  __ IncreaseFrameSize(12);        // 3 words of space for alignment
+  // Call to unresolved direct method trampoline (method_idx, sp, Thread*, is_static)
+  __ blx(R12);
+  // Save code address returned into R12
+  __ mov(R12, ShifterOperand(R0));
+  // Restore registers which may have been modified by GC and R0 which will now hold the method*
+  __ DecreaseFrameSize(12);
+  __ PopList(save);
+  // Leaf call to method's code
+  __ mov(PC, ShifterOperand(R12));
+
+  __ bkpt(0);
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  ByteArray* resolution_trampoline = ByteArray::Alloc(cs);
+  CHECK(resolution_trampoline != NULL);
+  MemoryRegion code(resolution_trampoline->GetData(), resolution_trampoline->GetLength());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline;
+}
+
 typedef void (*ThrowAme)(Method*, Thread*);
 
 ByteArray* CreateAbstractMethodErrorStub() {
diff --git a/src/stub_x86.cc b/src/stub_x86.cc
index 13f9c07..e4a56a7 100644
--- a/src/stub_x86.cc
+++ b/src/stub_x86.cc
@@ -9,6 +9,22 @@
 namespace art {
 namespace x86 {
 
+ByteArray* X86CreateResolutionTrampoline(bool) {
+  UniquePtr<X86Assembler> assembler( static_cast<X86Assembler*>(Assembler::Create(kX86)) );
+
+  // TODO: unimplemented
+  __ int3();
+
+  assembler->EmitSlowPaths();
+  size_t cs = assembler->CodeSize();
+  ByteArray* resolution_trampoline = ByteArray::Alloc(cs);
+  CHECK(resolution_trampoline != NULL);
+  MemoryRegion code(resolution_trampoline->GetData(), resolution_trampoline->GetLength());
+  assembler->FinalizeInstructions(code);
+
+  return resolution_trampoline;
+}
+
 typedef void (*ThrowAme)(Method*, Thread*);
 
 ByteArray* CreateAbstractMethodErrorStub() {
diff --git a/src/thread.cc b/src/thread.cc
index 90c172e..e4281a0 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -125,6 +125,7 @@
 #endif
   pDeliverException = art_deliver_exception_from_code;
   pThrowAbstractMethodErrorFromCode = ThrowAbstractMethodErrorFromCode;
+  pUnresolvedDirectMethodTrampolineFromCode = UnresolvedDirectMethodTrampolineFromCode;
   pF2l = F2L;
   pD2l = D2L;
   pMemcpy = memcpy;
diff --git a/src/thread.h b/src/thread.h
index 4b42a00..d1da623 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -116,6 +116,7 @@
   int (*pIdiv)(int, int);
   long long (*pLmul)(long long, long long);
   long long (*pLdivmod)(long long, long long);
+  void* (*pUnresolvedDirectMethodTrampolineFromCode)(int32_t, void*, Thread*, bool);
   void* (*pAllocObjectFromCode)(uint32_t, void*);
   void* (*pAllocArrayFromCode)(uint32_t, void*, int32_t);
   void* (*pCheckAndAllocArrayFromCode)(uint32_t, void*, int32_t);