Spill possibly reference holding registers for runtime calls.

Live references may be in registers when we crawl a stack for GC during
a runtime call. Whilst an exception won't occur we need to save the
callee save registers into the frame so the stack crawl can find their
values. Create variants of the callee save method to save just the
registers that are necessary.

Change-Id: I6fa479bffcbc333fe846f9bd3ef2e626e0209ed9
diff --git a/src/asm_support.h b/src/asm_support.h
index fb2f7cc..305e17d 100644
--- a/src/asm_support.h
+++ b/src/asm_support.h
@@ -8,10 +8,8 @@
 #define rSELF r9
 #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 280
-// Offset of field Thread::top_of_managed_stack_pc_ verified in InitCpu
-#define THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET 284
+// Offset of field Thread::suspend_count_ verified in InitCpu
+#define THREAD_SUSPEND_COUNT_OFFSET 388
 
 #elif defined(__i386__)
 // Offset of field Thread::self_ verified in InitCpu
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 71abb21..1ec482a 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1630,29 +1630,6 @@
   return !self->IsExceptionPending();
 }
 
-StaticStorageBase* ClassLinker::InitializeStaticStorageFromCode(uint32_t type_idx,
-                                                                const Method* referrer) {
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  Class* klass = class_linker->ResolveType(type_idx, referrer);
-  if (klass == NULL) {
-    CHECK(Thread::Current()->IsExceptionPending());
-    return NULL;  // Failure - Indicate to caller to deliver exception
-  }
-  // If we are the <clinit> of this class, just return our storage.
-  //
-  // Do not set the DexCache InitializedStaticStorage, since that
-  // implies <clinit> has finished running.
-  if (klass == referrer->GetDeclaringClass() && referrer->GetName()->Equals("<clinit>")) {
-    return klass;
-  }
-  if (!class_linker->EnsureInitialized(klass, true)) {
-    CHECK(Thread::Current()->IsExceptionPending());
-    return NULL;  // Failure - Indicate to caller to deliver exception
-  }
-  referrer->GetDexCacheInitializedStaticStorage()->Set(type_idx, klass);
-  return klass;
-}
-
 void ClassLinker::ConstructFieldMap(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
     Class* c, std::map<int, Field*>& field_map) {
   const ClassLoader* cl = c->GetClassLoader();
diff --git a/src/class_linker.h b/src/class_linker.h
index 75eefc9..304d37f 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -112,9 +112,6 @@
                      DexCache* dex_cache,
                      const ClassLoader* class_loader);
 
-  static StaticStorageBase* InitializeStaticStorageFromCode(uint32_t type_idx,
-                                                            const Method* referrer);
-
   // Resolve a method with a given ID from the DexFile, storing the
   // result in DexCache. The ClassLinker and ClassLoader are used as
   // in ResolveType. What is unique is the method type argument which
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index a646f4e..9ae64a3 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -162,7 +162,7 @@
     if (klass->IsInterface()) {
       EXPECT_TRUE(klass->IsAbstract());
       if (klass->NumDirectMethods() == 1) {
-        EXPECT_TRUE(klass->GetDirectMethod(0)->GetName()->Equals("<clinit>"));
+        EXPECT_TRUE(klass->GetDirectMethod(0)->IsClassInitializer());
         EXPECT_TRUE(klass->GetDirectMethod(0)->IsDirect());
       } else {
         EXPECT_EQ(0U, klass->NumDirectMethods());
@@ -949,6 +949,8 @@
   EXPECT_EQ(Aj2, A->FindVirtualMethodForVirtualOrInterface(Jj2));
 }
 
+extern Class* InitializeStaticStorage(uint32_t type_idx, const Method* referrer, Thread* self);
+
 TEST_F(ClassLinkerTest, InitializeStaticStorageFromCode) {
   // pretend we are trying to get the static storage for the StaticsFromCode class.
 
@@ -965,10 +967,10 @@
   uint32_t type_idx = FindTypeIdxByDescriptor(*dex_file, "LStaticsFromCode;");
 
   EXPECT_TRUE(clinit->GetDexCacheInitializedStaticStorage()->Get(type_idx) == NULL);
-  StaticStorageBase* uninit = class_linker_->InitializeStaticStorageFromCode(type_idx, clinit);
+  StaticStorageBase* uninit = InitializeStaticStorage(type_idx, clinit, Thread::Current());
   EXPECT_TRUE(uninit != NULL);
   EXPECT_TRUE(clinit->GetDexCacheInitializedStaticStorage()->Get(type_idx) == NULL);
-  StaticStorageBase* init = class_linker_->InitializeStaticStorageFromCode(type_idx, getS0);
+  StaticStorageBase* init = InitializeStaticStorage(type_idx, getS0, Thread::Current());
   EXPECT_TRUE(init != NULL);
   EXPECT_EQ(init, clinit->GetDexCacheInitializedStaticStorage()->Get(type_idx));
 }
diff --git a/src/common_test.h b/src/common_test.h
index 72701d9..84b97e9 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -127,23 +127,29 @@
     ASSERT_TRUE(runtime_.get() != NULL);
     class_linker_ = runtime_->GetClassLinker();
 
+    InstructionSet instruction_set = kNone;
 #if defined(__i386__)
-    InstructionSet insns = kX86;
+    instruction_set = kX86;
 #elif defined(__arm__)
-    InstructionSet insns = kThumb2;
-#else
-    InstructionSet insns = kNone;
+    instruction_set = kThumb2;
 #endif
-    runtime_->SetJniStubArray(JniCompiler::CreateJniStub(insns));
-    runtime_->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(insns));
-    runtime_->SetCalleeSaveMethod(runtime_->CreateCalleeSaveMethod(insns));
-    for (int i=0; i < Runtime::kMaxTrampolineMethodType; i++) {
+    runtime_->SetJniStubArray(JniCompiler::CreateJniStub(instruction_set));
+    runtime_->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(instruction_set));
+    for (int i=0; i < Runtime::kLastTrampolineMethodType; i++) {
       Runtime::TrampolineType type = Runtime::TrampolineType(i);
       if (!runtime_->HasResolutionStubArray(type)) {
-        runtime_->SetResolutionStubArray(Compiler::CreateResolutionStub(insns, type), type);
+        runtime_->SetResolutionStubArray(
+            Compiler::CreateResolutionStub(instruction_set, type), type);
       }
     }
-    compiler_.reset(new Compiler(insns));
+    for (int i=0; i < Runtime::kLastCalleeSaveType; i++) {
+      Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
+      if (!runtime_->HasCalleeSaveMethod(type)) {
+        runtime_->SetCalleeSaveMethod(
+            runtime_->CreateCalleeSaveMethod(instruction_set, type), type);
+      }
+    }
+    compiler_.reset(new Compiler(instruction_set));
 
     Heap::VerifyHeap();  // Check for heap corruption before the test
   }
diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc
index b63c37f..c136660 100644
--- a/src/compiler/codegen/arm/Thumb2/Gen.cc
+++ b/src/compiler/codegen/arm/Thumb2/Gen.cc
@@ -981,22 +981,22 @@
 
     oatFlushAllRegs(cUnit);
     DCHECK_EQ(LW_SHAPE_THIN, 0);
-    loadValueDirectFixed(cUnit, rlSrc, r1);  // Get obj
+    loadValueDirectFixed(cUnit, rlSrc, r0);  // Get obj
     oatLockCallTemps(cUnit);  // Prepare for explicit register usage
-    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir);
-    loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r3);
-    newLIR3(cUnit, kThumb2Ldrex, r2, r1,
+    genNullCheck(cUnit, rlSrc.sRegLow, r0, mir);
+    loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r2);
+    newLIR3(cUnit, kThumb2Ldrex, r1, r0,
             Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
     // Align owner
-    opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT);
+    opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
     // Is lock unheld on lock or held by us (==threadId) on unlock?
-    newLIR4(cUnit, kThumb2Bfi, r3, r2, 0, LW_LOCK_OWNER_SHIFT - 1);
-    newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
-    hopBranch = newLIR2(cUnit, kThumb2Cbnz, r2, 0);
-    newLIR4(cUnit, kThumb2Strex, r2, r3, r1,
+    newLIR4(cUnit, kThumb2Bfi, r2, r1, 0, LW_LOCK_OWNER_SHIFT - 1);
+    newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
+    hopBranch = newLIR2(cUnit, kThumb2Cbnz, r1, 0);
+    newLIR4(cUnit, kThumb2Strex, r1, r2, r0,
             Object::MonitorOffset().Int32Value() >> 2);
     oatGenMemBarrier(cUnit, kSY);
-    branch = newLIR2(cUnit, kThumb2Cbz, r2, 0);
+    branch = newLIR2(cUnit, kThumb2Cbz, r1, 0);
 
     hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
     hopTarget->defMask = ENCODE_ALL;
@@ -1005,7 +1005,6 @@
     // Go expensive route - artLockObjectFromCode(self, obj);
     loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pLockObjectFromCode),
                  rLR);
-    genRegCopy(cUnit, r0, rSELF);
     callRuntimeHelper(cUnit, rLR);
 
     // Resume here
@@ -1030,30 +1029,29 @@
 
     DCHECK_EQ(LW_SHAPE_THIN, 0);
     oatFlushAllRegs(cUnit);
-    loadValueDirectFixed(cUnit, rlSrc, r1);  // Get obj
+    loadValueDirectFixed(cUnit, rlSrc, r0);  // Get obj
     oatLockCallTemps(cUnit);  // Prepare for explicit register usage
-    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir);
-    loadWordDisp(cUnit, r1, Object::MonitorOffset().Int32Value(), r2); // Get lock
-    loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r3);
+    genNullCheck(cUnit, rlSrc.sRegLow, r0, mir);
+    loadWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r1); // Get lock
+    loadWordDisp(cUnit, rSELF, Thread::ThinLockIdOffset().Int32Value(), r2);
     // Is lock unheld on lock or held by us (==threadId) on unlock?
-    opRegRegImm(cUnit, kOpAnd, r12, r2, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
+    opRegRegImm(cUnit, kOpAnd, r3, r1, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
     // Align owner
-    opRegImm(cUnit, kOpLsl, r3, LW_LOCK_OWNER_SHIFT);
-    newLIR3(cUnit, kThumb2Bfc, r2, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
-    opRegReg(cUnit, kOpSub, r2, r3);
+    opRegImm(cUnit, kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
+    newLIR3(cUnit, kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
+    opRegReg(cUnit, kOpSub, r1, r2);
     hopBranch = opCondBranch(cUnit, kArmCondNe);
     oatGenMemBarrier(cUnit, kSY);
-    storeWordDisp(cUnit, r1, Object::MonitorOffset().Int32Value(), r12);
+    storeWordDisp(cUnit, r0, Object::MonitorOffset().Int32Value(), r3);
     branch = opNone(cUnit, kOpUncondBr);
 
     hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
     hopTarget->defMask = ENCODE_ALL;
     hopBranch->generic.target = (LIR*)hopTarget;
 
-    // Go expensive route - UnlockObjectFromCode(self, obj);
+    // Go expensive route - UnlockObjectFromCode(obj);
     loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pUnlockObjectFromCode),
                  rLR);
-    genRegCopy(cUnit, r0, rSELF);
     callRuntimeHelper(cUnit, rLR);
 
     // Resume here
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index e1ffab0..8d181d6 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -200,14 +200,17 @@
   if (!runtime->HasAbstractMethodErrorStubArray()) {
     runtime->SetAbstractMethodErrorStubArray(Compiler::CreateAbstractMethodErrorStub(kThumb2));
   }
-  for (int i=0; i < Runtime::kMaxTrampolineMethodType; i++) {
+  for (int i = 0; i < Runtime::kLastTrampolineMethodType; i++) {
     Runtime::TrampolineType type = Runtime::TrampolineType(i);
     if (!runtime->HasResolutionStubArray(type)) {
       runtime->SetResolutionStubArray(Compiler::CreateResolutionStub(kThumb2, type), type);
     }
   }
-  if (!runtime->HasCalleeSaveMethod()) {
-    runtime->SetCalleeSaveMethod(runtime->CreateCalleeSaveMethod(kThumb2));
+  for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
+    Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
+    if (!runtime->HasCalleeSaveMethod(type)) {
+      runtime->SetCalleeSaveMethod(runtime->CreateCalleeSaveMethod(kThumb2, type), type);
+    }
   }
   Compiler compiler(kThumb2);
   if (method_names.empty()) {
diff --git a/src/image.h b/src/image.h
index e2be3d4..86b71e1 100644
--- a/src/image.h
+++ b/src/image.h
@@ -72,6 +72,8 @@
     kStaticResolutionStubArray,
     kUnknownMethodResolutionStubArray,
     kCalleeSaveMethod,
+    kRefsOnlySaveMethod,
+    kRefsAndArgsSaveMethod,
     kOatLocation,
     kDexCaches,
     kClassRoots,
diff --git a/src/image_writer.cc b/src/image_writer.cc
index 9246440..8af9cf6 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -146,7 +146,12 @@
                    runtime->GetResolutionStubArray(Runtime::kStaticMethod));
   image_roots->Set(ImageHeader::kUnknownMethodResolutionStubArray,
                    runtime->GetResolutionStubArray(Runtime::kUnknownMethod));
-  image_roots->Set(ImageHeader::kCalleeSaveMethod, runtime->GetCalleeSaveMethod());
+  image_roots->Set(ImageHeader::kCalleeSaveMethod,
+                   runtime->GetCalleeSaveMethod(Runtime::kSaveAll));
+  image_roots->Set(ImageHeader::kRefsOnlySaveMethod,
+                   runtime->GetCalleeSaveMethod(Runtime::kRefsOnly));
+  image_roots->Set(ImageHeader::kRefsAndArgsSaveMethod,
+                   runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs));
   image_roots->Set(ImageHeader::kOatLocation,
                    String::AllocFromModifiedUtf8(oat_file_->GetLocation().c_str()));
   image_roots->Set(ImageHeader::kDexCaches,
diff --git a/src/object.cc b/src/object.cc
index 1f64faa..c43fe49 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -323,6 +323,10 @@
   SetObj(object, l);
 }
 
+bool Method::IsClassInitializer() const {
+  return IsStatic() && GetName()->Equals("<clinit>");
+}
+
 // TODO: get global references for these
 Class* Method::java_lang_reflect_Constructor_ = NULL;
 Class* Method::java_lang_reflect_Method_ = NULL;
diff --git a/src/object.h b/src/object.h
index 6bfde65..03fa359 100644
--- a/src/object.h
+++ b/src/object.h
@@ -623,9 +623,12 @@
 
   // Returns true if the method is a constructor.
   bool IsConstructor() const {
-    return (access_flags_ & kAccConstructor) != 0;
+    return (GetAccessFlags() & kAccConstructor) != 0;
   }
 
+  // Is this method <clinit>
+  bool IsClassInitializer() const;
+
   // Returns true if the method is static, private, or a constructor.
   bool IsDirect() const {
     return IsStatic() || IsPrivate() || IsConstructor();
@@ -945,7 +948,14 @@
 
   // Is this a hand crafted method used for something like describing callee saves?
   bool IsPhony() const {
-    bool result = this == Runtime::Current()->GetCalleeSaveMethod();
+    Runtime* runtime = Runtime::Current();
+    bool result = false;
+    for (int i=0; i < Runtime::kLastCalleeSaveType; i++) {
+      if (this == runtime->GetCalleeSaveMethod(Runtime::CalleeSaveType(i))) {
+        result = true;
+        break;
+      }
+    }
     // Check that if we do think it is phony it looks like the callee save method
     DCHECK(!result || GetCoreSpillMask() != 0);
     return result;
diff --git a/src/runtime.cc b/src/runtime.cc
index 5d2689d..7e27918 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -36,14 +36,17 @@
       java_vm_(NULL),
       jni_stub_array_(NULL),
       abstract_method_error_stub_array_(NULL),
-      callee_save_method_(NULL),
       started_(false),
       vfprintf_(NULL),
       exit_(NULL),
       abort_(NULL),
       stats_enabled_(false) {
-  resolution_stub_array_[0] = NULL;
-  resolution_stub_array_[1] = NULL;
+  for (int i = 0; i < Runtime::kLastTrampolineMethodType; i++) {
+    resolution_stub_array_[i] = NULL;
+  }
+  for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
+    callee_save_method_[i] = NULL;
+  }
 }
 
 Runtime::~Runtime() {
@@ -552,9 +555,12 @@
   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);
+  for (int i = 0; i < Runtime::kLastTrampolineMethodType; i++) {
+    visitor(resolution_stub_array_[i], arg);
+  }
+  for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
+    visitor(callee_save_method_[i], arg);
+  }
 
   //(*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
   //(*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
@@ -609,6 +615,7 @@
 
 ByteArray* Runtime::GetResolutionStubArray(TrampolineType type) const {
   CHECK(HasResolutionStubArray(type));
+  DCHECK_LT(static_cast<int>(type), static_cast<int>(kLastTrampolineMethodType));
   return resolution_stub_array_[type];
 }
 
@@ -618,67 +625,52 @@
   resolution_stub_array_[type] = resolution_stub_array;
 }
 
-Method* Runtime::CreateCalleeSaveMethod(InstructionSet insns) {
+Method* Runtime::CreateCalleeSaveMethod(InstructionSet insns, CalleeSaveType type) {
   Class* method_class = Method::GetMethodClass();
   Method* method = down_cast<Method*>(method_class->AllocObject());
   method->SetDeclaringClass(method_class);
-  method->SetName(intern_table_->InternStrong("$$$callee_save_method$$$"));
+  const char* name;
+  if (type == kSaveAll) {
+    name = "$$$callee_save_method$$$";
+  } else if (type == kRefsOnly) {
+    name = "$$$refs_only_callee_save_method$$$";
+  } else {
+    DCHECK(type == kRefsAndArgs);
+    name = "$$$refs_and_args_callee_save_method$$$";
+  }
+  method->SetName(intern_table_->InternStrong(name));
   method->SetSignature(intern_table_->InternStrong("()V"));
   method->SetCodeArray(NULL, insns);
   if ((insns == kThumb2) || (insns == kArm)) {
-    size_t frame_size = (12 /* gprs */ + 32 /* fprs */ + 4 /* data */) * kPointerSize;
+    uint32_t ref_spills = (1 << art::arm::R5) | (1 << art::arm::R6)  | (1 << art::arm::R7) |
+                          (1 << art::arm::R8) | (1 << art::arm::R10) | (1 << art::arm::R11);
+    uint32_t arg_spills = (1 << art::arm::R1) | (1 << art::arm::R2) | (1 << art::arm::R3);
+    uint32_t all_spills = (1 << art::arm::R4) | (1 << art::arm::R9);
+    uint32_t core_spills = ref_spills | (type == kRefsAndArgs ? arg_spills :0) |
+                           (type == kSaveAll ? all_spills :0) | (1 << art::arm::LR);
+    uint32_t fp_all_spills = (1 << art::arm::S0)  | (1 << art::arm::S1)  | (1 << art::arm::S2) |
+                             (1 << art::arm::S3)  | (1 << art::arm::S4)  | (1 << art::arm::S5) |
+                             (1 << art::arm::S6)  | (1 << art::arm::S7)  | (1 << art::arm::S8) |
+                             (1 << art::arm::S9)  | (1 << art::arm::S10) | (1 << art::arm::S11) |
+                             (1 << art::arm::S12) | (1 << art::arm::S13) | (1 << art::arm::S14) |
+                             (1 << art::arm::S15) | (1 << art::arm::S16) | (1 << art::arm::S17) |
+                             (1 << art::arm::S18) | (1 << art::arm::S19) | (1 << art::arm::S20) |
+                             (1 << art::arm::S21) | (1 << art::arm::S22) | (1 << art::arm::S23) |
+                             (1 << art::arm::S24) | (1 << art::arm::S25) | (1 << art::arm::S26) |
+                             (1 << art::arm::S27) | (1 << art::arm::S28) | (1 << art::arm::S29) |
+                             (1 << art::arm::S30) | (1 << art::arm::S31);
+    uint32_t fp_spills = type == kSaveAll ? fp_all_spills : 0;
+    size_t frame_size = RoundUp((__builtin_popcount(core_spills) /* gprs */ +
+                                 __builtin_popcount(fp_spills) /* fprs */ +
+                                 1 /* Method* */) * kPointerSize, kStackAlignment);
     method->SetFrameSizeInBytes(frame_size);
     method->SetReturnPcOffsetInBytes(frame_size - kPointerSize);
-    method->SetCoreSpillMask((1 << art::arm::R1) |
-                             (1 << art::arm::R2) |
-                             (1 << art::arm::R3) |
-                             (1 << art::arm::R4) |
-                             (1 << art::arm::R5) |
-                             (1 << art::arm::R6) |
-                             (1 << art::arm::R7) |
-                             (1 << art::arm::R8) |
-                             (1 << art::arm::R9) |
-                             (1 << art::arm::R10) |
-                             (1 << art::arm::R11) |
-                             (1 << art::arm::LR));
-    method->SetFpSpillMask((1 << art::arm::S0) |
-                           (1 << art::arm::S1) |
-                           (1 << art::arm::S2) |
-                           (1 << art::arm::S3) |
-                           (1 << art::arm::S4) |
-                           (1 << art::arm::S5) |
-                           (1 << art::arm::S6) |
-                           (1 << art::arm::S7) |
-                           (1 << art::arm::S8) |
-                           (1 << art::arm::S9) |
-                           (1 << art::arm::S10) |
-                           (1 << art::arm::S11) |
-                           (1 << art::arm::S12) |
-                           (1 << art::arm::S13) |
-                           (1 << art::arm::S14) |
-                           (1 << art::arm::S15) |
-                           (1 << art::arm::S16) |
-                           (1 << art::arm::S17) |
-                           (1 << art::arm::S18) |
-                           (1 << art::arm::S19) |
-                           (1 << art::arm::S20) |
-                           (1 << art::arm::S21) |
-                           (1 << art::arm::S22) |
-                           (1 << art::arm::S23) |
-                           (1 << art::arm::S24) |
-                           (1 << art::arm::S25) |
-                           (1 << art::arm::S26) |
-                           (1 << art::arm::S27) |
-                           (1 << art::arm::S28) |
-                           (1 << art::arm::S29) |
-                           (1 << art::arm::S30) |
-                           (1 << art::arm::S31));
+    method->SetCoreSpillMask(core_spills);
+    method->SetFpSpillMask(fp_spills);
   } else if (insns == kX86) {
     method->SetFrameSizeInBytes(32);
     method->SetReturnPcOffsetInBytes(28);
-    method->SetCoreSpillMask((1 << art::x86::EBX) |
-                             (1 << art::x86::EBP) |
-                             (1 << art::x86::ESI) |
+    method->SetCoreSpillMask((1 << art::x86::EBX) | (1 << art::x86::EBP) | (1 << art::x86::ESI) |
                              (1 << art::x86::EDI));
     method->SetFpSpillMask(0);
   } else {
@@ -687,19 +679,19 @@
   return method;
 }
 
-bool Runtime::HasCalleeSaveMethod() const {
-  return callee_save_method_ != NULL;
+bool Runtime::HasCalleeSaveMethod(CalleeSaveType type) const {
+  return callee_save_method_[type] != NULL;
 }
 
 // Returns a special method that describes all callee saves being spilled to the stack.
-Method* Runtime::GetCalleeSaveMethod() const {
-  CHECK(callee_save_method_ != NULL);
-  return callee_save_method_;
+Method* Runtime::GetCalleeSaveMethod(CalleeSaveType type) const {
+  CHECK(HasCalleeSaveMethod(type));
+  return callee_save_method_[type];
 }
 
-void Runtime::SetCalleeSaveMethod(Method* method) {
-  callee_save_method_ = method;
+void Runtime::SetCalleeSaveMethod(Method* method, CalleeSaveType type) {
+  DCHECK_LT(static_cast<int>(type), static_cast<int>(kLastCalleeSaveType));
+  callee_save_method_[type] = method;
 }
 
-
 }  // namespace art
diff --git a/src/runtime.h b/src/runtime.h
index 8c4ce7c..bb6bfda 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -160,7 +160,7 @@
     kInstanceMethod,
     kStaticMethod,
     kUnknownMethod,
-    kMaxTrampolineMethodType = kUnknownMethod
+    kLastTrampolineMethodType  // Value used for iteration
   };
   static TrampolineType GetTrampolineType(Method* method);
   bool HasResolutionStubArray(TrampolineType type) const;
@@ -168,10 +168,19 @@
   void SetResolutionStubArray(ByteArray* resolution_stub_array, TrampolineType type);
 
   // Returns a special method that describes all callee saves being spilled to the stack.
-  Method* CreateCalleeSaveMethod(InstructionSet insns);
-  bool HasCalleeSaveMethod() const;
-  Method* GetCalleeSaveMethod() const;
-  void SetCalleeSaveMethod(Method* method);
+  enum CalleeSaveType {
+    kSaveAll,
+    kRefsOnly,
+    kRefsAndArgs,
+    kLastCalleeSaveType  // Value used for iteration
+  };
+  Method* CreateCalleeSaveMethod(InstructionSet insns, CalleeSaveType type);
+  bool HasCalleeSaveMethod(CalleeSaveType type) const;
+  Method* GetCalleeSaveMethod(CalleeSaveType type) const;
+  void SetCalleeSaveMethod(Method* method, CalleeSaveType type);
+
+  Method* CreateRefOnlyCalleeSaveMethod(InstructionSet insns);
+  Method* CreateRefAndArgsCalleeSaveMethod(InstructionSet insns);
 
   int32_t GetStat(int kind);
 
@@ -232,9 +241,9 @@
 
   ByteArray* abstract_method_error_stub_array_;
 
-  ByteArray* resolution_stub_array_[kMaxTrampolineMethodType];
+  ByteArray* resolution_stub_array_[kLastTrampolineMethodType];
 
-  Method* callee_save_method_;
+  Method* callee_save_method_[kLastCalleeSaveType];
 
   bool started_;
 
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 9cc8804..6a11e11 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -20,6 +20,12 @@
 
 namespace art {
 
+// Place a special frame at the TOS that will save the callee saves for the given type
+static void  FinishCalleeSaveFrameSetup(Thread* self, Method** sp, Runtime::CalleeSaveType type) {
+  *sp = Runtime::Current()->GetCalleeSaveMethod(type);
+  self->SetTopOfStack(sp, 0);
+}
+
 // Temporary debugging hook for compiler.
 extern void DebugMe(Method* method, uint32_t info) {
   LOG(INFO) << "DebugMe";
@@ -62,9 +68,7 @@
    * and threw a NPE if NULL.  This routine responsible for setting
    * exception_ in thread and delivering the exception.
    */
-  // Place a special frame at the TOS that will save all callee saves
-  *sp = Runtime::Current()->GetCalleeSaveMethod();
-  thread->SetTopOfStack(sp, 0);
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
   if (exception == NULL) {
     thread->ThrowNewException("Ljava/lang/NullPointerException;", "throw with null exception");
   } else {
@@ -75,62 +79,52 @@
 
 // Deliver an exception that's pending on thread helping set up a callee save frame on the way
 extern "C" void artDeliverPendingExceptionFromCode(Thread* thread, Method** sp) {
-  *sp = Runtime::Current()->GetCalleeSaveMethod();
-  thread->SetTopOfStack(sp, 0);
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
   thread->DeliverException();
 }
 
 // Called by generated call to throw a NPE exception
 extern "C" void artThrowNullPointerExceptionFromCode(Thread* thread, Method** sp) {
-  // Place a special frame at the TOS that will save all callee saves
-  *sp = Runtime::Current()->GetCalleeSaveMethod();
-  thread->SetTopOfStack(sp, 0);
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
   thread->ThrowNewException("Ljava/lang/NullPointerException;", NULL);
   thread->DeliverException();
 }
 
 // Called by generated call to throw an arithmetic divide by zero exception
 extern "C" void artThrowDivZeroFromCode(Thread* thread, Method** sp) {
-  // Place a special frame at the TOS that will save all callee saves
-  *sp = Runtime::Current()->GetCalleeSaveMethod();
-  thread->SetTopOfStack(sp, 0);
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
   thread->ThrowNewException("Ljava/lang/ArithmeticException;", "divide by zero");
   thread->DeliverException();
 }
 
 // Called by generated call to throw an arithmetic divide by zero exception
 extern "C" void artThrowArrayBoundsFromCode(int index, int limit, Thread* thread, Method** sp) {
-  // Place a special frame at the TOS that will save all callee saves
-  *sp = Runtime::Current()->GetCalleeSaveMethod();
-  thread->SetTopOfStack(sp, 0);
-  thread->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;", "length=%d; index=%d",
-      limit, index);
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
+  thread->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
+                             "length=%d; index=%d", limit, index);
   thread->DeliverException();
 }
 
 // Called by the AbstractMethodError stub (not runtime support)
 extern void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread, Method** sp) {
-  *sp = Runtime::Current()->GetCalleeSaveMethod();
-  thread->SetTopOfStack(sp, 0);
-  thread->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;", "abstract method \"%s\"",
-      PrettyMethod(method).c_str());
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
+  thread->ThrowNewExceptionF("Ljava/lang/AbstractMethodError;",
+                             "abstract method \"%s\"", PrettyMethod(method).c_str());
   thread->DeliverException();
 }
 
 extern "C" void artThrowStackOverflowFromCode(Method* method, Thread* thread, Method** sp) {
-  // Place a special frame at the TOS that will save all callee saves
-  Runtime* runtime = Runtime::Current();
-  *sp = runtime->GetCalleeSaveMethod();
-  thread->SetTopOfStack(sp, 0);
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
   thread->SetStackEndForStackOverflow();  // Allow space on the stack for constructor to execute
   thread->ThrowNewExceptionF("Ljava/lang/StackOverflowError;",
       "stack size %zdkb; default stack size: %zdkb",
-      thread->GetStackSize() / KB, runtime->GetDefaultStackSize() / KB);
+      thread->GetStackSize() / KB, Runtime::Current()->GetDefaultStackSize() / KB);
   thread->ResetDefaultStackEnd();  // Return to default stack size
   thread->DeliverException();
 }
 
-std::string ClassNameFromIndex(Method* method, uint32_t ref, DexVerifier::VerifyErrorRefType ref_type, bool access) {
+static std::string ClassNameFromIndex(Method* method, uint32_t ref,
+                                      DexVerifier::VerifyErrorRefType ref_type, bool access) {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   const DexFile& dex_file = class_linker->FindDexFile(method->GetDeclaringClass()->GetDexCache());
 
@@ -160,7 +154,8 @@
   return result;
 }
 
-std::string FieldNameFromIndex(const Method* method, uint32_t ref, DexVerifier::VerifyErrorRefType ref_type, bool access) {
+static std::string FieldNameFromIndex(const Method* method, uint32_t ref,
+                                      DexVerifier::VerifyErrorRefType ref_type, bool access) {
   CHECK_EQ(static_cast<int>(ref_type), static_cast<int>(DexVerifier::VERIFY_ERROR_REF_FIELD));
 
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -181,7 +176,8 @@
   return result;
 }
 
-std::string MethodNameFromIndex(const Method* method, uint32_t ref, DexVerifier::VerifyErrorRefType ref_type, bool access) {
+static std::string MethodNameFromIndex(const Method* method, uint32_t ref,
+                                DexVerifier::VerifyErrorRefType ref_type, bool access) {
   CHECK_EQ(static_cast<int>(ref_type), static_cast<int>(DexVerifier::VERIFY_ERROR_REF_METHOD));
 
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -196,24 +192,21 @@
 
   std::string result;
   result += "tried to access method ";
-  result += class_name + "." + method_name + ":" + dex_file.CreateMethodDescriptor(id.proto_idx_, NULL);
+  result += class_name + "." + method_name + ":" +
+            dex_file.CreateMethodDescriptor(id.proto_idx_, NULL);
   result += " from class ";
   result += PrettyDescriptor(method->GetDeclaringClass()->GetDescriptor());
   return result;
 }
 
 extern "C" void artThrowVerificationErrorFromCode(int32_t kind, int32_t ref, Thread* self, Method** sp) {
-  // Place a special frame at the TOS that will save all callee saves
-  Runtime* runtime = Runtime::Current();
-  *sp = runtime->GetCalleeSaveMethod();
-  self->SetTopOfStack(sp, 0);
-
-  // We need the calling method as context to interpret 'ref'.
-  Frame frame = self->GetTopOfStack();
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  Frame frame = self->GetTopOfStack();  // We need the calling method as context to interpret 'ref'
   frame.Next();
   Method* method = frame.GetMethod();
 
-  DexVerifier::VerifyErrorRefType ref_type = static_cast<DexVerifier::VerifyErrorRefType>(kind >> kVerifyErrorRefTypeShift);
+  DexVerifier::VerifyErrorRefType ref_type =
+      static_cast<DexVerifier::VerifyErrorRefType>(kind >> kVerifyErrorRefTypeShift);
 
   const char* exception_class = "Ljava/lang/VerifyError;";
   std::string msg;
@@ -258,53 +251,37 @@
     CHECK(false);
     break;
   }
-
   self->ThrowNewException(exception_class, msg.c_str());
   self->DeliverException();
 }
 
 extern "C" void artThrowInternalErrorFromCode(int32_t errnum, Thread* thread, Method** sp) {
-  // Place a special frame at the TOS that will save all callee saves
-  Runtime* runtime = Runtime::Current();
-  *sp = runtime->GetCalleeSaveMethod();
-  thread->SetTopOfStack(sp, 0);
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
   LOG(WARNING) << "TODO: internal error detail message. errnum=" << errnum;
   thread->ThrowNewExceptionF("Ljava/lang/InternalError;", "errnum=%d", errnum);
   thread->DeliverException();
 }
 
 extern "C" void artThrowRuntimeExceptionFromCode(int32_t errnum, Thread* thread, Method** sp) {
-  // Place a special frame at the TOS that will save all callee saves
-  Runtime* runtime = Runtime::Current();
-  *sp = runtime->GetCalleeSaveMethod();
-  thread->SetTopOfStack(sp, 0);
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
   LOG(WARNING) << "TODO: runtime exception detail message. errnum=" << errnum;
   thread->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "errnum=%d", errnum);
   thread->DeliverException();
 }
 
 extern "C" void artThrowNoSuchMethodFromCode(int32_t method_idx, Thread* self, Method** sp) {
-  // Place a special frame at the TOS that will save all callee saves
-  Runtime* runtime = Runtime::Current();
-  *sp = runtime->GetCalleeSaveMethod();
-  self->SetTopOfStack(sp, 0);
-
-  // We need the calling method as context for the method_idx.
-  Frame frame = self->GetTopOfStack();
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+  Frame frame = self->GetTopOfStack();  // We need the calling method as context for the method_idx
   frame.Next();
   Method* method = frame.GetMethod();
-
   self->ThrowNewException("Ljava/lang/NoSuchMethodError;",
       MethodNameFromIndex(method, method_idx, DexVerifier::VERIFY_ERROR_REF_METHOD, false).c_str());
   self->DeliverException();
 }
 
 extern "C" void artThrowNegArraySizeFromCode(int32_t size, Thread* thread, Method** sp) {
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kSaveAll);
   LOG(WARNING) << "UNTESTED artThrowNegArraySizeFromCode";
-  // Place a special frame at the TOS that will save all callee saves
-  Runtime* runtime = Runtime::Current();
-  *sp = runtime->GetCalleeSaveMethod();
-  thread->SetTopOfStack(sp, 0);
   thread->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", size);
   thread->DeliverException();
 }
@@ -425,11 +402,9 @@
 // cannot be resolved, throw an error. If it can, use it to create an instance.
 extern "C" Object* artAllocObjectFromCode(uint32_t type_idx, Method* method, Thread* self, Method** sp) {
   // Place a special frame at the TOS that will save all callee saves
-  Runtime* runtime = Runtime::Current();
-  *sp = runtime->GetCalleeSaveMethod();
-  self->SetTopOfStack(sp, 0);
-
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
+  Runtime* runtime = Runtime::Current();
   if (klass == NULL) {
     klass = runtime->GetClassLinker()->ResolveType(type_idx, method);
     if (klass == NULL) {
@@ -446,7 +421,7 @@
 
 // Helper function to alloc array for OP_FILLED_NEW_ARRAY
 extern "C" Array* artCheckAndAllocArrayFromCode(uint32_t type_idx, Method* method,
-                                                int32_t component_count) {
+                                               int32_t component_count, Thread* self, Method** sp) {
   if (component_count < 0) {
     Thread::Current()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d",
                                          component_count);
@@ -479,12 +454,9 @@
 
 // Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If
 // it cannot be resolved, throw an error. If it can, use it to create an array.
-extern "C" Array* artAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count, Thread* self, Method** sp) {
-  // Place a special frame at the TOS that will save all callee saves
-  Runtime* runtime = Runtime::Current();
-  *sp = runtime->GetCalleeSaveMethod();
-  self->SetTopOfStack(sp, 0);
-
+extern "C" Array* artAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count,
+                                        Thread* self, Method** sp) {
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   if (component_count < 0) {
     Thread::Current()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d",
                                          component_count);
@@ -503,7 +475,8 @@
 }
 
 // Check whether it is safe to cast one class to the other, throw exception and return -1 on failure
-extern "C" int artCheckCastFromCode(const Class* a, const Class* b) {
+extern "C" int artCheckCastFromCode(const Class* a, const Class* b, Thread* self, Method** sp) {
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   DCHECK(a->IsClass()) << PrettyClass(a);
   DCHECK(b->IsClass()) << PrettyClass(b);
   if (b->IsAssignableFrom(a)) {
@@ -519,7 +492,9 @@
 
 // Tests whether 'element' can be assigned into an array of type 'array_class'.
 // Returns 0 on success and -1 if an exception is pending.
-extern "C" int artCanPutArrayElementFromCode(const Object* element, const Class* array_class) {
+extern "C" int artCanPutArrayElementFromCode(const Object* element, const Class* array_class,
+                                             Thread* self, Method** sp) {
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   DCHECK(array_class != NULL);
   // element can't be NULL as we catch this is screened in runtime_support
   Class* element_class = element->GetClass();
@@ -535,20 +510,56 @@
   }
 }
 
-extern "C" int artUnlockObjectFromCode(Thread* thread, Object* obj) {
-  DCHECK(obj != NULL);  // Assumed to have been checked before entry
-  return obj->MonitorExit(thread) ? 0 /* Success */ : -1 /* Failure */;
+Class* InitializeStaticStorage(uint32_t type_idx, const Method* referrer, Thread* self) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Class* klass = class_linker->ResolveType(type_idx, referrer);
+  if (klass == NULL) {
+    CHECK(self->IsExceptionPending());
+    return NULL;  // Failure - Indicate to caller to deliver exception
+  }
+  // If we are the <clinit> of this class, just return our storage.
+  //
+  // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
+  // running.
+  if (klass == referrer->GetDeclaringClass() && referrer->IsClassInitializer()) {
+    return klass;
+  }
+  if (!class_linker->EnsureInitialized(klass, true)) {
+    CHECK(self->IsExceptionPending());
+    return NULL;  // Failure - Indicate to caller to deliver exception
+  }
+  referrer->GetDexCacheInitializedStaticStorage()->Set(type_idx, klass);
+  return klass;
 }
 
-void LockObjectFromCode(Thread* thread, Object* obj) {
+extern "C" Class* artInitializeStaticStorageFromCode(uint32_t type_idx, const Method* referrer,
+                                                     Thread* self, Method** sp) {
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+  return InitializeStaticStorage(type_idx, referrer, self);
+}
+
+extern "C" int artUnlockObjectFromCode(Object* obj, Thread* self, Method** sp) {
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   DCHECK(obj != NULL);  // Assumed to have been checked before entry
-  obj->MonitorEnter(thread);
+  // MonitorExit may throw exception
+  return obj->MonitorExit(self) ? 0 /* Success */ : -1 /* Failure */;
+}
+
+extern "C" void artLockObjectFromCode(Object* obj, Thread* thread, Method** sp) {
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsOnly);
+  DCHECK(obj != NULL);        // Assumed to have been checked before entry
+  obj->MonitorEnter(thread);  // May block
   DCHECK(thread->HoldsLock(obj));
   // Only possible exception is NPE and is handled before entry
   DCHECK(!thread->IsExceptionPending());
 }
 
-extern "C" void artCheckSuspendFromCode(Thread* thread) {
+extern "C" void artCheckSuspendFromJni(Thread* thread) {
+  Runtime::Current()->GetThreadList()->FullSuspendCheck(thread);
+}
+
+extern "C" void artCheckSuspendFromCode(Thread* thread, Method** sp) {
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsOnly);
   Runtime::Current()->GetThreadList()->FullSuspendCheck(thread);
 }
 
@@ -567,7 +578,9 @@
  *  ubyte  data[size*width] table of data values (may contain a single-byte
  *                          padding at the end)
  */
-extern "C" int artHandleFillArrayDataFromCode(Array* array, const uint16_t* table) {
+extern "C" int artHandleFillArrayDataFromCode(Array* array, const uint16_t* table,
+                                              Thread* self, Method** sp) {
+  FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
   DCHECK_EQ(table[0], 0x0300);
   if (array == NULL) {
     Thread::Current()->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
@@ -590,14 +603,17 @@
 // See comments in runtime_support_asm.S
 extern "C" uint64_t artFindInterfaceMethodInCacheFromCode(uint32_t method_idx,
                                                           Object* this_object ,
-                                                          Method* caller_method) {
-  Thread* thread = Thread::Current();
+                                                          Thread* thread, Method** sp) {
+  FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsAndArgs);
   if (this_object == NULL) {
     thread->ThrowNewExceptionF("Ljava/lang/NullPointerException;",
         "null receiver during interface dispatch");
     return 0;
   }
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Frame frame = thread->GetTopOfStack();  // Compute calling method
+  frame.Next();
+  Method* caller_method = frame.GetMethod();
   Method* interface_method = class_linker->ResolveMethod(method_idx, caller_method, false);
   if (interface_method == NULL) {
     // Could not resolve interface method. Throw error and unwind
diff --git a/src/runtime_support.h b/src/runtime_support.h
index d47f25b..0a70c61 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -18,7 +18,7 @@
 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);
-extern "C" void artCheckSuspendFromCode(Thread* thread);
+extern "C" void artCheckSuspendFromJni(Thread* thread);
 extern int64_t D2L(double d);
 extern int64_t F2L(float f);
 
@@ -36,7 +36,7 @@
   extern "C" void art_check_cast_from_code(void*, void*);
   extern "C" void art_handle_fill_data_from_code(void*, void*);
   extern "C" void* art_initialize_static_storage_from_code(uint32_t, void*);
-  extern "C" void art_invoke_interface_trampoline(void*, void*, void*, void*);
+  extern "C" void art_invoke_interface_trampoline(uint32_t, void*);
   extern "C" void art_test_suspend();
   extern "C" void art_throw_array_bounds_from_code(int32_t index, int32_t limit);
   extern "C" void art_throw_div_zero_from_code();
@@ -45,7 +45,8 @@
   extern "C" void art_throw_null_pointer_exception_from_code();
   extern "C" void art_throw_stack_overflow_from_code(void*);
   extern "C" void art_throw_verification_error_from_code(int32_t src1, int32_t ref);
-  extern "C" void art_unlock_object_from_code(void*, void*);
+  extern "C" void art_lock_object_from_code(void*);
+  extern "C" void art_unlock_object_from_code(void*);
 
   /* Conversions */
   extern "C" float __aeabi_i2f(int op1);             // OP_INT_TO_FLOAT
diff --git a/src/runtime_support_asm.S b/src/runtime_support_asm.S
index 0e73d83..5b70fb7 100644
--- a/src/runtime_support_asm.S
+++ b/src/runtime_support_asm.S
@@ -9,11 +9,47 @@
     /* Deliver an exception pending on a thread */
     .extern artDeliverPendingException
 
-    /* Macro that sets up the callee save frame to conform with Runtime::CreateCalleeSaveMethod */
+    /*
+     * Macro that sets up the callee save frame to conform with
+     * Runtime::CreateCalleeSaveMethod(kSaveAll)
+     */
 .macro SETUP_CALLEE_SAVE_FRAME
-    push {r1-r11, lr}
+    push {r4-r11, lr} @ 9 words of callee saves
     vpush {s0-s31}
-    sub sp, #16  @ 4 words of space, bottom word will hold Method*
+    sub sp, #12       @ 3 words of space, bottom word will hold Method*
+.endm
+
+    /*
+     * Macro that sets up the callee save frame to conform with
+     * Runtime::CreateCalleeSaveMethod(kRefsOnly)
+     */
+.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+    push {r5-r8, r10-r11, lr} @ 7 words of callee saves
+    sub sp, #4                @ bottom word will hold Method*
+.endm
+
+.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    add sp, #4
+    pop {r5-r8, r10-r11, lr}  @ 7 words of callee saves
+.endm
+
+.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+    add sp, #4
+    pop {r5-r8, r10-r11, pc}  @ 7 words of callee saves
+.endm
+
+    /*
+     * Macro that sets up the callee save frame to conform with
+     * Runtime::CreateCalleeSaveMethod(kRefsAndArgs)
+     */
+.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves
+    sub sp, #8                       @ 2 words of space, bottom word will hold Method*
+.endm
+
+.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    add sp, #8
+    pop {r1-r3, r5-r8, r10-r11, lr}  @ 10 words of callee saves
 .endm
 
     .global art_deliver_exception_from_code
@@ -112,20 +148,18 @@
      * pointing back to the original caller.
      */
 art_invoke_interface_trampoline:
-    str    sp, [R9, #THREAD_TOP_OF_MANAGED_STACK_OFFSET]    @ record top of stack and pc in case of
-    str    lr, [R9, #THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET] @ walking stack
-    stmdb  sp!, {r1, r2, r3, lr}
-    ldr    r2, [sp, #16]                         @ load caller's Method*
-    bl     artFindInterfaceMethodInCacheFromCode @ (method_idx, this, callerMethod)
-    mov    r12, r1                               @ save r0->code_
-    ldmia  sp!, {r1, r2, r3, lr}                 @ restore arguments
-    cmp    r0, #0                                @ did we find the target?
-    bxne   r12                                   @ tail call to target if so
-                                                 @ set up for throwing exception
-    SETUP_CALLEE_SAVE_FRAME
-    mov    r0, r9                                @ pass Thread::Current
-    mov    r1, sp                                @ pass SP
-    b      artDeliverPendingExceptionFromCode    @ artDeliverPendingExceptionFromCode(Thread*, SP)
+    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME  @ save callee saves in case allocation triggers GC
+    mov    r2, r9                                 @ pass Thread::Current
+    mov    r3, sp                                 @ pass SP
+    bl     artFindInterfaceMethodInCacheFromCode  @ (method_idx, this, Thread*, SP)
+    mov    r12, r1                                @ save r0->code_
+    RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+    cmp    r0, #0                                 @ did we find the target?
+    bxne   r12                                    @ tail call to target if so
+    SETUP_CALLEE_SAVE_FRAME                       @ set up for throwing exception
+    mov    r0, r9                                 @ pass Thread::Current
+    mov    r1, sp                                 @ pass SP
+    b      artDeliverPendingExceptionFromCode     @ artDeliverPendingExceptionFromCode(Thread*, SP)
 
     .global art_handle_fill_data_from_code
     .extern artHandleFillArrayDataFromCode
@@ -134,20 +168,29 @@
      * failure.
      */
 art_handle_fill_data_from_code:
-    str    sp, [R9, #THREAD_TOP_OF_MANAGED_STACK_OFFSET]    @ record top of stack and pc in case of
-    str    lr, [R9, #THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET] @ walking stack
-    stmdb  sp!, {lr}                          @ Save LR
-    sub    sp, #12                            @ Align stack
-    bl     artHandleFillArrayDataFromCode     @ (Array* array, const uint16_t* table)
-    add    sp, #12
-    ldmia  sp!, {lr}                          @ restore LR
-    cmp    r0, #0                             @ success?
-    moveq  pc, lr                             @ return on success
-                                              @ set up for throwing exception
-    SETUP_CALLEE_SAVE_FRAME
-    mov    r0, r9                             @ pass Thread::Current
-    mov    r1, sp                             @ pass SP
-    b      artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*, SP)
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case exception allocation triggers GC
+    mov    r2, r9                          @ pass Thread::Current
+    mov    r3, sp                          @ pass SP
+    bl     artHandleFillArrayDataFromCode  @ (Array* array, const uint16_t* table, Thread*, SP)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    cmp    r0, #0                          @ success?
+    moveq  pc, lr                          @ return on success
+    SETUP_CALLEE_SAVE_FRAME                @ set up for throwing exception
+    mov    r0, r9                          @ pass Thread::Current
+    mov    r1, sp                          @ pass SP
+    b      artDeliverPendingExceptionFromCode  @ artDeliverPendingExceptionFromCode(Thread*, SP)
+
+    .global art_lock_object_from_code
+    .extern artLockObjectFromCode
+    /*
+     * Entry from managed code that calls artLockObjectFromCode, may block for GC
+     */
+art_lock_object_from_code:
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case we block
+    mov    r1, r9                     @ pass Thread::Current
+    mov    r2, sp                     @ pass SP
+    bl     artLockObjectFromCode      @ (Object* obj, Thread*, SP)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
 
     .global art_unlock_object_from_code
     .extern artUnlockObjectFromCode
@@ -155,19 +198,16 @@
      * Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
      */
 art_unlock_object_from_code:
-    str    sp, [R9, #THREAD_TOP_OF_MANAGED_STACK_OFFSET]    @ record top of stack and pc in case of
-    str    lr, [R9, #THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET] @ walking stack
-    stmdb  sp!, {lr}                          @ Save LR
-    sub    sp, #12                            @ Align stack
-    bl     artUnlockObjectFromCode            @ (Thread* thread, Object* obj)
-    add    sp, #12
-    ldmia  sp!, {lr}                          @ restore LR
-    cmp    r0, #0                             @ success?
-    moveq  pc, lr                             @ return on success
-                                              @ set up for throwing exception
-    SETUP_CALLEE_SAVE_FRAME
-    mov    r0, r9                             @ pass Thread::Current
-    mov    r1, sp                             @ pass SP
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case exception allocation triggers GC
+    mov    r1, r9                   @ pass Thread::Current
+    mov    r2, sp                   @ pass SP
+    bl     artUnlockObjectFromCode  @ (Object* obj, Thread*, SP)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    cmp    r0, #0                   @ success?
+    moveq  pc, lr                   @ return on success
+    SETUP_CALLEE_SAVE_FRAME         @ set up for throwing exception
+    mov    r0, r9                   @ pass Thread::Current
+    mov    r1, sp                   @ pass SP
     b      artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*, SP)
 
     .global art_check_cast_from_code
@@ -176,20 +216,17 @@
      * Entry from managed code that calls artCheckCastFromCode and delivers exception on failure.
      */
 art_check_cast_from_code:
-    str    sp, [R9, #THREAD_TOP_OF_MANAGED_STACK_OFFSET]    @ record top of stack and pc in case of
-    str    lr, [R9, #THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET] @ walking stack
-    stmdb  sp!, {lr}                          @ Save LR
-    sub    sp, #12                            @ Align stack
-    bl     artCheckCastFromCode               @ (Class* a, Class* b)
-    add    sp, #12
-    ldmia  sp!, {lr}                          @ restore LR
-    cmp    r0, #0                             @ success?
-    moveq  pc, lr                             @ return on success
-                                              @ set up for throwing exception
-    SETUP_CALLEE_SAVE_FRAME
-    mov    r0, r9                             @ pass Thread::Current
-    mov    r1, sp                             @ pass SP
-    b      artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*, SP)
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME    @ save callee saves in case exception allocation triggers GC
+    mov    r2, r9                       @ pass Thread::Current
+    mov    r3, sp                       @ pass SP
+    bl     artCheckCastFromCode         @ (Class* a, Class* b, Thread*, SP)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    cmp    r0, #0                       @ success?
+    moveq  pc, lr                       @ return on success
+    SETUP_CALLEE_SAVE_FRAME             @ set up for throwing exception
+    mov    r0, r9                       @ pass Thread::Current
+    mov    r1, sp                       @ pass SP
+    b      artDeliverPendingExceptionFromCode  @ artDeliverPendingExceptionFromCode(Thread*, SP)
 
     .global art_can_put_array_element_from_code
     .extern artCanPutArrayElementFromCode
@@ -198,46 +235,40 @@
      * failure.
      */
 art_can_put_array_element_from_code:
-    cmp    r0, #0                             @ return if element == NULL
+    cmp    r0, #0                         @ return if element == NULL
     moveq  pc, lr
-    str    sp, [R9, #THREAD_TOP_OF_MANAGED_STACK_OFFSET]    @ record top of stack and pc in case of
-    str    lr, [R9, #THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET] @ walking stack
-    stmdb  sp!, {lr}                          @ Save LR
-    sub    sp, #12                            @ Align stack
-    bl     artCanPutArrayElementFromCode      @ (Object* element, Class* array_class)
-    add    sp, #12
-    ldmia  sp!, {lr}                          @ restore LR
-    cmp    r0, #0                             @ success?
-    moveq  pc, lr                             @ return on success
-                                              @ set up for throwing exception
-    SETUP_CALLEE_SAVE_FRAME
-    mov    r0, r9                             @ pass Thread::Current
-    mov    r1, sp                             @ pass SP
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME    @ save callee saves in case exception allocation triggers GC
+    mov    r2, r9                         @ pass Thread::Current
+    mov    r3, sp                         @ pass SP
+    bl     artCanPutArrayElementFromCode  @ (Object* element, Class* array_class, Thread*, SP)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    cmp    r0, #0                         @ success?
+    moveq  pc, lr                         @ return on success
+    SETUP_CALLEE_SAVE_FRAME               @ set up for throwing exception
+    mov    r0, r9                         @ pass Thread::Current
+    mov    r1, sp                         @ pass SP
     b      artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*, SP)
 
     .global art_initialize_static_storage_from_code
-    .extern _ZN3art11ClassLinker31InitializeStaticStorageFromCodeEjPKNS_6MethodE
+    .extern artInitializeStaticStorageFromCode
     /*
      * Entry from managed code when uninitialized static storage, this stub will run the class
      * initializer and deliver the exception on error. On success the static storage base is
      * returned.
      */
 art_initialize_static_storage_from_code:
-    str    sp, [R9, #THREAD_TOP_OF_MANAGED_STACK_OFFSET]    @ record top of stack and pc in case of
-    str    lr, [R9, #THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET] @ walking stack
-    stmdb  sp!, {lr}                          @ Save LR
-    sub    sp, #12                            @ Align stack
-    @ ClassLinker::InitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer)
-    bl     _ZN3art11ClassLinker31InitializeStaticStorageFromCodeEjPKNS_6MethodE
-    add    sp, #12
-    ldmia  sp!, {lr}                          @ restore LR
-    cmp    r0, #0                             @ success if result is non-null
-    movne  pc, lr                             @ return on success
-                                              @ set up for throwing exception
-    SETUP_CALLEE_SAVE_FRAME
-    mov    r0, r9                             @ pass Thread::Current
-    mov    r1, sp                             @ pass SP
-    b      artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*, SP)
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME           @ save callee saves in case of GC
+    mov    r2, r9                              @ pass Thread::Current
+    mov    r3, sp                              @ pass SP
+    @ ClassLinker::InitializeStaticStorageFromCode(uint32_t type_idx, Method* referrer, Thread*, SP)
+    bl     artInitializeStaticStorageFromCode
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    cmp    r0, #0                              @ success if result is non-null
+    movne  pc, lr                              @ return on success
+    SETUP_CALLEE_SAVE_FRAME                    @ set up for throwing exception
+    mov    r0, r9                              @ pass Thread::Current
+    mov    r1, sp                              @ pass SP
+    b      artDeliverPendingExceptionFromCode  @ artDeliverPendingExceptionFromCode(Thread*, SP)
 
     .global art_alloc_object_from_code
     .extern artAllocObjectFromCode
@@ -245,19 +276,16 @@
      * Called by managed code to allocate an object
      */
 art_alloc_object_from_code:
-    SETUP_CALLEE_SAVE_FRAME
-    mov    r2, r9                  @ pass Thread::Current
-    mov    r3, sp                  @ pass SP
-    bl     artAllocObjectFromCode  @ (uint32_t type_idx, Method* method, Thread*, SP)
-    add    sp, #16
-    vpop   {s0-s31}
-    pop    {r1-r11, lr}
-    cmp    r0, #0                  @ success if result is non-null
-    movne  pc, lr                  @ return on success
-                                   @ set up for throwing exception
-    SETUP_CALLEE_SAVE_FRAME
-    mov    r0, r9                  @ pass Thread::Current
-    mov    r1, sp                  @ pass SP
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case of GC
+    mov    r2, r9                     @ pass Thread::Current
+    mov    r3, sp                     @ pass SP
+    bl     artAllocObjectFromCode     @ (uint32_t type_idx, Method* method, Thread*, SP)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    cmp    r0, #0                     @ success if result is non-null
+    movne  pc, lr                     @ return on success
+    SETUP_CALLEE_SAVE_FRAME           @ set up for throwing exception
+    mov    r0, r9                     @ pass Thread::Current
+    mov    r1, sp                     @ pass SP
     b      artDeliverPendingExceptionFromCode  @ artDeliverPendingExceptionFromCode(Thread*, SP)
 
     .global art_alloc_array_from_code
@@ -266,19 +294,17 @@
      * Called by managed code to allocate an array
      */
 art_alloc_array_from_code:
-    SETUP_CALLEE_SAVE_FRAME
-    mov    r3, r9                 @ pass Thread::Current
-    str    sp, [sp, #0]           @ pass SP
-    bl     artAllocArrayFromCode  @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP)
-    add    sp, #16
-    vpop   {s0-s31}
-    pop    {r1-r11, lr}
-    cmp    r0, #0                 @ success if result is non-null
-    movne  pc, lr                 @ return on success
-                                  @ set up for throwing exception
-    SETUP_CALLEE_SAVE_FRAME
-    mov    r0, r9                 @ pass Thread::Current
-    mov    r1, sp                 @ pass SP
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case of GC
+    mov    r3, r9                     @ pass Thread::Current
+    str    sp, [sp, #0]               @ pass SP
+    @ artAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP)
+    bl     artAllocArrayFromCode
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+    cmp    r0, #0                     @ success if result is non-null
+    movne  pc, lr                     @ return on success
+    SETUP_CALLEE_SAVE_FRAME           @ set up for throwing exception
+    mov    r0, r9                     @ pass Thread::Current
+    mov    r1, sp                     @ pass SP
     b      artDeliverPendingExceptionFromCode  @ artDeliverPendingExceptionFromCode(Thread*, SP)
 
     .global art_check_and_alloc_array_from_code
@@ -287,22 +313,32 @@
      * Called by managed code to allocate an array
      */
 art_check_and_alloc_array_from_code:
-    str    sp, [R9, #THREAD_TOP_OF_MANAGED_STACK_OFFSET]    @ record top of stack and pc in case of
-    str    lr, [R9, #THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET] @ walking stack
-    stmdb  sp!, {lr}                      @ Save LR
-    sub    sp, #12                        @ Align stack
-    bl     artCheckAndAllocArrayFromCode  @ (uint32_t type_idx, Method* method, int32_t count)
-    add    sp, #12
-    ldmia  sp!, {lr}                      @ restore LR
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME  @ save callee saves in case of GC
+    mov    r3, r9                     @ pass Thread::Current
+    str    sp, [sp, #0]               @ pass SP
+    @ artCheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t count, Thread* , SP)
+    bl     artCheckAndAllocArrayFromCode
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
     cmp    r0, #0                         @ success if result is non-null
     movne  pc, lr                         @ return on success
-                                          @ set up for throwing exception
-    stmdb  sp!, {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr}
-    sub    sp, #16                        @ 4 words of space, bottom word will hold Method*
+    SETUP_CALLEE_SAVE_FRAME               @ set up for throwing exception
     mov    r0, r9                         @ pass Thread::Current
     mov    r1, sp                         @ pass SP
     b      artDeliverPendingExceptionFromCode  @ artDeliverPendingExceptionFromCode(Thread*, SP)
 
+    .global art_test_suspend
+    .extern artCheckSuspendFromCode
+art_test_suspend:
+    ldr    r0, [rSELF, #THREAD_SUSPEND_COUNT_OFFSET]
+    mov    rSUSPEND, #SUSPEND_CHECK_INTERVAL  @ reset rSUSPEND to SUSPEND_CHECK_INTERVAL
+    cmp    r0, #0                             @ check Thread::Current()->suspend_count_ == 0
+    bxeq   rLR                                @ return if suspend_count_ == 0
+    mov    r0, rSELF
+    mov    r1, sp
+    SETUP_REF_ONLY_CALLEE_SAVE_FRAME          @ save callee saves for stack crawl
+    bl     artCheckSuspendFromCode            @ (Thread*, SP)
+    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+
     .global art_shl_long
 art_shl_long:
     /*
@@ -371,23 +407,6 @@
     mov     r1, r1, lsr r2              @  r1<- r1 >>> r2
     bx      lr
 
-    .balign 4
-    .global art_test_suspend
-    .extern artCheckSuspendFromCode
-art_test_suspend:
-    /*
-     * Check to see if there's a pending suspend request on our thread.
-     * reset rSUSPEND to SUSPEND_CHECK_INTERVAL.
-     * On entry, rSUSPEND holds the suspend request value
-     * [TUNING: move load of suspend check value into this stub.
-     */
-    cmp    rSUSPEND, #0
-    mov    rSUSPEND, #SUSPEND_CHECK_INTERVAL
-    bxeq   rLR
-    mov    r0, rSELF
-    b      artCheckSuspendFromCode
-
-
 #endif
 
 #if defined(__i386__)
diff --git a/src/space.cc b/src/space.cc
index d498b8b..248f7e8 100644
--- a/src/space.cc
+++ b/src/space.cc
@@ -97,7 +97,7 @@
 
 
 bool Space::InitFromImage(const std::string& image_file_name) {
-  const Runtime* runtime = Runtime::Current();
+  Runtime* runtime = Runtime::Current();
   if (runtime->IsVerboseStartup()) {
     LOG(INFO) << "Space::InitFromImage entering"
               << " image_file_name=" << image_file_name;
@@ -129,23 +129,27 @@
   DCHECK_EQ(0, memcmp(&image_header, image_header_, sizeof(ImageHeader)));
 
   Object* jni_stub_array = image_header.GetImageRoot(ImageHeader::kJniStubArray);
-  Runtime::Current()->SetJniStubArray(down_cast<ByteArray*>(jni_stub_array));
+  runtime->SetJniStubArray(down_cast<ByteArray*>(jni_stub_array));
 
   Object* ame_stub_array = image_header.GetImageRoot(ImageHeader::kAbstractMethodErrorStubArray);
-  Runtime::Current()->SetAbstractMethodErrorStubArray(down_cast<ByteArray*>(ame_stub_array));
+  runtime->SetAbstractMethodErrorStubArray(down_cast<ByteArray*>(ame_stub_array));
 
   Object* resolution_stub_array = image_header.GetImageRoot(ImageHeader::kInstanceResolutionStubArray);
-  Runtime::Current()->SetResolutionStubArray(
+  runtime->SetResolutionStubArray(
       down_cast<ByteArray*>(resolution_stub_array), Runtime::kInstanceMethod);
   resolution_stub_array = image_header.GetImageRoot(ImageHeader::kStaticResolutionStubArray);
-  Runtime::Current()->SetResolutionStubArray(
+  runtime->SetResolutionStubArray(
       down_cast<ByteArray*>(resolution_stub_array), Runtime::kStaticMethod);
   resolution_stub_array = image_header.GetImageRoot(ImageHeader::kUnknownMethodResolutionStubArray);
-  Runtime::Current()->SetResolutionStubArray(
+  runtime->SetResolutionStubArray(
       down_cast<ByteArray*>(resolution_stub_array), Runtime::kUnknownMethod);
 
   Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod);
-  Runtime::Current()->SetCalleeSaveMethod(down_cast<Method*>(callee_save_method));
+  runtime->SetCalleeSaveMethod(down_cast<Method*>(callee_save_method), Runtime::kSaveAll);
+  callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsOnlySaveMethod);
+  runtime->SetCalleeSaveMethod(down_cast<Method*>(callee_save_method), Runtime::kRefsOnly);
+  callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsAndArgsSaveMethod);
+  runtime->SetCalleeSaveMethod(down_cast<Method*>(callee_save_method), Runtime::kRefsAndArgs);
 
   Init(map.release());
   if (runtime->IsVerboseStartup()) {
diff --git a/src/stub_arm.cc b/src/stub_arm.cc
index aeee8c8..bb36415 100644
--- a/src/stub_arm.cc
+++ b/src/stub_arm.cc
@@ -11,8 +11,6 @@
 
 ByteArray* ArmCreateResolutionTrampoline(Runtime::TrampolineType type) {
   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
@@ -20,6 +18,7 @@
   // | R2       |    possible argument
   // | R1       |    possible argument
   // | R0       |    method index (loaded from code and method array - will be converted to Method*)
+  RegList save = (1 << R0) | (1 << R1) | (1 << R2) | (1 << R3) | (1 << LR);
   __ PushList(save);
   __ mov(R1, ShifterOperand(SP));  // Pass address of saved R0... in R1
   __ LoadFromOffset(kLoadWord, R12, TR,
@@ -29,13 +28,11 @@
   __ 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));
+  __ mov(R12, ShifterOperand(R0));  // Save code address returned into R12
   // 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));
+  __ mov(PC, ShifterOperand(R12));  // Leaf call to method's code
 
   __ bkpt(0);
 
@@ -53,21 +50,19 @@
 
 ByteArray* CreateAbstractMethodErrorStub() {
   UniquePtr<ArmAssembler> assembler( static_cast<ArmAssembler*>(Assembler::Create(kArm)) );
-
   // Save callee saves and ready frame for exception delivery
-  RegList save = (1 << R1) | (1 << R2) | (1 << R3) | (1 << R4) | (1 << R5) | (1 << R6) | (1 << R7) |
-                 (1 << R8) | (1 << R9) | (1 << R10) | (1 << R11) | (1 << LR);
-  __ PushList(save);
-  __ Emit(0xed2d0a20);  // vpush {s0-s31}
-  __ IncreaseFrameSize(16);  // 4 words of space, bottom word will hold callee save Method*
+  RegList save = (1 << R4) | (1 << R5) | (1 << R6) | (1 << R7) | (1 << R8) | (1 << R9) |
+                 (1 << R10) | (1 << R11) | (1 << LR);
+  __ PushList(save);         // push {r4-r11, lr} - 9 words of callee saves
+  __ Emit(0xed2d0a20);       // vpush {s0-s31}
+  __ IncreaseFrameSize(12);  // 3 words of space, bottom word will hold callee save Method*
 
   // R0 is the Method* already
   __ mov(R1, ShifterOperand(R9));  // Pass Thread::Current() in R1
   __ mov(R2, ShifterOperand(SP));  // Pass SP in R2
   // Call to throw AbstractMethodError
   __ LoadFromOffset(kLoadWord, R12, TR, OFFSETOF_MEMBER(Thread, pThrowAbstractMethodErrorFromCode));
-  // Leaf call to routine that never returns
-  __ mov(PC, ShifterOperand(R12));
+  __ mov(PC, ShifterOperand(R12));  // Leaf call to routine that never returns
 
   __ bkpt(0);
 
@@ -85,35 +80,20 @@
 
 ByteArray* CreateJniStub() {
   UniquePtr<ArmAssembler> assembler( static_cast<ArmAssembler*>(Assembler::Create(kArm)) );
-
+  // Build frame and save argument registers and LR.
   RegList save = (1 << R0) | (1 << R1) | (1 << R2) | (1 << R3) | (1 << LR);
-
-  // Build frame and save registers. Save 5 registers.
   __ PushList(save);
-  // Ensure 16-byte alignment
-  __ AddConstant(SP, -12);
-
-  // Pass Thread::Current() in R0
-  __ mov(R0, ShifterOperand(R9));
-
+  __ AddConstant(SP, -12);         // Ensure 16-byte alignment
+  __ mov(R0, ShifterOperand(R9));  // Pass Thread::Current() in R0
   // Call FindNativeMethod
   __ LoadFromOffset(kLoadWord, R12, TR, OFFSETOF_MEMBER(Thread, pFindNativeMethod));
   __ blx(R12);
-
-  // Save result of FindNativeMethod in R12
-  __ mov(R12, ShifterOperand(R0));
-
-  // Restore registers (including outgoing arguments)
-  __ AddConstant(SP, 12);
+  __ mov(R12, ShifterOperand(R0));  // Save result of FindNativeMethod in R12
+  __ AddConstant(SP, 12);  // Restore registers (including outgoing arguments)
   __ PopList(save);
-
   __ cmp(R12, ShifterOperand(0));
-
-  // If R12 != 0 tail call into native code
-  __ mov(PC, ShifterOperand(R12), NE);
-
-  // Return to caller to handle exception
-  __ mov(PC, ShifterOperand(LR));
+  __ mov(PC, ShifterOperand(R12), NE);  // If R12 != 0 tail call into native code
+  __ mov(PC, ShifterOperand(LR));  // Return to caller to handle exception
 
   assembler->EmitSlowPaths();
 
diff --git a/src/thread.cc b/src/thread.cc
index e264600..532b581 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -121,6 +121,7 @@
   pThrowNullPointerFromCode = art_throw_null_pointer_exception_from_code;
   pThrowStackOverflowFromCode = art_throw_stack_overflow_from_code;
   pThrowVerificationErrorFromCode = art_throw_verification_error_from_code;
+  pLockObjectFromCode = art_lock_object_from_code;
   pUnlockObjectFromCode = art_unlock_object_from_code;
 #endif
   pDeliverException = art_deliver_exception_from_code;
@@ -138,9 +139,8 @@
   pInitializeTypeFromCode = InitializeTypeFromCode;
   pResolveMethodFromCode = ResolveMethodFromCode;
   pInstanceofNonTrivialFromCode = Class::IsAssignableFromCode;
-  pLockObjectFromCode = LockObjectFromCode;
   pFindInstanceFieldFromCode = Field::FindInstanceFieldFromCode;
-  pCheckSuspendFromCode = artCheckSuspendFromCode;
+  pCheckSuspendFromCode = artCheckSuspendFromJni;
   pFindNativeMethod = FindNativeMethod;
   pDecodeJObjectInThread = DecodeJObjectInThread;
   pResolveStringFromCode = ResolveStringFromCode;
diff --git a/src/thread.h b/src/thread.h
index c898a76..c66e77e 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -131,13 +131,13 @@
   uint32_t (*pInstanceofNonTrivialFromCode) (const Class*, const Class*);
   void (*pCheckCastFromCode) (void*, void*);
   Method* (*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, struct DvmDex*);
-  void (*pUnlockObjectFromCode)(void*, void*);
-  void (*pLockObjectFromCode)(Thread*, Object*);
+  void (*pUnlockObjectFromCode)(void*);
+  void (*pLockObjectFromCode)(void*);
   void (*pDeliverException)(void*);
   void (*pHandleFillArrayDataFromCode)(void*, void*);
   Class* (*pInitializeTypeFromCode)(uint32_t, Method*);
   void (*pResolveMethodFromCode)(Method*, uint32_t);
-  void (*pInvokeInterfaceTrampoline)(void*, void*, void*, void*);
+  void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
   void* (*pInitializeStaticStorage)(uint32_t, void*);
   Field* (*pFindInstanceFieldFromCode)(uint32_t, const Method*);
   void (*pCheckSuspendFromCode)(Thread*);
diff --git a/src/thread_arm.cc b/src/thread_arm.cc
index 52f1b20..a7efc1a 100644
--- a/src/thread_arm.cc
+++ b/src/thread_arm.cc
@@ -8,9 +8,7 @@
 namespace art {
 
 void Thread::InitCpu() {
-  CHECK_EQ(THREAD_TOP_OF_MANAGED_STACK_OFFSET, OFFSETOF_MEMBER(Thread, top_of_managed_stack_));
-  CHECK_EQ(THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET,
-           OFFSETOF_MEMBER(Thread, top_of_managed_stack_pc_));
+  CHECK_EQ(THREAD_SUSPEND_COUNT_OFFSET, OFFSETOF_MEMBER(Thread, suspend_count_));
 }
 
 }  // namespace art